diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 15ad2a8c1..6281acb67 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -350,9 +350,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "CogAudio" */; compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* CogAudio */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -405,6 +411,7 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -421,6 +428,7 @@ INSTALL_PATH = "@executable_path/../Frameworks"; OTHER_LDFLAGS = ""; PRODUCT_NAME = CogAudio; + SDKROOT = macosx10.6; WARNING_LDFLAGS = ""; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -431,8 +439,8 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = ( - ppc, i386, + ppc, ); DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -447,6 +455,7 @@ INSTALL_PATH = "@executable_path/../Frameworks"; OTHER_LDFLAGS = ""; PRODUCT_NAME = CogAudio; + SDKROOT = macosx10.6; WARNING_LDFLAGS = ""; WRAPPER_EXTENSION = framework; }; diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 161419cf4..6ed44b558 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -87,7 +87,6 @@ 17A8F6860D6A7FCA0095DA13 /* repeat_none.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A8F6830D6A7FCA0095DA13 /* repeat_none.png */; }; 17A8F6870D6A7FCA0095DA13 /* repeat_one.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A8F6840D6A7FCA0095DA13 /* repeat_one.png */; }; 17A8F71A0D6A89730095DA13 /* repeat_album.png in Resources */ = {isa = PBXBuildFile; fileRef = 17A8F7190D6A89730095DA13 /* repeat_album.png */; }; - 17B6FA7F0D48225300C3BEF1 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17BF2B270CDD77EB007E1295 /* Sparkle.framework */; }; 17B7CF5C0F5A05EE00A47027 /* pauseBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B7CF590F5A05EE00A47027 /* pauseBadge.png */; }; 17B7CF5D0F5A05EE00A47027 /* playBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B7CF5A0F5A05EE00A47027 /* playBadge.png */; }; 17B7CF5E0F5A05EE00A47027 /* stopBadge.png in Resources */ = {isa = PBXBuildFile; fileRef = 17B7CF5B0F5A05EE00A47027 /* stopBadge.png */; }; @@ -97,7 +96,6 @@ 17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */; }; 17BB5CFB0B8A86350009ACB1 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5CF80B8A86350009ACB1 /* CoreAudioKit.framework */; }; 17BB5EA60B8A87850009ACB1 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BB5EA50B8A87850009ACB1 /* IOKit.framework */; }; - 17BF2B280CDD7802007E1295 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17BF2B270CDD77EB007E1295 /* Sparkle.framework */; }; 17C809910C3BD201005707C4 /* WavPack.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808C80C3BD1DD005707C4 /* WavPack.bundle */; }; 17C809920C3BD206005707C4 /* Vorbis.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808BF0C3BD1D2005707C4 /* Vorbis.bundle */; }; 17C809930C3BD21D005707C4 /* TagLib.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 17C808B60C3BD1C5005707C4 /* TagLib.bundle */; }; @@ -193,20 +191,6 @@ remoteGlobalIDString = 8D5B49AC048680CD000E48DA; remoteInfo = "AudioOverload Plugin"; }; - 17BF2B260CDD77EB007E1295 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8DC2EF5B0486A6940098B216; - remoteInfo = Sparkle; - }; - 17BF2B390CDD7827007E1295 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = 8DC2EF4F0486A6940098B216; - remoteInfo = Sparkle; - }; 17C808780C3BD167005707C4 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 17C808710C3BD167005707C4 /* FileSource.xcodeproj */; @@ -518,7 +502,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 17B6FA7F0D48225300C3BEF1 /* Sparkle.framework in CopyFiles */, 17F561400C3BD4F30019975C /* CogAudio.framework in CopyFiles */, 170680840B950164006BA573 /* Growl.framework in CopyFiles */, ); @@ -661,7 +644,6 @@ 17BB5CF70B8A86350009ACB1 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = ""; }; 17BB5CF80B8A86350009ACB1 /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = /System/Library/Frameworks/CoreAudioKit.framework; sourceTree = ""; }; 17BB5EA50B8A87850009ACB1 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; - 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Sparkle.xcodeproj; path = Frameworks/Sparkle/Sparkle.xcodeproj; sourceTree = ""; }; 17C808660C3BD0F8005707C4 /* CoreAudio.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CoreAudio.xcodeproj; path = Plugins/CoreAudio/CoreAudio.xcodeproj; sourceTree = SOURCE_ROOT; }; 17C808710C3BD167005707C4 /* FileSource.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FileSource.xcodeproj; path = Plugins/FileSource/FileSource.xcodeproj; sourceTree = ""; }; 17C8087A0C3BD173005707C4 /* Flac.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Flac.xcodeproj; path = Plugins/Flac/Flac.xcodeproj; sourceTree = ""; }; @@ -778,7 +760,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 17BF2B280CDD7802007E1295 /* Sparkle.framework in Frameworks */, 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, 8E6889240AAA403C00AD3950 /* Carbon.framework in Frameworks */, 17BB5CED0B8A86010009ACB1 /* AudioToolbox.framework in Frameworks */, @@ -826,7 +807,6 @@ 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { isa = PBXGroup; children = ( - 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */, 17F5612A0C3BD4DC0019975C /* CogAudio.xcodeproj */, 170680620B950158006BA573 /* Growl.framework */, 8E6889230AAA403C00AD3950 /* Carbon.framework */, @@ -1036,14 +1016,6 @@ name = Products; sourceTree = ""; }; - 17BF2B200CDD77EB007E1295 /* Products */ = { - isa = PBXGroup; - children = ( - 17BF2B270CDD77EB007E1295 /* Sparkle.framework */, - ); - name = Products; - sourceTree = ""; - }; 17C808720C3BD167005707C4 /* Products */ = { isa = PBXGroup; children = ( @@ -1511,7 +1483,6 @@ 17F3BB8B0CBC566200864489 /* PBXTargetDependency */, 17C8F44C0CBEDD37008D969D /* PBXTargetDependency */, 17C8F7DA0CBEF3F9008D969D /* PBXTargetDependency */, - 17BF2B3A0CDD7827007E1295 /* PBXTargetDependency */, 17643CBC0D5BD44900F0A9FE /* PBXTargetDependency */, 17B7CF960F5A0A3700A47027 /* PBXTargetDependency */, ); @@ -1526,8 +1497,11 @@ /* Begin PBXProject section */ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Cog" */; compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, @@ -1610,10 +1584,6 @@ ProductGroup = 17C808A80C3BD1BA005707C4 /* Products */; ProjectRef = 17C808A70C3BD1BA005707C4 /* Shorten.xcodeproj */; }, - { - ProductGroup = 17BF2B200CDD77EB007E1295 /* Products */; - ProjectRef = 17BF2B1F0CDD77EB007E1295 /* Sparkle.xcodeproj */; - }, { ProductGroup = 17C808B10C3BD1C5005707C4 /* Products */; ProjectRef = 17C808B00C3BD1C5005707C4 /* TagLib.xcodeproj */; @@ -1646,13 +1616,6 @@ remoteRef = 17B7CF7B0F5A09FD00A47027 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 17BF2B270CDD77EB007E1295 /* Sparkle.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = Sparkle.framework; - remoteRef = 17BF2B260CDD77EB007E1295 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 17C808790C3BD167005707C4 /* FileSource.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -1958,11 +1921,6 @@ name = "AudioOverload Plugin"; targetProxy = 17B7CF950F5A0A3700A47027 /* PBXContainerItemProxy */; }; - 17BF2B3A0CDD7827007E1295 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = Sparkle; - targetProxy = 17BF2B390CDD7827007E1295 /* PBXContainerItemProxy */; - }; 17C8097E0C3BD1F5005707C4 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = Musepack; @@ -2166,9 +2124,11 @@ OTHER_LDFLAGS = ( "-weak_framework", CogAudio, + "-undefined", + dynamic_lookup, ); PRODUCT_NAME = Cog; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = app; ZERO_LINK = NO; }; @@ -2202,9 +2162,11 @@ OTHER_LDFLAGS = ( "-weak_framework", CogAudio, + "-undefined", + dynamic_lookup, ); PRODUCT_NAME = Cog; - SDKROOT = "$(DEVELOPER_SDK_DIR)/MacOSX10.5.sdk"; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = app; }; name = Release; diff --git a/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj b/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj index b8f8d6d70..9c5f74c9b 100644 --- a/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj +++ b/Frameworks/AudioOverload/AudioOverload.xcodeproj/project.pbxproj @@ -441,9 +441,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "AudioOverload" */; compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* AudioOverload */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -522,6 +528,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -545,6 +552,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; USE_HEADERMAP = NO; WRAPPER_EXTENSION = framework; }; @@ -554,6 +562,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -574,6 +586,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; USE_HEADERMAP = NO; WRAPPER_EXTENSION = framework; }; @@ -589,7 +602,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -602,7 +615,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c b/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c index 5192df6ae..5b73ab000 100644 --- a/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c +++ b/Frameworks/AudioOverload/aosdk/eng_psf/peops2/spu.c @@ -540,7 +540,7 @@ static void *MAINThread(int samp2run) lastch=ch; // lastns=ns; // changemeback - return; + return 0; } } diff --git a/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj b/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj index 64b70fc19..9a71db60c 100644 --- a/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj +++ b/Frameworks/Dumb/Dumb.xcodeproj/project.pbxproj @@ -9,7 +9,7 @@ /* Begin PBXBuildFile section */ 17C8F63E0CBEE797008D969D /* dumb.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60C0CBEE797008D969D /* dumb.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17C8F63F0CBEE797008D969D /* dumb.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60E0CBEE797008D969D /* dumb.h */; }; - 17C8F6400CBEE797008D969D /* it.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60F0CBEE797008D969D /* it.h */; }; + 17C8F6400CBEE797008D969D /* it.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F60F0CBEE797008D969D /* it.h */; settings = {ATTRIBUTES = (Public, ); }; }; 17C8F6410CBEE797008D969D /* atexit.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F6120CBEE797008D969D /* atexit.c */; }; 17C8F6420CBEE797008D969D /* duhlen.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F6130CBEE797008D969D /* duhlen.c */; }; 17C8F6430CBEE797008D969D /* duhtag.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F6140CBEE797008D969D /* duhtag.c */; }; @@ -49,6 +49,61 @@ 17C8F6680CBEE797008D969D /* readxm.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F63B0CBEE797008D969D /* readxm.c */; }; 17C8F6690CBEE797008D969D /* readxm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F63C0CBEE797008D969D /* readxm2.c */; }; 17C8F66A0CBEE797008D969D /* xmeffect.c in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F63D0CBEE797008D969D /* xmeffect.c */; }; + 8370B62617F60FE2001A4D7A /* barray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B61E17F60FE2001A4D7A /* barray.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 8370B62817F60FE2001A4D7A /* dumbfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62017F60FE2001A4D7A /* dumbfile.h */; }; + 8370B62A17F60FE2001A4D7A /* lpc.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62217F60FE2001A4D7A /* lpc.h */; }; + 8370B62B17F60FE2001A4D7A /* riff.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62317F60FE2001A4D7A /* riff.h */; }; + 8370B62C17F60FE2001A4D7A /* stack_alloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62417F60FE2001A4D7A /* stack_alloc.h */; }; + 8370B62D17F60FE2001A4D7A /* tarray.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B62517F60FE2001A4D7A /* tarray.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 8370B63417F61001001A4D7A /* barray.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B62E17F61001001A4D7A /* barray.c */; }; + 8370B63517F61001001A4D7A /* blip_buf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B62F17F61001001A4D7A /* blip_buf.c */; }; + 8370B63617F61001001A4D7A /* lanczos_resampler.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63017F61001001A4D7A /* lanczos_resampler.c */; }; + 8370B63717F61001001A4D7A /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63117F61001001A4D7A /* lpc.c */; }; + 8370B63817F61001001A4D7A /* riff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63217F61001001A4D7A /* riff.c */; }; + 8370B63917F61001001A4D7A /* tarray.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63317F61001001A4D7A /* tarray.c */; }; + 8370B66317F61038001A4D7A /* load669.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63A17F61038001A4D7A /* load669.c */; }; + 8370B66417F61038001A4D7A /* load6692.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63B17F61038001A4D7A /* load6692.c */; }; + 8370B66517F61038001A4D7A /* loadamf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63C17F61038001A4D7A /* loadamf.c */; }; + 8370B66617F61038001A4D7A /* loadamf2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63D17F61038001A4D7A /* loadamf2.c */; }; + 8370B66717F61038001A4D7A /* loadany.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63E17F61038001A4D7A /* loadany.c */; }; + 8370B66817F61038001A4D7A /* loadany2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B63F17F61038001A4D7A /* loadany2.c */; }; + 8370B66917F61038001A4D7A /* loadasy.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64017F61038001A4D7A /* loadasy.c */; }; + 8370B66A17F61038001A4D7A /* loadasy2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64117F61038001A4D7A /* loadasy2.c */; }; + 8370B66B17F61038001A4D7A /* loadmtm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64217F61038001A4D7A /* loadmtm.c */; }; + 8370B66C17F61038001A4D7A /* loadmtm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64317F61038001A4D7A /* loadmtm2.c */; }; + 8370B66D17F61038001A4D7A /* loadokt.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64417F61038001A4D7A /* loadokt.c */; }; + 8370B66E17F61038001A4D7A /* loadokt2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64517F61038001A4D7A /* loadokt2.c */; }; + 8370B66F17F61038001A4D7A /* loadoldpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64617F61038001A4D7A /* loadoldpsm.c */; }; + 8370B67017F61038001A4D7A /* loadoldpsm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64717F61038001A4D7A /* loadoldpsm2.c */; }; + 8370B67117F61038001A4D7A /* loadpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64817F61038001A4D7A /* loadpsm.c */; }; + 8370B67217F61038001A4D7A /* loadpsm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64917F61038001A4D7A /* loadpsm2.c */; }; + 8370B67317F61038001A4D7A /* loadptm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64A17F61038001A4D7A /* loadptm.c */; }; + 8370B67417F61038001A4D7A /* loadptm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64B17F61038001A4D7A /* loadptm2.c */; }; + 8370B67517F61038001A4D7A /* loadriff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64C17F61038001A4D7A /* loadriff.c */; }; + 8370B67617F61038001A4D7A /* loadriff2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64D17F61038001A4D7A /* loadriff2.c */; }; + 8370B67717F61038001A4D7A /* loadstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64E17F61038001A4D7A /* loadstm.c */; }; + 8370B67817F61038001A4D7A /* loadstm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B64F17F61038001A4D7A /* loadstm2.c */; }; + 8370B67917F61038001A4D7A /* ptmeffect.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65017F61038001A4D7A /* ptmeffect.c */; }; + 8370B67A17F61038001A4D7A /* read669.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65117F61038001A4D7A /* read669.c */; }; + 8370B67B17F61038001A4D7A /* read6692.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65217F61038001A4D7A /* read6692.c */; }; + 8370B67C17F61038001A4D7A /* readam.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65317F61038001A4D7A /* readam.c */; }; + 8370B67D17F61038001A4D7A /* readamf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65417F61038001A4D7A /* readamf.c */; }; + 8370B67E17F61038001A4D7A /* readamf2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65517F61038001A4D7A /* readamf2.c */; }; + 8370B67F17F61038001A4D7A /* readany.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65617F61038001A4D7A /* readany.c */; }; + 8370B68017F61038001A4D7A /* readany2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65717F61038001A4D7A /* readany2.c */; }; + 8370B68117F61038001A4D7A /* readasy.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65817F61038001A4D7A /* readasy.c */; }; + 8370B68217F61038001A4D7A /* readdsmf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65917F61038001A4D7A /* readdsmf.c */; }; + 8370B68317F61038001A4D7A /* readmtm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65A17F61038001A4D7A /* readmtm.c */; }; + 8370B68417F61038001A4D7A /* readokt.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65B17F61038001A4D7A /* readokt.c */; }; + 8370B68517F61038001A4D7A /* readokt2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65C17F61038001A4D7A /* readokt2.c */; }; + 8370B68617F61038001A4D7A /* readoldpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65D17F61038001A4D7A /* readoldpsm.c */; }; + 8370B68717F61038001A4D7A /* readpsm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65E17F61038001A4D7A /* readpsm.c */; }; + 8370B68817F61038001A4D7A /* readptm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B65F17F61038001A4D7A /* readptm.c */; }; + 8370B68917F61038001A4D7A /* readriff.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B66017F61038001A4D7A /* readriff.c */; }; + 8370B68A17F61038001A4D7A /* readstm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B66117F61038001A4D7A /* readstm.c */; }; + 8370B68B17F61038001A4D7A /* readstm2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B66217F61038001A4D7A /* readstm2.c */; }; + 8370B7E917F62A40001A4D7A /* blip_buf.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B7E717F62A40001A4D7A /* blip_buf.h */; }; + 8370B7EA17F62A40001A4D7A /* lanczos_resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B7E817F62A40001A4D7A /* lanczos_resampler.h */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -103,6 +158,61 @@ 17C8F63B0CBEE797008D969D /* readxm.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = readxm.c; sourceTree = ""; }; 17C8F63C0CBEE797008D969D /* readxm2.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = readxm2.c; sourceTree = ""; }; 17C8F63D0CBEE797008D969D /* xmeffect.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; path = xmeffect.c; sourceTree = ""; }; + 8370B61E17F60FE2001A4D7A /* barray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = barray.h; sourceTree = ""; }; + 8370B62017F60FE2001A4D7A /* dumbfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dumbfile.h; sourceTree = ""; }; + 8370B62217F60FE2001A4D7A /* lpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lpc.h; sourceTree = ""; }; + 8370B62317F60FE2001A4D7A /* riff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = riff.h; sourceTree = ""; }; + 8370B62417F60FE2001A4D7A /* stack_alloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stack_alloc.h; sourceTree = ""; }; + 8370B62517F60FE2001A4D7A /* tarray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = tarray.h; sourceTree = ""; }; + 8370B62E17F61001001A4D7A /* barray.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = barray.c; sourceTree = ""; }; + 8370B62F17F61001001A4D7A /* blip_buf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blip_buf.c; sourceTree = ""; }; + 8370B63017F61001001A4D7A /* lanczos_resampler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lanczos_resampler.c; sourceTree = ""; }; + 8370B63117F61001001A4D7A /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = ""; }; + 8370B63217F61001001A4D7A /* riff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = riff.c; sourceTree = ""; }; + 8370B63317F61001001A4D7A /* tarray.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = tarray.c; sourceTree = ""; }; + 8370B63A17F61038001A4D7A /* load669.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = load669.c; sourceTree = ""; }; + 8370B63B17F61038001A4D7A /* load6692.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = load6692.c; sourceTree = ""; }; + 8370B63C17F61038001A4D7A /* loadamf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadamf.c; sourceTree = ""; }; + 8370B63D17F61038001A4D7A /* loadamf2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadamf2.c; sourceTree = ""; }; + 8370B63E17F61038001A4D7A /* loadany.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadany.c; sourceTree = ""; }; + 8370B63F17F61038001A4D7A /* loadany2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadany2.c; sourceTree = ""; }; + 8370B64017F61038001A4D7A /* loadasy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadasy.c; sourceTree = ""; }; + 8370B64117F61038001A4D7A /* loadasy2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadasy2.c; sourceTree = ""; }; + 8370B64217F61038001A4D7A /* loadmtm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadmtm.c; sourceTree = ""; }; + 8370B64317F61038001A4D7A /* loadmtm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadmtm2.c; sourceTree = ""; }; + 8370B64417F61038001A4D7A /* loadokt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadokt.c; sourceTree = ""; }; + 8370B64517F61038001A4D7A /* loadokt2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadokt2.c; sourceTree = ""; }; + 8370B64617F61038001A4D7A /* loadoldpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadoldpsm.c; sourceTree = ""; }; + 8370B64717F61038001A4D7A /* loadoldpsm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadoldpsm2.c; sourceTree = ""; }; + 8370B64817F61038001A4D7A /* loadpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadpsm.c; sourceTree = ""; }; + 8370B64917F61038001A4D7A /* loadpsm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadpsm2.c; sourceTree = ""; }; + 8370B64A17F61038001A4D7A /* loadptm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadptm.c; sourceTree = ""; }; + 8370B64B17F61038001A4D7A /* loadptm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadptm2.c; sourceTree = ""; }; + 8370B64C17F61038001A4D7A /* loadriff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadriff.c; sourceTree = ""; }; + 8370B64D17F61038001A4D7A /* loadriff2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadriff2.c; sourceTree = ""; }; + 8370B64E17F61038001A4D7A /* loadstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadstm.c; sourceTree = ""; }; + 8370B64F17F61038001A4D7A /* loadstm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = loadstm2.c; sourceTree = ""; }; + 8370B65017F61038001A4D7A /* ptmeffect.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = ptmeffect.c; sourceTree = ""; }; + 8370B65117F61038001A4D7A /* read669.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = read669.c; sourceTree = ""; }; + 8370B65217F61038001A4D7A /* read6692.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = read6692.c; sourceTree = ""; }; + 8370B65317F61038001A4D7A /* readam.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readam.c; sourceTree = ""; }; + 8370B65417F61038001A4D7A /* readamf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readamf.c; sourceTree = ""; }; + 8370B65517F61038001A4D7A /* readamf2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readamf2.c; sourceTree = ""; }; + 8370B65617F61038001A4D7A /* readany.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readany.c; sourceTree = ""; }; + 8370B65717F61038001A4D7A /* readany2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readany2.c; sourceTree = ""; }; + 8370B65817F61038001A4D7A /* readasy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readasy.c; sourceTree = ""; }; + 8370B65917F61038001A4D7A /* readdsmf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readdsmf.c; sourceTree = ""; }; + 8370B65A17F61038001A4D7A /* readmtm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readmtm.c; sourceTree = ""; }; + 8370B65B17F61038001A4D7A /* readokt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readokt.c; sourceTree = ""; }; + 8370B65C17F61038001A4D7A /* readokt2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readokt2.c; sourceTree = ""; }; + 8370B65D17F61038001A4D7A /* readoldpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readoldpsm.c; sourceTree = ""; }; + 8370B65E17F61038001A4D7A /* readpsm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readpsm.c; sourceTree = ""; }; + 8370B65F17F61038001A4D7A /* readptm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readptm.c; sourceTree = ""; }; + 8370B66017F61038001A4D7A /* readriff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readriff.c; sourceTree = ""; }; + 8370B66117F61038001A4D7A /* readstm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readstm.c; sourceTree = ""; }; + 8370B66217F61038001A4D7A /* readstm2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readstm2.c; sourceTree = ""; }; + 8370B7E717F62A40001A4D7A /* blip_buf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blip_buf.h; sourceTree = ""; }; + 8370B7E817F62A40001A4D7A /* lanczos_resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lanczos_resampler.h; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* Dumb.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Dumb.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -197,6 +307,14 @@ 17C8F60D0CBEE797008D969D /* internal */ = { isa = PBXGroup; children = ( + 8370B7E717F62A40001A4D7A /* blip_buf.h */, + 8370B7E817F62A40001A4D7A /* lanczos_resampler.h */, + 8370B61E17F60FE2001A4D7A /* barray.h */, + 8370B62017F60FE2001A4D7A /* dumbfile.h */, + 8370B62217F60FE2001A4D7A /* lpc.h */, + 8370B62317F60FE2001A4D7A /* riff.h */, + 8370B62417F60FE2001A4D7A /* stack_alloc.h */, + 8370B62517F60FE2001A4D7A /* tarray.h */, 17C8F60E0CBEE797008D969D /* dumb.h */, 17C8F60F0CBEE797008D969D /* it.h */, ); @@ -236,6 +354,12 @@ 17C8F61E0CBEE797008D969D /* helpers */ = { isa = PBXGroup; children = ( + 8370B62E17F61001001A4D7A /* barray.c */, + 8370B62F17F61001001A4D7A /* blip_buf.c */, + 8370B63017F61001001A4D7A /* lanczos_resampler.c */, + 8370B63117F61001001A4D7A /* lpc.c */, + 8370B63217F61001001A4D7A /* riff.c */, + 8370B63317F61001001A4D7A /* tarray.c */, 17C8F61F0CBEE797008D969D /* clickrem.c */, 17C8F6200CBEE797008D969D /* memfile.c */, 17C8F6210CBEE797008D969D /* resamp2.inc */, @@ -252,6 +376,47 @@ 17C8F6280CBEE797008D969D /* it */ = { isa = PBXGroup; children = ( + 8370B63A17F61038001A4D7A /* load669.c */, + 8370B63B17F61038001A4D7A /* load6692.c */, + 8370B63C17F61038001A4D7A /* loadamf.c */, + 8370B63D17F61038001A4D7A /* loadamf2.c */, + 8370B63E17F61038001A4D7A /* loadany.c */, + 8370B63F17F61038001A4D7A /* loadany2.c */, + 8370B64017F61038001A4D7A /* loadasy.c */, + 8370B64117F61038001A4D7A /* loadasy2.c */, + 8370B64217F61038001A4D7A /* loadmtm.c */, + 8370B64317F61038001A4D7A /* loadmtm2.c */, + 8370B64417F61038001A4D7A /* loadokt.c */, + 8370B64517F61038001A4D7A /* loadokt2.c */, + 8370B64617F61038001A4D7A /* loadoldpsm.c */, + 8370B64717F61038001A4D7A /* loadoldpsm2.c */, + 8370B64817F61038001A4D7A /* loadpsm.c */, + 8370B64917F61038001A4D7A /* loadpsm2.c */, + 8370B64A17F61038001A4D7A /* loadptm.c */, + 8370B64B17F61038001A4D7A /* loadptm2.c */, + 8370B64C17F61038001A4D7A /* loadriff.c */, + 8370B64D17F61038001A4D7A /* loadriff2.c */, + 8370B64E17F61038001A4D7A /* loadstm.c */, + 8370B64F17F61038001A4D7A /* loadstm2.c */, + 8370B65017F61038001A4D7A /* ptmeffect.c */, + 8370B65117F61038001A4D7A /* read669.c */, + 8370B65217F61038001A4D7A /* read6692.c */, + 8370B65317F61038001A4D7A /* readam.c */, + 8370B65417F61038001A4D7A /* readamf.c */, + 8370B65517F61038001A4D7A /* readamf2.c */, + 8370B65617F61038001A4D7A /* readany.c */, + 8370B65717F61038001A4D7A /* readany2.c */, + 8370B65817F61038001A4D7A /* readasy.c */, + 8370B65917F61038001A4D7A /* readdsmf.c */, + 8370B65A17F61038001A4D7A /* readmtm.c */, + 8370B65B17F61038001A4D7A /* readokt.c */, + 8370B65C17F61038001A4D7A /* readokt2.c */, + 8370B65D17F61038001A4D7A /* readoldpsm.c */, + 8370B65E17F61038001A4D7A /* readpsm.c */, + 8370B65F17F61038001A4D7A /* readptm.c */, + 8370B66017F61038001A4D7A /* readriff.c */, + 8370B66117F61038001A4D7A /* readstm.c */, + 8370B66217F61038001A4D7A /* readstm2.c */, 17C8F6290CBEE797008D969D /* itload.c */, 17C8F62A0CBEE797008D969D /* itload2.c */, 17C8F62B0CBEE797008D969D /* itmisc.c */, @@ -285,8 +450,16 @@ buildActionMask = 2147483647; files = ( 17C8F63E0CBEE797008D969D /* dumb.h in Headers */, - 17C8F63F0CBEE797008D969D /* dumb.h in Headers */, 17C8F6400CBEE797008D969D /* it.h in Headers */, + 8370B62D17F60FE2001A4D7A /* tarray.h in Headers */, + 8370B62617F60FE2001A4D7A /* barray.h in Headers */, + 8370B7EA17F62A40001A4D7A /* lanczos_resampler.h in Headers */, + 8370B7E917F62A40001A4D7A /* blip_buf.h in Headers */, + 17C8F63F0CBEE797008D969D /* dumb.h in Headers */, + 8370B62B17F60FE2001A4D7A /* riff.h in Headers */, + 8370B62A17F60FE2001A4D7A /* lpc.h in Headers */, + 8370B62817F60FE2001A4D7A /* dumbfile.h in Headers */, + 8370B62C17F60FE2001A4D7A /* stack_alloc.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -317,9 +490,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Dumb" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* Dumb */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -346,29 +525,56 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8370B66D17F61038001A4D7A /* loadokt.c in Sources */, + 8370B68A17F61038001A4D7A /* readstm.c in Sources */, 17C8F6410CBEE797008D969D /* atexit.c in Sources */, 17C8F6420CBEE797008D969D /* duhlen.c in Sources */, + 8370B66E17F61038001A4D7A /* loadokt2.c in Sources */, + 8370B63817F61001001A4D7A /* riff.c in Sources */, 17C8F6430CBEE797008D969D /* duhtag.c in Sources */, + 8370B63517F61001001A4D7A /* blip_buf.c in Sources */, + 8370B68117F61038001A4D7A /* readasy.c in Sources */, + 8370B67217F61038001A4D7A /* loadpsm2.c in Sources */, 17C8F6440CBEE797008D969D /* dumbfile.c in Sources */, + 8370B68317F61038001A4D7A /* readmtm.c in Sources */, 17C8F6450CBEE797008D969D /* loadduh.c in Sources */, + 8370B67D17F61038001A4D7A /* readamf.c in Sources */, + 8370B63717F61001001A4D7A /* lpc.c in Sources */, 17C8F6460CBEE797008D969D /* makeduh.c in Sources */, 17C8F6470CBEE797008D969D /* rawsig.c in Sources */, 17C8F6480CBEE797008D969D /* readduh.c in Sources */, + 8370B67917F61038001A4D7A /* ptmeffect.c in Sources */, 17C8F6490CBEE797008D969D /* register.c in Sources */, + 8370B67C17F61038001A4D7A /* readam.c in Sources */, 17C8F64A0CBEE797008D969D /* rendduh.c in Sources */, + 8370B63417F61001001A4D7A /* barray.c in Sources */, + 8370B67817F61038001A4D7A /* loadstm2.c in Sources */, + 8370B66A17F61038001A4D7A /* loadasy2.c in Sources */, + 8370B68717F61038001A4D7A /* readpsm.c in Sources */, + 8370B67B17F61038001A4D7A /* read6692.c in Sources */, 17C8F64B0CBEE797008D969D /* rendsig.c in Sources */, 17C8F64C0CBEE797008D969D /* unload.c in Sources */, 17C8F64D0CBEE797008D969D /* clickrem.c in Sources */, 17C8F64E0CBEE797008D969D /* memfile.c in Sources */, 17C8F6510CBEE797008D969D /* resample.c in Sources */, + 8370B66F17F61038001A4D7A /* loadoldpsm.c in Sources */, 17C8F6530CBEE797008D969D /* sampbuf.c in Sources */, 17C8F6540CBEE797008D969D /* silence.c in Sources */, 17C8F6550CBEE797008D969D /* stdfile.c in Sources */, + 8370B67417F61038001A4D7A /* loadptm2.c in Sources */, + 8370B68817F61038001A4D7A /* readptm.c in Sources */, + 8370B67317F61038001A4D7A /* loadptm.c in Sources */, + 8370B66417F61038001A4D7A /* load6692.c in Sources */, 17C8F6560CBEE797008D969D /* itload.c in Sources */, 17C8F6570CBEE797008D969D /* itload2.c in Sources */, + 8370B63617F61001001A4D7A /* lanczos_resampler.c in Sources */, 17C8F6580CBEE797008D969D /* itmisc.c in Sources */, + 8370B67517F61038001A4D7A /* loadriff.c in Sources */, + 8370B66917F61038001A4D7A /* loadasy.c in Sources */, 17C8F6590CBEE797008D969D /* itorder.c in Sources */, + 8370B66317F61038001A4D7A /* load669.c in Sources */, 17C8F65A0CBEE797008D969D /* itread.c in Sources */, + 8370B68B17F61038001A4D7A /* readstm2.c in Sources */, 17C8F65B0CBEE797008D969D /* itread2.c in Sources */, 17C8F65C0CBEE797008D969D /* itrender.c in Sources */, 17C8F65D0CBEE797008D969D /* itunload.c in Sources */, @@ -376,14 +582,34 @@ 17C8F65F0CBEE797008D969D /* loadmod2.c in Sources */, 17C8F6600CBEE797008D969D /* loads3m.c in Sources */, 17C8F6610CBEE797008D969D /* loads3m2.c in Sources */, + 8370B63917F61001001A4D7A /* tarray.c in Sources */, + 8370B66B17F61038001A4D7A /* loadmtm.c in Sources */, + 8370B68517F61038001A4D7A /* readokt2.c in Sources */, 17C8F6620CBEE797008D969D /* loadxm.c in Sources */, 17C8F6630CBEE797008D969D /* loadxm2.c in Sources */, + 8370B67117F61038001A4D7A /* loadpsm.c in Sources */, + 8370B67617F61038001A4D7A /* loadriff2.c in Sources */, + 8370B66C17F61038001A4D7A /* loadmtm2.c in Sources */, + 8370B67A17F61038001A4D7A /* read669.c in Sources */, + 8370B66717F61038001A4D7A /* loadany.c in Sources */, + 8370B68017F61038001A4D7A /* readany2.c in Sources */, 17C8F6640CBEE797008D969D /* readmod.c in Sources */, + 8370B67017F61038001A4D7A /* loadoldpsm2.c in Sources */, + 8370B68217F61038001A4D7A /* readdsmf.c in Sources */, 17C8F6650CBEE797008D969D /* readmod2.c in Sources */, + 8370B68617F61038001A4D7A /* readoldpsm.c in Sources */, 17C8F6660CBEE797008D969D /* reads3m.c in Sources */, 17C8F6670CBEE797008D969D /* reads3m2.c in Sources */, 17C8F6680CBEE797008D969D /* readxm.c in Sources */, + 8370B68917F61038001A4D7A /* readriff.c in Sources */, 17C8F6690CBEE797008D969D /* readxm2.c in Sources */, + 8370B66617F61038001A4D7A /* loadamf2.c in Sources */, + 8370B68417F61038001A4D7A /* readokt.c in Sources */, + 8370B66817F61038001A4D7A /* loadany2.c in Sources */, + 8370B66517F61038001A4D7A /* loadamf.c in Sources */, + 8370B67717F61038001A4D7A /* loadstm.c in Sources */, + 8370B67E17F61038001A4D7A /* readamf2.c in Sources */, + 8370B67F17F61038001A4D7A /* readany.c in Sources */, 17C8F66A0CBEE797008D969D /* xmeffect.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -421,6 +647,7 @@ OBJROOT = ../../build; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; @@ -448,6 +675,7 @@ OBJROOT = ../../build; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme new file mode 100644 index 000000000..92fd667c2 --- /dev/null +++ b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/Dumb Framework.xcscheme @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..50d544562 --- /dev/null +++ b/Frameworks/Dumb/Dumb.xcodeproj/xcuserdata/Chris.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + Dumb Framework.xcscheme + + orderHint + 10 + + + SuppressBuildableAutocreation + + 8DC2EF4F0486A6940098B216 + + primary + + + + + diff --git a/Frameworks/Dumb/dumb/include/dumb.h b/Frameworks/Dumb/dumb/include/dumb.h index 2789d7ee3..77d8fc1e1 100644 --- a/Frameworks/Dumb/dumb/include/dumb.h +++ b/Frameworks/Dumb/dumb/include/dumb.h @@ -1,687 +1,784 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * dumb.h - The user header file for DUMB. / / \ \ - * | < / \_ - * Include this file in any of your files in | \/ /\ / - * which you wish to use the DUMB functions \_ / > / - * and variables. | \ / / - * | ' / - * Allegro users, you will probably want aldumb.h. \__/ - */ - -#ifndef DUMB_H -#define DUMB_H - - -#include -#include - - -#ifdef __cplusplus - extern "C" { -#endif - - -#define DUMB_MAJOR_VERSION 0 -#define DUMB_MINOR_VERSION 9 -#define DUMB_REVISION_VERSION 3 - -#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) - -#define DUMB_VERSION_STR "0.9.3" - -#define DUMB_NAME "DUMB v"DUMB_VERSION_STR - -#define DUMB_YEAR 2005 -#define DUMB_MONTH 8 -#define DUMB_DAY 7 - -#define DUMB_YEAR_STR2 "05" -#define DUMB_YEAR_STR4 "2005" -#define DUMB_MONTH_STR1 "8" -#define DUMB_DAY_STR1 "7" - -#if DUMB_MONTH < 10 -#define DUMB_MONTH_STR2 "0"DUMB_MONTH_STR1 -#else -#define DUMB_MONTH_STR2 DUMB_MONTH_STR1 -#endif - -#if DUMB_DAY < 10 -#define DUMB_DAY_STR2 "0"DUMB_DAY_STR1 -#else -#define DUMB_DAY_STR2 DUMB_DAY_STR1 -#endif - - -/* WARNING: The month and day were inadvertently swapped in the v0.8 release. - * Please do not compare this constant against any date in 2002. In - * any case, DUMB_VERSION is probably more useful for this purpose. - */ -#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY) - -#define DUMB_DATE_STR DUMB_DAY_STR1"."DUMB_MONTH_STR1"."DUMB_YEAR_STR4 - - -#undef MIN -#undef MAX -#undef MID - -#define MIN(x,y) (((x) < (y)) ? (x) : (y)) -#define MAX(x,y) (((x) > (y)) ? (x) : (y)) -#define MID(x,y,z) MAX((x), MIN((y), (z))) - -#undef ABS -#define ABS(x) (((x) >= 0) ? (x) : (-(x))) - - -#ifdef DEBUGMODE - -#ifndef ASSERT -#include -#define ASSERT(n) assert(n) -#endif -#ifndef TRACE -// it would be nice if this did actually trace ... -#define TRACE 1 ? (void)0 : (void)printf -#endif - -#else - -#ifndef ASSERT -#define ASSERT(n) -#endif -#ifndef TRACE -#define TRACE 1 ? (void)0 : (void)printf -#endif - -#endif - - -#define DUMB_ID(a,b,c,d) (((unsigned int)(a) << 24) | \ - ((unsigned int)(b) << 16) | \ - ((unsigned int)(c) << 8) | \ - ((unsigned int)(d) )) - - - -#ifndef LONG_LONG -#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__ -#define LONG_LONG long long -#elif defined _MSC_VER || defined __WATCOMC__ -#define LONG_LONG __int64 -#elif defined __sgi -#define LONG_LONG long long -#else -#error 64-bit integer type unknown -#endif -#endif - -#if __GNUC__ * 100 + __GNUC_MINOR__ >= 301 /* GCC 3.1+ */ -#ifndef DUMB_DECLARE_DEPRECATED -#define DUMB_DECLARE_DEPRECATED -#endif -#define DUMB_DEPRECATED __attribute__((__deprecated__)) -#else -#define DUMB_DEPRECATED -#endif - - -/* Basic Sample Type. Normal range is -0x800000 to 0x7FFFFF. */ - -typedef int sample_t; - - -/* Library Clean-up Management */ - -int dumb_atexit(void (*proc)(void)); - -void dumb_exit(void); - - -/* File Input Functions */ - -typedef struct DUMBFILE_SYSTEM -{ - void *(*open)(const char *filename); - int (*skip)(void *f, long n); - int (*getc)(void *f); - long (*getnc)(char *ptr, long n, void *f); - void (*close)(void *f); -} -DUMBFILE_SYSTEM; - -typedef struct DUMBFILE DUMBFILE; - -void register_dumbfile_system(DUMBFILE_SYSTEM *dfs); - -DUMBFILE *dumbfile_open(const char *filename); -DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs); - -long dumbfile_pos(DUMBFILE *f); -int dumbfile_skip(DUMBFILE *f, long n); - -int dumbfile_getc(DUMBFILE *f); - -int dumbfile_igetw(DUMBFILE *f); -int dumbfile_mgetw(DUMBFILE *f); - -long dumbfile_igetl(DUMBFILE *f); -long dumbfile_mgetl(DUMBFILE *f); - -unsigned long dumbfile_cgetul(DUMBFILE *f); -signed long dumbfile_cgetsl(DUMBFILE *f); - -long dumbfile_getnc(char *ptr, long n, DUMBFILE *f); - -int dumbfile_error(DUMBFILE *f); -int dumbfile_close(DUMBFILE *f); - - -/* stdio File Input Module */ - -void dumb_register_stdfiles(void); - -DUMBFILE *dumbfile_open_stdfile(FILE *p); - - -/* Memory File Input Module */ - -DUMBFILE *dumbfile_open_memory(const char *data, long size); - - -/* DUH Management */ - -typedef struct DUH DUH; - -#define DUH_SIGNATURE DUMB_ID('D','U','H','!') - -void unload_duh(DUH *duh); - -DUH *load_duh(const char *filename); -DUH *read_duh(DUMBFILE *f); - -long duh_get_length(DUH *duh); - -const char *duh_get_tag(DUH *duh, const char *key); - - -/* Signal Rendering Functions */ - -typedef struct DUH_SIGRENDERER DUH_SIGRENDERER; - -DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos); - -#ifdef DUMB_DECLARE_DEPRECATED -typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long length); -/* This is deprecated, but is not marked as such because GCC tends to - * complain spuriously when the typedef is used later. See comments below. - */ - -void duh_sigrenderer_set_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_CALLBACK callback, void *data -) DUMB_DEPRECATED; -/* The 'callback' argument's type has changed for const-correctness. See the - * DUH_SIGRENDERER_CALLBACK definition just above. Also note that the samples - * in the buffer are now 256 times as large; the normal range is -0x800000 to - * 0x7FFFFF. The function has been renamed partly because its functionality - * has changed slightly and partly so that its name is more meaningful. The - * new one is duh_sigrenderer_set_analyser_callback(), and the typedef for - * the function pointer has also changed, from DUH_SIGRENDERER_CALLBACK to - * DUH_SIGRENDERER_ANALYSER_CALLBACK. (If you wanted to use this callback to - * apply a DSP effect, don't worry; there is a better way of doing this. It - * is undocumented, so contact me and I shall try to help. Contact details - * are in readme.txt.) - */ - -typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length); -/* This is deprecated, but is not marked as such because GCC tends to - * complain spuriously when the typedef is used later. See comments below. - */ - -void duh_sigrenderer_set_analyser_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data -) DUMB_DEPRECATED; -/* This is deprecated because the meaning of the 'samples' parameter in the - * callback needed to change. For stereo applications, the array used to be - * indexed with samples[channel][pos]. It is now indexed with - * samples[0][pos*2+channel]. Mono sample data are still indexed with - * samples[0][pos]. The array is still 2D because samples will probably only - * ever be interleaved in twos. In order to fix your code, adapt it to the - * new sample layout and then call - * duh_sigrenderer_set_sample_analyser_callback below instead of this - * function. - */ -#endif - -typedef void (*DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length); - -void duh_sigrenderer_set_sample_analyser_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data -); - -int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); -long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); - -void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value); - -#ifdef DUMB_DECLARE_DEPRECATED -long duh_sigrenderer_get_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) DUMB_DEPRECATED; -/* The sample format has changed, so if you were using this function, - * you should switch to duh_sigrenderer_generate_samples() and change - * how you interpret the samples array. See the comments for - * duh_sigrenderer_set_analyser_callback(). - */ -#endif - -long duh_sigrenderer_generate_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -); - -void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples); - -void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); - - -/* DUH Rendering Functions */ - -long duh_render( - DUH_SIGRENDERER *sigrenderer, - int bits, int unsign, - float volume, float delta, - long size, void *sptr -); - -#ifdef DUMB_DECLARE_DEPRECATED - -long duh_render_signal( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) DUMB_DEPRECATED; -/* Please use duh_sigrenderer_generate_samples(), and see the - * comments for the deprecated duh_sigrenderer_get_samples() too. - */ - -typedef DUH_SIGRENDERER DUH_RENDERER DUMB_DEPRECATED; -/* Please use DUH_SIGRENDERER instead of DUH_RENDERER. */ - -DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) DUMB_DEPRECATED; -/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */ - -int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; -long duh_renderer_get_position(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; -/* Please use the duh_sigrenderer_*() equivalents of these two functions. */ - -void duh_end_renderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; -/* Please use duh_end_sigrenderer() instead. */ - -DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) DUMB_DEPRECATED; -DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; -DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; -/* These functions have become no-ops that just return the parameter. - * So, for instance, replace - * duh_renderer_encapsulate_sigrenderer(my_sigrenderer) - * with - * my_sigrenderer - */ - -#endif - - -/* Impulse Tracker Support */ - -extern int dumb_it_max_to_mix; - -typedef struct DUMB_IT_SIGDATA DUMB_IT_SIGDATA; -typedef struct DUMB_IT_SIGRENDERER DUMB_IT_SIGRENDERER; - -DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh); -DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos); -DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); - -DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder); - -void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); -void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); -void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data); - -int dumb_it_callback_terminate(void *data); -int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte); - -DUH *dumb_load_it(const char *filename); -DUH *dumb_load_xm(const char *filename); -DUH *dumb_load_s3m(const char *filename); -DUH *dumb_load_mod(const char *filename); - -DUH *dumb_read_it(DUMBFILE *f); -DUH *dumb_read_xm(DUMBFILE *f); -DUH *dumb_read_s3m(DUMBFILE *f); -DUH *dumb_read_mod(DUMBFILE *f); - -DUH *dumb_load_it_quick(const char *filename); -DUH *dumb_load_xm_quick(const char *filename); -DUH *dumb_load_s3m_quick(const char *filename); -DUH *dumb_load_mod_quick(const char *filename); - -DUH *dumb_read_it_quick(DUMBFILE *f); -DUH *dumb_read_xm_quick(DUMBFILE *f); -DUH *dumb_read_s3m_quick(DUMBFILE *f); -DUH *dumb_read_mod_quick(DUMBFILE *f); - -long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata); -void dumb_it_do_initial_runthrough(DUH *duh); - -const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd); - -int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd); -int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd); -int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd); - -const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i); -const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i); -const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i); -const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i); - -int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv); - -int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv); - -int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed); - -int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd); -void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo); - -int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel); -void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume); - -int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr); -int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr); - -int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr); -void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv); - -int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr); -void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo); - -int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr); -void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed); - -#define DUMB_IT_N_CHANNELS 64 -#define DUMB_IT_N_NNA_CHANNELS 192 -#define DUMB_IT_TOTAL_CHANNELS (DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS) - -/* Channels passed to any of these functions are 0-based */ -int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel); -void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume); - -int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel); -void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted); - -typedef struct DUMB_IT_CHANNEL_STATE DUMB_IT_CHANNEL_STATE; - -struct DUMB_IT_CHANNEL_STATE -{ - int channel; /* 0-based; meaningful for NNA channels */ - int sample; /* 1-based; 0 if nothing playing, then other fields undef */ - int freq; /* in Hz */ - float volume; /* 1.0 maximum; affected by ALL factors, inc. mixing vol */ - unsigned char pan; /* 0-64, 100 for surround */ - signed char subpan; /* use (pan + subpan/256.0f) or ((pan<<8)+subpan) */ - unsigned char filter_cutoff; /* 0-127 cutoff=127 AND resonance=0 */ - unsigned char filter_subcutoff; /* 0-255 -> no filters (subcutoff */ - unsigned char filter_resonance; /* 0-127 always 0 in this case) */ - /* subcutoff only changes from zero if filter envelopes are in use. The - * calculation (filter_cutoff + filter_subcutoff/256.0f) gives a more - * accurate filter cutoff measurement as a float. It would often be more - * useful to use a scaled int such as ((cutoff<<8) + subcutoff). - */ -}; - -/* Values of 64 or more will access NNA channels here. */ -void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state); - - -/* Signal Design Helper Values */ - -/* Use pow(DUMB_SEMITONE_BASE, n) to get the 'delta' value to transpose up by - * n semitones. To transpose down, use negative n. - */ -#define DUMB_SEMITONE_BASE 1.059463094359295309843105314939748495817 - -/* Use pow(DUMB_QUARTERTONE_BASE, n) to get the 'delta' value to transpose up - * by n quartertones. To transpose down, use negative n. - */ -#define DUMB_QUARTERTONE_BASE 1.029302236643492074463779317738953977823 - -/* Use pow(DUMB_PITCH_BASE, n) to get the 'delta' value to transpose up by n - * units. In this case, 256 units represent one semitone; 3072 units - * represent one octave. These units are used by the sequence signal (SEQU). - */ -#define DUMB_PITCH_BASE 1.000225659305069791926712241547647863626 - - -/* Signal Design Function Types */ - -typedef void sigdata_t; -typedef void sigrenderer_t; - -typedef sigdata_t *(*DUH_LOAD_SIGDATA)(DUH *duh, DUMBFILE *file); - -typedef sigrenderer_t *(*DUH_START_SIGRENDERER)( - DUH *duh, - sigdata_t *sigdata, - int n_channels, - long pos -); - -typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)( - sigrenderer_t *sigrenderer, - unsigned char id, long value -); - -typedef long (*DUH_SIGRENDERER_GENERATE_SAMPLES)( - sigrenderer_t *sigrenderer, - float volume, float delta, - long size, sample_t **samples -); - -typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( - sigrenderer_t *sigrenderer, - float volume, - sample_t *samples -); - -typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer); - -typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata); - - -/* Signal Design Function Registration */ - -typedef struct DUH_SIGTYPE_DESC -{ - long type; - DUH_LOAD_SIGDATA load_sigdata; - DUH_START_SIGRENDERER start_sigrenderer; - DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam; - DUH_SIGRENDERER_GENERATE_SAMPLES sigrenderer_generate_samples; - DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample; - DUH_END_SIGRENDERER end_sigrenderer; - DUH_UNLOAD_SIGDATA unload_sigdata; -} -DUH_SIGTYPE_DESC; - -void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc); - - -// Decide where to put these functions; new heading? - -sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type); - -DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos); -sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type); - - -/* Standard Signal Types */ - -//void dumb_register_sigtype_sample(void); - - -/* Sample Buffer Allocation Helpers */ - -#ifdef DUMB_DECLARE_DEPRECATED -sample_t **create_sample_buffer(int n_channels, long length) DUMB_DEPRECATED; -/* DUMB has been changed to interleave stereo samples. Use - * allocate_sample_buffer() instead, and see the comments for - * duh_sigrenderer_set_analyser_callback(). - */ -#endif -sample_t **allocate_sample_buffer(int n_channels, long length); -void destroy_sample_buffer(sample_t **samples); - - -/* Silencing Helper */ - -void dumb_silence(sample_t *samples, long length); - - -/* Click Removal Helpers */ - -typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER; - -DUMB_CLICK_REMOVER *dumb_create_click_remover(void); -void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step); -void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife); -sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr); -void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr); - -DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n); -void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); -void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); -void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife); -void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset); -void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr); - - -/* Resampling Helpers */ - -#define DUMB_RQ_ALIASING 0 -#define DUMB_RQ_LINEAR 1 -#define DUMB_RQ_CUBIC 2 -#define DUMB_RQ_N_LEVELS 3 -extern int dumb_resampling_quality; - -typedef struct DUMB_RESAMPLER DUMB_RESAMPLER; - -typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data); - -struct DUMB_RESAMPLER -{ - void *src; - long pos; - int subpos; - long start, end; - int dir; - DUMB_RESAMPLE_PICKUP pickup; - void *pickup_data; - int min_quality; - int max_quality; - /* Everything below this point is internal: do not use. */ - union { - sample_t x24[3*2]; - short x16[3*2]; - signed char x8[3*2]; - } x; - int overshot; -}; - -void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, int src_channels, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, int src_channels, long pos, long start, long end); -long dumb_resample_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_2_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_end_resampler(DUMB_RESAMPLER *resampler); - -void dumb_reset_resampler_16(DUMB_RESAMPLER *resampler, short *src, int src_channels, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler_16(short *src, int src_channels, long pos, long start, long end); -long dumb_resample_16_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_16_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_16_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_16_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_16_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_16_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_16_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_16_2_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_end_resampler_16(DUMB_RESAMPLER *resampler); - -void dumb_reset_resampler_8(DUMB_RESAMPLER *resampler, signed char *src, int src_channels, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, int src_channels, long pos, long start, long end); -long dumb_resample_8_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_8_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_8_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_8_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_8_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_8_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_8_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_8_2_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_end_resampler_8(DUMB_RESAMPLER *resampler); - -void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end); -DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end); -long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta); -long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta); -void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, float volume, sample_t *dst); -void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst); -void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler); - - -/* DUH Construction */ - -DUH *make_duh( - long length, - int n_tags, - const char *const tag[][2], - int n_signals, - DUH_SIGTYPE_DESC *desc[], - sigdata_t *sigdata[] -); - -void duh_set_length(DUH *duh, long length); - - -#ifdef __cplusplus - } -#endif - - -#endif /* DUMB_H */ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * dumb.h - The user header file for DUMB. / / \ \ + * | < / \_ + * Include this file in any of your files in | \/ /\ / + * which you wish to use the DUMB functions \_ / > / + * and variables. | \ / / + * | ' / + * Allegro users, you will probably want aldumb.h. \__/ + */ + +#ifndef DUMB_H +#define DUMB_H + + +#include +#include + +#ifdef _DEBUG +#define _CRTDBG_MAP_ALLOC +#include +#endif + +#ifdef __cplusplus + extern "C" { +#endif + + +#define DUMB_MAJOR_VERSION 0 +#define DUMB_MINOR_VERSION 9 +#define DUMB_REVISION_VERSION 3 + +#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION) + +#define DUMB_VERSION_STR "0.9.3" + +#define DUMB_NAME "DUMB v" DUMB_VERSION_STR + +#define DUMB_YEAR 2005 +#define DUMB_MONTH 8 +#define DUMB_DAY 7 + +#define DUMB_YEAR_STR2 "05" +#define DUMB_YEAR_STR4 "2005" +#define DUMB_MONTH_STR1 "8" +#define DUMB_DAY_STR1 "7" + +#if DUMB_MONTH < 10 +#define DUMB_MONTH_STR2 "0" DUMB_MONTH_STR1 +#else +#define DUMB_MONTH_STR2 DUMB_MONTH_STR1 +#endif + +#if DUMB_DAY < 10 +#define DUMB_DAY_STR2 "0" DUMB_DAY_STR1 +#else +#define DUMB_DAY_STR2 DUMB_DAY_STR1 +#endif + + +/* WARNING: The month and day were inadvertently swapped in the v0.8 release. + * Please do not compare this constant against any date in 2002. In + * any case, DUMB_VERSION is probably more useful for this purpose. + */ +#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY) + +#define DUMB_DATE_STR DUMB_DAY_STR1 "." DUMB_MONTH_STR1 "." DUMB_YEAR_STR4 + + +#undef MIN +#undef MAX +#undef MID + +#define MIN(x,y) (((x) < (y)) ? (x) : (y)) +#define MAX(x,y) (((x) > (y)) ? (x) : (y)) +#define MID(x,y,z) MAX((x), MIN((y), (z))) + +#undef ABS +#define ABS(x) (((x) >= 0) ? (x) : (-(x))) + + +#ifdef DEBUGMODE + +#ifndef ASSERT +#include +#define ASSERT(n) assert(n) +#endif +#ifndef TRACE +// it would be nice if this did actually trace ... +#define TRACE 1 ? (void)0 : (void)printf +#endif + +#else + +#ifndef ASSERT +#define ASSERT(n) +#endif +#ifndef TRACE +#define TRACE 1 ? (void)0 : (void)printf +#endif + +#endif + + +#define DUMB_ID(a,b,c,d) (((unsigned int)(a) << 24) | \ + ((unsigned int)(b) << 16) | \ + ((unsigned int)(c) << 8) | \ + ((unsigned int)(d) )) + + + +#ifndef LONG_LONG +#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__ +#define LONG_LONG long long +#elif defined _MSC_VER || defined __WATCOMC__ +#define LONG_LONG __int64 +#elif defined __sgi +#define LONG_LONG long long +#else +#error 64-bit integer type unknown +#endif +#endif + +#if __GNUC__ * 100 + __GNUC_MINOR__ >= 301 /* GCC 3.1+ */ +#ifndef DUMB_DECLARE_DEPRECATED +#define DUMB_DECLARE_DEPRECATED +#endif +#define DUMB_DEPRECATED __attribute__((__deprecated__)) +#else +#define DUMB_DEPRECATED +#endif + + +/* Basic Sample Type. Normal range is -0x800000 to 0x7FFFFF. */ + +typedef int sample_t; + + +/* Library Clean-up Management */ + +int dumb_atexit(void (*proc)(void)); + +void dumb_exit(void); + + +/* File Input Functions */ + +typedef struct DUMBFILE_SYSTEM +{ + void *(*open)(const char *filename); + int (*skip)(void *f, long n); + int (*getc)(void *f); + long (*getnc)(char *ptr, long n, void *f); + void (*close)(void *f); + int (*seek)(void *f, long n); + long (*get_size)(void *f); +} +DUMBFILE_SYSTEM; + +typedef struct DUMBFILE DUMBFILE; + +void register_dumbfile_system(const DUMBFILE_SYSTEM *dfs); + +DUMBFILE *dumbfile_open(const char *filename); +DUMBFILE *dumbfile_open_ex(void *file, const DUMBFILE_SYSTEM *dfs); + +long dumbfile_pos(DUMBFILE *f); +int dumbfile_skip(DUMBFILE *f, long n); + +#define DFS_SEEK_SET 0 +#define DFS_SEEK_CUR 1 +#define DFS_SEEK_END 2 + +int dumbfile_seek(DUMBFILE *f, long n, int origin); + +long dumbfile_get_size(DUMBFILE *f); + +int dumbfile_getc(DUMBFILE *f); + +int dumbfile_igetw(DUMBFILE *f); +int dumbfile_mgetw(DUMBFILE *f); + +long dumbfile_igetl(DUMBFILE *f); +long dumbfile_mgetl(DUMBFILE *f); + +unsigned long dumbfile_cgetul(DUMBFILE *f); +signed long dumbfile_cgetsl(DUMBFILE *f); + +long dumbfile_getnc(char *ptr, long n, DUMBFILE *f); + +int dumbfile_error(DUMBFILE *f); +int dumbfile_close(DUMBFILE *f); + + +/* stdio File Input Module */ + +void dumb_register_stdfiles(void); + +DUMBFILE *dumbfile_open_stdfile(FILE *p); + + +/* Memory File Input Module */ + +DUMBFILE *dumbfile_open_memory(const char *data, long size); + + +/* DUH Management */ + +typedef struct DUH DUH; + +#define DUH_SIGNATURE DUMB_ID('D','U','H','!') + +void unload_duh(DUH *duh); + +DUH *load_duh(const char *filename); +DUH *read_duh(DUMBFILE *f); + +long duh_get_length(DUH *duh); + +const char *duh_get_tag(DUH *duh, const char *key); + +/* Signal Rendering Functions */ + +typedef struct DUH_SIGRENDERER DUH_SIGRENDERER; + +DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos); + +#ifdef DUMB_DECLARE_DEPRECATED +typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long length); +/* This is deprecated, but is not marked as such because GCC tends to + * complain spuriously when the typedef is used later. See comments below. + */ + +void duh_sigrenderer_set_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_CALLBACK callback, void *data +) DUMB_DEPRECATED; +/* The 'callback' argument's type has changed for const-correctness. See the + * DUH_SIGRENDERER_CALLBACK definition just above. Also note that the samples + * in the buffer are now 256 times as large; the normal range is -0x800000 to + * 0x7FFFFF. The function has been renamed partly because its functionality + * has changed slightly and partly so that its name is more meaningful. The + * new one is duh_sigrenderer_set_analyser_callback(), and the typedef for + * the function pointer has also changed, from DUH_SIGRENDERER_CALLBACK to + * DUH_SIGRENDERER_ANALYSER_CALLBACK. (If you wanted to use this callback to + * apply a DSP effect, don't worry; there is a better way of doing this. It + * is undocumented, so contact me and I shall try to help. Contact details + * are in readme.txt.) + */ + +typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length); +/* This is deprecated, but is not marked as such because GCC tends to + * complain spuriously when the typedef is used later. See comments below. + */ + +void duh_sigrenderer_set_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data +) DUMB_DEPRECATED; +/* This is deprecated because the meaning of the 'samples' parameter in the + * callback needed to change. For stereo applications, the array used to be + * indexed with samples[channel][pos]. It is now indexed with + * samples[0][pos*2+channel]. Mono sample data are still indexed with + * samples[0][pos]. The array is still 2D because samples will probably only + * ever be interleaved in twos. In order to fix your code, adapt it to the + * new sample layout and then call + * duh_sigrenderer_set_sample_analyser_callback below instead of this + * function. + */ +#endif + +typedef void (*DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length); + +void duh_sigrenderer_set_sample_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data +); + +int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer); +long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer); + +void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value); + +#ifdef DUMB_DECLARE_DEPRECATED +long duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) DUMB_DEPRECATED; +/* The sample format has changed, so if you were using this function, + * you should switch to duh_sigrenderer_generate_samples() and change + * how you interpret the samples array. See the comments for + * duh_sigrenderer_set_analyser_callback(). + */ +#endif + +long duh_sigrenderer_generate_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +); + +void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples); + +void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer); + + +/* DUH Rendering Functions */ + +long duh_render( + DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, + float volume, float delta, + long size, void *sptr +); + +#ifdef DUMB_DECLARE_DEPRECATED + +long duh_render_signal( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) DUMB_DEPRECATED; +/* Please use duh_sigrenderer_generate_samples(), and see the + * comments for the deprecated duh_sigrenderer_get_samples() too. + */ + +typedef DUH_SIGRENDERER DUH_RENDERER DUMB_DEPRECATED; +/* Please use DUH_SIGRENDERER instead of DUH_RENDERER. */ + +DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) DUMB_DEPRECATED; +/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */ + +int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +long duh_renderer_get_position(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +/* Please use the duh_sigrenderer_*() equivalents of these two functions. */ + +void duh_end_renderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +/* Please use duh_end_sigrenderer() instead. */ + +DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) DUMB_DEPRECATED; +DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED; +/* These functions have become no-ops that just return the parameter. + * So, for instance, replace + * duh_renderer_encapsulate_sigrenderer(my_sigrenderer) + * with + * my_sigrenderer + */ + +#endif + + +/* Impulse Tracker Support */ + +extern int dumb_it_max_to_mix; + +typedef struct DUMB_IT_SIGDATA DUMB_IT_SIGDATA; +typedef struct DUMB_IT_SIGRENDERER DUMB_IT_SIGRENDERER; + +DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh); +DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos); +DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer); + +int dumb_it_trim_silent_patterns(DUH * duh); + +typedef int (*dumb_scan_callback)(void *, int, long); +int dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data); + +DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder); + +void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality); + +void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style); + +void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); +void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); +void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data); +void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data); + +int dumb_it_callback_terminate(void *data); +int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte); + +/* dumb_*_mod*: restrict_ |= 1-Don't read 15 sample files / 2-Use old pattern counting method */ + +DUH *dumb_load_it(const char *filename); +DUH *dumb_load_xm(const char *filename); +DUH *dumb_load_s3m(const char *filename); +DUH *dumb_load_stm(const char *filename); +DUH *dumb_load_mod(const char *filename, int restrict_); +DUH *dumb_load_ptm(const char *filename); +DUH *dumb_load_669(const char *filename); +DUH *dumb_load_psm(const char *filename, int subsong); +DUH *dumb_load_old_psm(const char * filename); +DUH *dumb_load_mtm(const char *filename); +DUH *dumb_load_riff(const char *filename); +DUH *dumb_load_asy(const char *filename); +DUH *dumb_load_amf(const char *filename); +DUH *dumb_load_okt(const char *filename); + +DUH *dumb_read_it(DUMBFILE *f); +DUH *dumb_read_xm(DUMBFILE *f); +DUH *dumb_read_s3m(DUMBFILE *f); +DUH *dumb_read_stm(DUMBFILE *f); +DUH *dumb_read_mod(DUMBFILE *f, int restrict_); +DUH *dumb_read_ptm(DUMBFILE *f); +DUH *dumb_read_669(DUMBFILE *f); +DUH *dumb_read_psm(DUMBFILE *f, int subsong); +DUH *dumb_read_old_psm(DUMBFILE *f); +DUH *dumb_read_mtm(DUMBFILE *f); +DUH *dumb_read_riff(DUMBFILE *f); +DUH *dumb_read_asy(DUMBFILE *f); +DUH *dumb_read_amf(DUMBFILE *f); +DUH *dumb_read_okt(DUMBFILE *f); + +DUH *dumb_load_it_quick(const char *filename); +DUH *dumb_load_xm_quick(const char *filename); +DUH *dumb_load_s3m_quick(const char *filename); +DUH *dumb_load_stm_quick(const char *filename); +DUH *dumb_load_mod_quick(const char *filename, int restrict_); +DUH *dumb_load_ptm_quick(const char *filename); +DUH *dumb_load_669_quick(const char *filename); +DUH *dumb_load_psm_quick(const char *filename, int subsong); +DUH *dumb_load_old_psm_quick(const char * filename); +DUH *dumb_load_mtm_quick(const char *filename); +DUH *dumb_load_riff_quick(const char *filename); +DUH *dumb_load_asy_quick(const char *filename); +DUH *dumb_load_amf_quick(const char *filename); +DUH *dumb_load_okt_quick(const char *filename); + +DUH *dumb_read_it_quick(DUMBFILE *f); +DUH *dumb_read_xm_quick(DUMBFILE *f); +DUH *dumb_read_s3m_quick(DUMBFILE *f); +DUH *dumb_read_stm_quick(DUMBFILE *f); +DUH *dumb_read_mod_quick(DUMBFILE *f, int restrict_); +DUH *dumb_read_ptm_quick(DUMBFILE *f); +DUH *dumb_read_669_quick(DUMBFILE *f); +DUH *dumb_read_psm_quick(DUMBFILE *f, int subsong); +DUH *dumb_read_old_psm_quick(DUMBFILE *f); +DUH *dumb_read_mtm_quick(DUMBFILE *f); +DUH *dumb_read_riff_quick(DUMBFILE *f); +DUH *dumb_read_asy_quick(DUMBFILE *f); +DUH *dumb_read_amf_quick(DUMBFILE *f); +DUH *dumb_read_okt_quick(DUMBFILE *f); + +DUH *dumb_read_any_quick(DUMBFILE *f, int restrict_, int subsong); +DUH *dumb_read_any(DUMBFILE *f, int restrict_, int subsong); + +DUH *dumb_load_any_quick(const char *filename, int restrict_, int subsong); +DUH *dumb_load_any(const char *filename, int restrict_, int subsong); + +long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder); +void dumb_it_do_initial_runthrough(DUH *duh); + +int dumb_get_psm_subsong_count(DUMBFILE *f); + +const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd); + +int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd); +int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd); +int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd); + +const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i); +const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i); + +int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv); + +int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv); + +int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed); + +int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd); +void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo); + +int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel); +void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume); + +int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr); +int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr); + +int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr); +void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv); + +int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr); +void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo); + +int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr); +void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed); + +#define DUMB_IT_N_CHANNELS 64 +#define DUMB_IT_N_NNA_CHANNELS 192 +#define DUMB_IT_TOTAL_CHANNELS (DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS) + +/* Channels passed to any of these functions are 0-based */ +int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel); +void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume); + +int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel); +void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted); + +typedef struct DUMB_IT_CHANNEL_STATE DUMB_IT_CHANNEL_STATE; + +struct DUMB_IT_CHANNEL_STATE +{ + int channel; /* 0-based; meaningful for NNA channels */ + int sample; /* 1-based; 0 if nothing playing, then other fields undef */ + int freq; /* in Hz */ + float volume; /* 1.0 maximum; affected by ALL factors, inc. mixing vol */ + unsigned char pan; /* 0-64, 100 for surround */ + signed char subpan; /* use (pan + subpan/256.0f) or ((pan<<8)+subpan) */ + unsigned char filter_cutoff; /* 0-127 cutoff=127 AND resonance=0 */ + unsigned char filter_subcutoff; /* 0-255 -> no filters (subcutoff */ + unsigned char filter_resonance; /* 0-127 always 0 in this case) */ + /* subcutoff only changes from zero if filter envelopes are in use. The + * calculation (filter_cutoff + filter_subcutoff/256.0f) gives a more + * accurate filter cutoff measurement as a float. It would often be more + * useful to use a scaled int such as ((cutoff<<8) + subcutoff). + */ +}; + +/* Values of 64 or more will access NNA channels here. */ +void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state); + + +/* Signal Design Helper Values */ + +/* Use pow(DUMB_SEMITONE_BASE, n) to get the 'delta' value to transpose up by + * n semitones. To transpose down, use negative n. + */ +#define DUMB_SEMITONE_BASE 1.059463094359295309843105314939748495817 + +/* Use pow(DUMB_QUARTERTONE_BASE, n) to get the 'delta' value to transpose up + * by n quartertones. To transpose down, use negative n. + */ +#define DUMB_QUARTERTONE_BASE 1.029302236643492074463779317738953977823 + +/* Use pow(DUMB_PITCH_BASE, n) to get the 'delta' value to transpose up by n + * units. In this case, 256 units represent one semitone; 3072 units + * represent one octave. These units are used by the sequence signal (SEQU). + */ +#define DUMB_PITCH_BASE 1.000225659305069791926712241547647863626 + + +/* Signal Design Function Types */ + +typedef void sigdata_t; +typedef void sigrenderer_t; + +typedef sigdata_t *(*DUH_LOAD_SIGDATA)(DUH *duh, DUMBFILE *file); + +typedef sigrenderer_t *(*DUH_START_SIGRENDERER)( + DUH *duh, + sigdata_t *sigdata, + int n_channels, + long pos +); + +typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)( + sigrenderer_t *sigrenderer, + unsigned char id, long value +); + +typedef long (*DUH_SIGRENDERER_GENERATE_SAMPLES)( + sigrenderer_t *sigrenderer, + float volume, float delta, + long size, sample_t **samples +); + +typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)( + sigrenderer_t *sigrenderer, + float volume, + sample_t *samples +); + +typedef long (*DUH_SIGRENDERER_GET_POSITION)( + sigrenderer_t *sigrenderer +); + +typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer); + +typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata); + + +/* Signal Design Function Registration */ + +typedef struct DUH_SIGTYPE_DESC +{ + long type; + DUH_LOAD_SIGDATA load_sigdata; + DUH_START_SIGRENDERER start_sigrenderer; + DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam; + DUH_SIGRENDERER_GENERATE_SAMPLES sigrenderer_generate_samples; + DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample; + DUH_SIGRENDERER_GET_POSITION sigrenderer_get_position; + DUH_END_SIGRENDERER end_sigrenderer; + DUH_UNLOAD_SIGDATA unload_sigdata; +} +DUH_SIGTYPE_DESC; + +void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc); + +int duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata); + + +// Decide where to put these functions; new heading? + +sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type); + +DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos); +sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type); + + +/* Standard Signal Types */ + +//void dumb_register_sigtype_sample(void); + + +/* Sample Buffer Allocation Helpers */ + +#ifdef DUMB_DECLARE_DEPRECATED +sample_t **create_sample_buffer(int n_channels, long length) DUMB_DEPRECATED; +/* DUMB has been changed to interleave stereo samples. Use + * allocate_sample_buffer() instead, and see the comments for + * duh_sigrenderer_set_analyser_callback(). + */ +#endif +sample_t **allocate_sample_buffer(int n_channels, long length); +void destroy_sample_buffer(sample_t **samples); + + +/* Silencing Helper */ + +void dumb_silence(sample_t *samples, long length); + + +/* Click Removal Helpers */ + +typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER; + +DUMB_CLICK_REMOVER *dumb_create_click_remover(void); +void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step); +void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife); +sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr); +void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr); + +DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n); +void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); +void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step); +void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife); +void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset); +void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr); + + +/* Resampling Helpers */ + +#define DUMB_RQ_ALIASING 0 +#define DUMB_RQ_LINEAR 1 +#define DUMB_RQ_CUBIC 2 +#define DUMB_RQ_FIR 3 +#define DUMB_RQ_N_LEVELS 4 +extern int dumb_resampling_quality; + +typedef struct DUMB_RESAMPLER DUMB_RESAMPLER; + +typedef struct DUMB_VOLUME_RAMP_INFO DUMB_VOLUME_RAMP_INFO; + +typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data); + +typedef struct blip_t blip_t; + +struct DUMB_RESAMPLER +{ + void *src; + long pos; + int subpos; + long start, end; + int dir; + DUMB_RESAMPLE_PICKUP pickup; + void *pickup_data; + int quality; + /* Everything below this point is internal: do not use. */ + union { + sample_t x24[3*2]; + short x16[3*2]; + signed char x8[3*2]; + } x; + int overshot; + int last_clock; + int last_amp[2]; + blip_t* blip_buffer[2]; + double fir_resampler_ratio; + void* fir_resampler[2]; +}; + +struct DUMB_VOLUME_RAMP_INFO +{ + float volume; + float delta; + float target; + float mix; +}; + +void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, int src_channels, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +void dumb_resample_get_current_sample_1_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_1_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_2_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_2_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler(DUMB_RESAMPLER *resampler); + +void dumb_reset_resampler_16(DUMB_RESAMPLER *resampler, short *src, int src_channels, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_16(short *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_16_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_16_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_16_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_16_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +void dumb_resample_get_current_sample_16_1_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_16_1_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_16_2_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_16_2_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler_16(DUMB_RESAMPLER *resampler); + +void dumb_reset_resampler_8(DUMB_RESAMPLER *resampler, signed char *src, int src_channels, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_8_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_8_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_8_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_8_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +void dumb_resample_get_current_sample_8_1_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_8_1_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_8_2_1(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_8_2_2(DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler_8(DUMB_RESAMPLER *resampler); + +void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end, int quality); +DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end, int quality); +long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta); +long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta); +void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst); +void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst); +void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler); + + +/* DUH Construction */ + +DUH *make_duh( + long length, + int n_tags, + const char *const tag[][2], + int n_signals, + DUH_SIGTYPE_DESC *desc[], + sigdata_t *sigdata[] +); + +void duh_set_length(DUH *duh, long length); + + +#ifdef __cplusplus + } +#endif + + +#endif /* DUMB_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/aldumb.h b/Frameworks/Dumb/dumb/include/internal/aldumb.h new file mode 100644 index 000000000..9c02c01ff --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/aldumb.h @@ -0,0 +1,27 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * internal/aldumb.h - The internal header file / / \ \ + * for DUMB with Allegro. | < / \_ + * | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#ifndef INTERNAL_ALDUMB_H +#define INTERNAL_ALDUMB_H + + +void _dat_unload_duh(void *duh); + + +#endif /* INTERNAL_DUMB_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/barray.h b/Frameworks/Dumb/dumb/include/internal/barray.h new file mode 100644 index 000000000..53c9a6cf3 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/barray.h @@ -0,0 +1,20 @@ +#ifndef _B_ARRAY_H_ +#define _B_ARRAY_H_ + +#include + +void * bit_array_create(size_t size); +void bit_array_destroy(void * array); +void * bit_array_dup(void * array); + +void bit_array_reset(void * array); + +void bit_array_set(void * array, size_t bit); +int bit_array_test(void * array, size_t bit); +int bit_array_test_range(void * array, size_t bit, size_t count); +void bit_array_clear(void * array, size_t bit); + +void bit_array_merge(void * array, void * source, size_t offset); +void bit_array_mask(void * array, void * source, size_t offset); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/blip_buf.h b/Frameworks/Dumb/dumb/include/internal/blip_buf.h new file mode 100644 index 000000000..6888fa37a --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/blip_buf.h @@ -0,0 +1,77 @@ +/** \file +Sample buffer that resamples from input clock rate to output sample rate */ + +/* blip_buf 1.1.0 */ +#ifndef BLIP_BUF_H +#define BLIP_BUF_H + +#ifdef __cplusplus + extern "C" { +#endif + +/** First parameter of most functions is blip_t*, or const blip_t* if nothing +is changed. */ +typedef struct blip_t blip_t; + +/** Creates new buffer that can hold at most sample_count samples. Sets rates +so that there are blip_max_ratio clocks per sample. Returns pointer to new +buffer, or NULL if insufficient memory. */ +blip_t* blip_new( int sample_count ); + +blip_t* blip_dup( blip_t* ); + +/** Sets approximate input clock rate and output sample rate. For every +clock_rate input clocks, approximately sample_rate samples are generated. */ +void blip_set_rates( blip_t*, double clock_rate, double sample_rate ); + +enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate, +clock_rate must not be greater than sample_rate*blip_max_ratio. */ +blip_max_ratio = 1 << 20 }; + +/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ +void blip_clear( blip_t* ); + +/** Adds positive/negative delta into buffer at specified clock time. */ +void blip_add_delta( blip_t*, unsigned int clock_time, int delta ); + +/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */ +void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta ); + +/** Length of time frame, in clocks, needed to make sample_count additional +samples available. */ +int blip_clocks_needed( const blip_t*, int sample_count ); + +enum { /** Maximum number of samples that can be generated from one time frame. */ +blip_max_frame = 4000 }; + +/** Makes input clocks before clock_duration available for reading as output +samples. Also begins new time frame at clock_duration, so that clock time 0 in +the new time frame specifies the same clock as clock_duration in the old time +frame specified. Deltas can have been added slightly past clock_duration (up to +however many clocks there are in two output samples). */ +void blip_end_frame( blip_t*, unsigned int clock_duration ); + +/** Number of buffered samples available for reading. */ +int blip_samples_avail( const blip_t* ); + +/** Reads and removes at most 'count' samples and writes them to 'out'. If +'stereo' is true, writes output to every other element of 'out', allowing easy +interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed +samples. Returns number of samples actually read. */ +int blip_read_samples( blip_t*, int out [], int count ); + +/** Reads the current integrator and returns it */ +int blip_peek_sample( blip_t* ); + +/** Frees buffer. No effect if NULL is passed. */ +void blip_delete( blip_t* ); + + +/* Deprecated */ +typedef blip_t blip_buffer_t; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/dumb.h b/Frameworks/Dumb/dumb/include/internal/dumb.h index 99823f15c..bed595666 100644 --- a/Frameworks/Dumb/dumb/include/internal/dumb.h +++ b/Frameworks/Dumb/dumb/include/internal/dumb.h @@ -1,61 +1,61 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * internal/dumb.h - DUMB's internal declarations. / / \ \ - * | < / \_ - * This header file provides access to the | \/ /\ / - * internal structure of DUMB, and is liable \_ / > / - * to change, mutate or cease to exist at any | \ / / - * moment. Include it at your own peril. | ' / - * \__/ - * ... - * - * Seriously. You don't need access to anything in this file. All right, you - * probably do actually. But if you use it, you will be relying on a specific - * version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please - * contact the authors so that we can provide a public API for what you need. - */ - -#ifndef INTERNAL_DUMB_H -#define INTERNAL_DUMB_H - - -typedef struct DUH_SIGTYPE_DESC_LINK -{ - struct DUH_SIGTYPE_DESC_LINK *next; - DUH_SIGTYPE_DESC *desc; -} -DUH_SIGTYPE_DESC_LINK; - - -typedef struct DUH_SIGNAL -{ - sigdata_t *sigdata; - DUH_SIGTYPE_DESC *desc; -} -DUH_SIGNAL; - - -struct DUH -{ - long length; - - int n_tags; - char *(*tag)[2]; - - int n_signals; - DUH_SIGNAL **signal; -}; - - -DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type); - - -#endif /* INTERNAL_DUMB_H */ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * internal/dumb.h - DUMB's internal declarations. / / \ \ + * | < / \_ + * This header file provides access to the | \/ /\ / + * internal structure of DUMB, and is liable \_ / > / + * to change, mutate or cease to exist at any | \ / / + * moment. Include it at your own peril. | ' / + * \__/ + * ... + * + * Seriously. You don't need access to anything in this file. All right, you + * probably do actually. But if you use it, you will be relying on a specific + * version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please + * contact the authors so that we can provide a public API for what you need. + */ + +#ifndef INTERNAL_DUMB_H +#define INTERNAL_DUMB_H + + +typedef struct DUH_SIGTYPE_DESC_LINK +{ + struct DUH_SIGTYPE_DESC_LINK *next; + DUH_SIGTYPE_DESC *desc; +} +DUH_SIGTYPE_DESC_LINK; + + +typedef struct DUH_SIGNAL +{ + sigdata_t *sigdata; + DUH_SIGTYPE_DESC *desc; +} +DUH_SIGNAL; + + +struct DUH +{ + long length; + + int n_tags; + char *(*tag)[2]; + + int n_signals; + DUH_SIGNAL **signal; +}; + + +DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type); + + +#endif /* INTERNAL_DUMB_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/dumbfile.h b/Frameworks/Dumb/dumb/include/internal/dumbfile.h new file mode 100644 index 000000000..c83cc9a00 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/dumbfile.h @@ -0,0 +1,13 @@ +#ifndef DUMBFILE_H +#define DUMBFILE_H + +#include "../dumb.h" + +struct DUMBFILE +{ + const DUMBFILE_SYSTEM *dfs; + void *file; + long pos; +}; + +#endif // DUMBFILE_H diff --git a/Frameworks/Dumb/dumb/include/internal/fir_resampler.h b/Frameworks/Dumb/dumb/include/internal/fir_resampler.h new file mode 100644 index 000000000..140eefb39 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/fir_resampler.h @@ -0,0 +1,18 @@ +#ifndef _FIR_RESAMPLER_H_ +#define _FIR_RESAMPLER_H_ + +void fir_init(); + +void * fir_resampler_create(); +void fir_resampler_delete(void *); +void * fir_resampler_dup(void *); + +int fir_resampler_get_free_count(void *); +void fir_resampler_write_sample(void *, short sample); +void fir_resampler_set_rate( void *, double new_factor ); +int fir_resampler_ready(void *); +void fir_resampler_clear(void *); +int fir_resampler_get_sample(void *); +void fir_resampler_remove_sample(void *); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/it.h b/Frameworks/Dumb/dumb/include/internal/it.h index 0fbbeacd0..8ca5fada6 100644 --- a/Frameworks/Dumb/dumb/include/internal/it.h +++ b/Frameworks/Dumb/dumb/include/internal/it.h @@ -1,733 +1,927 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * internal/it.h - Internal stuff for IT playback / / \ \ - * and MOD/XM/S3M conversion. | < / \_ - * | \/ /\ / - * This header file provides access to the \_ / > / - * internal structure of DUMB, and is liable | \ / / - * to change, mutate or cease to exist at any | ' / - * moment. Include it at your own peril. \__/ - * - * ... - * - * Seriously. You don't need access to anything in this file. All right, you - * probably do actually. But if you use it, you will be relying on a specific - * version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please - * contact the authors so that we can provide a public API for what you need. - */ - -#ifndef INTERNAL_IT_H -#define INTERNAL_IT_H - - - -#include - - - -/** TO DO: THINK ABOUT THE FOLLOWING: - -sigdata->flags & IT_COMPATIBLE_GXX - - Bit 5: On = Link Effect G's memory with Effect E/F. Also - Gxx with an instrument present will cause the - envelopes to be retriggered. If you change a - sample on a row with Gxx, it'll adjust the - frequency of the current note according to: - - NewFrequency = OldFrequency * NewC5 / OldC5; -*/ - - - -/* These #defines are TEMPORARY. They are used to write alternative code to - * handle ambiguities in the format specification. The correct code in each - * case will be determined most likely by experimentation. - */ -#define STEREO_SAMPLES_COUNT_AS_TWO -#define INVALID_ORDERS_END_SONG -#define INVALID_NOTES_CAUSE_NOTE_CUT -#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP -#define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM - - - -#define SIGTYPE_IT DUMB_ID('I', 'T', ' ', ' ') - -#define IT_SIGNATURE DUMB_ID('I', 'M', 'P', 'M') -#define IT_INSTRUMENT_SIGNATURE DUMB_ID('I', 'M', 'P', 'I') -#define IT_SAMPLE_SIGNATURE DUMB_ID('I', 'M', 'P', 'S') - - - -/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get - * the interval between ticks. - */ -#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6)) - - - -/* I'm not going to try to explain this, because I didn't derive it very - * formally ;) - */ -/* #define AMIGA_DIVISOR ((float)(4.0 * 14317056.0)) */ -/* I believe the following one to be more accurate. */ -#define AMIGA_DIVISOR ((float)(8.0 * 7159090.5)) - - - -typedef struct IT_MIDI IT_MIDI; -typedef struct IT_FILTER_STATE IT_FILTER_STATE; -typedef struct IT_ENVELOPE IT_ENVELOPE; -typedef struct IT_INSTRUMENT IT_INSTRUMENT; -typedef struct IT_SAMPLE IT_SAMPLE; -typedef struct IT_ENTRY IT_ENTRY; -typedef struct IT_PATTERN IT_PATTERN; -typedef struct IT_PLAYING_ENVELOPE IT_PLAYING_ENVELOPE; -typedef struct IT_PLAYING IT_PLAYING; -typedef struct IT_CHANNEL IT_CHANNEL; -typedef struct IT_CHECKPOINT IT_CHECKPOINT; -typedef struct IT_CALLBACKS IT_CALLBACKS; - - - -struct IT_MIDI -{ - unsigned char SFmacro[16][16]; // read these from 0x120 - unsigned char SFmacrolen[16]; - unsigned short SFmacroz[16]; /* Bitfield; bit 0 set = z in first position */ - unsigned char Zmacro[128][16]; // read these from 0x320 - unsigned char Zmacrolen[128]; -}; - - - -struct IT_FILTER_STATE -{ - sample_t currsample, prevsample; -}; - - - -#define IT_ENVELOPE_ON 1 -#define IT_ENVELOPE_LOOP_ON 2 -#define IT_ENVELOPE_SUSTAIN_LOOP 4 -#define IT_ENVELOPE_PITCH_IS_FILTER 128 - -struct IT_ENVELOPE -{ - unsigned char flags; - unsigned char n_nodes; - unsigned char loop_start; - unsigned char loop_end; - unsigned char sus_loop_start; - unsigned char sus_loop_end; - signed char node_y[25]; - unsigned short node_t[25]; -}; - - - -#define NNA_NOTE_CUT 0 -#define NNA_NOTE_CONTINUE 1 -#define NNA_NOTE_OFF 2 -#define NNA_NOTE_FADE 3 - -#define DCT_OFF 0 -#define DCT_NOTE 1 -#define DCT_SAMPLE 2 -#define DCT_INSTRUMENT 3 - -#define DCA_NOTE_CUT 0 -#define DCA_NOTE_OFF 1 -#define DCA_NOTE_FADE 2 - -struct IT_INSTRUMENT -{ - unsigned char name[27]; - unsigned char filename[14]; - - int fadeout; - - IT_ENVELOPE volume_envelope; - IT_ENVELOPE pan_envelope; - IT_ENVELOPE pitch_envelope; - - unsigned char new_note_action; - unsigned char dup_check_type; - unsigned char dup_check_action; - unsigned char pp_separation; - unsigned char pp_centre; - unsigned char global_volume; - unsigned char default_pan; - unsigned char random_volume; - unsigned char random_pan; - - unsigned char filter_cutoff; - unsigned char filter_resonance; - - unsigned char map_note[120]; - unsigned short map_sample[120]; -}; - - - -#define IT_SAMPLE_EXISTS 1 -#define IT_SAMPLE_16BIT 2 -#define IT_SAMPLE_STEREO 4 -#define IT_SAMPLE_LOOP 16 -#define IT_SAMPLE_SUS_LOOP 32 -#define IT_SAMPLE_PINGPONG_LOOP 64 -#define IT_SAMPLE_PINGPONG_SUS_LOOP 128 - -#define IT_VIBRATO_SINE 0 -#define IT_VIBRATO_SAWTOOTH 1 /* Ramp down */ -#define IT_VIBRATO_SQUARE 2 -#define IT_VIBRATO_RANDOM 3 - -struct IT_SAMPLE -{ - unsigned char name[29]; - unsigned char filename[14]; - unsigned char flags; - unsigned char global_volume; - unsigned char default_volume; - unsigned char default_pan; - /* default_pan: - * 0-255 for XM - * ignored for MOD - * otherwise, 0-64, and add 128 to enable - */ - - long length; - long loop_start; - long loop_end; - long C5_speed; - long sus_loop_start; - long sus_loop_end; - - unsigned char vibrato_speed; - unsigned char vibrato_depth; - unsigned char vibrato_rate; - unsigned char vibrato_waveform; - - void *data; -}; - - - -#define IT_ENTRY_NOTE 1 -#define IT_ENTRY_INSTRUMENT 2 -#define IT_ENTRY_VOLPAN 4 -#define IT_ENTRY_EFFECT 8 - -#define IT_SET_END_ROW(entry) ((entry)->channel = 255) -#define IT_IS_END_ROW(entry) ((entry)->channel >= DUMB_IT_N_CHANNELS) - -#define IT_NOTE_OFF 255 -#define IT_NOTE_CUT 254 - -#define IT_ENVELOPE_SHIFT 8 - -#define IT_SURROUND 100 -#define IT_IS_SURROUND(pan) ((pan) > 64) -#define IT_IS_SURROUND_SHIFTED(pan) ((pan) > 64 << IT_ENVELOPE_SHIFT) - -#define IT_SET_SPEED 1 -#define IT_JUMP_TO_ORDER 2 -#define IT_BREAK_TO_ROW 3 -#define IT_VOLUME_SLIDE 4 -#define IT_PORTAMENTO_DOWN 5 -#define IT_PORTAMENTO_UP 6 -#define IT_TONE_PORTAMENTO 7 -#define IT_VIBRATO 8 -#define IT_TREMOR 9 -#define IT_ARPEGGIO 10 -#define IT_VOLSLIDE_VIBRATO 11 -#define IT_VOLSLIDE_TONEPORTA 12 -#define IT_SET_CHANNEL_VOLUME 13 -#define IT_CHANNEL_VOLUME_SLIDE 14 -#define IT_SET_SAMPLE_OFFSET 15 -#define IT_PANNING_SLIDE 16 -#define IT_RETRIGGER_NOTE 17 -#define IT_TREMOLO 18 -#define IT_S 19 -#define IT_SET_SONG_TEMPO 20 -#define IT_FINE_VIBRATO 21 -#define IT_SET_GLOBAL_VOLUME 22 -#define IT_GLOBAL_VOLUME_SLIDE 23 -#define IT_SET_PANNING 24 -#define IT_PANBRELLO 25 -#define IT_MIDI_MACRO 26 //see MIDI.TXT - -/* Some effects needed for XM compatibility */ -#define IT_XM_PORTAMENTO_DOWN 27 -#define IT_XM_PORTAMENTO_UP 28 -#define IT_XM_FINE_VOLSLIDE_DOWN 29 -#define IT_XM_FINE_VOLSLIDE_UP 30 -#define IT_XM_RETRIGGER_NOTE 31 -#define IT_XM_KEY_OFF 32 -#define IT_XM_SET_ENVELOPE_POSITION 33 - -#define IT_N_EFFECTS 34 - -/* These represent the top nibble of the command value. */ -#define IT_S_SET_FILTER 0 /* Greyed out in IT... */ -#define IT_S_SET_GLISSANDO_CONTROL 1 /* Greyed out in IT... */ -#define IT_S_FINETUNE 2 /* Greyed out in IT... */ -#define IT_S_SET_VIBRATO_WAVEFORM 3 -#define IT_S_SET_TREMOLO_WAVEFORM 4 -#define IT_S_SET_PANBRELLO_WAVEFORM 5 -#define IT_S_FINE_PATTERN_DELAY 6 -#define IT_S7 7 -#define IT_S_SET_PAN 8 -#define IT_S_SET_SURROUND_SOUND 9 -#define IT_S_SET_HIGH_OFFSET 10 -#define IT_S_PATTERN_LOOP 11 -#define IT_S_DELAYED_NOTE_CUT 12 -#define IT_S_NOTE_DELAY 13 -#define IT_S_PATTERN_DELAY 14 -#define IT_S_SET_MIDI_MACRO 15 - -/* -S0x Set filter -S1x Set glissando control -S2x Set finetune - - -S3x Set vibrato waveform to type x -S4x Set tremelo waveform to type x -S5x Set panbrello waveform to type x - Waveforms for commands S3x, S4x and S5x: - 0: Sine wave - 1: Ramp down - 2: Square wave - 3: Random wave -S6x Pattern delay for x ticks -S70 Past note cut -S71 Past note off -S72 Past note fade -S73 Set NNA to note cut -S74 Set NNA to continue -S75 Set NNA to note off -S76 Set NNA to note fade -S77 Turn off volume envelope -S78 Turn on volume envelope -S79 Turn off panning envelope -S7A Turn on panning envelope -S7B Turn off pitch envelope -S7C Turn on pitch envelope -S8x Set panning position -S91 Set surround sound -SAy Set high value of sample offset yxx00h -SB0 Set loopback point -SBx Loop x times to loopback point -SCx Note cut after x ticks -SDx Note delay for x ticks -SEx Pattern delay for x rows -SFx Set parameterised MIDI Macro -*/ - -struct IT_ENTRY -{ - unsigned char channel; /* End of row if channel >= DUMB_IT_N_CHANNELS */ - unsigned char mask; - unsigned char note; - unsigned char instrument; - unsigned char volpan; - unsigned char effect; - unsigned char effectvalue; -}; - - - -struct IT_PATTERN -{ - int n_rows; - int n_entries; - IT_ENTRY *entry; -}; - - - -#define IT_STEREO 1 -#define IT_USE_INSTRUMENTS 4 -#define IT_LINEAR_SLIDES 8 /* If not set, use Amiga slides */ -#define IT_OLD_EFFECTS 16 -#define IT_COMPATIBLE_GXX 32 - -/* Make sure IT_WAS_AN_XM and IT_WAS_A_MOD aren't set accidentally */ -#define IT_REAL_FLAGS 63 - -#define IT_WAS_AN_XM 64 /* Set for both XMs and MODs */ -#define IT_WAS_A_MOD 128 - -#define IT_ORDER_END 255 -#define IT_ORDER_SKIP 254 - -struct DUMB_IT_SIGDATA -{ - unsigned char name[29]; - - unsigned char *song_message; - - int n_orders; - int n_instruments; - int n_samples; - int n_patterns; - - int flags; - - int global_volume; - int mixing_volume; - int speed; - int tempo; - int pan_separation; - - unsigned char channel_pan[DUMB_IT_N_CHANNELS]; - unsigned char channel_volume[DUMB_IT_N_CHANNELS]; - - unsigned char *order; - unsigned char restart_position; /* for XM compatiblity */ - - IT_INSTRUMENT *instrument; - IT_SAMPLE *sample; - IT_PATTERN *pattern; - - IT_MIDI *midi; - - IT_CHECKPOINT *checkpoint; -}; - - - -struct IT_PLAYING_ENVELOPE -{ - int next_node; - int tick; - int value; -}; - - - -#define IT_PLAYING_BACKGROUND 1 -#define IT_PLAYING_SUSTAINOFF 2 -#define IT_PLAYING_FADING 4 -#define IT_PLAYING_DEAD 8 - -struct IT_PLAYING -{ - int flags; - - IT_CHANNEL *channel; - IT_SAMPLE *sample; - IT_INSTRUMENT *instrument; - IT_INSTRUMENT *env_instrument; - - unsigned short sampnum; - unsigned char instnum; - - unsigned char channel_volume; - - unsigned char volume; - unsigned short pan; - - unsigned char note; - - unsigned char filter_cutoff; - unsigned char filter_resonance; - - unsigned short true_filter_cutoff; /* These incorporate the filter envelope, and will not */ - unsigned char true_filter_resonance; /* be changed if they would be set to 127<<8 and 0. */ - - unsigned char vibrato_speed; - unsigned char vibrato_depth; - unsigned char vibrato_n; /* May be specified twice: volpan & effect. */ - unsigned char vibrato_time; - - unsigned char tremolo_speed; - unsigned char tremolo_depth; - unsigned char tremolo_time; - - unsigned char sample_vibrato_time; - int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */ - - int slide; - float delta; - - IT_PLAYING_ENVELOPE volume_envelope; - IT_PLAYING_ENVELOPE pan_envelope; - IT_PLAYING_ENVELOPE pitch_envelope; - - int fadeoutcount; - - IT_FILTER_STATE filter_state[2]; /* Left and right */ - - DUMB_RESAMPLER resampler; - - /* time_lost is used to emulate Impulse Tracker's sample looping - * characteristics. When time_lost is added to pos, the result represents - * the position in the theoretical version of the sample where all loops - * have been expanded. If this is stored, the resampling helpers will - * safely convert it for use with new loop boundaries. The situation is - * slightly more complicated if dir == -1 when the change takes place; we - * must reflect pos off the loop end point and set dir to 1 before - * proceeding. - */ - long time_lost; -}; - - - -#define IT_CHANNEL_MUTED 1 - -struct IT_CHANNEL -{ - int flags; - - unsigned char volume; - signed char volslide; - signed char xm_volslide; - signed char panslide; - - /* xm_volslide is used for volume slides done in the volume column in an - * XM file, since it seems the volume column slide is applied first, - * followed by clamping, followed by the effects column slide. IT does - * not exhibit this behaviour, so xm_volslide is maintained at zero. - */ - - unsigned char pan; - unsigned short truepan; - - unsigned char channelvolume; - signed char channelvolslide; - - unsigned char instrument; - unsigned char note; - - unsigned char SFmacro; - - unsigned char filter_cutoff; - unsigned char filter_resonance; - - unsigned char key_off_count; - unsigned char note_cut_count; - unsigned char note_delay_count; - IT_ENTRY *note_delay_entry; - - int arpeggio; - unsigned char retrig; - unsigned char xm_retrig; - int retrig_tick; - - unsigned char tremor; - unsigned char tremor_time; /* Bit 6 set if note on; bit 7 set if tremor active. */ - - int portamento; - int toneporta; - unsigned char destnote; - - /** WARNING - for neatness, should one or both of these be in the IT_PLAYING struct? */ - unsigned short sample; - unsigned char truenote; - - unsigned char midi_state; - - signed char lastvolslide; - unsigned char lastDKL; - unsigned char lastEF; /* Doubles as last portamento up for XM files */ - unsigned char lastG; - unsigned char lastHspeed; - unsigned char lastHdepth; - unsigned char lastRspeed; - unsigned char lastRdepth; - unsigned char lastI; - unsigned char lastJ; /* Doubles as last portamento down for XM files */ - unsigned char lastN; - unsigned char lastO; - unsigned char high_offset; - unsigned char lastP; - unsigned char lastQ; - unsigned char lastS; - unsigned char pat_loop_row; - unsigned char pat_loop_count; - unsigned char pat_loop_end_row; /* Used to catch infinite pattern loops */ - unsigned char lastW; - - unsigned char xm_lastE1; - unsigned char xm_lastE2; - unsigned char xm_lastEA; - unsigned char xm_lastEB; - unsigned char xm_lastX1; - unsigned char xm_lastX2; - - IT_PLAYING *playing; -}; - - - -struct DUMB_IT_SIGRENDERER -{ - DUMB_IT_SIGDATA *sigdata; - - int n_channels; - - unsigned char globalvolume; - signed char globalvolslide; - - unsigned char tempo; - signed char temposlide; - - IT_CHANNEL channel[DUMB_IT_N_CHANNELS]; - - IT_PLAYING *playing[DUMB_IT_N_NNA_CHANNELS]; - - int tick; - int speed; - int rowcount; - - int order; /* Set to -1 if the song is terminated by a callback. */ - int row; - int processorder; - int processrow; - int breakrow; - int pat_loop_row; - - int n_rows; - - IT_ENTRY *entry_start; - IT_ENTRY *entry; - IT_ENTRY *entry_end; - - long time_left; /* Time before the next tick is processed */ - int sub_time_left; - - DUMB_CLICK_REMOVER **click_remover; - - IT_CALLBACKS *callbacks; -}; - - - -struct IT_CHECKPOINT -{ - IT_CHECKPOINT *next; - long time; - DUMB_IT_SIGRENDERER *sigrenderer; -}; - - - -struct IT_CALLBACKS -{ - int (*loop)(void *data); - void *loop_data; - /* Return 1 to prevent looping; the music will terminate abruptly. If you - * want to make the music stop but allow samples to fade (beware, as they - * might not fade at all!), use dumb_it_sr_set_speed() and set the speed - * to 0. Note that xm_speed_zero() will not be called if you set the - * speed manually, and also that this will work for IT and S3M files even - * though the music can't stop in this way by itself. - */ - - int (*xm_speed_zero)(void *data); - void *xm_speed_zero_data; - /* Return 1 to terminate the mod, without letting samples fade. */ - - int (*midi)(void *data, int channel, unsigned char byte); - void *midi_data; - /* Return 1 to prevent DUMB from subsequently interpreting the MIDI bytes - * itself. In other words, return 1 if the Zxx macros in an IT file are - * controlling filters and shouldn't be. - */ -}; - - - -void _dumb_it_end_sigrenderer(sigrenderer_t *sigrenderer); -void _dumb_it_unload_sigdata(sigdata_t *vsigdata); - -extern DUH_SIGTYPE_DESC _dumb_sigtype_it; - - - -#define XM_APPREGIO 0 -#define XM_PORTAMENTO_UP 1 -#define XM_PORTAMENTO_DOWN 2 -#define XM_TONE_PORTAMENTO 3 -#define XM_VIBRATO 4 -#define XM_VOLSLIDE_TONEPORTA 5 -#define XM_VOLSLIDE_VIBRATO 6 -#define XM_TREMOLO 7 -#define XM_SET_PANNING 8 -#define XM_SAMPLE_OFFSET 9 -#define XM_VOLUME_SLIDE 10 /* A */ -#define XM_POSITION_JUMP 11 /* B */ -#define XM_SET_CHANNEL_VOLUME 12 /* C */ -#define XM_PATTERN_BREAK 13 /* D */ -#define XM_E 14 /* E */ -#define XM_SET_TEMPO_BPM 15 /* F */ -#define XM_SET_GLOBAL_VOLUME 16 /* G */ -#define XM_GLOBAL_VOLUME_SLIDE 17 /* H */ -#define XM_KEY_OFF 20 /* K (undocumented) */ -#define XM_SET_ENVELOPE_POSITION 21 /* L */ -#define XM_PANNING_SLIDE 25 /* P */ -#define XM_MULTI_RETRIG 27 /* R */ -#define XM_TREMOR 29 /* T */ -#define XM_X 33 /* X */ -#define XM_N_EFFECTS (10+26) - -#define XM_E_SET_FILTER 0x0 -#define XM_E_FINE_PORTA_UP 0x1 -#define XM_E_FINE_PORTA_DOWN 0x2 -#define XM_E_SET_GLISSANDO_CONTROL 0x3 -#define XM_E_SET_VIBRATO_CONTROL 0x4 -#define XM_E_SET_FINETUNE 0x5 -#define XM_E_SET_LOOP 0x6 -#define XM_E_SET_TREMOLO_CONTROL 0x7 -#define XM_E_RETRIG_NOTE 0x9 -#define XM_E_FINE_VOLSLIDE_UP 0xA -#define XM_E_FINE_VOLSLIDE_DOWN 0xB -#define XM_E_NOTE_CUT 0xC -#define XM_E_NOTE_DELAY 0xD -#define XM_E_PATTERN_DELAY 0xE - -#define XM_X_EXTRAFINE_PORTA_UP 1 -#define XM_X_EXTRAFINE_PORTA_DOWN 2 - -/* To make my life a bit simpler during conversion, effect E:xy is converted - * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That - * way, these effects can be manipulated like regular effects. - */ -#define EBASE (XM_N_EFFECTS) -#define XBASE (EBASE+16) -#define SBASE (IT_N_EFFECTS) - -#define EFFECT_VALUE(x, y) (((x)<<4)|(y)) -#define HIGH(v) ((v)>>4) -#define LOW(v) ((v)&0x0F) -#define SET_HIGH(v, x) v = (((x)<<4)|((v)&0x0F)) -#define SET_LOW(v, y) v = (((v)&0xF0)|(y)) -#define BCD_TO_NORMAL(v) (HIGH(v)*10+LOW(v)) - - - -#if 0 -unsigned char **_dumb_malloc2(int w, int h); -void _dumb_free2(unsigned char **line); -#endif - -void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry); -int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata); - - - -#endif /* INTERNAL_IT_H */ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * internal/it.h - Internal stuff for IT playback / / \ \ + * and MOD/XM/S3M conversion. | < / \_ + * | \/ /\ / + * This header file provides access to the \_ / > / + * internal structure of DUMB, and is liable | \ / / + * to change, mutate or cease to exist at any | ' / + * moment. Include it at your own peril. \__/ + * + * ... + * + * Seriously. You don't need access to anything in this file. All right, you + * probably do actually. But if you use it, you will be relying on a specific + * version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please + * contact the authors so that we can provide a public API for what you need. + */ + +#ifndef INTERNAL_IT_H +#define INTERNAL_IT_H + + +#define BIT_ARRAY_BULLSHIT + +#include + +#ifdef __FRAMEWORK__ +#include +#include +#else +#include "barray.h" +#include "tarray.h" +#endif + + +/** TO DO: THINK ABOUT THE FOLLOWING: + +sigdata->flags & IT_COMPATIBLE_GXX + + Bit 5: On = Link Effect G's memory with Effect E/F. Also + Gxx with an instrument present will cause the + envelopes to be retriggered. If you change a + sample on a row with Gxx, it'll adjust the + frequency of the current note according to: + + NewFrequency = OldFrequency * NewC5 / OldC5; +*/ + + + +/* These #defines are TEMPORARY. They are used to write alternative code to + * handle ambiguities in the format specification. The correct code in each + * case will be determined most likely by experimentation. + */ +//#define STEREO_SAMPLES_COUNT_AS_TWO +#define INVALID_ORDERS_END_SONG +#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP +#define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM + + + +#define SIGTYPE_IT DUMB_ID('I', 'T', ' ', ' ') + +#define IT_SIGNATURE DUMB_ID('I', 'M', 'P', 'M') +#define IT_INSTRUMENT_SIGNATURE DUMB_ID('I', 'M', 'P', 'I') +#define IT_SAMPLE_SIGNATURE DUMB_ID('I', 'M', 'P', 'S') + +// olivier sux +#define IT_MPTX_SIGNATURE DUMB_ID('X', 'T', 'P', 'M') +#define IT_INSM_SIGNATURE DUMB_ID('M', 'S', 'N', 'I') + + +/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get + * the interval between ticks. + */ +#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6)) + + + +/* I'm not going to try to explain this, because I didn't derive it very + * formally ;) + */ +/* #define AMIGA_DIVISOR ((float)(4.0 * 14317056.0)) */ +/* I believe the following one to be more accurate. */ +//#define AMIGA_DIVISOR ((float)(8.0 * 7159090.5)) +#define AMIGA_CLOCK 3546895 +#define AMIGA_DIVISOR ((float)(16.0 * AMIGA_CLOCK)) + + + +typedef struct IT_MIDI IT_MIDI; +typedef struct IT_FILTER_STATE IT_FILTER_STATE; +typedef struct IT_ENVELOPE IT_ENVELOPE; +typedef struct IT_INSTRUMENT IT_INSTRUMENT; +typedef struct IT_SAMPLE IT_SAMPLE; +typedef struct IT_ENTRY IT_ENTRY; +typedef struct IT_PATTERN IT_PATTERN; +typedef struct IT_PLAYING_ENVELOPE IT_PLAYING_ENVELOPE; +typedef struct IT_PLAYING IT_PLAYING; +typedef struct IT_CHANNEL IT_CHANNEL; +typedef struct IT_CHECKPOINT IT_CHECKPOINT; +typedef struct IT_CALLBACKS IT_CALLBACKS; + + + +struct IT_MIDI +{ + unsigned char SFmacro[16][16]; // read these from 0x120 + unsigned char SFmacrolen[16]; + unsigned short SFmacroz[16]; /* Bitfield; bit 0 set = z in first position */ + unsigned char Zmacro[128][16]; // read these from 0x320 + unsigned char Zmacrolen[128]; +}; + + + +struct IT_FILTER_STATE +{ + sample_t currsample, prevsample; +}; + + + +#define IT_ENVELOPE_ON 1 +#define IT_ENVELOPE_LOOP_ON 2 +#define IT_ENVELOPE_SUSTAIN_LOOP 4 +#define IT_ENVELOPE_CARRY 8 +#define IT_ENVELOPE_PITCH_IS_FILTER 128 + +struct IT_ENVELOPE +{ + unsigned char flags; + unsigned char n_nodes; + unsigned char loop_start; + unsigned char loop_end; + unsigned char sus_loop_start; + unsigned char sus_loop_end; + signed char node_y[25]; + unsigned short node_t[25]; +}; + + + +#define NNA_NOTE_CUT 0 +#define NNA_NOTE_CONTINUE 1 +#define NNA_NOTE_OFF 2 +#define NNA_NOTE_FADE 3 + +#define DCT_OFF 0 +#define DCT_NOTE 1 +#define DCT_SAMPLE 2 +#define DCT_INSTRUMENT 3 + +#define DCA_NOTE_CUT 0 +#define DCA_NOTE_OFF 1 +#define DCA_NOTE_FADE 2 + +struct IT_INSTRUMENT +{ + unsigned char name[27]; + unsigned char filename[14]; + + int fadeout; + + IT_ENVELOPE volume_envelope; + IT_ENVELOPE pan_envelope; + IT_ENVELOPE pitch_envelope; + + unsigned char new_note_action; + unsigned char dup_check_type; + unsigned char dup_check_action; + signed char pp_separation; + unsigned char pp_centre; + unsigned char global_volume; + unsigned char default_pan; + unsigned char random_volume; + unsigned char random_pan; + + unsigned char filter_cutoff; + unsigned char filter_resonance; + + unsigned char map_note[120]; + unsigned short map_sample[120]; + + //int output; +}; + + + +#define IT_SAMPLE_EXISTS 1 +#define IT_SAMPLE_16BIT 2 +#define IT_SAMPLE_STEREO 4 +#define IT_SAMPLE_LOOP 16 +#define IT_SAMPLE_SUS_LOOP 32 +#define IT_SAMPLE_PINGPONG_LOOP 64 +#define IT_SAMPLE_PINGPONG_SUS_LOOP 128 + +#define IT_VIBRATO_SINE 0 +#define IT_VIBRATO_SAWTOOTH 1 +#define IT_VIBRATO_SQUARE 2 +#define IT_VIBRATO_RANDOM 3 +#define IT_VIBRATO_XM_SQUARE 4 +#define IT_VIBRATO_RAMP_DOWN 5 +#define IT_VIBRATO_RAMP_UP 6 + +struct IT_SAMPLE +{ + unsigned char name[35]; + unsigned char filename[15]; + unsigned char flags; + unsigned char global_volume; + unsigned char default_volume; + unsigned char default_pan; + /* default_pan: + * 0-255 for XM + * ignored for MOD + * otherwise, 0-64, and add 128 to enable + */ + + long length; + long loop_start; + long loop_end; + long C5_speed; + long sus_loop_start; + long sus_loop_end; + + unsigned char vibrato_speed; + unsigned char vibrato_depth; + unsigned char vibrato_rate; + unsigned char vibrato_waveform; + + signed short finetune; + + void *data; + + int max_resampling_quality; +}; + + + +#define IT_ENTRY_NOTE 1 +#define IT_ENTRY_INSTRUMENT 2 +#define IT_ENTRY_VOLPAN 4 +#define IT_ENTRY_EFFECT 8 + +#define IT_SET_END_ROW(entry) ((entry)->channel = 255) +#define IT_IS_END_ROW(entry) ((entry)->channel >= DUMB_IT_N_CHANNELS) + +#define IT_NOTE_OFF 255 +#define IT_NOTE_CUT 254 + +#define IT_ENVELOPE_SHIFT 8 + +#define IT_SURROUND 100 +#define IT_IS_SURROUND(pan) ((pan) > 64) +#define IT_IS_SURROUND_SHIFTED(pan) ((pan) > 64 << IT_ENVELOPE_SHIFT) + +#define IT_SET_SPEED 1 +#define IT_JUMP_TO_ORDER 2 +#define IT_BREAK_TO_ROW 3 +#define IT_VOLUME_SLIDE 4 +#define IT_PORTAMENTO_DOWN 5 +#define IT_PORTAMENTO_UP 6 +#define IT_TONE_PORTAMENTO 7 +#define IT_VIBRATO 8 +#define IT_TREMOR 9 +#define IT_ARPEGGIO 10 +#define IT_VOLSLIDE_VIBRATO 11 +#define IT_VOLSLIDE_TONEPORTA 12 +#define IT_SET_CHANNEL_VOLUME 13 +#define IT_CHANNEL_VOLUME_SLIDE 14 +#define IT_SET_SAMPLE_OFFSET 15 +#define IT_PANNING_SLIDE 16 +#define IT_RETRIGGER_NOTE 17 +#define IT_TREMOLO 18 +#define IT_S 19 +#define IT_SET_SONG_TEMPO 20 +#define IT_FINE_VIBRATO 21 +#define IT_SET_GLOBAL_VOLUME 22 +#define IT_GLOBAL_VOLUME_SLIDE 23 +#define IT_SET_PANNING 24 +#define IT_PANBRELLO 25 +#define IT_MIDI_MACRO 26 //see MIDI.TXT + +/* Some effects needed for XM compatibility */ +#define IT_XM_PORTAMENTO_DOWN 27 +#define IT_XM_PORTAMENTO_UP 28 +#define IT_XM_FINE_VOLSLIDE_DOWN 29 +#define IT_XM_FINE_VOLSLIDE_UP 30 +#define IT_XM_RETRIGGER_NOTE 31 +#define IT_XM_KEY_OFF 32 +#define IT_XM_SET_ENVELOPE_POSITION 33 + +/* More effects needed for PTM compatibility */ +#define IT_PTM_NOTE_SLIDE_DOWN 34 +#define IT_PTM_NOTE_SLIDE_UP 35 +#define IT_PTM_NOTE_SLIDE_DOWN_RETRIG 36 +#define IT_PTM_NOTE_SLIDE_UP_RETRIG 37 + +/* More effects needed for OKT compatibility */ +#define IT_OKT_NOTE_SLIDE_DOWN 38 +#define IT_OKT_NOTE_SLIDE_DOWN_ROW 39 +#define IT_OKT_NOTE_SLIDE_UP 40 +#define IT_OKT_NOTE_SLIDE_UP_ROW 41 +#define IT_OKT_ARPEGGIO_3 42 +#define IT_OKT_ARPEGGIO_4 43 +#define IT_OKT_ARPEGGIO_5 44 +#define IT_OKT_VOLUME_SLIDE_DOWN 45 +#define IT_OKT_VOLUME_SLIDE_UP 46 + +#define IT_N_EFFECTS 47 + +/* These represent the top nibble of the command value. */ +#define IT_S_SET_FILTER 0 /* Greyed out in IT... */ +#define IT_S_SET_GLISSANDO_CONTROL 1 /* Greyed out in IT... */ +#define IT_S_FINETUNE 2 /* Greyed out in IT... */ +#define IT_S_SET_VIBRATO_WAVEFORM 3 +#define IT_S_SET_TREMOLO_WAVEFORM 4 +#define IT_S_SET_PANBRELLO_WAVEFORM 5 +#define IT_S_FINE_PATTERN_DELAY 6 +#define IT_S7 7 +#define IT_S_SET_PAN 8 +#define IT_S_SET_SURROUND_SOUND 9 +#define IT_S_SET_HIGH_OFFSET 10 +#define IT_S_PATTERN_LOOP 11 +#define IT_S_DELAYED_NOTE_CUT 12 +#define IT_S_NOTE_DELAY 13 +#define IT_S_PATTERN_DELAY 14 +#define IT_S_SET_MIDI_MACRO 15 + +/* +S0x Set filter +S1x Set glissando control +S2x Set finetune + + +S3x Set vibrato waveform to type x +S4x Set tremelo waveform to type x +S5x Set panbrello waveform to type x + Waveforms for commands S3x, S4x and S5x: + 0: Sine wave + 1: Ramp down + 2: Square wave + 3: Random wave +S6x Pattern delay for x ticks +S70 Past note cut +S71 Past note off +S72 Past note fade +S73 Set NNA to note cut +S74 Set NNA to continue +S75 Set NNA to note off +S76 Set NNA to note fade +S77 Turn off volume envelope +S78 Turn on volume envelope +S79 Turn off panning envelope +S7A Turn on panning envelope +S7B Turn off pitch envelope +S7C Turn on pitch envelope +S8x Set panning position +S91 Set surround sound +SAy Set high value of sample offset yxx00h +SB0 Set loopback point +SBx Loop x times to loopback point +SCx Note cut after x ticks +SDx Note delay for x ticks +SEx Pattern delay for x rows +SFx Set parameterised MIDI Macro +*/ + +struct IT_ENTRY +{ + unsigned char channel; /* End of row if channel >= DUMB_IT_N_CHANNELS */ + unsigned char mask; + unsigned char note; + unsigned char instrument; + unsigned char volpan; + unsigned char effect; + unsigned char effectvalue; +}; + + + +struct IT_PATTERN +{ + int n_rows; + int n_entries; + IT_ENTRY *entry; +}; + + + +#define IT_STEREO 1 +#define IT_USE_INSTRUMENTS 4 +#define IT_LINEAR_SLIDES 8 /* If not set, use Amiga slides */ +#define IT_OLD_EFFECTS 16 +#define IT_COMPATIBLE_GXX 32 + +/* Make sure IT_WAS_AN_XM and IT_WAS_A_MOD aren't set accidentally */ +#define IT_REAL_FLAGS 63 + +#define IT_WAS_AN_XM 64 /* Set for both XMs and MODs */ +#define IT_WAS_A_MOD 128 + +#define IT_WAS_AN_S3M 256 + +#define IT_WAS_A_PTM 512 + +#define IT_WAS_A_669 1024 + +#define IT_WAS_AN_OKT 2048 + +#define IT_WAS_AN_STM 4096 + +#define IT_WAS_PROCESSED 8192 /* Will be set the first time a sigdata passes through a sigrenderer */ + +#define IT_ORDER_END 255 +#define IT_ORDER_SKIP 254 + +struct DUMB_IT_SIGDATA +{ + unsigned char name[65]; + + unsigned char *song_message; + + int n_orders; + int n_instruments; + int n_samples; + int n_patterns; + int n_pchannels; + + int flags; + + int global_volume; + int mixing_volume; + int speed; + int tempo; + int pan_separation; + + unsigned char channel_pan[DUMB_IT_N_CHANNELS]; + unsigned char channel_volume[DUMB_IT_N_CHANNELS]; + + unsigned char *order; + unsigned char restart_position; /* for XM compatiblity */ + + IT_INSTRUMENT *instrument; + IT_SAMPLE *sample; + IT_PATTERN *pattern; + + IT_MIDI *midi; + + IT_CHECKPOINT *checkpoint; +}; + + + +struct IT_PLAYING_ENVELOPE +{ + int next_node; + int tick; + int value; +}; + + + +#define IT_PLAYING_BACKGROUND 1 +#define IT_PLAYING_SUSTAINOFF 2 +#define IT_PLAYING_FADING 4 +#define IT_PLAYING_DEAD 8 +#define IT_PLAYING_REVERSE 16 + +struct IT_PLAYING +{ + int flags; + + int resampling_quality; + + IT_CHANNEL *channel; + IT_SAMPLE *sample; + IT_INSTRUMENT *instrument; + IT_INSTRUMENT *env_instrument; + + unsigned short sampnum; + unsigned char instnum; + + unsigned char declick_stage; + float declick_volume; + + float float_volume[2]; + float ramp_volume[2]; + float ramp_delta[2]; + + unsigned char channel_volume; + + unsigned char volume; + unsigned short pan; + + signed char volume_offset, panning_offset; + + unsigned char note; + + unsigned char enabled_envelopes; + + unsigned char filter_cutoff; + unsigned char filter_resonance; + + unsigned short true_filter_cutoff; /* These incorporate the filter envelope, and will not */ + unsigned char true_filter_resonance; /* be changed if they would be set to 127<<8 and 0. */ + + unsigned char vibrato_speed; + unsigned char vibrato_depth; + unsigned char vibrato_n; /* May be specified twice: volpan & effect. */ + unsigned char vibrato_time; + unsigned char vibrato_waveform; + + unsigned char tremolo_speed; + unsigned char tremolo_depth; + unsigned char tremolo_time; + unsigned char tremolo_waveform; + + unsigned char panbrello_speed; + unsigned char panbrello_depth; + unsigned char panbrello_time; + unsigned char panbrello_waveform; + signed char panbrello_random; + + unsigned char sample_vibrato_time; + unsigned char sample_vibrato_waveform; + int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */ + + int slide; + float delta; + int finetune; + + IT_PLAYING_ENVELOPE volume_envelope; + IT_PLAYING_ENVELOPE pan_envelope; + IT_PLAYING_ENVELOPE pitch_envelope; + + int fadeoutcount; + + IT_FILTER_STATE filter_state[2]; /* Left and right */ + + DUMB_RESAMPLER resampler; + + /* time_lost is used to emulate Impulse Tracker's sample looping + * characteristics. When time_lost is added to pos, the result represents + * the position in the theoretical version of the sample where all loops + * have been expanded. If this is stored, the resampling helpers will + * safely convert it for use with new loop boundaries. The situation is + * slightly more complicated if dir == -1 when the change takes place; we + * must reflect pos off the loop end point and set dir to 1 before + * proceeding. + */ + long time_lost; + + //int output; +}; + + + +#define IT_CHANNEL_MUTED 1 + +#define IT_ENV_VOLUME 1 +#define IT_ENV_PANNING 2 +#define IT_ENV_PITCH 4 + +struct IT_CHANNEL +{ + int flags; + + unsigned char volume; + signed char volslide; + signed char xm_volslide; + signed char panslide; + + /* xm_volslide is used for volume slides done in the volume column in an + * XM file, since it seems the volume column slide is applied first, + * followed by clamping, followed by the effects column slide. IT does + * not exhibit this behaviour, so xm_volslide is maintained at zero. + */ + + unsigned char pan; + unsigned short truepan; + + unsigned char channelvolume; + signed char channelvolslide; + + unsigned char instrument; + unsigned char note; + + unsigned char SFmacro; + + unsigned char filter_cutoff; + unsigned char filter_resonance; + + unsigned char key_off_count; + unsigned char note_cut_count; + unsigned char note_delay_count; + IT_ENTRY *note_delay_entry; + + unsigned char new_note_action; + + unsigned char const* arpeggio_table; + signed char arpeggio_offsets[3]; + + int arpeggio_shift; + unsigned char retrig; + unsigned char xm_retrig; + int retrig_tick; + + unsigned char tremor; + unsigned char tremor_time; /* Bit 6 set if note on; bit 7 set if tremor active. */ + + unsigned char vibrato_waveform; + unsigned char tremolo_waveform; + unsigned char panbrello_waveform; + + int portamento; + int toneporta; + int toneslide; + unsigned char toneslide_tick, last_toneslide_tick, ptm_toneslide, ptm_last_toneslide, okt_toneslide; + unsigned char destnote; + unsigned char toneslide_retrig; + + unsigned char glissando; + + /** WARNING - for neatness, should one or both of these be in the IT_PLAYING struct? */ + unsigned short sample; + unsigned char truenote; + + unsigned char midi_state; + + signed char lastvolslide; + unsigned char lastDKL; + unsigned char lastEF; /* Doubles as last portamento up for XM files */ + unsigned char lastG; + unsigned char lastHspeed; + unsigned char lastHdepth; + unsigned char lastRspeed; + unsigned char lastRdepth; + unsigned char lastYspeed; + unsigned char lastYdepth; + unsigned char lastI; + unsigned char lastJ; /* Doubles as last portamento down for XM files */ + unsigned char lastN; + unsigned char lastO; + unsigned char high_offset; + unsigned char lastP; + unsigned char lastQ; + unsigned char lastS; + unsigned char pat_loop_row; + unsigned char pat_loop_count; + unsigned char pat_loop_end_row; /* Used to catch infinite pattern loops */ + unsigned char lastW; + + unsigned char xm_lastE1; + unsigned char xm_lastE2; + unsigned char xm_lastEA; + unsigned char xm_lastEB; + unsigned char xm_lastX1; + unsigned char xm_lastX2; + + unsigned char inv_loop_delay; + unsigned char inv_loop_speed; + int inv_loop_offset; + + IT_PLAYING *playing; + +#ifdef BIT_ARRAY_BULLSHIT + void * played_patjump; + int played_patjump_order; +#endif + + //int output; +}; + + + +struct DUMB_IT_SIGRENDERER +{ + DUMB_IT_SIGDATA *sigdata; + + int n_channels; + + int resampling_quality; + + unsigned char globalvolume; + signed char globalvolslide; + + int tempo; + signed char temposlide; + + IT_CHANNEL channel[DUMB_IT_N_CHANNELS]; + + IT_PLAYING *playing[DUMB_IT_N_NNA_CHANNELS]; + + int tick; + int speed; + int rowcount; + + int order; /* Set to -1 if the song is terminated by a callback. */ + int row; + int processorder; + int processrow; + int breakrow; + + int restart_position; + + int n_rows; + + IT_ENTRY *entry_start; + IT_ENTRY *entry; + IT_ENTRY *entry_end; + + long time_left; /* Time before the next tick is processed */ + int sub_time_left; + + DUMB_CLICK_REMOVER **click_remover; + + IT_CALLBACKS *callbacks; + +#ifdef BIT_ARRAY_BULLSHIT + /* bit array, which rows are played, only checked by pattern break or loop commands */ + void * played; + + /* + Loop indicator for internal processes, may also be useful for external processes + 0 - Not looped + 1 - Looped + -1 - Continued past loop + */ + int looped; + + /* + Kept until looped + */ + LONG_LONG time_played; + + void * row_timekeeper; +#endif + + long gvz_time; + int gvz_sub_time; + + int ramp_style; + + //int max_output; +}; + + + +struct IT_CHECKPOINT +{ + IT_CHECKPOINT *next; + long time; + DUMB_IT_SIGRENDERER *sigrenderer; +}; + + + +struct IT_CALLBACKS +{ + int (*loop)(void *data); + void *loop_data; + /* Return 1 to prevent looping; the music will terminate abruptly. If you + * want to make the music stop but allow samples to fade (beware, as they + * might not fade at all!), use dumb_it_sr_set_speed() and set the speed + * to 0. Note that xm_speed_zero() will not be called if you set the + * speed manually, and also that this will work for IT and S3M files even + * though the music can't stop in this way by itself. + */ + + int (*xm_speed_zero)(void *data); + void *xm_speed_zero_data; + /* Return 1 to terminate the mod, without letting samples fade. */ + + int (*midi)(void *data, int channel, unsigned char byte); + void *midi_data; + /* Return 1 to prevent DUMB from subsequently interpreting the MIDI bytes + * itself. In other words, return 1 if the Zxx macros in an IT file are + * controlling filters and shouldn't be. + */ + + int (*global_volume_zero)(void *data); + void *global_volume_zero_data; + /* Return 1 to terminate the module when global volume is set to zero. */ +}; + + + +void _dumb_it_end_sigrenderer(sigrenderer_t *sigrenderer); +void _dumb_it_unload_sigdata(sigdata_t *vsigdata); + +extern DUH_SIGTYPE_DESC _dumb_sigtype_it; + + + +#define XM_APPREGIO 0 +#define XM_PORTAMENTO_UP 1 +#define XM_PORTAMENTO_DOWN 2 +#define XM_TONE_PORTAMENTO 3 +#define XM_VIBRATO 4 +#define XM_VOLSLIDE_TONEPORTA 5 +#define XM_VOLSLIDE_VIBRATO 6 +#define XM_TREMOLO 7 +#define XM_SET_PANNING 8 +#define XM_SAMPLE_OFFSET 9 +#define XM_VOLUME_SLIDE 10 /* A */ +#define XM_POSITION_JUMP 11 /* B */ +#define XM_SET_CHANNEL_VOLUME 12 /* C */ +#define XM_PATTERN_BREAK 13 /* D */ +#define XM_E 14 /* E */ +#define XM_SET_TEMPO_BPM 15 /* F */ +#define XM_SET_GLOBAL_VOLUME 16 /* G */ +#define XM_GLOBAL_VOLUME_SLIDE 17 /* H */ +#define XM_KEY_OFF 20 /* K (undocumented) */ +#define XM_SET_ENVELOPE_POSITION 21 /* L */ +#define XM_PANNING_SLIDE 25 /* P */ +#define XM_MULTI_RETRIG 27 /* R */ +#define XM_TREMOR 29 /* T */ +#define XM_X 33 /* X */ +#define XM_N_EFFECTS (10+26) + +#define XM_E_SET_FILTER 0x0 +#define XM_E_FINE_PORTA_UP 0x1 +#define XM_E_FINE_PORTA_DOWN 0x2 +#define XM_E_SET_GLISSANDO_CONTROL 0x3 +#define XM_E_SET_VIBRATO_CONTROL 0x4 +#define XM_E_SET_FINETUNE 0x5 +#define XM_E_SET_LOOP 0x6 +#define XM_E_SET_TREMOLO_CONTROL 0x7 +#define XM_E_SET_PANNING 0x8 +#define XM_E_RETRIG_NOTE 0x9 +#define XM_E_FINE_VOLSLIDE_UP 0xA +#define XM_E_FINE_VOLSLIDE_DOWN 0xB +#define XM_E_NOTE_CUT 0xC +#define XM_E_NOTE_DELAY 0xD +#define XM_E_PATTERN_DELAY 0xE +#define XM_E_SET_MIDI_MACRO 0xF + +#define XM_X_EXTRAFINE_PORTA_UP 1 +#define XM_X_EXTRAFINE_PORTA_DOWN 2 + +/* To make my life a bit simpler during conversion, effect E:xy is converted + * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That + * way, these effects can be manipulated like regular effects. + */ +#define EBASE (XM_N_EFFECTS) +#define XBASE (EBASE+16) +#define SBASE (IT_N_EFFECTS) + +#define EFFECT_VALUE(x, y) (((x)<<4)|(y)) +#define HIGH(v) ((v)>>4) +#define LOW(v) ((v)&0x0F) +#define SET_HIGH(v, x) v = (((x)<<4)|((v)&0x0F)) +#define SET_LOW(v, y) v = (((v)&0xF0)|(y)) +#define BCD_TO_NORMAL(v) (HIGH(v)*10+LOW(v)) + + + +#if 0 +unsigned char **_dumb_malloc2(int w, int h); +void _dumb_free2(unsigned char **line); +#endif + +void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod); +int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata); + + +#define PTM_APPREGIO 0 +#define PTM_PORTAMENTO_UP 1 +#define PTM_PORTAMENTO_DOWN 2 +#define PTM_TONE_PORTAMENTO 3 +#define PTM_VIBRATO 4 +#define PTM_VOLSLIDE_TONEPORTA 5 +#define PTM_VOLSLIDE_VIBRATO 6 +#define PTM_TREMOLO 7 +#define PTM_SAMPLE_OFFSET 9 +#define PTM_VOLUME_SLIDE 10 /* A */ +#define PTM_POSITION_JUMP 11 /* B */ +#define PTM_SET_CHANNEL_VOLUME 12 /* C */ +#define PTM_PATTERN_BREAK 13 /* D */ +#define PTM_E 14 /* E */ +#define PTM_SET_TEMPO_BPM 15 /* F */ +#define PTM_SET_GLOBAL_VOLUME 16 /* G */ +#define PTM_RETRIGGER 17 /* H */ +#define PTM_FINE_VIBRATO 18 /* I */ +#define PTM_NOTE_SLIDE_UP 19 /* J */ +#define PTM_NOTE_SLIDE_DOWN 20 /* K */ +#define PTM_NOTE_SLIDE_UP_RETRIG 21 /* L */ +#define PTM_NOTE_SLIDE_DOWN_RETRIG 22 /* M */ +#define PTM_N_EFFECTS 23 + +#define PTM_E_FINE_PORTA_DOWN 0x1 +#define PTM_E_FINE_PORTA_UP 0x2 +#define PTM_E_SET_VIBRATO_CONTROL 0x4 +#define PTM_E_SET_FINETUNE 0x5 +#define PTM_E_SET_LOOP 0x6 +#define PTM_E_SET_TREMOLO_CONTROL 0x7 +#define PTM_E_SET_PANNING 0x8 +#define PTM_E_RETRIG_NOTE 0x9 +#define PTM_E_FINE_VOLSLIDE_UP 0xA +#define PTM_E_FINE_VOLSLIDE_DOWN 0xB +#define PTM_E_NOTE_CUT 0xC +#define PTM_E_NOTE_DELAY 0xD +#define PTM_E_PATTERN_DELAY 0xE + +/* To make my life a bit simpler during conversion, effect E:xy is converted + * to effect number EBASE+x:y. The same applies to effect X, and IT's S. That + * way, these effects can be manipulated like regular effects. + */ +#define PTM_EBASE (PTM_N_EFFECTS) + +void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry); + +long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f); + +void _dumb_it_interleave_stereo_sample(IT_SAMPLE *sample); + +#endif /* INTERNAL_IT_H */ diff --git a/Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h b/Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h new file mode 100644 index 000000000..a691697f0 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/lanczos_resampler.h @@ -0,0 +1,19 @@ +#ifndef _LANCZOS_RESAMPLER_H_ +#define _LANCZOS_RESAMPLER_H_ + +void lanczos_init(); + +void * lanczos_resampler_create(); +void lanczos_resampler_delete(void *); +void * lanczos_resampler_dup(void *); + +int lanczos_resampler_get_free_count(void *); +void lanczos_resampler_write_sample(void *, short sample); +void lanczos_resampler_set_rate( void *, double new_factor ); +int lanczos_resampler_ready(void *); +void lanczos_resampler_clear(void *); +int lanczos_resampler_get_sample_count(void *); +int lanczos_resampler_get_sample(void *); +void lanczos_resampler_remove_sample(void *); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/lpc.h b/Frameworks/Dumb/dumb/include/internal/lpc.h new file mode 100644 index 000000000..8c585fa5f --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/lpc.h @@ -0,0 +1,30 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: LPC low level routines + last mod: $Id: lpc.h 16037 2009-05-26 21:10:58Z xiphmont $ + + ********************************************************************/ + +#ifndef _V_LPC_H_ +#define _V_LPC_H_ + +/* simple linear scale LPC code */ +extern float vorbis_lpc_from_data(float *data,float *lpc,int n,int m); + +extern void vorbis_lpc_predict(float *coeff,float *prime,int m, + float *data,long n); + +struct DUMB_IT_SIGDATA; +extern void dumb_it_add_lpc(struct DUMB_IT_SIGDATA *sigdata); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/riff.h b/Frameworks/Dumb/dumb/include/internal/riff.h new file mode 100644 index 000000000..d8705a95f --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/riff.h @@ -0,0 +1,24 @@ +#ifndef RIFF_H +#define RIFF_H + +struct riff; + +struct riff_chunk +{ + unsigned type; + long offset; + unsigned size; + struct riff * nested; +}; + +struct riff +{ + unsigned type; + unsigned chunk_count; + struct riff_chunk * chunks; +}; + +struct riff * riff_parse( DUMBFILE * f, long offset, long size, unsigned proper ); +void riff_free( struct riff * ); + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/stack_alloc.h b/Frameworks/Dumb/dumb/include/internal/stack_alloc.h new file mode 100644 index 000000000..b82edec05 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/stack_alloc.h @@ -0,0 +1,113 @@ +/* Copyright (C) 2002 Jean-Marc Valin */ +/** + @file stack_alloc.h + @brief Temporary memory allocation on stack +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef STACK_ALLOC_H +#define STACK_ALLOC_H + +#ifdef WIN32 +# include +#else +# ifdef HAVE_ALLOCA_H +# include +# else +# include +# endif +#endif + +/** + * @def ALIGN(stack, size) + * + * Aligns the stack to a 'size' boundary + * + * @param stack Stack + * @param size New size boundary + */ + +/** + * @def PUSH(stack, size, type) + * + * Allocates 'size' elements of type 'type' on the stack + * + * @param stack Stack + * @param size Number of elements + * @param type Type of element + */ + +/** + * @def VARDECL(var) + * + * Declare variable on stack + * + * @param var Variable to declare + */ + +/** + * @def ALLOC(var, size, type) + * + * Allocate 'size' elements of 'type' on stack + * + * @param var Name of variable to allocate + * @param size Number of elements + * @param type Type of element + */ + +#ifdef ENABLE_VALGRIND + +#include + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#else + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#endif + +#if defined(VAR_ARRAYS) +#define VARDECL(var) +#define ALLOC(var, size, type) type var[size] +#elif defined(USE_ALLOCA) +#define VARDECL(var) var +#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size)) +#else +#define VARDECL(var) var +#define ALLOC(var, size, type) var = PUSH(stack, size, type) +#endif + + +#endif diff --git a/Frameworks/Dumb/dumb/include/internal/tarray.h b/Frameworks/Dumb/dumb/include/internal/tarray.h new file mode 100644 index 000000000..4cb4771a9 --- /dev/null +++ b/Frameworks/Dumb/dumb/include/internal/tarray.h @@ -0,0 +1,25 @@ +#ifndef _T_ARRAY_H_ +#define _T_ARRAY_H_ + +#include + +#ifdef __FRAMEWORK__ +#include +#else +#include "../dumb.h" +#endif + +void * timekeeping_array_create(size_t size); +void timekeeping_array_destroy(void * array); +void * timekeeping_array_dup(void * array); + +void timekeeping_array_reset(void * array, size_t loop_start); + +void timekeeping_array_push(void * array, size_t index, LONG_LONG time); +void timekeeping_array_bump(void * array, size_t index); + +unsigned int timekeeping_array_get_count(void * array, size_t index); + +LONG_LONG timekeeping_array_get_item(void * array, size_t index); + +#endif diff --git a/Frameworks/Dumb/dumb/prj/.gitignore b/Frameworks/Dumb/dumb/prj/.gitignore new file mode 100644 index 000000000..36d588baa --- /dev/null +++ b/Frameworks/Dumb/dumb/prj/.gitignore @@ -0,0 +1,3 @@ +dumb-build-Desktop-Release +dumb-build-Desktop-Debug +*.user diff --git a/Frameworks/Dumb/dumb/prj/dumb/dumb.pro b/Frameworks/Dumb/dumb/prj/dumb/dumb.pro new file mode 100644 index 000000000..e013fa5ce --- /dev/null +++ b/Frameworks/Dumb/dumb/prj/dumb/dumb.pro @@ -0,0 +1,132 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2012-12-22T16:33:53 +# +#------------------------------------------------- + +QT -= core gui + +TARGET = dumb +TEMPLATE = lib +CONFIG += staticlib + +DEFINES += _USE_SSE + +INCLUDEPATH += ../../include + +QMAKE_CFLAGS += -msse + +SOURCES += \ + ../../src/core/unload.c \ + ../../src/core/rendsig.c \ + ../../src/core/rendduh.c \ + ../../src/core/register.c \ + ../../src/core/readduh.c \ + ../../src/core/rawsig.c \ + ../../src/core/makeduh.c \ + ../../src/core/loadduh.c \ + ../../src/core/dumbfile.c \ + ../../src/core/duhtag.c \ + ../../src/core/duhlen.c \ + ../../src/core/atexit.c \ + ../../src/helpers/stdfile.c \ + ../../src/helpers/silence.c \ + ../../src/helpers/sampbuf.c \ + ../../src/helpers/riff.c \ + ../../src/helpers/resample.c \ + ../../src/helpers/memfile.c \ + ../../src/helpers/clickrem.c \ + ../../src/helpers/blip_buf.c \ + ../../src/helpers/barray.c \ + ../../src/helpers/tarray.c \ + ../../src/it/xmeffect.c \ + ../../src/it/readxm2.c \ + ../../src/it/readxm.c \ + ../../src/it/readstm2.c \ + ../../src/it/readstm.c \ + ../../src/it/reads3m2.c \ + ../../src/it/reads3m.c \ + ../../src/it/readriff.c \ + ../../src/it/readptm.c \ + ../../src/it/readpsm.c \ + ../../src/it/readoldpsm.c \ + ../../src/it/readokt2.c \ + ../../src/it/readokt.c \ + ../../src/it/readmtm.c \ + ../../src/it/readmod2.c \ + ../../src/it/readmod.c \ + ../../src/it/readdsmf.c \ + ../../src/it/readasy.c \ + ../../src/it/readamf2.c \ + ../../src/it/readamf.c \ + ../../src/it/readam.c \ + ../../src/it/read6692.c \ + ../../src/it/read669.c \ + ../../src/it/ptmeffect.c \ + ../../src/it/loadxm2.c \ + ../../src/it/loadxm.c \ + ../../src/it/loadstm2.c \ + ../../src/it/loadstm.c \ + ../../src/it/loads3m2.c \ + ../../src/it/loads3m.c \ + ../../src/it/loadriff2.c \ + ../../src/it/loadriff.c \ + ../../src/it/loadptm2.c \ + ../../src/it/loadptm.c \ + ../../src/it/loadpsm2.c \ + ../../src/it/loadpsm.c \ + ../../src/it/loadoldpsm2.c \ + ../../src/it/loadoldpsm.c \ + ../../src/it/loadokt2.c \ + ../../src/it/loadokt.c \ + ../../src/it/loadmtm2.c \ + ../../src/it/loadmtm.c \ + ../../src/it/loadmod2.c \ + ../../src/it/loadmod.c \ + ../../src/it/loadasy2.c \ + ../../src/it/loadasy.c \ + ../../src/it/loadamf2.c \ + ../../src/it/loadamf.c \ + ../../src/it/load6692.c \ + ../../src/it/load669.c \ + ../../src/it/itunload.c \ + ../../src/it/itrender.c \ + ../../src/it/itread2.c \ + ../../src/it/itread.c \ + ../../src/it/itorder.c \ + ../../src/it/itmisc.c \ + ../../src/it/itload2.c \ + ../../src/it/itload.c \ + ../../src/it/readany.c \ + ../../src/it/loadany2.c \ + ../../src/it/loadany.c \ + ../../src/it/readany2.c \ + ../../src/helpers/lanczos_resampler.c \ + ../../src/helpers/lpc.c + +HEADERS += \ + ../../include/dumb.h \ + ../../include/internal/riff.h \ + ../../include/internal/it.h \ + ../../include/internal/dumb.h \ + ../../include/internal/blip_buf.h \ + ../../include/internal/barray.h \ + ../../include/internal/tarray.h \ + ../../include/internal/aldumb.h \ + ../../include/internal/lanczos_resampler.h \ + ../../include/internal/stack_alloc.h \ + ../../include/internal/lpc.h \ + ../../include/internal/dumbfile.h +unix:!symbian { + maemo5 { + target.path = /opt/usr/lib + } else { + target.path = /usr/lib + } + INSTALLS += target +} + +OTHER_FILES += \ + ../../src/helpers/resample.inc \ + ../../src/helpers/resamp3.inc \ + ../../src/helpers/resamp2.inc diff --git a/Frameworks/Dumb/dumb/src/core/atexit.c b/Frameworks/Dumb/dumb/src/core/atexit.c index 16c6abdb2..64814efda 100644 --- a/Frameworks/Dumb/dumb/src/core/atexit.c +++ b/Frameworks/Dumb/dumb/src/core/atexit.c @@ -1,71 +1,71 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * atexit.c - Library Clean-up Management. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -typedef struct DUMB_ATEXIT_PROC -{ - struct DUMB_ATEXIT_PROC *next; - void (*proc)(void); -} -DUMB_ATEXIT_PROC; - - - -static DUMB_ATEXIT_PROC *dumb_atexit_proc = NULL; - - - -int dumb_atexit(void (*proc)(void)) -{ - DUMB_ATEXIT_PROC *dap = dumb_atexit_proc; - - while (dap) { - if (dap->proc == proc) return 0; - dap = dap->next; - } - - dap = malloc(sizeof(*dap)); - - if (!dap) - return -1; - - dap->next = dumb_atexit_proc; - dap->proc = proc; - dumb_atexit_proc = dap; - - return 0; -} - - - -void dumb_exit(void) -{ - while (dumb_atexit_proc) { - DUMB_ATEXIT_PROC *next = dumb_atexit_proc->next; - (*dumb_atexit_proc->proc)(); - free(dumb_atexit_proc); - dumb_atexit_proc = next; - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * atexit.c - Library Clean-up Management. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +typedef struct DUMB_ATEXIT_PROC +{ + struct DUMB_ATEXIT_PROC *next; + void (*proc)(void); +} +DUMB_ATEXIT_PROC; + + + +static DUMB_ATEXIT_PROC *dumb_atexit_proc = NULL; + + + +int dumb_atexit(void (*proc)(void)) +{ + DUMB_ATEXIT_PROC *dap = dumb_atexit_proc; + + while (dap) { + if (dap->proc == proc) return 0; + dap = dap->next; + } + + dap = malloc(sizeof(*dap)); + + if (!dap) + return -1; + + dap->next = dumb_atexit_proc; + dap->proc = proc; + dumb_atexit_proc = dap; + + return 0; +} + + + +void dumb_exit(void) +{ + while (dumb_atexit_proc) { + DUMB_ATEXIT_PROC *next = dumb_atexit_proc->next; + (*dumb_atexit_proc->proc)(); + free(dumb_atexit_proc); + dumb_atexit_proc = next; + } +} diff --git a/Frameworks/Dumb/dumb/src/core/duhlen.c b/Frameworks/Dumb/dumb/src/core/duhlen.c index 4d79fc099..2c3a35767 100644 --- a/Frameworks/Dumb/dumb/src/core/duhlen.c +++ b/Frameworks/Dumb/dumb/src/core/duhlen.c @@ -1,42 +1,42 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * duhlen.c - Functions to set and return the / / \ \ - * length of a DUH. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * Note that the length of a DUH is a constant | ' / - * stored in the DUH struct and in the DUH disk \__/ - * format. It will be calculated on loading for - * other formats in which the length is not explicitly stored. Also note that - * it does not necessarily correspond to the length of time for which the DUH - * will generate samples. Rather it represents a suitable point for a player - * such as Winamp to stop, and in any good DUH it will allow for any final - * flourish to fade out and be appreciated. - */ - -#include "dumb.h" -#include "internal/dumb.h" - - - -long duh_get_length(DUH *duh) -{ - return duh ? duh->length : 0; -} - - - -void duh_set_length(DUH *duh, long length) -{ - if (duh) - duh->length = length; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * duhlen.c - Functions to set and return the / / \ \ + * length of a DUH. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * Note that the length of a DUH is a constant | ' / + * stored in the DUH struct and in the DUH disk \__/ + * format. It will be calculated on loading for + * other formats in which the length is not explicitly stored. Also note that + * it does not necessarily correspond to the length of time for which the DUH + * will generate samples. Rather it represents a suitable point for a player + * such as Winamp to stop, and in any good DUH it will allow for any final + * flourish to fade out and be appreciated. + */ + +#include "dumb.h" +#include "internal/dumb.h" + + + +long duh_get_length(DUH *duh) +{ + return duh ? duh->length : 0; +} + + + +void duh_set_length(DUH *duh, long length) +{ + if (duh) + duh->length = length; +} diff --git a/Frameworks/Dumb/dumb/src/core/duhtag.c b/Frameworks/Dumb/dumb/src/core/duhtag.c index b150467d3..77061094e 100644 --- a/Frameworks/Dumb/dumb/src/core/duhtag.c +++ b/Frameworks/Dumb/dumb/src/core/duhtag.c @@ -1,38 +1,38 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * duhtag.c - Function to return the tags stored / / \ \ - * in a DUH struct (typically author | < / \_ - * information). | \/ /\ / - * \_ / > / - * By entheh. | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -const char *duh_get_tag(DUH *duh, const char *key) -{ - int i; - ASSERT(key); - if (!duh || !duh->tag) return NULL; - - for (i = 0; i < duh->n_tags; i++) - if (strcmp(key, duh->tag[i][0]) == 0) - return duh->tag[i][1]; - - return NULL; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * duhtag.c - Function to return the tags stored / / \ \ + * in a DUH struct (typically author | < / \_ + * information). | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +const char *duh_get_tag(DUH *duh, const char *key) +{ + int i; + ASSERT(key); + if (!duh || !duh->tag) return NULL; + + for (i = 0; i < duh->n_tags; i++) + if (strcmp(key, duh->tag[i][0]) == 0) + return duh->tag[i][1]; + + return NULL; +} diff --git a/Frameworks/Dumb/dumb/src/core/dumbfile.c b/Frameworks/Dumb/dumb/src/core/dumbfile.c index 71108c0c3..65e89746b 100644 --- a/Frameworks/Dumb/dumb/src/core/dumbfile.c +++ b/Frameworks/Dumb/dumb/src/core/dumbfile.c @@ -1,401 +1,418 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * dumbfile.c - Hookable, strictly sequential / / \ \ - * file input functions. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" - - - -static DUMBFILE_SYSTEM *the_dfs = NULL; - - - -void register_dumbfile_system(DUMBFILE_SYSTEM *dfs) -{ - ASSERT(dfs); - ASSERT(dfs->open); - ASSERT(dfs->getc); - ASSERT(dfs->close); - the_dfs = dfs; -} - - - -struct DUMBFILE -{ - DUMBFILE_SYSTEM *dfs; - void *file; - long pos; -}; - - - -DUMBFILE *dumbfile_open(const char *filename) -{ - DUMBFILE *f; - - ASSERT(the_dfs); - - f = malloc(sizeof(*f)); - - if (!f) - return NULL; - - f->dfs = the_dfs; - - f->file = (*the_dfs->open)(filename); - - if (!f->file) { - free(f); - return NULL; - } - - f->pos = 0; - - return f; -} - - - -DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs) -{ - DUMBFILE *f; - - ASSERT(dfs); - ASSERT(dfs->getc); - ASSERT(file); - - f = malloc(sizeof(*f)); - - if (!f) { - if (dfs->close) - (*dfs->close)(file); - return NULL; - } - - f->dfs = dfs; - f->file = file; - - f->pos = 0; - - return f; -} - - - -long dumbfile_pos(DUMBFILE *f) -{ - ASSERT(f); - - return f->pos; -} - - - -int dumbfile_skip(DUMBFILE *f, long n) -{ - int rv; - - ASSERT(f); - ASSERT(n >= 0); - - if (f->pos < 0) - return -1; - - f->pos += n; - - if (f->dfs->skip) { - rv = (*f->dfs->skip)(f->file, n); - if (rv) { - f->pos = -1; - return rv; - } - } else { - while (n) { - rv = (*f->dfs->getc)(f->file); - if (rv < 0) { - f->pos = -1; - return rv; - } - n--; - } - } - - return 0; -} - - - -int dumbfile_getc(DUMBFILE *f) -{ - int rv; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - rv = (*f->dfs->getc)(f->file); - - if (rv < 0) { - f->pos = -1; - return rv; - } - - f->pos++; - - return rv; -} - - - -int dumbfile_igetw(DUMBFILE *f) -{ - int l, h; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - l = (*f->dfs->getc)(f->file); - if (l < 0) { - f->pos = -1; - return l; - } - - h = (*f->dfs->getc)(f->file); - if (h < 0) { - f->pos = -1; - return h; - } - - f->pos += 2; - - return l | (h << 8); -} - - - -int dumbfile_mgetw(DUMBFILE *f) -{ - int l, h; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - h = (*f->dfs->getc)(f->file); - if (h < 0) { - f->pos = -1; - return h; - } - - l = (*f->dfs->getc)(f->file); - if (l < 0) { - f->pos = -1; - return l; - } - - f->pos += 2; - - return l | (h << 8); -} - - - -long dumbfile_igetl(DUMBFILE *f) -{ - unsigned long rv, b; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - rv = (*f->dfs->getc)(f->file); - if ((signed long)rv < 0) { - f->pos = -1; - return rv; - } - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 8; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 16; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 24; - - f->pos += 4; - - return rv; -} - - - -long dumbfile_mgetl(DUMBFILE *f) -{ - unsigned long rv, b; - - ASSERT(f); - - if (f->pos < 0) - return -1; - - rv = (*f->dfs->getc)(f->file); - if ((signed long)rv < 0) { - f->pos = -1; - return rv; - } - rv <<= 24; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 16; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b << 8; - - b = (*f->dfs->getc)(f->file); - if ((signed long)b < 0) { - f->pos = -1; - return b; - } - rv |= b; - - f->pos += 4; - - return rv; -} - - - -unsigned long dumbfile_cgetul(DUMBFILE *f) -{ - unsigned long rv = 0; - int v; - - do { - v = dumbfile_getc(f); - - if (v < 0) - return v; - - rv <<= 7; - rv |= v & 0x7F; - } while (v & 0x80); - - return rv; -} - - - -signed long dumbfile_cgetsl(DUMBFILE *f) -{ - unsigned long rv = dumbfile_cgetul(f); - - if (f->pos < 0) - return rv; - - return (rv >> 1) | (rv << 31); -} - - - -long dumbfile_getnc(char *ptr, long n, DUMBFILE *f) -{ - long rv; - - ASSERT(f); - ASSERT(n >= 0); - - if (f->pos < 0) - return -1; - - if (f->dfs->getnc) { - rv = (*f->dfs->getnc)(ptr, n, f->file); - if (rv < n) { - f->pos = -1; - return MAX(rv, 0); - } - } else { - for (rv = 0; rv < n; rv++) { - int c = (*f->dfs->getc)(f->file); - if (c < 0) { - f->pos = -1; - return rv; - } - *ptr++ = c; - } - } - - f->pos += rv; - - return rv; -} - - - -int dumbfile_error(DUMBFILE *f) -{ - ASSERT(f); - - return f->pos < 0; -} - - - -int dumbfile_close(DUMBFILE *f) -{ - int rv; - - ASSERT(f); - - rv = f->pos < 0; - - if (f->dfs->close) - (*f->dfs->close)(f->file); - - free(f); - - return rv; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * dumbfile.c - Hookable, strictly sequential / / \ \ + * file input functions. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" + + + +static const DUMBFILE_SYSTEM *the_dfs = NULL; + + + +void register_dumbfile_system(const DUMBFILE_SYSTEM *dfs) +{ + ASSERT(dfs); + ASSERT(dfs->open); + ASSERT(dfs->getc); + ASSERT(dfs->close); + ASSERT(dfs->seek); + ASSERT(dfs->get_size); + the_dfs = dfs; +} + + + +#include "internal/dumbfile.h" + + + +DUMBFILE *dumbfile_open(const char *filename) +{ + DUMBFILE *f; + + ASSERT(the_dfs); + + f = (DUMBFILE *) malloc(sizeof(*f)); + + if (!f) + return NULL; + + f->dfs = the_dfs; + + f->file = (*the_dfs->open)(filename); + + if (!f->file) { + free(f); + return NULL; + } + + f->pos = 0; + + return f; +} + + + +DUMBFILE *dumbfile_open_ex(void *file, const DUMBFILE_SYSTEM *dfs) +{ + DUMBFILE *f; + + ASSERT(dfs); + ASSERT(dfs->getc); + ASSERT(file); + + f = (DUMBFILE *) malloc(sizeof(*f)); + + if (!f) { + if (dfs->close) + (*dfs->close)(file); + return NULL; + } + + f->dfs = dfs; + f->file = file; + + f->pos = 0; + + return f; +} + + + +long dumbfile_pos(DUMBFILE *f) +{ + ASSERT(f); + + return f->pos; +} + + + +int dumbfile_skip(DUMBFILE *f, long n) +{ + int rv; + + ASSERT(f); + ASSERT(n >= 0); + + if (f->pos < 0) + return -1; + + f->pos += n; + + if (f->dfs->skip) { + rv = (*f->dfs->skip)(f->file, n); + if (rv) { + f->pos = -1; + return rv; + } + } else { + while (n) { + rv = (*f->dfs->getc)(f->file); + if (rv < 0) { + f->pos = -1; + return rv; + } + n--; + } + } + + return 0; +} + + + +int dumbfile_getc(DUMBFILE *f) +{ + int rv; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + + if (rv < 0) { + f->pos = -1; + return rv; + } + + f->pos++; + + return rv; +} + + + +int dumbfile_igetw(DUMBFILE *f) +{ + int l, h; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + l = (*f->dfs->getc)(f->file); + if (l < 0) { + f->pos = -1; + return l; + } + + h = (*f->dfs->getc)(f->file); + if (h < 0) { + f->pos = -1; + return h; + } + + f->pos += 2; + + return l | (h << 8); +} + + + +int dumbfile_mgetw(DUMBFILE *f) +{ + int l, h; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + h = (*f->dfs->getc)(f->file); + if (h < 0) { + f->pos = -1; + return h; + } + + l = (*f->dfs->getc)(f->file); + if (l < 0) { + f->pos = -1; + return l; + } + + f->pos += 2; + + return l | (h << 8); +} + + + +long dumbfile_igetl(DUMBFILE *f) +{ + unsigned long rv, b; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + if ((signed long)rv < 0) { + f->pos = -1; + return rv; + } + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 8; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 16; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 24; + + f->pos += 4; + + return rv; +} + + + +long dumbfile_mgetl(DUMBFILE *f) +{ + unsigned long rv, b; + + ASSERT(f); + + if (f->pos < 0) + return -1; + + rv = (*f->dfs->getc)(f->file); + if ((signed long)rv < 0) { + f->pos = -1; + return rv; + } + rv <<= 24; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 16; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b << 8; + + b = (*f->dfs->getc)(f->file); + if ((signed long)b < 0) { + f->pos = -1; + return b; + } + rv |= b; + + f->pos += 4; + + return rv; +} + + + +unsigned long dumbfile_cgetul(DUMBFILE *f) +{ + unsigned long rv = 0; + int v; + + do { + v = dumbfile_getc(f); + + if (v < 0) + return v; + + rv <<= 7; + rv |= v & 0x7F; + } while (v & 0x80); + + return rv; +} + + + +signed long dumbfile_cgetsl(DUMBFILE *f) +{ + unsigned long rv = dumbfile_cgetul(f); + + if (f->pos < 0) + return rv; + + return (rv >> 1) | (rv << 31); +} + + + +long dumbfile_getnc(char *ptr, long n, DUMBFILE *f) +{ + long rv; + + ASSERT(f); + ASSERT(n >= 0); + + if (f->pos < 0) + return -1; + + if (f->dfs->getnc) { + rv = (*f->dfs->getnc)(ptr, n, f->file); + if (rv < n) { + f->pos = -1; + return MAX(rv, 0); + } + } else { + for (rv = 0; rv < n; rv++) { + int c = (*f->dfs->getc)(f->file); + if (c < 0) { + f->pos = -1; + return rv; + } + *ptr++ = c; + } + } + + f->pos += rv; + + return rv; +} + + + +int dumbfile_seek(DUMBFILE *f, long n, int origin) +{ + switch ( origin ) + { + case DFS_SEEK_CUR: n += f->pos; break; + case DFS_SEEK_END: n += (*f->dfs->get_size)(f->file); break; + } + f->pos = n; + return (*f->dfs->seek)(f->file, n); +} + + + +long dumbfile_get_size(DUMBFILE *f) +{ + return (*f->dfs->get_size)(f->file); +} + + + +int dumbfile_error(DUMBFILE *f) +{ + ASSERT(f); + + return f->pos < 0; +} + + + +int dumbfile_close(DUMBFILE *f) +{ + int rv; + + ASSERT(f); + + rv = f->pos < 0; + + if (f->dfs->close) + (*f->dfs->close)(f->file); + + free(f); + + return rv; +} diff --git a/Frameworks/Dumb/dumb/src/core/loadduh.c b/Frameworks/Dumb/dumb/src/core/loadduh.c index 7dfe5cc10..e954fe24c 100644 --- a/Frameworks/Dumb/dumb/src/core/loadduh.c +++ b/Frameworks/Dumb/dumb/src/core/loadduh.c @@ -1,42 +1,42 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loadduh.c - Code to read a DUH from a file, / / \ \ - * opening and closing the file for | < / \_ - * you. | \/ /\ / - * \_ / > / - * By entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/dumb.h" - - - -/* load_duh(): loads a .duh file, returning a pointer to a DUH struct. - * When you have finished with it, you must pass the pointer to unload_duh() - * so that the memory can be freed. - */ -DUH *load_duh(const char *filename) -{ - DUH *duh; - DUMBFILE *f = dumbfile_open(filename); - - if (!f) - return NULL; - - duh = read_duh(f); - - dumbfile_close(f); - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadduh.c - Code to read a DUH from a file, / / \ \ + * opening and closing the file for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/dumb.h" + + + +/* load_duh(): loads a .duh file, returning a pointer to a DUH struct. + * When you have finished with it, you must pass the pointer to unload_duh() + * so that the memory can be freed. + */ +DUH *load_duh(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = read_duh(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/core/makeduh.c b/Frameworks/Dumb/dumb/src/core/makeduh.c index 8345fe4fe..345e2d65b 100644 --- a/Frameworks/Dumb/dumb/src/core/makeduh.c +++ b/Frameworks/Dumb/dumb/src/core/makeduh.c @@ -1,132 +1,151 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * makeduh.c - Function to construct a DUH from / / \ \ - * its components. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -static DUH_SIGNAL *make_signal(DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata) -{ - DUH_SIGNAL *signal; - - ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer)); - ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample); - - signal = malloc(sizeof(*signal)); - - if (!signal) { - if (desc->unload_sigdata) - if (sigdata) - (*desc->unload_sigdata)(sigdata); - return NULL; - } - - signal->desc = desc; - signal->sigdata = sigdata; - - return signal; -} - - - -DUH *make_duh( - long length, - int n_tags, - const char *const tags[][2], - int n_signals, - DUH_SIGTYPE_DESC *desc[], - sigdata_t *sigdata[] -) -{ - DUH *duh = malloc(sizeof(*duh)); - int i; - int fail; - - if (duh) { - duh->n_signals = n_signals; - - duh->signal = malloc(n_signals * sizeof(*duh->signal)); - - if (!duh->signal) { - free(duh); - duh = NULL; - } - } - - if (!duh) { - for (i = 0; i < n_signals; i++) - if (desc[i]->unload_sigdata) - if (sigdata[i]) - (*desc[i]->unload_sigdata)(sigdata[i]); - return NULL; - } - - duh->n_tags = 0; - duh->tag = NULL; - - fail = 0; - - for (i = 0; i < n_signals; i++) { - duh->signal[i] = make_signal(desc[i], sigdata[i]); - if (!duh->signal[i]) - fail = 1; - } - - if (fail) { - unload_duh(duh); - return NULL; - } - - duh->length = length; - - { - int mem = n_tags * 2; /* account for NUL terminators here */ - char *ptr; - - for (i = 0; i < n_tags; i++) - mem += strlen(tags[i][0]) + strlen(tags[i][1]); - - if (mem <= 0) return duh; - - duh->tag = malloc(n_tags * sizeof(*duh->tag)); - if (!duh->tag) return duh; - duh->tag[0][0] = malloc(mem); - if (!duh->tag[0][0]) { - free(duh->tag); - duh->tag = NULL; - return duh; - } - duh->n_tags = n_tags; - ptr = duh->tag[0][0]; - for (i = 0; i < n_tags; i++) { - duh->tag[i][0] = ptr; - strcpy(ptr, tags[i][0]); - ptr += strlen(tags[i][0]) + 1; - duh->tag[i][1] = ptr; - strcpy(ptr, tags[i][1]); - ptr += strlen(tags[i][1]) + 1; - } - } - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * makeduh.c - Function to construct a DUH from / / \ \ + * its components. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static DUH_SIGNAL *make_signal(DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata) +{ + DUH_SIGNAL *signal; + + ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer)); + ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample); + + signal = malloc(sizeof(*signal)); + + if (!signal) { + if (desc->unload_sigdata) + if (sigdata) + (*desc->unload_sigdata)(sigdata); + return NULL; + } + + signal->desc = desc; + signal->sigdata = sigdata; + + return signal; +} + + + +DUH *make_duh( + long length, + int n_tags, + const char *const tags[][2], + int n_signals, + DUH_SIGTYPE_DESC *desc[], + sigdata_t *sigdata[] +) +{ + DUH *duh = malloc(sizeof(*duh)); + int i; + int fail; + + if (duh) { + duh->n_signals = n_signals; + + duh->signal = malloc(n_signals * sizeof(*duh->signal)); + + if (!duh->signal) { + free(duh); + duh = NULL; + } + } + + if (!duh) { + for (i = 0; i < n_signals; i++) + if (desc[i]->unload_sigdata) + if (sigdata[i]) + (*desc[i]->unload_sigdata)(sigdata[i]); + return NULL; + } + + duh->n_tags = 0; + duh->tag = NULL; + + fail = 0; + + for (i = 0; i < n_signals; i++) { + duh->signal[i] = make_signal(desc[i], sigdata[i]); + if (!duh->signal[i]) + fail = 1; + } + + if (fail) { + unload_duh(duh); + return NULL; + } + + duh->length = length; + + { + int mem = n_tags * 2; /* account for NUL terminators here */ + char *ptr; + + for (i = 0; i < n_tags; i++) + mem += strlen(tags[i][0]) + strlen(tags[i][1]); + + if (mem <= 0) return duh; + + duh->tag = malloc(n_tags * sizeof(*duh->tag)); + if (!duh->tag) return duh; + duh->tag[0][0] = malloc(mem); + if (!duh->tag[0][0]) { + free(duh->tag); + duh->tag = NULL; + return duh; + } + duh->n_tags = n_tags; + ptr = duh->tag[0][0]; + for (i = 0; i < n_tags; i++) { + duh->tag[i][0] = ptr; + strcpy(ptr, tags[i][0]); + ptr += strlen(tags[i][0]) + 1; + duh->tag[i][1] = ptr; + strcpy(ptr, tags[i][1]); + ptr += strlen(tags[i][1]) + 1; + } + } + + return duh; +} + +int duh_add_signal(DUH *duh, DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata) +{ + DUH_SIGNAL **signal; + + if ( !duh || !desc || !sigdata ) return -1; + + signal = ( DUH_SIGNAL ** ) realloc( duh->signal, ( duh->n_signals + 1 ) * sizeof( *duh->signal ) ); + if ( !signal ) return -1; + duh->signal = signal; + + memmove( signal + 1, signal, duh->n_signals * sizeof( *signal ) ); + duh->n_signals++; + + signal[ 0 ] = make_signal( desc, sigdata ); + if ( !signal[ 0 ] ) return -1; + + return 0; +} diff --git a/Frameworks/Dumb/dumb/src/core/rawsig.c b/Frameworks/Dumb/dumb/src/core/rawsig.c index 926c99065..8a750a67e 100644 --- a/Frameworks/Dumb/dumb/src/core/rawsig.c +++ b/Frameworks/Dumb/dumb/src/core/rawsig.c @@ -1,44 +1,58 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * rawsig.c - Function to retrieve raw signal / / \ \ - * data from a DUH provided you know | < / \_ - * what type of signal it is. | \/ /\ / - * \_ / > / - * By entheh. | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -/* You have to specify the type of sigdata, proving you know what to do with - * the pointer. If you get it wrong, you can expect NULL back. - */ -sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type) -{ - DUH_SIGNAL *signal; - - if (!duh) return NULL; - - if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL; - - signal = duh->signal[sig]; - - if (signal && signal->desc->type == type) - return signal->sigdata; - - return NULL; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * rawsig.c - Function to retrieve raw signal / / \ \ + * data from a DUH provided you know | < / \_ + * what type of signal it is. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +/* You have to specify the type of sigdata, proving you know what to do with + * the pointer. If you get it wrong, you can expect NULL back. + */ +sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type) +{ + int i; + DUH_SIGNAL *signal; + + if (!duh) return NULL; + + if ( sig >= 0 ) + { + if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL; + + signal = duh->signal[sig]; + + if (signal && signal->desc->type == type) + return signal->sigdata; + } + else + { + for ( i = 0; i < duh->n_signals; i++ ) + { + signal = duh->signal[i]; + + if (signal && signal->desc->type == type) + return signal->sigdata; + } + } + + return NULL; +} diff --git a/Frameworks/Dumb/dumb/src/core/readduh.c b/Frameworks/Dumb/dumb/src/core/readduh.c index 514b04a07..0fb775b2b 100644 --- a/Frameworks/Dumb/dumb/src/core/readduh.c +++ b/Frameworks/Dumb/dumb/src/core/readduh.c @@ -1,107 +1,107 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * readduh.c - Code to read a DUH from an open / / \ \ - * file. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -static DUH_SIGNAL *read_signal(DUH *duh, DUMBFILE *f) -{ - DUH_SIGNAL *signal; - long type; - - signal = malloc(sizeof(*signal)); - - if (!signal) - return NULL; - - type = dumbfile_mgetl(f); - if (dumbfile_error(f)) { - free(signal); - return NULL; - } - - signal->desc = _dumb_get_sigtype_desc(type); - if (!signal->desc) { - free(signal); - return NULL; - } - - if (signal->desc->load_sigdata) { - signal->sigdata = (*signal->desc->load_sigdata)(duh, f); - if (!signal->sigdata) { - free(signal); - return NULL; - } - } else - signal->sigdata = NULL; - - return signal; -} - - - -/* read_duh(): reads a DUH from an already open DUMBFILE, and returns its - * pointer, or null on error. The file is not closed. - */ -DUH *read_duh(DUMBFILE *f) -{ - DUH *duh; - int i; - - if (dumbfile_mgetl(f) != DUH_SIGNATURE) - return NULL; - - duh = malloc(sizeof(*duh)); - if (!duh) - return NULL; - - duh->length = dumbfile_igetl(f); - if (dumbfile_error(f) || duh->length <= 0) { - free(duh); - return NULL; - } - - duh->n_signals = dumbfile_igetl(f); - if (dumbfile_error(f) || duh->n_signals <= 0) { - free(duh); - return NULL; - } - - duh->signal = malloc(sizeof(*duh->signal) * duh->n_signals); - if (!duh->signal) { - free(duh); - return NULL; - } - - for (i = 0; i < duh->n_signals; i++) - duh->signal[i] = NULL; - - for (i = 0; i < duh->n_signals; i++) { - if (!(duh->signal[i] = read_signal(duh, f))) { - unload_duh(duh); - return NULL; - } - } - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readduh.c - Code to read a DUH from an open / / \ \ + * file. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static DUH_SIGNAL *read_signal(DUH *duh, DUMBFILE *f) +{ + DUH_SIGNAL *signal; + long type; + + signal = malloc(sizeof(*signal)); + + if (!signal) + return NULL; + + type = dumbfile_mgetl(f); + if (dumbfile_error(f)) { + free(signal); + return NULL; + } + + signal->desc = _dumb_get_sigtype_desc(type); + if (!signal->desc) { + free(signal); + return NULL; + } + + if (signal->desc->load_sigdata) { + signal->sigdata = (*signal->desc->load_sigdata)(duh, f); + if (!signal->sigdata) { + free(signal); + return NULL; + } + } else + signal->sigdata = NULL; + + return signal; +} + + + +/* read_duh(): reads a DUH from an already open DUMBFILE, and returns its + * pointer, or null on error. The file is not closed. + */ +DUH *read_duh(DUMBFILE *f) +{ + DUH *duh; + int i; + + if (dumbfile_mgetl(f) != DUH_SIGNATURE) + return NULL; + + duh = malloc(sizeof(*duh)); + if (!duh) + return NULL; + + duh->length = dumbfile_igetl(f); + if (dumbfile_error(f) || duh->length <= 0) { + free(duh); + return NULL; + } + + duh->n_signals = dumbfile_igetl(f); + if (dumbfile_error(f) || duh->n_signals <= 0) { + free(duh); + return NULL; + } + + duh->signal = malloc(sizeof(*duh->signal) * duh->n_signals); + if (!duh->signal) { + free(duh); + return NULL; + } + + for (i = 0; i < duh->n_signals; i++) + duh->signal[i] = NULL; + + for (i = 0; i < duh->n_signals; i++) { + if (!(duh->signal[i] = read_signal(duh, f))) { + unload_duh(duh); + return NULL; + } + } + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/core/register.c b/Frameworks/Dumb/dumb/src/core/register.c index 66dd45241..2e16c9a7e 100644 --- a/Frameworks/Dumb/dumb/src/core/register.c +++ b/Frameworks/Dumb/dumb/src/core/register.c @@ -1,104 +1,104 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * register.c - Signal type registration. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -static DUH_SIGTYPE_DESC_LINK *sigtype_desc = NULL; -static DUH_SIGTYPE_DESC_LINK **sigtype_desc_tail = &sigtype_desc; - - - -/* destroy_sigtypes(): frees all memory allocated while registering signal - * types. This function is set up to be called by dumb_exit(). - */ -static void destroy_sigtypes(void) -{ - DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc, *next; - sigtype_desc = NULL; - sigtype_desc_tail = &sigtype_desc; - - while (desc_link) { - next = desc_link->next; - free(desc_link); - desc_link = next; - } -} - - - -/* dumb_register_sigtype(): registers a new signal type with DUMB. The signal - * type is identified by a four-character string (e.g. "WAVE"), which you can - * encode using the the DUMB_ID() macro (e.g. DUMB_ID('W','A','V','E')). The - * signal's behaviour is defined by four functions, whose pointers you pass - * here. See the documentation for details. - * - * If a DUH tries to use a signal that has not been registered using this - * function, then the library will fail to load the DUH. - */ -void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc) -{ - DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc; - - ASSERT((desc->load_sigdata && desc->unload_sigdata) || (!desc->load_sigdata && !desc->unload_sigdata)); - ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer)); - ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample); - - if (desc_link) { - do { - if (desc_link->desc->type == desc->type) { - desc_link->desc = desc; - return; - } - desc_link = desc_link->next; - } while (desc_link); - } else - dumb_atexit(&destroy_sigtypes); - - desc_link = *sigtype_desc_tail = malloc(sizeof(DUH_SIGTYPE_DESC_LINK)); - - if (!desc_link) - return; - - desc_link->next = NULL; - sigtype_desc_tail = &desc_link->next; - - desc_link->desc = desc; -} - - - -/* _dumb_get_sigtype_desc(): searches the registered functions for a signal - * type matching the parameter. If such a sigtype is found, it returns a - * pointer to a sigtype descriptor containing the necessary functions to - * manage the signal. If none is found, it returns NULL. - */ -DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type) -{ - DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc; - - while (desc_link && desc_link->desc->type != type) - desc_link = desc_link->next; - - return desc_link ? desc_link->desc : NULL; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * register.c - Signal type registration. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static DUH_SIGTYPE_DESC_LINK *sigtype_desc = NULL; +static DUH_SIGTYPE_DESC_LINK **sigtype_desc_tail = &sigtype_desc; + + + +/* destroy_sigtypes(): frees all memory allocated while registering signal + * types. This function is set up to be called by dumb_exit(). + */ +static void destroy_sigtypes(void) +{ + DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc, *next; + sigtype_desc = NULL; + sigtype_desc_tail = &sigtype_desc; + + while (desc_link) { + next = desc_link->next; + free(desc_link); + desc_link = next; + } +} + + + +/* dumb_register_sigtype(): registers a new signal type with DUMB. The signal + * type is identified by a four-character string (e.g. "WAVE"), which you can + * encode using the the DUMB_ID() macro (e.g. DUMB_ID('W','A','V','E')). The + * signal's behaviour is defined by four functions, whose pointers you pass + * here. See the documentation for details. + * + * If a DUH tries to use a signal that has not been registered using this + * function, then the library will fail to load the DUH. + */ +void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc) +{ + DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc; + + ASSERT((desc->load_sigdata && desc->unload_sigdata) || (!desc->load_sigdata && !desc->unload_sigdata)); + ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer)); + ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample); + + if (desc_link) { + do { + if (desc_link->desc->type == desc->type) { + desc_link->desc = desc; + return; + } + desc_link = desc_link->next; + } while (desc_link); + } else + dumb_atexit(&destroy_sigtypes); + + desc_link = *sigtype_desc_tail = malloc(sizeof(DUH_SIGTYPE_DESC_LINK)); + + if (!desc_link) + return; + + desc_link->next = NULL; + sigtype_desc_tail = &desc_link->next; + + desc_link->desc = desc; +} + + + +/* _dumb_get_sigtype_desc(): searches the registered functions for a signal + * type matching the parameter. If such a sigtype is found, it returns a + * pointer to a sigtype descriptor containing the necessary functions to + * manage the signal. If none is found, it returns NULL. + */ +DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type) +{ + DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc; + + while (desc_link && desc_link->desc->type != type) + desc_link = desc_link->next; + + return desc_link ? desc_link->desc : NULL; +} diff --git a/Frameworks/Dumb/dumb/src/core/rendduh.c b/Frameworks/Dumb/dumb/src/core/rendduh.c index 1639b938e..1effa3eb4 100644 --- a/Frameworks/Dumb/dumb/src/core/rendduh.c +++ b/Frameworks/Dumb/dumb/src/core/rendduh.c @@ -1,184 +1,184 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * rendduh.c - Functions for rendering a DUH into / / \ \ - * an end-user sample format. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -/* On the x86, we can use some tricks to speed stuff up */ -#if (defined _MSC_VER) || (defined __DJGPP__) || (defined __MINGW__) -// Can't we detect Linux and other x86 platforms here? :/ - -#define FAST_MID(var, min, max) { \ - var -= (min); \ - var &= (~var) >> (sizeof(var) * CHAR_BIT - 1); \ - var += (min); \ - var -= (max); \ - var &= var >> (sizeof(var) * CHAR_BIT - 1); \ - var += (max); \ -} - -#define CONVERT8(src, pos, signconv) { \ - signed int f = (src + 0x8000) >> 16; \ - FAST_MID(f, -128, 127); \ - ((char*)sptr)[pos] = (char)f ^ signconv; \ -} - -#define CONVERT16(src, pos, signconv) { \ - signed int f = (src + 0x80) >> 8; \ - FAST_MID(f, -32768, 32767); \ - ((short*)sptr)[pos] = (short)(f ^ signconv); \ -} - -#else - -#define CONVERT8(src, pos, signconv) \ -{ \ - signed int f = (src + 0x8000) >> 16; \ - f = MID(-128, f, 127); \ - ((char *)sptr)[pos] = (char)f ^ signconv; \ -} - - - -#define CONVERT16(src, pos, signconv) \ -{ \ - signed int f = (src + 0x80) >> 8; \ - f = MID(-32768, f, 32767); \ - ((short *)sptr)[pos] = (short)(f ^ signconv); \ -} - -#endif - - - -/* DEPRECATED */ -DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) -{ - return duh_start_sigrenderer(duh, 0, n_channels, pos); -} - - - -long duh_render( - DUH_SIGRENDERER *sigrenderer, - int bits, int unsign, - float volume, float delta, - long size, void *sptr -) -{ - long n; - - sample_t **sampptr; - - int n_channels; - - ASSERT(bits == 8 || bits == 16); - ASSERT(sptr); - - if (!sigrenderer) - return 0; - - n_channels = duh_sigrenderer_get_n_channels(sigrenderer); - - ASSERT(n_channels > 0); - /* This restriction will be removed when need be. At the moment, tightly - * optimised loops exist for exactly one or two channels. - */ - ASSERT(n_channels <= 2); - - sampptr = allocate_sample_buffer(n_channels, size); - - if (!sampptr) - return 0; - - dumb_silence(sampptr[0], n_channels * size); - - size = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, sampptr); - - if (bits == 16) { - int signconv = unsign ? 0x8000 : 0x0000; - - for (n = 0; n < size * n_channels; n++) { - CONVERT16(sampptr[0][n], n, signconv); - } - } else { - char signconv = unsign ? 0x80 : 0x00; - - for (n = 0; n < size * n_channels; n++) { - CONVERT8(sampptr[0][n], n, signconv); - } - } - - destroy_sample_buffer(sampptr); - - return size; -} - - - -/* DEPRECATED */ -int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) -{ - return duh_sigrenderer_get_n_channels(dr); -} - - - -/* DEPRECATED */ -long duh_renderer_get_position(DUH_SIGRENDERER *dr) -{ - return duh_sigrenderer_get_position(dr); -} - - - -/* DEPRECATED */ -void duh_end_renderer(DUH_SIGRENDERER *dr) -{ - duh_end_sigrenderer(dr); -} - - - -/* DEPRECATED */ -DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) -{ - return sigrenderer; -} - - - -/* DEPRECATED */ -DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) -{ - return dr; -} - - - -/* DEPRECATED */ -DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) -{ - return dr; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * rendduh.c - Functions for rendering a DUH into / / \ \ + * an end-user sample format. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +/* On the x86, we can use some tricks to speed stuff up */ +#if (defined _MSC_VER) || (defined __DJGPP__) || (defined __MINGW__) +// Can't we detect Linux and other x86 platforms here? :/ + +#define FAST_MID(var, min, max) { \ + var -= (min); \ + var &= (~var) >> (sizeof(var) * CHAR_BIT - 1); \ + var += (min); \ + var -= (max); \ + var &= var >> (sizeof(var) * CHAR_BIT - 1); \ + var += (max); \ +} + +#define CONVERT8(src, pos, signconv) { \ + signed int f = (src + 0x8000) >> 16; \ + FAST_MID(f, -128, 127); \ + ((char*)sptr)[pos] = (char)f ^ signconv; \ +} + +#define CONVERT16(src, pos, signconv) { \ + signed int f = (src + 0x80) >> 8; \ + FAST_MID(f, -32768, 32767); \ + ((short*)sptr)[pos] = (short)(f ^ signconv); \ +} + +#else + +#define CONVERT8(src, pos, signconv) \ +{ \ + signed int f = (src + 0x8000) >> 16; \ + f = MID(-128, f, 127); \ + ((char *)sptr)[pos] = (char)f ^ signconv; \ +} + + + +#define CONVERT16(src, pos, signconv) \ +{ \ + signed int f = (src + 0x80) >> 8; \ + f = MID(-32768, f, 32767); \ + ((short *)sptr)[pos] = (short)(f ^ signconv); \ +} + +#endif + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) +{ + return duh_start_sigrenderer(duh, 0, n_channels, pos); +} + + + +long duh_render( + DUH_SIGRENDERER *sigrenderer, + int bits, int unsign, + float volume, float delta, + long size, void *sptr +) +{ + long n; + + sample_t **sampptr; + + int n_channels; + + ASSERT(bits == 8 || bits == 16); + ASSERT(sptr); + + if (!sigrenderer) + return 0; + + n_channels = duh_sigrenderer_get_n_channels(sigrenderer); + + ASSERT(n_channels > 0); + /* This restriction will be removed when need be. At the moment, tightly + * optimised loops exist for exactly one or two channels. + */ + ASSERT(n_channels <= 2); + + sampptr = allocate_sample_buffer(n_channels, size); + + if (!sampptr) + return 0; + + dumb_silence(sampptr[0], n_channels * size); + + size = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, sampptr); + + if (bits == 16) { + int signconv = unsign ? 0x8000 : 0x0000; + + for (n = 0; n < size * n_channels; n++) { + CONVERT16(sampptr[0][n], n, signconv); + } + } else { + char signconv = unsign ? 0x80 : 0x00; + + for (n = 0; n < size * n_channels; n++) { + CONVERT8(sampptr[0][n], n, signconv); + } + } + + destroy_sample_buffer(sampptr); + + return size; +} + + + +/* DEPRECATED */ +int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) +{ + return duh_sigrenderer_get_n_channels(dr); +} + + + +/* DEPRECATED */ +long duh_renderer_get_position(DUH_SIGRENDERER *dr) +{ + return duh_sigrenderer_get_position(dr); +} + + + +/* DEPRECATED */ +void duh_end_renderer(DUH_SIGRENDERER *dr) +{ + duh_end_sigrenderer(dr); +} + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) +{ + return sigrenderer; +} + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) +{ + return dr; +} + + + +/* DEPRECATED */ +DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) +{ + return dr; +} diff --git a/Frameworks/Dumb/dumb/src/core/rendsig.c b/Frameworks/Dumb/dumb/src/core/rendsig.c index 1b7cf65f0..0111c52a2 100644 --- a/Frameworks/Dumb/dumb/src/core/rendsig.c +++ b/Frameworks/Dumb/dumb/src/core/rendsig.c @@ -1,344 +1,352 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * rendsig.c - Wrappers to render samples from / / \ \ - * the signals in a DUH. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -struct DUH_SIGRENDERER -{ - DUH_SIGTYPE_DESC *desc; - - sigrenderer_t *sigrenderer; - - int n_channels; - - long pos; - int subpos; - - DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback; - void *callback_data; -}; - - - -DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos) -{ - DUH_SIGRENDERER *sigrenderer; - - DUH_SIGNAL *signal; - DUH_START_SIGRENDERER proc; - - if (!duh) - return NULL; - - if ((unsigned int)sig >= (unsigned int)duh->n_signals) - return NULL; - - signal = duh->signal[sig]; - if (!signal) - return NULL; - - sigrenderer = malloc(sizeof(*sigrenderer)); - if (!sigrenderer) - return NULL; - - sigrenderer->desc = signal->desc; - - proc = sigrenderer->desc->start_sigrenderer; - - if (proc) { - duh->signal[sig] = NULL; - sigrenderer->sigrenderer = (*proc)(duh, signal->sigdata, n_channels, pos); - duh->signal[sig] = signal; - - if (!sigrenderer->sigrenderer) { - free(sigrenderer); - return NULL; - } - } else - sigrenderer->sigrenderer = NULL; - - sigrenderer->n_channels = n_channels; - - sigrenderer->pos = pos; - sigrenderer->subpos = 0; - - sigrenderer->callback = NULL; - - return sigrenderer; -} - - - -#include -void duh_sigrenderer_set_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_CALLBACK callback, void *data -) -{ - (void)sigrenderer; - (void)callback; - (void)data; - fprintf(stderr, - "Call to deprecated function duh_sigrenderer_set_callback(). The callback\n" - "was not installed. See dumb/docs/deprec.txt for how to fix this.\n"); -} - - - -void duh_sigrenderer_set_analyser_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data -) -{ - (void)sigrenderer; - (void)callback; - (void)data; - fprintf(stderr, - "Call to deprecated function duh_sigrenderer_set_analyser_callback(). The\n" - "callback was not installed. See dumb/docs/deprec.txt for how to fix this.\n"); -} - - - -void duh_sigrenderer_set_sample_analyser_callback( - DUH_SIGRENDERER *sigrenderer, - DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data -) -{ - if (sigrenderer) { - sigrenderer->callback = callback; - sigrenderer->callback_data = data; - } -} - - - -int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) -{ - return sigrenderer ? sigrenderer->n_channels : 0; -} - - - -long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) -{ - return sigrenderer ? sigrenderer->pos : -1; -} - - - -void duh_sigrenderer_set_sigparam( - DUH_SIGRENDERER *sigrenderer, - unsigned char id, long value -) -{ - DUH_SIGRENDERER_SET_SIGPARAM proc; - - if (!sigrenderer) return; - - proc = sigrenderer->desc->sigrenderer_set_sigparam; - if (proc) - (*proc)(sigrenderer->sigrenderer, id, value); - else - TRACE("Parameter #%d = %ld for signal %c%c%c%c, which does not take parameters.\n", - (int)id, - value, - (int)(sigrenderer->desc->type >> 24), - (int)(sigrenderer->desc->type >> 16), - (int)(sigrenderer->desc->type >> 8), - (int)(sigrenderer->desc->type)); -} - - - -long duh_sigrenderer_generate_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - long rendered; - LONG_LONG t; - - if (!sigrenderer) return 0; - - rendered = (*sigrenderer->desc->sigrenderer_generate_samples) - (sigrenderer->sigrenderer, volume, delta, size, samples); - - if (rendered) { - if (sigrenderer->callback) - (*sigrenderer->callback)(sigrenderer->callback_data, - (const sample_t *const *)samples, sigrenderer->n_channels, rendered); - - t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered; - - sigrenderer->pos += (long)(t >> 16); - sigrenderer->subpos = (int)t & 65535; - } - - return rendered; -} - - - -/* DEPRECATED */ -long duh_sigrenderer_get_samples( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - sample_t **s; - long rendered; - long i; - int j; - if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL); - s = allocate_sample_buffer(sigrenderer->n_channels, size); - if (!s) return 0; - dumb_silence(s[0], sigrenderer->n_channels * size); - rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s); - for (j = 0; j < sigrenderer->n_channels; j++) - for (i = 0; i < rendered; i++) - samples[j][i] += s[0][i*sigrenderer->n_channels+j]; - destroy_sample_buffer(s); - return rendered; -} - - - -/* DEPRECATED */ -long duh_render_signal( - DUH_SIGRENDERER *sigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - sample_t **s; - long rendered; - long i; - int j; - if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL); - s = allocate_sample_buffer(sigrenderer->n_channels, size); - if (!s) return 0; - dumb_silence(s[0], sigrenderer->n_channels * size); - rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s); - for (j = 0; j < sigrenderer->n_channels; j++) - for (i = 0; i < rendered; i++) - samples[j][i] += s[0][i*sigrenderer->n_channels+j] >> 8; - destroy_sample_buffer(s); - return rendered; -} - - - -void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples) -{ - if (sigrenderer) - (*sigrenderer->desc->sigrenderer_get_current_sample)(sigrenderer->sigrenderer, volume, samples); -} - - - -void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer) -{ - if (sigrenderer) { - if (sigrenderer->desc->end_sigrenderer) - if (sigrenderer->sigrenderer) - (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer); - - free(sigrenderer); - } -} - - - -DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos) -{ - DUH_SIGRENDERER *sigrenderer; - - if (desc->start_sigrenderer && !vsigrenderer) return NULL; - - sigrenderer = malloc(sizeof(*sigrenderer)); - if (!sigrenderer) { - if (desc->end_sigrenderer) - if (vsigrenderer) - (*desc->end_sigrenderer)(vsigrenderer); - return NULL; - } - - sigrenderer->desc = desc; - sigrenderer->sigrenderer = vsigrenderer; - - sigrenderer->n_channels = n_channels; - - sigrenderer->pos = pos; - sigrenderer->subpos = 0; - - sigrenderer->callback = NULL; - - return sigrenderer; -} - - - -sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type) -{ - if (sigrenderer && sigrenderer->desc->type == type) - return sigrenderer->sigrenderer; - - return NULL; -} - - - -#if 0 -// This function is disabled because we don't know whether we want to destroy -// the sigrenderer if the type doesn't match. We don't even know if we need -// the function at all. Who would want to keep an IT_SIGRENDERER (for -// instance) without keeping the DUH_SIGRENDERER? -sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type) -{ - if (sigrenderer && sigrenderer->desc->type == type) { - - - - if (sigrenderer) { - if (sigrenderer->desc->end_sigrenderer) - if (sigrenderer->sigrenderer) - (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer); - - free(sigrenderer); - } - - - - - - - return sigrenderer->sigrenderer; - } - - return NULL; -} -#endif +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * rendsig.c - Wrappers to render samples from / / \ \ + * the signals in a DUH. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +struct DUH_SIGRENDERER +{ + DUH_SIGTYPE_DESC *desc; + + sigrenderer_t *sigrenderer; + + int n_channels; + + long pos; + int subpos; + + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback; + void *callback_data; +}; + + + +DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos) +{ + DUH_SIGRENDERER *sigrenderer; + + DUH_SIGNAL *signal; + DUH_START_SIGRENDERER proc; + + if (!duh) + return NULL; + + if ((unsigned int)sig >= (unsigned int)duh->n_signals) + return NULL; + + signal = duh->signal[sig]; + if (!signal) + return NULL; + + sigrenderer = malloc(sizeof(*sigrenderer)); + if (!sigrenderer) + return NULL; + + sigrenderer->desc = signal->desc; + + proc = sigrenderer->desc->start_sigrenderer; + + if (proc) { + duh->signal[sig] = NULL; + sigrenderer->sigrenderer = (*proc)(duh, signal->sigdata, n_channels, pos); + duh->signal[sig] = signal; + + if (!sigrenderer->sigrenderer) { + free(sigrenderer); + return NULL; + } + } else + sigrenderer->sigrenderer = NULL; + + sigrenderer->n_channels = n_channels; + + sigrenderer->pos = pos; + sigrenderer->subpos = 0; + + sigrenderer->callback = NULL; + + return sigrenderer; +} + + + +#include +void duh_sigrenderer_set_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_CALLBACK callback, void *data +) +{ + (void)sigrenderer; + (void)callback; + (void)data; + /*fprintf(stderr, + "Call to deprecated function duh_sigrenderer_set_callback(). The callback\n" + "was not installed. See dumb/docs/deprec.txt for how to fix this.\n");*/ +} + + + +void duh_sigrenderer_set_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data +) +{ + (void)sigrenderer; + (void)callback; + (void)data; + fprintf(stderr, + "Call to deprecated function duh_sigrenderer_set_analyser_callback(). The\n" + "callback was not installed. See dumb/docs/deprec.txt for how to fix this.\n"); +} + + + +void duh_sigrenderer_set_sample_analyser_callback( + DUH_SIGRENDERER *sigrenderer, + DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data +) +{ + if (sigrenderer) { + sigrenderer->callback = callback; + sigrenderer->callback_data = data; + } +} + + + +int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) +{ + return sigrenderer ? sigrenderer->n_channels : 0; +} + + + +long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) +{ + DUH_SIGRENDERER_GET_POSITION proc; + + if (!sigrenderer) return -1; + + proc = sigrenderer->desc->sigrenderer_get_position; + if (proc) + return (*proc)(sigrenderer->sigrenderer); + else + return sigrenderer->pos; +} + + + +void duh_sigrenderer_set_sigparam( + DUH_SIGRENDERER *sigrenderer, + unsigned char id, long value +) +{ + DUH_SIGRENDERER_SET_SIGPARAM proc; + + if (!sigrenderer) return; + + proc = sigrenderer->desc->sigrenderer_set_sigparam; + if (proc) + (*proc)(sigrenderer->sigrenderer, id, value); + else + TRACE("Parameter #%d = %ld for signal %c%c%c%c, which does not take parameters.\n", + (int)id, + value, + (int)(sigrenderer->desc->type >> 24), + (int)(sigrenderer->desc->type >> 16), + (int)(sigrenderer->desc->type >> 8), + (int)(sigrenderer->desc->type)); +} + + + +long duh_sigrenderer_generate_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + long rendered; + LONG_LONG t; + + if (!sigrenderer) return 0; + + rendered = (*sigrenderer->desc->sigrenderer_generate_samples) + (sigrenderer->sigrenderer, volume, delta, size, samples); + + if (rendered) { + if (sigrenderer->callback) + (*sigrenderer->callback)(sigrenderer->callback_data, + (const sample_t *const *)samples, sigrenderer->n_channels, rendered); + + t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered; + + sigrenderer->pos += (long)(t >> 16); + sigrenderer->subpos = (int)t & 65535; + } + + return rendered; +} + + + +/* DEPRECATED */ +long duh_sigrenderer_get_samples( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + sample_t **s; + long rendered; + long i; + int j; + if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL); + s = allocate_sample_buffer(sigrenderer->n_channels, size); + if (!s) return 0; + dumb_silence(s[0], sigrenderer->n_channels * size); + rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s); + for (j = 0; j < sigrenderer->n_channels; j++) + for (i = 0; i < rendered; i++) + samples[j][i] += s[0][i*sigrenderer->n_channels+j]; + destroy_sample_buffer(s); + return rendered; +} + + + +/* DEPRECATED */ +long duh_render_signal( + DUH_SIGRENDERER *sigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + sample_t **s; + long rendered; + long i; + int j; + if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL); + s = allocate_sample_buffer(sigrenderer->n_channels, size); + if (!s) return 0; + dumb_silence(s[0], sigrenderer->n_channels * size); + rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s); + for (j = 0; j < sigrenderer->n_channels; j++) + for (i = 0; i < rendered; i++) + samples[j][i] += s[0][i*sigrenderer->n_channels+j] >> 8; + destroy_sample_buffer(s); + return rendered; +} + + + +void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples) +{ + if (sigrenderer) + (*sigrenderer->desc->sigrenderer_get_current_sample)(sigrenderer->sigrenderer, volume, samples); +} + + + +void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer) +{ + if (sigrenderer) { + if (sigrenderer->desc->end_sigrenderer) + if (sigrenderer->sigrenderer) + (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer); + + free(sigrenderer); + } +} + + + +DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos) +{ + DUH_SIGRENDERER *sigrenderer; + + if (desc->start_sigrenderer && !vsigrenderer) return NULL; + + sigrenderer = malloc(sizeof(*sigrenderer)); + if (!sigrenderer) { + if (desc->end_sigrenderer) + if (vsigrenderer) + (*desc->end_sigrenderer)(vsigrenderer); + return NULL; + } + + sigrenderer->desc = desc; + sigrenderer->sigrenderer = vsigrenderer; + + sigrenderer->n_channels = n_channels; + + sigrenderer->pos = pos; + sigrenderer->subpos = 0; + + sigrenderer->callback = NULL; + + return sigrenderer; +} + + + +sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type) +{ + if (sigrenderer && sigrenderer->desc->type == type) + return sigrenderer->sigrenderer; + + return NULL; +} + + + +#if 0 +// This function is disabled because we don't know whether we want to destroy +// the sigrenderer if the type doesn't match. We don't even know if we need +// the function at all. Who would want to keep an IT_SIGRENDERER (for +// instance) without keeping the DUH_SIGRENDERER? +sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type) +{ + if (sigrenderer && sigrenderer->desc->type == type) { + + + + if (sigrenderer) { + if (sigrenderer->desc->end_sigrenderer) + if (sigrenderer->sigrenderer) + (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer); + + free(sigrenderer); + } + + + + + + + return sigrenderer->sigrenderer; + } + + return NULL; +} +#endif diff --git a/Frameworks/Dumb/dumb/src/core/unload.c b/Frameworks/Dumb/dumb/src/core/unload.c index 11d81e26e..f241f718d 100644 --- a/Frameworks/Dumb/dumb/src/core/unload.c +++ b/Frameworks/Dumb/dumb/src/core/unload.c @@ -1,64 +1,64 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * unload.c - Code to free a DUH from memory. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/dumb.h" - - - -static void destroy_signal(DUH_SIGNAL *signal) -{ - if (signal) { - if (signal->desc) - if (signal->desc->unload_sigdata) - if (signal->sigdata) - (*signal->desc->unload_sigdata)(signal->sigdata); - - free(signal); - } -} - - - -/* unload_duh(): destroys a DUH struct. You must call this for every DUH - * struct created, when you've finished with it. - */ -void unload_duh(DUH *duh) -{ - int i; - - if (duh) { - if (duh->signal) { - for (i = 0; i < duh->n_signals; i++) - destroy_signal(duh->signal[i]); - - free(duh->signal); - } - - if (duh->tag) { - if (duh->tag[0][0]) - free(duh->tag[0][0]); - free(duh->tag); - } - - free(duh); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * unload.c - Code to free a DUH from memory. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/dumb.h" + + + +static void destroy_signal(DUH_SIGNAL *signal) +{ + if (signal) { + if (signal->desc) + if (signal->desc->unload_sigdata) + if (signal->sigdata) + (*signal->desc->unload_sigdata)(signal->sigdata); + + free(signal); + } +} + + + +/* unload_duh(): destroys a DUH struct. You must call this for every DUH + * struct created, when you've finished with it. + */ +void unload_duh(DUH *duh) +{ + int i; + + if (duh) { + if (duh->signal) { + for (i = 0; i < duh->n_signals; i++) + destroy_signal(duh->signal[i]); + + free(duh->signal); + } + + if (duh->tag) { + if (duh->tag[0][0]) + free(duh->tag[0][0]); + free(duh->tag); + } + + free(duh); + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/barray.c b/Frameworks/Dumb/dumb/src/helpers/barray.c new file mode 100644 index 000000000..95fe7af10 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/barray.c @@ -0,0 +1,159 @@ +#include "internal/barray.h" + +#include + + +void * bit_array_create(size_t size) +{ + size_t bsize = ((size + 7) >> 3) + sizeof(size_t); + void * ret = calloc(1, bsize); + if (ret) *(size_t *)ret = size; + return ret; +} + +void bit_array_destroy(void * array) +{ + if (array) free(array); +} + +void * bit_array_dup(void * array) +{ + if (array) + { + size_t * size = (size_t *) array; + size_t bsize = ((*size + 7) >> 3) + sizeof(*size); + void * ret = malloc(bsize); + if (ret) memcpy(ret, array, bsize); + return ret; + } + return NULL; +} + +void bit_array_reset(void * array) +{ + if (array) + { + size_t * size = (size_t *) array; + size_t bsize = (*size + 7) >> 3; + memset(size + 1, 0, bsize); + } +} + + +void bit_array_set(void * array, size_t bit) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + ptr[bit >> 3] |= (1U << (bit & 7)); + } + } +} + +int bit_array_test(void * array, size_t bit) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + if (ptr[bit >> 3] & (1U << (bit & 7))) + { + return 1; + } + } + } + return 0; +} + +int bit_array_test_range(void * array, size_t bit, size_t count) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + if ((bit & 7) && (count > 8)) + { + while ((bit < *size) && count && (bit & 7)) + { + if (ptr[bit >> 3] & (1U << (bit & 7))) return 1; + bit++; + count--; + } + } + if (!(bit & 7)) + { + while (((*size - bit) >= 8) && (count >= 8)) + { + if (ptr[bit >> 3]) return 1; + bit += 8; + count -= 8; + } + } + while ((bit < *size) && count) + { + if (ptr[bit >> 3] & (1U << (bit & 7))) return 1; + bit++; + count--; + } + } + } + return 0; +} + +void bit_array_clear(void * array, size_t bit) +{ + if (array) + { + size_t * size = (size_t *) array; + if (bit < *size) + { + unsigned char * ptr = (unsigned char *)(size + 1); + ptr[bit >> 3] &= ~(1U << (bit & 7)); + } + } +} + +void bit_array_merge(void * dest, void * source, size_t offset) +{ + if (dest && source) + { + size_t * dsize = (size_t *) dest; + size_t * ssize = (size_t *) source; + size_t soffset = 0; + while (offset < *dsize && soffset < *ssize) + { + if (bit_array_test(source, soffset)) + { + bit_array_set(dest, offset); + } + soffset++; + offset++; + } + } +} + +void bit_array_mask(void * dest, void * source, size_t offset) +{ + if (dest && source) + { + size_t * dsize = (size_t *) dest; + size_t * ssize = (size_t *) source; + size_t soffset = 0; + while (offset < *dsize && soffset < *ssize) + { + if (bit_array_test(source, soffset)) + { + bit_array_clear(dest, offset); + } + soffset++; + offset++; + } + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/blip_buf.c b/Frameworks/Dumb/dumb/src/helpers/blip_buf.c new file mode 100644 index 000000000..9f26f71cb --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/blip_buf.c @@ -0,0 +1,354 @@ +/* blip_buf 1.1.0. http://www.slack.net/~ant/ */ + +#include "internal/blip_buf.h" + +#include +#include +#include +#include + +/* Library Copyright (C) 2003-2009 Shay Green. This library is free software; +you can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +library is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#if defined (BLARGG_TEST) && BLARGG_TEST + #include "blargg_test.h" +#endif + +/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000. +Avoids constants that don't fit in 32 bits. */ +#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF + typedef unsigned long fixed_t; + enum { pre_shift = 32 }; + +#elif defined(ULLONG_MAX) + typedef unsigned long long fixed_t; + enum { pre_shift = 32 }; + +#else + typedef unsigned fixed_t; + enum { pre_shift = 0 }; + +#endif + +enum { time_bits = pre_shift + 20 }; + +static fixed_t const time_unit = (fixed_t) 1 << time_bits; + +enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */ +enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */ + +enum { half_width = 8 }; +enum { buf_extra = half_width*2 + end_frame_extra }; +enum { phase_bits = 5 }; +enum { phase_count = 1 << phase_bits }; +enum { delta_bits = 15 }; +enum { delta_unit = 1 << delta_bits }; +enum { frac_bits = time_bits - pre_shift }; + +/* We could eliminate avail and encode whole samples in offset, but that would +limit the total buffered samples to blip_max_frame. That could only be +increased by decreasing time_bits, which would reduce resample ratio accuracy. +*/ + +/** Sample buffer that resamples to output rate and accumulates samples +until they're read out */ +struct blip_t +{ + fixed_t factor; + fixed_t offset; + int avail; + int size; + int integrator; +}; + +typedef int buf_t; + +/* probably not totally portable */ +#define SAMPLES( buf ) ((buf_t*) ((buf) + 1)) + +/* Arithmetic (sign-preserving) right shift */ +#define ARITH_SHIFT( n, shift ) \ + ((n) >> (shift)) + +enum { max_sample = +32767 }; +enum { min_sample = -32768 }; + +#define CLAMP( n ) \ + {\ + if ( (short) n != n )\ + n = ARITH_SHIFT( n, 16 ) ^ max_sample;\ + } + +static void check_assumptions( void ) +{ + int n; + + #if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" + #endif + + assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */ + + n = max_sample * 2; + CLAMP( n ); + assert( n == max_sample ); + + n = min_sample * 2; + CLAMP( n ); + assert( n == min_sample ); + + assert( blip_max_ratio <= time_unit ); + assert( blip_max_frame <= (fixed_t) -1 >> time_bits ); +} + +blip_t* blip_new( int size ) +{ + blip_t* m; + assert( size >= 0 ); + + m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) ); + if ( m ) + { + m->factor = time_unit / blip_max_ratio; + m->size = size; + blip_clear( m ); + check_assumptions(); + } + return m; +} + +blip_t* blip_dup( blip_t* m ) +{ + size_t size = sizeof *m + (m->size + buf_extra) * sizeof(buf_t); + blip_t* r = (blip_t*) malloc( size ); + if ( r ) memcpy( r, m, size ); + return r; +} + +void blip_delete( blip_t* m ) +{ + if ( m != NULL ) + { + /* Clear fields in case user tries to use after freeing */ + memset( m, 0, sizeof *m ); + free( m ); + } +} + +void blip_set_rates( blip_t* m, double clock_rate, double sample_rate ) +{ + double factor = time_unit * sample_rate / clock_rate; + m->factor = (fixed_t) factor; + + /* Fails if clock_rate exceeds maximum, relative to sample_rate */ + assert( 0 <= factor - m->factor && factor - m->factor < 1 ); + + /* Avoid requiring math.h. Equivalent to + m->factor = (int) ceil( factor ) */ + if ( m->factor < factor ) + m->factor++; + + /* At this point, factor is most likely rounded up, but could still + have been rounded down in the floating-point calculation. */ +} + +void blip_clear( blip_t* m ) +{ + /* We could set offset to 0, factor/2, or factor-1. 0 is suitable if + factor is rounded up. factor-1 is suitable if factor is rounded down. + Since we don't know rounding direction, factor/2 accommodates either, + with the slight loss of showing an error in half the time. Since for + a 64-bit factor this is years, the halving isn't a problem. */ + + m->offset = m->factor / 2; + m->avail = 0; + m->integrator = 0; + memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) ); +} + +int blip_clocks_needed( const blip_t* m, int samples ) +{ + fixed_t needed; + + /* Fails if buffer can't hold that many more samples */ + assert( samples >= 0 && m->avail + samples <= m->size ); + + needed = (fixed_t) samples * time_unit; + if ( needed < m->offset ) + return 0; + + return (needed - m->offset + m->factor - 1) / m->factor; +} + +void blip_end_frame( blip_t* m, unsigned t ) +{ + fixed_t off = t * m->factor + m->offset; + m->avail += off >> time_bits; + m->offset = off & (time_unit - 1); + + /* Fails if buffer size was exceeded */ + assert( m->avail <= m->size ); +} + +int blip_samples_avail( const blip_t* m ) +{ + return m->avail; +} + +static void remove_samples( blip_t* m, int count ) +{ + buf_t* buf = SAMPLES( m ); + int remain = m->avail + buf_extra - count; + m->avail -= count; + + memmove( &buf [0], &buf [count], remain * sizeof buf [0] ); + memset( &buf [remain], 0, count * sizeof buf [0] ); +} + +int blip_read_samples( blip_t* m, int out [], int count ) +{ + assert( count >= 0 ); + + if ( count > m->avail ) + count = m->avail; + + if ( count ) + { + buf_t const* in = SAMPLES( m ); + buf_t const* end = in + count; + int sum = m->integrator; + do + { + /* Eliminate fraction */ + int s = ARITH_SHIFT( sum, delta_bits - 8 ); + + sum += *in++; + + *out = s; + out++; + + /* High-pass filter */ + sum -= s >> (8 - (delta_bits - bass_shift)); //<< (delta_bits - bass_shift - 8); + } + while ( in != end ); + m->integrator = sum; + + remove_samples( m, count ); + } + + return count; +} + +int blip_peek_sample( blip_t* m ) +{ + return ARITH_SHIFT( m->integrator, delta_bits - 8 ); +} + +/* Things that didn't help performance on x86: + __attribute__((aligned(128))) + #define short int + restrict +*/ + +/* Sinc_Generator( 0.9, 0.55, 4.5 ) */ +static short const bl_step [phase_count + 1] [half_width] = +{ +{ 43, -115, 350, -488, 1136, -914, 5861,21022}, +{ 44, -118, 348, -473, 1076, -799, 5274,21001}, +{ 45, -121, 344, -454, 1011, -677, 4706,20936}, +{ 46, -122, 336, -431, 942, -549, 4156,20829}, +{ 47, -123, 327, -404, 868, -418, 3629,20679}, +{ 47, -122, 316, -375, 792, -285, 3124,20488}, +{ 47, -120, 303, -344, 714, -151, 2644,20256}, +{ 46, -117, 289, -310, 634, -17, 2188,19985}, +{ 46, -114, 273, -275, 553, 117, 1758,19675}, +{ 44, -108, 255, -237, 471, 247, 1356,19327}, +{ 43, -103, 237, -199, 390, 373, 981,18944}, +{ 42, -98, 218, -160, 310, 495, 633,18527}, +{ 40, -91, 198, -121, 231, 611, 314,18078}, +{ 38, -84, 178, -81, 153, 722, 22,17599}, +{ 36, -76, 157, -43, 80, 824, -241,17092}, +{ 34, -68, 135, -3, 8, 919, -476,16558}, +{ 32, -61, 115, 34, -60, 1006, -683,16001}, +{ 29, -52, 94, 70, -123, 1083, -862,15422}, +{ 27, -44, 73, 106, -184, 1152,-1015,14824}, +{ 25, -36, 53, 139, -239, 1211,-1142,14210}, +{ 22, -27, 34, 170, -290, 1261,-1244,13582}, +{ 20, -20, 16, 199, -335, 1301,-1322,12942}, +{ 18, -12, -3, 226, -375, 1331,-1376,12293}, +{ 15, -4, -19, 250, -410, 1351,-1408,11638}, +{ 13, 3, -35, 272, -439, 1361,-1419,10979}, +{ 11, 9, -49, 292, -464, 1362,-1410,10319}, +{ 9, 16, -63, 309, -483, 1354,-1383, 9660}, +{ 7, 22, -75, 322, -496, 1337,-1339, 9005}, +{ 6, 26, -85, 333, -504, 1312,-1280, 8355}, +{ 4, 31, -94, 341, -507, 1278,-1205, 7713}, +{ 3, 35, -102, 347, -506, 1238,-1119, 7082}, +{ 1, 40, -110, 350, -499, 1190,-1021, 6464}, +{ 0, 43, -115, 350, -488, 1136, -914, 5861} +}; + +/* Shifting by pre_shift allows calculation using unsigned int rather than +possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient. +And by having pre_shift 32, a 32-bit platform can easily do the shift by +simply ignoring the low half. */ + +void blip_add_delta( blip_t* m, unsigned time, int delta ) +{ + unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); + buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); + + int const phase_shift = frac_bits - phase_bits; + int phase = fixed >> phase_shift & (phase_count - 1); + short const* in = bl_step [phase]; + short const* rev = bl_step [phase_count - phase]; + + int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1); + int delta2 = (delta * interp) >> delta_bits; + delta -= delta2; + + /* Fails if buffer size was exceeded */ + assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); + + out [0] += in[0]*delta + in[half_width+0]*delta2; + out [1] += in[1]*delta + in[half_width+1]*delta2; + out [2] += in[2]*delta + in[half_width+2]*delta2; + out [3] += in[3]*delta + in[half_width+3]*delta2; + out [4] += in[4]*delta + in[half_width+4]*delta2; + out [5] += in[5]*delta + in[half_width+5]*delta2; + out [6] += in[6]*delta + in[half_width+6]*delta2; + out [7] += in[7]*delta + in[half_width+7]*delta2; + + in = rev; + out [ 8] += in[7]*delta + in[7-half_width]*delta2; + out [ 9] += in[6]*delta + in[6-half_width]*delta2; + out [10] += in[5]*delta + in[5-half_width]*delta2; + out [11] += in[4]*delta + in[4-half_width]*delta2; + out [12] += in[3]*delta + in[3-half_width]*delta2; + out [13] += in[2]*delta + in[2-half_width]*delta2; + out [14] += in[1]*delta + in[1-half_width]*delta2; + out [15] += in[0]*delta + in[0-half_width]*delta2; +} + +void blip_add_delta_fast( blip_t* m, unsigned time, int delta ) +{ + unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); + buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); + + int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1); + int delta2 = delta * interp; + + /* Fails if buffer size was exceeded */ + assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); + + out [7] += delta * delta_unit - delta2; + out [8] += delta2; +} diff --git a/Frameworks/Dumb/dumb/src/helpers/clickrem.c b/Frameworks/Dumb/dumb/src/helpers/clickrem.c index 9109e3241..336b492de 100644 --- a/Frameworks/Dumb/dumb/src/helpers/clickrem.c +++ b/Frameworks/Dumb/dumb/src/helpers/clickrem.c @@ -1,281 +1,281 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * clickrem.c - Click removal helpers. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include -#include "dumb.h" - - - -typedef struct DUMB_CLICK DUMB_CLICK; - - -struct DUMB_CLICK_REMOVER -{ - DUMB_CLICK *click; - int n_clicks; - - int offset; -}; - - -struct DUMB_CLICK -{ - DUMB_CLICK *next; - long pos; - sample_t step; -}; - - - -DUMB_CLICK_REMOVER *dumb_create_click_remover(void) -{ - DUMB_CLICK_REMOVER *cr = malloc(sizeof(*cr)); - if (!cr) return NULL; - - cr->click = NULL; - cr->n_clicks = 0; - - cr->offset = 0; - - return cr; -} - - - -void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step) -{ - DUMB_CLICK *click; - - ASSERT(pos >= 0); - - if (!cr || !step) return; - - if (pos == 0) { - cr->offset -= step; - return; - } - - click = malloc(sizeof(*click)); - if (!click) return; - - click->pos = pos; - click->step = step; - - click->next = cr->click; - cr->click = click; - cr->n_clicks++; -} - - - -static DUMB_CLICK *dumb_click_mergesort(DUMB_CLICK *click, int n_clicks) -{ - int i; - DUMB_CLICK *c1, *c2, **cp; - - if (n_clicks <= 1) return click; - - /* Split the list into two */ - c1 = click; - cp = &c1; - for (i = 0; i < n_clicks; i += 2) cp = &(*cp)->next; - c2 = *cp; - *cp = NULL; - - /* Sort the sublists */ - c1 = dumb_click_mergesort(c1, (n_clicks + 1) >> 1); - c2 = dumb_click_mergesort(c2, n_clicks >> 1); - - /* Merge them */ - cp = &click; - while (c1 && c2) { - if (c1->pos > c2->pos) { - *cp = c2; - c2 = c2->next; - } else { - *cp = c1; - c1 = c1->next; - } - cp = &(*cp)->next; - } - if (c2) - *cp = c2; - else - *cp = c1; - - return click; -} - - - -void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife) -{ - DUMB_CLICK *click; - long pos = 0; - int offset; - int factor; - - if (!cr) return; - - factor = (int)floor(pow(0.5, 1.0/halflife) * (1U << 31)); - - click = dumb_click_mergesort(cr->click, cr->n_clicks); - cr->click = NULL; - cr->n_clicks = 0; - - length *= step; - - while (click) { - DUMB_CLICK *next = click->next; - int end = click->pos * step; - ASSERT(end <= length); - offset = cr->offset; - if (offset < 0) { - offset = -offset; - while (pos < end) { - samples[pos] -= offset; - offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); - pos += step; - } - offset = -offset; - } else { - while (pos < end) { - samples[pos] += offset; - offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); - pos += step; - } - } - cr->offset = offset - click->step; - free(click); - click = next; - } - - offset = cr->offset; - if (offset < 0) { - offset = -offset; - while (pos < length) { - samples[pos] -= offset; - offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); - pos += step; - } - offset = -offset; - } else { - while (pos < length) { - samples[pos] += offset; - offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); - pos += step; - } - } - cr->offset = offset; -} - - - -sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr) -{ - return cr ? cr->offset : 0; -} - - - -void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr) -{ - if (cr) { - DUMB_CLICK *click = cr->click; - while (click) { - DUMB_CLICK *next = click->next; - free(click); - click = next; - } - free(cr); - } -} - - - -DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n) -{ - int i; - DUMB_CLICK_REMOVER **cr; - if (n <= 0) return NULL; - cr = malloc(n * sizeof(*cr)); - if (!cr) return NULL; - for (i = 0; i < n; i++) cr[i] = dumb_create_click_remover(); - return cr; -} - - - -void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) -{ - if (cr) { - int i; - for (i = 0; i < n; i++) - dumb_record_click(cr[i], pos, step[i]); - } -} - - - -void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) -{ - if (cr) { - int i; - for (i = 0; i < n; i++) - dumb_record_click(cr[i], pos, -step[i]); - } -} - - - -void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife) -{ - if (cr) { - int i; - for (i = 0; i < n >> 1; i++) { - dumb_remove_clicks(cr[i << 1], samples[i], length, 2, halflife); - dumb_remove_clicks(cr[(i << 1) + 1], samples[i] + 1, length, 2, halflife); - } - if (n & 1) - dumb_remove_clicks(cr[i << 1], samples[i], length, 1, halflife); - } -} - - - -void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset) -{ - if (cr) { - int i; - for (i = 0; i < n; i++) - if (cr[i]) offset[i] += cr[i]->offset; - } -} - - - -void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr) -{ - if (cr) { - int i; - for (i = 0; i < n; i++) dumb_destroy_click_remover(cr[i]); - free(cr); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * clickrem.c - Click removal helpers. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include "dumb.h" + + + +typedef struct DUMB_CLICK DUMB_CLICK; + + +struct DUMB_CLICK_REMOVER +{ + DUMB_CLICK *click; + int n_clicks; + + int offset; +}; + + +struct DUMB_CLICK +{ + DUMB_CLICK *next; + long pos; + sample_t step; +}; + + + +DUMB_CLICK_REMOVER *dumb_create_click_remover(void) +{ + DUMB_CLICK_REMOVER *cr = malloc(sizeof(*cr)); + if (!cr) return NULL; + + cr->click = NULL; + cr->n_clicks = 0; + + cr->offset = 0; + + return cr; +} + + + +void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step) +{ + DUMB_CLICK *click; + + ASSERT(pos >= 0); + + if (!cr || !step) return; + + if (pos == 0) { + cr->offset -= step; + return; + } + + click = malloc(sizeof(*click)); + if (!click) return; + + click->pos = pos; + click->step = step; + + click->next = cr->click; + cr->click = click; + cr->n_clicks++; +} + + + +static DUMB_CLICK *dumb_click_mergesort(DUMB_CLICK *click, int n_clicks) +{ + int i; + DUMB_CLICK *c1, *c2, **cp; + + if (n_clicks <= 1) return click; + + /* Split the list into two */ + c1 = click; + cp = &c1; + for (i = 0; i < n_clicks; i += 2) cp = &(*cp)->next; + c2 = *cp; + *cp = NULL; + + /* Sort the sublists */ + c1 = dumb_click_mergesort(c1, (n_clicks + 1) >> 1); + c2 = dumb_click_mergesort(c2, n_clicks >> 1); + + /* Merge them */ + cp = &click; + while (c1 && c2) { + if (c1->pos > c2->pos) { + *cp = c2; + c2 = c2->next; + } else { + *cp = c1; + c1 = c1->next; + } + cp = &(*cp)->next; + } + if (c2) + *cp = c2; + else + *cp = c1; + + return click; +} + + + +void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife) +{ + DUMB_CLICK *click; + long pos = 0; + int offset; + int factor; + + if (!cr) return; + + factor = (int)floor(pow(0.5, 1.0/halflife) * (1U << 31)); + + click = dumb_click_mergesort(cr->click, cr->n_clicks); + cr->click = NULL; + cr->n_clicks = 0; + + length *= step; + + while (click) { + DUMB_CLICK *next = click->next; + int end = click->pos * step; + ASSERT(end <= length); + offset = cr->offset; + if (offset < 0) { + offset = -offset; + while (pos < end) { + samples[pos] -= offset; + offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32); + pos += step; + } + offset = -offset; + } else { + while (pos < end) { + samples[pos] += offset; + offset = (int)(((LONG_LONG)(offset << 1) * factor) >> 32); + pos += step; + } + } + cr->offset = offset - click->step; + free(click); + click = next; + } + + offset = cr->offset; + if (offset < 0) { + offset = -offset; + while (pos < length) { + samples[pos] -= offset; + offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); + pos += step; + } + offset = -offset; + } else { + while (pos < length) { + samples[pos] += offset; + offset = (int)((LONG_LONG)(offset << 1) * factor >> 32); + pos += step; + } + } + cr->offset = offset; +} + + + +sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr) +{ + return cr ? cr->offset : 0; +} + + + +void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr) +{ + if (cr) { + DUMB_CLICK *click = cr->click; + while (click) { + DUMB_CLICK *next = click->next; + free(click); + click = next; + } + free(cr); + } +} + + + +DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n) +{ + int i; + DUMB_CLICK_REMOVER **cr; + if (n <= 0) return NULL; + cr = malloc(n * sizeof(*cr)); + if (!cr) return NULL; + for (i = 0; i < n; i++) cr[i] = dumb_create_click_remover(); + return cr; +} + + + +void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + dumb_record_click(cr[i], pos, step[i]); + } +} + + + +void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + dumb_record_click(cr[i], pos, -step[i]); + } +} + + + +void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife) +{ + if (cr) { + int i; + for (i = 0; i < n >> 1; i++) { + dumb_remove_clicks(cr[i << 1], samples[i], length, 2, halflife); + dumb_remove_clicks(cr[(i << 1) + 1], samples[i] + 1, length, 2, halflife); + } + if (n & 1) + dumb_remove_clicks(cr[i << 1], samples[i], length, 1, halflife); + } +} + + + +void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) + if (cr[i]) offset[i] += cr[i]->offset; + } +} + + + +void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr) +{ + if (cr) { + int i; + for (i = 0; i < n; i++) dumb_destroy_click_remover(cr[i]); + free(cr); + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/fir_resampler.c b/Frameworks/Dumb/dumb/src/helpers/fir_resampler.c new file mode 100644 index 000000000..cc6460769 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/fir_resampler.c @@ -0,0 +1,281 @@ +#include +#include +#include + +#include "internal/fir_resampler.h" + +enum { fir_width = 16 }; + +enum { fir_max_res = 1024 }; +enum { fir_min_width = (fir_width < 4 ? 4 : fir_width) }; +enum { fir_adj_width = fir_min_width / 4 * 4 + 2 }; +enum { fir_stereo = 1 }; /* channel count, not boolean value */ +enum { fir_write_offset = fir_adj_width * fir_stereo }; + +enum { fir_buffer_size = fir_width * 2 }; + +typedef short fir_impulse[fir_adj_width]; + +/* exp slope to 31/32 of ln(8) */ +static const double fir_ratios[32] = { + 1.000, 1.067, 1.139, 1.215, 1.297, 1.384, 1.477, 1.576, + 1.682, 1.795, 1.915, 2.044, 2.181, 2.327, 2.484, 2.650, + 2.828, 3.018, 3.221, 3.437, 3.668, 3.914, 4.177, 4.458, + 4.757, 5.076, 5.417, 5.781, 6.169, 6.583, 7.025, 7.497 +}; + +static fir_impulse fir_impulses[32][fir_max_res]; + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale, + int count, short* out ) +{ + double const maxh = 256; + double const step = PI / maxh * spacing; + double const to_w = maxh * 2 / width; + double const pow_a_n = pow( rolloff, maxh ); + + double angle = (count / 2 - 1 + offset) * -step; + + scale /= maxh * 2; + + while ( count-- ) + { + double w; + *out++ = 0; + w = angle * to_w; + if ( fabs( w ) < PI ) + { + double rolloff_cos_a = rolloff * cos( angle ); + double num = 1 - rolloff_cos_a - + pow_a_n * cos( maxh * angle ) + + pow_a_n * rolloff * cos( (maxh - 1) * angle ); + double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; + double sinc = scale * num / den - scale; + + out [-1] = (short) (cos( w ) * sinc + sinc); + } + angle += step; + } +} + +typedef struct fir_resampler +{ + int write_pos, write_filled; + int read_pos, read_filled; + unsigned short phase; + unsigned int phase_inc; + unsigned int ratio_set; + int buffer_in[fir_buffer_size * 2]; + int buffer_out[fir_buffer_size]; +} fir_resampler; + +void * fir_resampler_create() +{ + fir_resampler * r = ( fir_resampler * ) malloc( sizeof(fir_resampler) ); + if ( !r ) return 0; + + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; + r->phase_inc = 0; + r->ratio_set = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); + memset( r->buffer_out, 0, sizeof(r->buffer_out) ); + + return r; +} + +void fir_resampler_delete(void * _r) +{ + free( _r ); +} + +void * fir_resampler_dup(void * _r) +{ + fir_resampler * r_in = ( fir_resampler * ) _r; + fir_resampler * r_out = ( fir_resampler * ) malloc( sizeof(fir_resampler) ); + if ( !r_out ) return 0; + + r_out->write_pos = r_in->write_pos; + r_out->write_filled = r_in->write_filled; + r_out->read_pos = r_in->read_pos; + r_out->read_filled = r_in->read_filled; + r_out->phase = r_in->phase; + r_out->phase_inc = r_in->phase_inc; + r_out->ratio_set = r_in->ratio_set; + memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) ); + memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) ); + + return r_out; +} + +int fir_resampler_get_free_count(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + return fir_buffer_size - r->write_filled; +} + +int fir_resampler_ready(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + return r->write_filled > fir_adj_width; +} + +void fir_resampler_clear(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); +} + +void fir_resampler_set_rate(void *_r, double new_factor) +{ + fir_resampler * r = ( fir_resampler * ) _r; + r->phase_inc = (int)( new_factor * 65536.0 ); + r->ratio_set = 0; + while ( r->ratio_set < 31 && new_factor > fir_ratios[ r->ratio_set ] ) r->ratio_set++; +} + +void fir_resampler_write_sample(void *_r, short s) +{ + fir_resampler * r = ( fir_resampler * ) _r; + + if ( r->write_filled < fir_buffer_size ) + { + int s32 = s; + + r->buffer_in[ r->write_pos ] = s32; + r->buffer_in[ r->write_pos + fir_buffer_size ] = s32; + + ++r->write_filled; + + r->write_pos = ( r->write_pos + 1 ) % fir_buffer_size; + } +} + +void fir_init() +{ + double const rolloff = 0.999; + double const gain = 1.0; + + int const res = fir_max_res; + + int i; + + for (i = 0; i < 32; i++) + { + double const ratio_ = fir_ratios[ i ]; + + double fraction = 1.0 / (double)fir_max_res; + + double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double pos = 0.0; + short* out = (short*) fir_impulses[ i ]; + int n; + for ( n = res; --n >= 0; ) + { + gen_sinc( rolloff, (int) (fir_adj_width * filter + 1) & ~1, pos, filter, + (double) (0x7FFF * gain * filter), (int) fir_adj_width, out ); + out += fir_adj_width; + + pos += fraction; + } + } +} + +int fir_resampler_run(void *_r, int ** out_, int * out_end) +{ + fir_resampler * r = ( fir_resampler * ) _r; + int in_size = r->write_filled; + int const* in_ = r->buffer_in + fir_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= fir_write_offset; + if ( in_size > 0 ) + { + int* out = *out_; + int const* in = in_; + int const* const in_end = in + in_size; + int phase = r->phase; + int phase_inc = r->phase_inc; + int ratio_set = r->ratio_set; + + do + { + // accumulate in extended precision + short const* imp = fir_impulses[ratio_set][(phase & 0xFFC0) >> 6]; + int pt = imp [0]; + int s = pt * in [0]; + int n; + if ( out >= out_end ) + break; + for ( n = (fir_adj_width - 2) / 2; n; --n ) + { + pt = imp [1]; + s += pt * in [1]; + + // pre-increment more efficient on some RISC processors + imp += 2; + pt = imp [0]; + in += 2; + s += pt * in [0]; + } + pt = imp [1]; + s += pt * in [1]; + + phase += phase_inc; + + in += (phase >> 16) - fir_adj_width + 2; + + phase &= 65535; + + *out++ = (int) (s >> 7); + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = in - in_; + + r->write_filled -= used; + } + + return used; +} + +int fir_resampler_get_sample(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + if ( r->read_filled < 1 ) + { + int write_pos = ( r->read_pos + r->read_filled ) % fir_buffer_size; + int write_size = fir_buffer_size - write_pos; + int * out = r->buffer_out + write_pos; + if ( write_size > ( fir_buffer_size - r->read_filled ) ) + write_size = fir_buffer_size - r->read_filled; + fir_resampler_run( r, &out, out + write_size ); + r->read_filled += out - r->buffer_out - write_pos; + } + if ( r->read_filled < 1 ) + return 0; + return r->buffer_out[ r->read_pos ]; +} + +void fir_resampler_remove_sample(void *_r) +{ + fir_resampler * r = ( fir_resampler * ) _r; + if ( r->read_filled > 0 ) + { + --r->read_filled; + r->read_pos = ( r->read_pos + 1 ) % fir_buffer_size; + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c b/Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c new file mode 100644 index 000000000..d99abf595 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/lanczos_resampler.c @@ -0,0 +1,229 @@ +#include +#include +#define _USE_MATH_DEFINES +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "internal/lanczos_resampler.h" + +enum { LANCZOS_RESOLUTION = 8192 }; +enum { LANCZOS_WIDTH = 8 }; +enum { LANCZOS_SAMPLES = LANCZOS_RESOLUTION * LANCZOS_WIDTH }; + +static double lanczos_lut[LANCZOS_SAMPLES + 1]; + +enum { lanczos_buffer_size = LANCZOS_WIDTH * 4 }; + +int fEqual(const double b, const double a) +{ + return fabs(a - b) < 1.0e-6; +} + +static double sinc(double x) +{ + return fEqual(x, 0.0) ? 1.0 : sin(x * M_PI) / (x * M_PI); +} + +void lanczos_init() +{ + unsigned i; + double dx = (double)(LANCZOS_WIDTH) / LANCZOS_SAMPLES, x = 0.0; + for (i = 0; i < LANCZOS_SAMPLES + 1; ++i, x += dx) + lanczos_lut[i] = abs(x) < LANCZOS_WIDTH ? sinc(x) * sinc(x / LANCZOS_WIDTH) : 0.0; +} + +typedef struct lanczos_resampler +{ + int write_pos, write_filled; + int read_pos, read_filled; + unsigned short phase; + unsigned int phase_inc; + float buffer_in[lanczos_buffer_size * 2]; + int buffer_out[lanczos_buffer_size]; +} lanczos_resampler; + +void * lanczos_resampler_create() +{ + lanczos_resampler * r = ( lanczos_resampler * ) malloc( sizeof(lanczos_resampler) ); + if ( !r ) return 0; + + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; + r->phase_inc = 0; + memset( r->buffer_in, 0, sizeof(r->buffer_in) ); + memset( r->buffer_out, 0, sizeof(r->buffer_out) ); + + return r; +} + +void lanczos_resampler_delete(void * _r) +{ + free( _r ); +} + +void * lanczos_resampler_dup(void * _r) +{ + lanczos_resampler * r_in = ( lanczos_resampler * ) _r; + lanczos_resampler * r_out = ( lanczos_resampler * ) malloc( sizeof(lanczos_resampler) ); + if ( !r_out ) return 0; + + r_out->write_pos = r_in->write_pos; + r_out->write_filled = r_in->write_filled; + r_out->read_pos = r_in->read_pos; + r_out->read_filled = r_in->read_filled; + r_out->phase = r_in->phase; + r_out->phase_inc = r_in->phase_inc; + memcpy( r_out->buffer_in, r_in->buffer_in, sizeof(r_in->buffer_in) ); + memcpy( r_out->buffer_out, r_in->buffer_out, sizeof(r_in->buffer_out) ); + + return r_out; +} + +int lanczos_resampler_get_free_count(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + return lanczos_buffer_size - r->write_filled; +} + +int lanczos_resampler_ready(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + return r->write_filled > (LANCZOS_WIDTH * 2); +} + +void lanczos_resampler_clear(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + r->write_pos = 0; + r->write_filled = 0; + r->read_pos = 0; + r->read_filled = 0; + r->phase = 0; +} + +void lanczos_resampler_set_rate(void *_r, double new_factor) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + r->phase_inc = (int)( new_factor * LANCZOS_RESOLUTION ); +} + +void lanczos_resampler_write_sample(void *_r, short s) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + + if ( r->write_filled < lanczos_buffer_size ) + { + float s32 = s; + + r->buffer_in[ r->write_pos ] = s32; + r->buffer_in[ r->write_pos + lanczos_buffer_size ] = s32; + + ++r->write_filled; + + r->write_pos = ( r->write_pos + 1 ) % lanczos_buffer_size; + } +} + +static int lanczos_resampler_run(lanczos_resampler * r, int ** out_, int * out_end) +{ + int in_size = r->write_filled; + float const* in_ = r->buffer_in + lanczos_buffer_size + r->write_pos - r->write_filled; + int used = 0; + in_size -= LANCZOS_WIDTH * 2; + if ( in_size > 0 ) + { + int* out = *out_; + float const* in = in_; + float const* const in_end = in + in_size; + int phase = r->phase; + int phase_inc = r->phase_inc; + + int step = phase_inc > LANCZOS_RESOLUTION ? LANCZOS_RESOLUTION * LANCZOS_RESOLUTION / phase_inc : LANCZOS_RESOLUTION; + + do + { + // accumulate in extended precision + double kernel[LANCZOS_WIDTH * 2], kernel_sum = 0.0; + int i = LANCZOS_WIDTH; + int phase_adj = phase * step / LANCZOS_RESOLUTION; + double sample; + + if ( out >= out_end ) + break; + + for (; i >= -LANCZOS_WIDTH + 1; --i) + { + int pos = i * step; + kernel_sum += kernel[i + LANCZOS_WIDTH - 1] = lanczos_lut[abs(phase_adj - pos)]; + } + for (sample = 0, i = 0; i < LANCZOS_WIDTH * 2; ++i) + sample += in[i] * kernel[i]; + *out++ = (int) (sample / kernel_sum * 256.0); + + phase += phase_inc; + + in += phase >> 13; + + phase &= 8191; + } + while ( in < in_end ); + + r->phase = phase; + *out_ = out; + + used = in - in_; + + r->write_filled -= used; + } + + return used; +} + +static void lanczos_resampler_fill(lanczos_resampler * r) +{ + while ( r->write_filled > (LANCZOS_WIDTH * 2) && + r->read_filled < lanczos_buffer_size ) + { + int write_pos = ( r->read_pos + r->read_filled ) % lanczos_buffer_size; + int write_size = lanczos_buffer_size - write_pos; + int * out = r->buffer_out + write_pos; + if ( write_size > ( lanczos_buffer_size - r->read_filled ) ) + write_size = lanczos_buffer_size - r->read_filled; + lanczos_resampler_run( r, &out, out + write_size ); + r->read_filled += out - r->buffer_out - write_pos; + } +} + +int lanczos_resampler_get_sample_count(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + if ( r->read_filled < 1 ) + lanczos_resampler_fill( r ); + return r->read_filled; +} + +int lanczos_resampler_get_sample(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + if ( r->read_filled < 1 ) + lanczos_resampler_fill( r ); + if ( r->read_filled < 1 ) + return 0; + return r->buffer_out[ r->read_pos ]; +} + +void lanczos_resampler_remove_sample(void *_r) +{ + lanczos_resampler * r = ( lanczos_resampler * ) _r; + if ( r->read_filled > 0 ) + { + --r->read_filled; + r->read_pos = ( r->read_pos + 1 ) % lanczos_buffer_size; + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/lpc.c b/Frameworks/Dumb/dumb/src/helpers/lpc.c new file mode 100644 index 000000000..6eb53a977 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/lpc.c @@ -0,0 +1,320 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: LPC low level routines + last mod: $Id: lpc.c 16227 2009-07-08 06:58:46Z xiphmont $ + + ********************************************************************/ + +/* Some of these routines (autocorrelator, LPC coefficient estimator) + are derived from code written by Jutta Degener and Carsten Bormann; + thus we include their copyright below. The entirety of this file + is freely redistributable on the condition that both of these + copyright notices are preserved without modification. */ + +/* Preserved Copyright: *********************************************/ + +/* Copyright 1992, 1993, 1994 by Jutta Degener and Carsten Bormann, +Technische Universita"t Berlin + +Any use of this software is permitted provided that this notice is not +removed and that neither the authors nor the Technische Universita"t +Berlin are deemed to have made any representations as to the +suitability of this software for any purpose nor are held responsible +for any defects of this software. THERE IS ABSOLUTELY NO WARRANTY FOR +THIS SOFTWARE. + +As a matter of courtesy, the authors request to be informed about uses +this software has found, about bugs in this software, and about any +improvements that may be of general interest. + +Berlin, 28.11.1994 +Jutta Degener +Carsten Bormann + +*********************************************************************/ + +#include +#include +#include +#include "internal/stack_alloc.h" +#include "internal/lpc.h" + +/* Autocorrelation LPC coeff generation algorithm invented by + N. Levinson in 1947, modified by J. Durbin in 1959. */ + +/* Input : n elements of time doamin data + Output: m lpc coefficients, excitation energy */ + +float vorbis_lpc_from_data(float *data,float *lpci,int n,int m){ + double *aut=alloca(sizeof(*aut)*(m+1)); + double *lpc=alloca(sizeof(*lpc)*(m)); + double error; + double epsilon; + int i,j; + + /* autocorrelation, p+1 lag coefficients */ + j=m+1; + while(j--){ + double d=0; /* double needed for accumulator depth */ + for(i=j;in_samples; n++ ) { + IT_SAMPLE * sample = sigdata->sample + n; + if ( ( sample->flags & ( IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP) ) == IT_SAMPLE_EXISTS ) { + /* If we have enough sample data to train the filter, use the filter to generate the padding */ + if ( sample->length >= lpc_order ) { + lpc_samples = sample->length; + if (lpc_samples > lpc_max) lpc_samples = lpc_max; + offset = sample->length - lpc_samples; + + if ( sample->flags & IT_SAMPLE_STEREO ) + { + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) sample->data; + s16 += offset * 2; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s16[ o * 2 + 0 ]; + lpc_input[ o + lpc_max ] = s16[ o * 2 + 1 ]; + } + } + else + { + s8 = ( signed char * ) sample->data; + s8 += offset * 2; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s8[ o * 2 + 0 ]; + lpc_input[ o + lpc_max ] = s8[ o * 2 + 1 ]; + } + } + + vorbis_lpc_from_data( lpc_input, lpc, lpc_samples, lpc_order ); + vorbis_lpc_from_data( lpc_input + lpc_max, lpc + lpc_order, lpc_samples, lpc_order ); + + vorbis_lpc_predict( lpc, lpc_input + lpc_samples - lpc_order, lpc_order, lpc_output, lpc_extra ); + vorbis_lpc_predict( lpc + lpc_order, lpc_input + lpc_max + lpc_samples - lpc_order, lpc_order, lpc_output + lpc_extra, lpc_extra ); + + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) realloc( sample->data, ( sample->length + lpc_extra ) * 2 * sizeof(short) ); + sample->data = s16; + + s16 += sample->length * 2; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s16[ o * 2 + 0 ] = lpc_output[ o ]; + s16[ o * 2 + 1 ] = lpc_output[ o + lpc_extra ]; + } + } + else + { + s8 = ( signed char * ) realloc( sample->data, ( sample->length + lpc_extra ) * 2 ); + sample->data = s8; + + s8 += sample->length * 2; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s8[ o * 2 + 0 ] = lpc_output[ o ]; + s8[ o * 2 + 1 ] = lpc_output[ o + lpc_extra ]; + } + } + } + else + { + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) sample->data; + s16 += offset; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s16[ o ]; + } + } + else + { + s8 = ( signed char * ) sample->data; + s8 += offset; + for ( o = 0; o < lpc_samples; o++ ) + { + lpc_input[ o ] = s8[ o ]; + } + } + + vorbis_lpc_from_data( lpc_input, lpc, lpc_samples, lpc_order ); + + vorbis_lpc_predict( lpc, lpc_input + lpc_samples - lpc_order, lpc_order, lpc_output, lpc_extra ); + + if ( sample->flags & IT_SAMPLE_16BIT ) + { + s16 = ( signed short * ) realloc( sample->data, ( sample->length + lpc_extra ) * sizeof(short) ); + sample->data = s16; + + s16 += sample->length; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s16[ o ] = lpc_output[ o ]; + } + } + else + { + s8 = ( signed char * ) realloc( sample->data, sample->length + lpc_extra ); + sample->data = s8; + + s8 += sample->length; + sample->length += lpc_extra; + + for ( o = 0; o < lpc_extra; o++ ) + { + s8[ o ] = lpc_output[ o ]; + } + } + } + } + else + /* Otherwise, pad with silence. */ + { + offset = sample->length; + lpc_samples = lpc_extra; + + sample->length += lpc_samples; + + n = 1; + if ( sample->flags & IT_SAMPLE_STEREO ) n *= 2; + if ( sample->flags & IT_SAMPLE_16BIT ) n *= 2; + + offset *= n; + lpc_samples *= n; + + sample->data = realloc( sample->data, offset + lpc_samples ); + memset( (char*)sample->data + offset, 0, lpc_samples ); + } + } + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/memfile.c b/Frameworks/Dumb/dumb/src/helpers/memfile.c index b65ab5f78..623c4d6d0 100644 --- a/Frameworks/Dumb/dumb/src/helpers/memfile.c +++ b/Frameworks/Dumb/dumb/src/helpers/memfile.c @@ -1,96 +1,117 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * memfile.c - Module for reading data from / / \ \ - * memory using a DUMBFILE. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include - -#include "dumb.h" - - - -typedef struct MEMFILE MEMFILE; - -struct MEMFILE -{ - const char *ptr; - long left; -}; - - - -static int dumb_memfile_skip(void *f, long n) -{ - MEMFILE *m = f; - if (n > m->left) return -1; - m->ptr += n; - m->left -= n; - return 0; -} - - - -static int dumb_memfile_getc(void *f) -{ - MEMFILE *m = f; - if (m->left <= 0) return -1; - m->left--; - return *(const unsigned char *)m->ptr++; -} - - - -static long dumb_memfile_getnc(char *ptr, long n, void *f) -{ - MEMFILE *m = f; - if (n > m->left) n = m->left; - memcpy(ptr, m->ptr, n); - m->ptr += n; - m->left -= n; - return n; -} - - - -static void dumb_memfile_close(void *f) -{ - free(f); -} - - - -static DUMBFILE_SYSTEM memfile_dfs = { - NULL, - &dumb_memfile_skip, - &dumb_memfile_getc, - &dumb_memfile_getnc, - &dumb_memfile_close -}; - - - -DUMBFILE *dumbfile_open_memory(const char *data, long size) -{ - MEMFILE *m = malloc(sizeof(*m)); - if (!m) return NULL; - - m->ptr = data; - m->left = size; - - return dumbfile_open_ex(m, &memfile_dfs); -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * memfile.c - Module for reading data from / / \ \ + * memory using a DUMBFILE. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" + + + +typedef struct MEMFILE MEMFILE; + +struct MEMFILE +{ + const char *ptr, *ptr_begin; + long left, size; +}; + + + +static int dumb_memfile_skip(void *f, long n) +{ + MEMFILE *m = f; + if (n > m->left) return -1; + m->ptr += n; + m->left -= n; + return 0; +} + + + +static int dumb_memfile_getc(void *f) +{ + MEMFILE *m = f; + if (m->left <= 0) return -1; + m->left--; + return *(const unsigned char *)m->ptr++; +} + + + +static long dumb_memfile_getnc(char *ptr, long n, void *f) +{ + MEMFILE *m = f; + if (n > m->left) n = m->left; + memcpy(ptr, m->ptr, n); + m->ptr += n; + m->left -= n; + return n; +} + + + +static void dumb_memfile_close(void *f) +{ + free(f); +} + + +static int dumb_memfile_seek(void *f, long n) +{ + MEMFILE *m = f; + + m->ptr = m->ptr_begin + n; + m->left = m->size - n; + + return 0; +} + + +static long dumb_memfile_get_size(void *f) +{ + MEMFILE *m = f; + return m->size; +} + + +static const DUMBFILE_SYSTEM memfile_dfs = { + NULL, + &dumb_memfile_skip, + &dumb_memfile_getc, + &dumb_memfile_getnc, + &dumb_memfile_close, + &dumb_memfile_seek, + &dumb_memfile_get_size +}; + + + +DUMBFILE *dumbfile_open_memory(const char *data, long size) +{ + MEMFILE *m = malloc(sizeof(*m)); + if (!m) return NULL; + + m->ptr_begin = data; + m->ptr = data; + m->left = size; + m->size = size; + + return dumbfile_open_ex(m, &memfile_dfs); +} diff --git a/Frameworks/Dumb/dumb/src/helpers/resamp2.inc b/Frameworks/Dumb/dumb/src/helpers/resamp2.inc index 2619bd713..b343d23b2 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resamp2.inc +++ b/Frameworks/Dumb/dumb/src/helpers/resamp2.inc @@ -1,134 +1,179 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * resamp2.inc - Resampling helper template. / / \ \ - * | < / \_ - * By Bob and entheh. | \/ /\ / - * \_ / > / - * In order to find a good trade-off between | \ / / - * speed and accuracy in this code, some tests | ' / - * were carried out regarding the behaviour of \__/ - * long long ints with gcc. The following code - * was tested: - * - * int a, b, c; - * c = ((long long)a * b) >> 16; - * - * DJGPP GCC Version 3.0.3 generated the following assembly language code for - * the multiplication and scaling, leaving the 32-bit result in EAX. - * - * movl -8(%ebp), %eax ; read one int into EAX - * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX - * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX - * - * Note that a 32*32->64 multiplication is performed, allowing for high - * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), - * so it is a minor concern when four multiplications are being performed - * (the cubic resampler). On the Pentium MMX and earlier, it takes four or - * more cycles, so this method is unsuitable for use in the low-quality - * resamplers. - * - * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, - * defined in dumb.h. We may investigate later what code MSVC generates, but - * if it seems too slow then we suggest you use a good compiler. - * - * FIXME: these comments are somewhat out of date now. - */ - - - -#define SUFFIX3 _1 - -/* For convenience, returns nonzero on stop. */ -static int process_pickup(DUMB_RESAMPLER *resampler) -{ - if (resampler->overshot < 0) { - resampler->overshot = 0; - dumb_resample(resampler, NULL, 2, MONO_DEST_VOLUME_ZEROS, 1.0f); /* Doesn't matter which SUFFIX3. */ - COPYSRC(resampler->X, 0, resampler->X, 1); - } - - for (;;) { - SRCTYPE *src = resampler->src; - - if (resampler->dir < 0) { - if (resampler->overshot >= 3 && resampler->pos+3 >= resampler->start) COPYSRC(resampler->X, 0, src, resampler->pos+3); - if (resampler->overshot >= 2 && resampler->pos+2 >= resampler->start) COPYSRC(resampler->X, 1, src, resampler->pos+2); - if (resampler->overshot >= 1 && resampler->pos+1 >= resampler->start) COPYSRC(resampler->X, 2, src, resampler->pos+1); - resampler->overshot = resampler->start - resampler->pos - 1; - } else { - if (resampler->overshot >= 3 && resampler->pos-3 < resampler->end) COPYSRC(resampler->X, 0, src, resampler->pos-3); - if (resampler->overshot >= 2 && resampler->pos-2 < resampler->end) COPYSRC(resampler->X, 1, src, resampler->pos-2); - if (resampler->overshot >= 1 && resampler->pos-1 < resampler->end) COPYSRC(resampler->X, 2, src, resampler->pos-1); - resampler->overshot = resampler->pos - resampler->end; - } - - if (resampler->overshot < 0) { - resampler->overshot = 0; - return 0; - } - - if (!resampler->pickup) { - resampler->dir = 0; - return 1; - } - (*resampler->pickup)(resampler, resampler->pickup_data); - if (resampler->dir == 0) return 1; - ASSERT(resampler->dir == -1 || resampler->dir == 1); - } -} - - - -/* Create mono destination resampler. */ -/* SUFFIX3 was set above. */ -#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS -#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES -#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES -#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO -#define MIX_ALIAS(op, offset) MONO_DEST_MIX_ALIAS(op, offset) -#define MIX_LINEAR(op, o0, o1) MONO_DEST_MIX_LINEAR(op, o0, o1) -#define MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) -#define MIX_ZEROS(op) *dst++ op 0 -#include "resamp3.inc" - -/* Create stereo destination resampler. */ -#define SUFFIX3 _2 -#define VOLUME_PARAMETERS float volume_left, float volume_right -#define VOLUME_VARIABLES lvol, rvol -#define SET_VOLUME_VARIABLES { \ - lvol = (int)floor(volume_left * 65536.0 + 0.5); \ - rvol = (int)floor(volume_right * 65536.0 + 0.5); \ -} -#define VOLUMES_ARE_ZERO (lvol == 0 && rvol == 0) -#define MIX_ALIAS(op, offset) STEREO_DEST_MIX_ALIAS(op, offset) -#define MIX_LINEAR(op, o0, o1) STEREO_DEST_MIX_LINEAR(op, o0, o1) -#define MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) -#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; } -#include "resamp3.inc" - - - -#undef STEREO_DEST_MIX_CUBIC -#undef MONO_DEST_MIX_CUBIC -#undef STEREO_DEST_MIX_LINEAR -#undef MONO_DEST_MIX_LINEAR -#undef STEREO_DEST_MIX_ALIAS -#undef MONO_DEST_MIX_ALIAS -#undef MONO_DEST_VOLUMES_ARE_ZERO -#undef SET_MONO_DEST_VOLUME_VARIABLES -#undef MONO_DEST_VOLUME_ZEROS -#undef MONO_DEST_VOLUME_VARIABLES -#undef MONO_DEST_VOLUME_PARAMETERS -#undef COPYSRC2 -#undef COPYSRC -#undef DIVIDE_BY_SRC_CHANNELS -#undef SRC_CHANNELS -#undef SUFFIX2 +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resamp2.inc - Resampling helper template. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + + + +#define SUFFIX3 _1 + +/* For convenience, returns nonzero on stop. */ +static int process_pickup(DUMB_RESAMPLER *resampler) +{ + if (resampler->overshot < 0) { + resampler->overshot = 0; + dumb_resample(resampler, NULL, 2, MONO_DEST_VOLUME_ZEROS, 1.0f); /* Doesn't matter which SUFFIX3. */ + COPYSRC(resampler->X, 0, resampler->X, 1); + } + + for (;;) { + SRCTYPE *src = resampler->src; + + if (resampler->dir < 0) { + if (resampler->overshot >= 3 && resampler->pos+3 >= resampler->start) COPYSRC(resampler->X, 0, src, resampler->pos+3); + if (resampler->overshot >= 2 && resampler->pos+2 >= resampler->start) COPYSRC(resampler->X, 1, src, resampler->pos+2); + if (resampler->overshot >= 1 && resampler->pos+1 >= resampler->start) COPYSRC(resampler->X, 2, src, resampler->pos+1); + resampler->overshot = resampler->start - resampler->pos - 1; + } else { + if (resampler->overshot >= 3 && resampler->pos-3 < resampler->end) COPYSRC(resampler->X, 0, src, resampler->pos-3); + if (resampler->overshot >= 2 && resampler->pos-2 < resampler->end) COPYSRC(resampler->X, 1, src, resampler->pos-2); + if (resampler->overshot >= 1 && resampler->pos-1 < resampler->end) COPYSRC(resampler->X, 2, src, resampler->pos-1); + resampler->overshot = resampler->pos - resampler->end; + } + + if (resampler->overshot < 0) { + resampler->overshot = 0; + return 0; + } + + if (!resampler->pickup) { + resampler->dir = 0; + return 1; + } + (*resampler->pickup)(resampler, resampler->pickup_data); + if (resampler->dir == 0) return 1; + ASSERT(resampler->dir == -1 || resampler->dir == 1); + } +} + + + +/* Create mono destination resampler. */ +/* SUFFIX3 was set above. */ +#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS +#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES +#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES +#define RETURN_VOLUME_VARIABLES RETURN_MONO_DEST_VOLUME_VARIABLES +#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO +#define MIX_ALIAS(count) MONO_DEST_MIX_ALIAS(count) +#define PEEK_ALIAS MONO_DEST_PEEK_ALIAS +#define PEEK_FIR MONO_DEST_PEEK_FIR +#define MIX_FIR MONO_DEST_MIX_FIR +#define MIX_LINEAR(op, upd, o0, o1) MONO_DEST_MIX_LINEAR(op, upd, o0, o1) +#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) +#define MIX_ZEROS(op) *dst++ op 0 +#include "resamp3.inc" + +/* Create stereo destination resampler. */ +#define SUFFIX3 _2 +#define VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right +#define VOLUME_VARIABLES lvol, lvolr, lvold, lvolt, lvolm, rvol, rvolr, rvold, rvolt, rvolm +#define SET_VOLUME_VARIABLES { \ + if ( volume_left ) { \ + lvolr = (int)(volume_left->volume * 16777216.0); \ + lvold = (int)(volume_left->delta * 16777216.0); \ + lvolt = (int)(volume_left->target * 16777216.0); \ + lvolm = (int)(volume_left->mix * 16777216.0); \ + lvol = MULSCV( lvolr, lvolm ); \ + if ( lvolr == lvolt ) volume_left = NULL; \ + } else { \ + lvol = 0; \ + lvold = 0; \ + lvolt = 0; \ + lvolm = 0; \ + } \ + if ( volume_right ) { \ + rvolr = (int)(volume_right->volume * 16777216.0); \ + rvold = (int)(volume_right->delta * 16777216.0); \ + rvolt = (int)(volume_right->target * 16777216.0); \ + rvolm = (int)(volume_right->mix * 16777216.0); \ + rvol = MULSCV( rvolr, rvolm ); \ + if ( rvolr == rvolt ) volume_right = NULL; \ + } else { \ + rvol = 0; \ + rvold = 0; \ + rvolt = 0; \ + rvolm = 0; \ + } \ +} +#define RETURN_VOLUME_VARIABLES { \ + if ( volume_left ) volume_left->volume = (float)lvolr / 16777216.0f; \ + if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \ +} +#define VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0) +#define MIX_ALIAS(count) STEREO_DEST_MIX_ALIAS(count) +#define PEEK_ALIAS STEREO_DEST_PEEK_ALIAS +#define PEEK_FIR STEREO_DEST_PEEK_FIR +#define MIX_FIR STEREO_DEST_MIX_FIR +#define MIX_LINEAR(op, upd, o0, o1) STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) +#define MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) +#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; } +#include "resamp3.inc" + + + +#undef STEREO_DEST_MIX_CUBIC +#undef MONO_DEST_MIX_CUBIC +#undef STEREO_DEST_MIX_LINEAR +#undef MONO_DEST_MIX_LINEAR +#undef STEREO_DEST_MIX_ALIAS +#undef MONO_DEST_MIX_ALIAS +#undef MONO_DEST_VOLUMES_ARE_ZERO +#undef SET_MONO_DEST_VOLUME_VARIABLES +#undef RETURN_MONO_DEST_VOLUME_VARIABLES +#undef MONO_DEST_VOLUME_ZEROS +#undef MONO_DEST_VOLUME_VARIABLES +#undef MONO_DEST_VOLUME_PARAMETERS +#undef STEREO_DEST_PEEK_ALIAS +#undef MONO_DEST_PEEK_ALIAS +#undef POKE_ALIAS +#undef MONO_DEST_PEEK_FIR +#undef STEREO_DEST_PEEK_FIR +#undef MONO_DEST_MIX_FIR +#undef STEREO_DEST_MIX_FIR +#undef ADVANCE_FIR +#undef POKE_FIR +#undef COPYSRC2 +#undef COPYSRC +#undef DIVIDE_BY_SRC_CHANNELS +#undef SRC_CHANNELS +#undef SUFFIX2 diff --git a/Frameworks/Dumb/dumb/src/helpers/resamp3.inc b/Frameworks/Dumb/dumb/src/helpers/resamp3.inc index 4bdada3dd..e0bc8ee78 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resamp3.inc +++ b/Frameworks/Dumb/dumb/src/helpers/resamp3.inc @@ -1,371 +1,441 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * resamp3.inc - Resampling helper template. / / \ \ - * | < / \_ - * By Bob and entheh. | \/ /\ / - * \_ / > / - * In order to find a good trade-off between | \ / / - * speed and accuracy in this code, some tests | ' / - * were carried out regarding the behaviour of \__/ - * long long ints with gcc. The following code - * was tested: - * - * int a, b, c; - * c = ((long long)a * b) >> 16; - * - * DJGPP GCC Version 3.0.3 generated the following assembly language code for - * the multiplication and scaling, leaving the 32-bit result in EAX. - * - * movl -8(%ebp), %eax ; read one int into EAX - * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX - * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX - * - * Note that a 32*32->64 multiplication is performed, allowing for high - * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), - * so it is a minor concern when four multiplications are being performed - * (the cubic resampler). On the Pentium MMX and earlier, it takes four or - * more cycles, so this method is unsuitable for use in the low-quality - * resamplers. - * - * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, - * defined in dumb.h. We may investigate later what code MSVC generates, but - * if it seems too slow then we suggest you use a good compiler. - * - * FIXME: these comments are somewhat out of date now. - */ - - - -long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta) -{ - int dt; - int VOLUME_VARIABLES; - long done; - long todo; - int quality; - - if (!resampler || resampler->dir == 0) return 0; - ASSERT(resampler->dir == -1 || resampler->dir == 1); - - done = 0; - dt = (int)(delta * 65536.0 + 0.5); - SET_VOLUME_VARIABLES; - - if (VOLUMES_ARE_ZERO) dst = NULL; - - init_cubic(); - - quality = dumb_resampling_quality; - if (quality > resampler->max_quality) quality = resampler->max_quality; - else if (quality < resampler->min_quality) quality = resampler->min_quality; - - while (done < dst_size) { - if (process_pickup(resampler)) return done; - - if ((resampler->dir ^ dt) < 0) - dt = -dt; - - if (resampler->dir < 0) - todo = (long)((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt); - else - todo = (long)((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt); - - if (todo < 0) - todo = 0; - else if (todo > dst_size - done) - todo = dst_size - done; - - done += todo; - - { - SRCTYPE *src = resampler->src; - long pos = resampler->pos; - int subpos = resampler->subpos; - long diff = pos; - long overshot; - if (resampler->dir < 0) { - if (!dst) { - /* Silence or simulation */ - LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo; - pos += (long)(new_subpos >> 16); - subpos = (long)new_subpos & 65535; - } else if (quality <= DUMB_RQ_ALIASING) { - /* Aliasing, backwards */ - SRCTYPE xbuf[2*SRC_CHANNELS]; - SRCTYPE *x = &xbuf[0]; - SRCTYPE *xstart; - COPYSRC(xbuf, 0, resampler->X, 1); - COPYSRC(xbuf, 1, resampler->X, 2); - while (todo && x < &xbuf[2*SRC_CHANNELS]) { - // TODO: check what happens when multiple tempo slides occur per row - HEAVYASSERT(pos >= resampler->start); - MIX_ALIAS(+=, 0); - subpos += dt; - pos += subpos >> 16; - x -= (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = xstart = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - MIX_ALIAS(+=, 2); - subpos += dt; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - pos += DIVIDE_BY_SRC_CHANNELS(x - xstart); - } else if (quality <= DUMB_RQ_LINEAR) { - /* Linear interpolation, backwards */ - SRCTYPE xbuf[3*SRC_CHANNELS]; - SRCTYPE *x = &xbuf[1*SRC_CHANNELS]; - COPYSRC(xbuf, 0, resampler->X, 1); - COPYSRC(xbuf, 1, resampler->X, 2); - COPYSRC(xbuf, 2, src, pos); - while (todo && x < &xbuf[3*SRC_CHANNELS]) { - HEAVYASSERT(pos >= resampler->start); - MIX_LINEAR(+=, 0, -1); - subpos += dt; - pos += subpos >> 16; - x -= (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - // TODO: use xstart for others too - x = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - HEAVYASSERT(pos >= resampler->start); - MIX_LINEAR(+=, 1, 2); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } else { - /* Cubic interpolation, backwards */ - SRCTYPE xbuf[6*SRC_CHANNELS]; - SRCTYPE *x = &xbuf[3*SRC_CHANNELS]; - COPYSRC(xbuf, 0, resampler->X, 0); - COPYSRC(xbuf, 1, resampler->X, 1); - COPYSRC(xbuf, 2, resampler->X, 2); - COPYSRC(xbuf, 3, src, pos); - if (pos-1 >= resampler->start) COPYSRC(xbuf, 4, src, pos-1); - if (pos-2 >= resampler->start) COPYSRC(xbuf, 5, src, pos-2); - while (todo && x < &xbuf[6*SRC_CHANNELS]) { - HEAVYASSERT(pos >= resampler->start); - MIX_CUBIC(+=, x, x, 0, -1, -2, -3); - subpos += dt; - pos += subpos >> 16; - x -= (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - HEAVYASSERT(pos >= resampler->start); - MIX_CUBIC(+=, x, x, 0, 1, 2, 3); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } - diff = diff - pos; - overshot = resampler->start - pos - 1; - if (diff >= 3) { - COPYSRC2(resampler->X, 0, overshot < 3, src, pos+3); - COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2); - COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); - } else if (diff >= 2) { - COPYSRC(resampler->X, 0, resampler->X, 2); - COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2); - COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); - } else if (diff >= 1) { - COPYSRC(resampler->X, 0, resampler->X, 1); - COPYSRC(resampler->X, 1, resampler->X, 2); - COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); - } - } else { - if (!dst) { - /* Silence or simulation */ - LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo; - pos += (long)(new_subpos >> 16); - subpos = (long)new_subpos & 65535; - } else if (dumb_resampling_quality <= DUMB_RQ_ALIASING) { - /* Aliasing, forwards */ - SRCTYPE xbuf[2*SRC_CHANNELS]; - SRCTYPE *x = &xbuf[0]; - SRCTYPE *xstart; - COPYSRC(xbuf, 0, resampler->X, 1); - COPYSRC(xbuf, 1, resampler->X, 2); - while (todo && x < &xbuf[2*SRC_CHANNELS]) { - HEAVYASSERT(pos < resampler->end); - MIX_ALIAS(+=, 0); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = xstart = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - MIX_ALIAS(+=, -2); - subpos += dt; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - pos += DIVIDE_BY_SRC_CHANNELS(x - xstart); - } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) { - /* Linear interpolation, forwards */ - SRCTYPE xbuf[3*SRC_CHANNELS]; - SRCTYPE *x = &xbuf[1*SRC_CHANNELS]; - COPYSRC(xbuf, 0, resampler->X, 1); - COPYSRC(xbuf, 1, resampler->X, 2); - COPYSRC(xbuf, 2, src, pos); - while (todo && x < &xbuf[3*SRC_CHANNELS]) { - HEAVYASSERT(pos < resampler->end); - MIX_LINEAR(+=, -1, 0); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - HEAVYASSERT(pos < resampler->end); - MIX_LINEAR(+=, -2, -1); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } else { - /* Cubic interpolation, forwards */ - SRCTYPE xbuf[6*SRC_CHANNELS]; - SRCTYPE *x = &xbuf[3*SRC_CHANNELS]; - COPYSRC(xbuf, 0, resampler->X, 0); - COPYSRC(xbuf, 1, resampler->X, 1); - COPYSRC(xbuf, 2, resampler->X, 2); - COPYSRC(xbuf, 3, src, pos); - if (pos+1 < resampler->end) COPYSRC(xbuf, 4, src, pos+1); - if (pos+2 < resampler->end) COPYSRC(xbuf, 5, src, pos+2); - while (todo && x < &xbuf[6*SRC_CHANNELS]) { - HEAVYASSERT(pos < resampler->end); - MIX_CUBIC(+=, x, x, -3, -2, -1, 0); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - todo--; - } - x = &src[pos*SRC_CHANNELS]; - LOOP4(todo, - HEAVYASSERT(pos < resampler->end); - MIX_CUBIC(+=, x, x, -3, -2, -1, 0); - subpos += dt; - pos += subpos >> 16; - x += (subpos >> 16) * SRC_CHANNELS; - subpos &= 65535; - ); - } - diff = pos - diff; - overshot = pos - resampler->end; - if (diff >= 3) { - COPYSRC2(resampler->X, 0, overshot < 3, src, pos-3); - COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2); - COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); - } else if (diff >= 2) { - COPYSRC(resampler->X, 0, resampler->X, 2); - COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2); - COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); - } else if (diff >= 1) { - COPYSRC(resampler->X, 0, resampler->X, 1); - COPYSRC(resampler->X, 1, resampler->X, 2); - COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); - } - } - resampler->pos = pos; - resampler->subpos = subpos; - } - } - - return done; -} - - - -void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETERS, sample_t *dst) -{ - int VOLUME_VARIABLES; - SRCTYPE *src; - long pos; - int subpos; - int quality; - SRCTYPE *x; - - if (!resampler || resampler->dir == 0) { MIX_ZEROS(=); return; } - ASSERT(resampler->dir == -1 || resampler->dir == 1); - - if (process_pickup(resampler)) { MIX_ZEROS(=); return; } - - SET_VOLUME_VARIABLES; - - if (VOLUMES_ARE_ZERO) { MIX_ZEROS(=); return; } - - init_cubic(); - - quality = dumb_resampling_quality; - if (quality > resampler->max_quality) quality = resampler->max_quality; - else if (quality < resampler->min_quality) quality = resampler->min_quality; - - src = resampler->src; - pos = resampler->pos; - subpos = resampler->subpos; - x = resampler->X; - - if (resampler->dir < 0) { - HEAVYASSERT(pos >= resampler->start); - if (dumb_resampling_quality <= 0) { - /* Aliasing, backwards */ - MIX_ALIAS(=, 1); - } else if (quality <= DUMB_RQ_LINEAR) { - /* Linear interpolation, backwards */ - MIX_LINEAR(=, 2, 1); - } else { - /* Cubic interpolation, backwards */ - MIX_CUBIC(=, src, x, pos, 2, 1, 0); - } - } else { - HEAVYASSERT(pos < resampler->end); - if (dumb_resampling_quality <= 0) { - /* Aliasing */ - MIX_ALIAS(=, 1); - } else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) { - /* Linear interpolation, forwards */ - MIX_LINEAR(=, 1, 2); - } else { - /* Cubic interpolation, forwards */ - MIX_CUBIC(=, x, src, 0, 1, 2, pos); - } - } -} - - - -#undef MIX_ZEROS -#undef MIX_CUBIC -#undef MIX_LINEAR -#undef MIX_ALIAS -#undef VOLUMES_ARE_ZERO -#undef SET_VOLUME_VARIABLES -#undef VOLUME_VARIABLES -#undef VOLUME_PARAMETERS -#undef SUFFIX3 +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resamp3.inc - Resampling helper template. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + + + +long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta) +{ + int dt, inv_dt; + int VOLUME_VARIABLES; + long done; + long todo; + LONG_LONG todo64; + int quality; + int blip_samples[256*SRC_CHANNELS]; + + if (!resampler || resampler->dir == 0) return 0; + ASSERT(resampler->dir == -1 || resampler->dir == 1); + + done = 0; + dt = (int)(delta * 65536.0 + 0.5); + if (dt == 0 || dt == (int)-0x80000000) return 0; + inv_dt = (int)(1.0 / delta * 65536.0 + 0.5); + SET_VOLUME_VARIABLES; + + if (VOLUMES_ARE_ZERO) dst = NULL; + + init_cubic(); + + quality = resampler->quality; + + while (done < dst_size) { + if (process_pickup(resampler)) { + RETURN_VOLUME_VARIABLES; + return done; + } + + if ((resampler->dir ^ dt) < 0) + dt = -dt; + + if (resampler->dir < 0) + todo64 = ((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt); + else + todo64 = ((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt); + + if (todo64 < 0) + todo = 0; + else if (todo64 > dst_size - done) + todo = dst_size - done; + else + todo = (long) todo64; + + done += todo; + + { + SRCTYPE *src = resampler->src; + long pos = resampler->pos; + int subpos = resampler->subpos; + long diff = pos; + long overshot; + if (resampler->dir < 0) { + if (!dst) { + /* Silence or simulation */ + LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo; + pos += (long)(new_subpos >> 16); + subpos = (long)new_subpos & 65535; + } else if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing, backwards */ + int todo_clocks = todo << 16, todo_clocks_set = todo_clocks; + SRCTYPE xbuf[2*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[0]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) { + // TODO: check what happens when multiple tempo slides occur per row + HEAVYASSERT(pos >= resampler->start); + POKE_ALIAS(0); + pos--; + x += SRC_CHANNELS; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo_clocks ) { + todo_clocks_set = todo_clocks; + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + todo_clocks -= todo_clocks_set; + while ( resampler->last_clock < todo_clocks_set ) + { + POKE_ALIAS(2); + pos--; + x -= SRC_CHANNELS; + } + todo = todo_clocks_set >> 16; + MIX_ALIAS( todo ); + } + } else if (quality <= DUMB_RQ_LINEAR) { + /* Linear interpolation, backwards */ + SRCTYPE xbuf[3*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[1*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + COPYSRC(xbuf, 2, src, pos); + while (todo && x < &xbuf[3*SRC_CHANNELS]) { + HEAVYASSERT(pos >= resampler->start); + MIX_LINEAR(+=, 1, 0, -1); + subpos += dt; + pos += subpos >> 16; + x -= (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + // TODO: use xstart for others too + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos >= resampler->start); + MIX_LINEAR(+=, 1, 1, 2); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else if (quality <= DUMB_RQ_CUBIC) { + /* Cubic interpolation, backwards */ + SRCTYPE xbuf[6*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[3*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 0); + COPYSRC(xbuf, 1, resampler->X, 1); + COPYSRC(xbuf, 2, resampler->X, 2); + COPYSRC(xbuf, 3, src, pos); + if (pos-1 >= resampler->start) COPYSRC(xbuf, 4, src, pos-1); + if (pos-2 >= resampler->start) COPYSRC(xbuf, 5, src, pos-2); + while (todo && x < &xbuf[6*SRC_CHANNELS]) { + HEAVYASSERT(pos >= resampler->start); + MIX_CUBIC(+=, 1, x, x, 0, -1, -2, -3); + subpos += dt; + pos += subpos >> 16; + x -= (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos >= resampler->start); + MIX_CUBIC(+=, 1, x, x, 0, 1, 2, 3); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else { + /* FIR resampling, backwards */ + SRCTYPE *x; + if ( resampler->fir_resampler_ratio != delta ) { + lanczos_resampler_set_rate( resampler->fir_resampler[0], delta ); + lanczos_resampler_set_rate( resampler->fir_resampler[1], delta ); + resampler->fir_resampler_ratio = delta; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo ) { + while ( lanczos_resampler_get_free_count( resampler->fir_resampler[0] ) && + pos >= resampler->start ) + { + POKE_FIR(0); + pos--; + x -= SRC_CHANNELS; + } + if ( !lanczos_resampler_get_sample_count( resampler->fir_resampler[0] ) ) break; + MIX_FIR; + ADVANCE_FIR; + --todo; + } + done -= todo; + } + diff = diff - pos; + overshot = resampler->start - pos - 1; + if (diff >= 3) { + COPYSRC2(resampler->X, 0, overshot < 3, src, pos+3); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); + } else if (diff >= 2) { + COPYSRC(resampler->X, 0, resampler->X, 2); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); + } else if (diff >= 1) { + COPYSRC(resampler->X, 0, resampler->X, 1); + COPYSRC(resampler->X, 1, resampler->X, 2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1); + } + } else { + if (!dst) { + /* Silence or simulation */ + LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo; + pos += (long)(new_subpos >> 16); + subpos = (long)new_subpos & 65535; + } else if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing, forwards */ + int todo_clocks = todo << 16, todo_clocks_set = todo_clocks; + SRCTYPE xbuf[2*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[0]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + while (resampler->last_clock < todo_clocks_set && x < &xbuf[2*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + POKE_ALIAS(0); + pos++; + x += SRC_CHANNELS; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo_clocks ) { + todo_clocks_set = todo_clocks; + if ( todo_clocks_set > 256 * 65536 ) todo_clocks_set = 256 * 65536; + todo_clocks -= todo_clocks_set; + while ( resampler->last_clock < todo_clocks_set ) + { + POKE_ALIAS(-2); + pos++; + x += SRC_CHANNELS; + } + todo = todo_clocks_set >> 16; + MIX_ALIAS( todo ); + } + } else if (quality <= DUMB_RQ_LINEAR) { + /* Linear interpolation, forwards */ + SRCTYPE xbuf[3*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[1*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 1); + COPYSRC(xbuf, 1, resampler->X, 2); + COPYSRC(xbuf, 2, src, pos); + while (todo && x < &xbuf[3*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + MIX_LINEAR(+=, 1, -1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos < resampler->end); + MIX_LINEAR(+=, 1, -2, -1); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else if (quality <= DUMB_RQ_CUBIC) { + /* Cubic interpolation, forwards */ + SRCTYPE xbuf[6*SRC_CHANNELS]; + SRCTYPE *x = &xbuf[3*SRC_CHANNELS]; + COPYSRC(xbuf, 0, resampler->X, 0); + COPYSRC(xbuf, 1, resampler->X, 1); + COPYSRC(xbuf, 2, resampler->X, 2); + COPYSRC(xbuf, 3, src, pos); + if (pos+1 < resampler->end) COPYSRC(xbuf, 4, src, pos+1); + if (pos+2 < resampler->end) COPYSRC(xbuf, 5, src, pos+2); + while (todo && x < &xbuf[6*SRC_CHANNELS]) { + HEAVYASSERT(pos < resampler->end); + MIX_CUBIC(+=, 1, x, x, -3, -2, -1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + todo--; + } + x = &src[pos*SRC_CHANNELS]; + LOOP4(todo, + HEAVYASSERT(pos < resampler->end); + MIX_CUBIC(+=, 1, x, x, -3, -2, -1, 0); + subpos += dt; + pos += subpos >> 16; + x += (subpos >> 16) * SRC_CHANNELS; + subpos &= 65535; + ); + } else { + /* FIR resampling, forwards */ + SRCTYPE *x; + if ( resampler->fir_resampler_ratio != delta ) { + lanczos_resampler_set_rate( resampler->fir_resampler[0], delta ); + lanczos_resampler_set_rate( resampler->fir_resampler[1], delta ); + resampler->fir_resampler_ratio = delta; + } + x = &src[pos*SRC_CHANNELS]; + while ( todo ) { + while ( lanczos_resampler_get_free_count( resampler->fir_resampler[0] ) && + pos < resampler->end ) + { + POKE_FIR(0); + pos++; + x += SRC_CHANNELS; + } + if ( !lanczos_resampler_get_sample_count( resampler->fir_resampler[0] ) ) break; + MIX_FIR; + ADVANCE_FIR; + --todo; + } + done -= todo; + } + diff = pos - diff; + overshot = pos - resampler->end; + if (diff >= 3) { + COPYSRC2(resampler->X, 0, overshot < 3, src, pos-3); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); + } else if (diff >= 2) { + COPYSRC(resampler->X, 0, resampler->X, 2); + COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); + } else if (diff >= 1) { + COPYSRC(resampler->X, 0, resampler->X, 1); + COPYSRC(resampler->X, 1, resampler->X, 2); + COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1); + } + } + resampler->pos = pos; + resampler->subpos = subpos; + } + } + + RETURN_VOLUME_VARIABLES; + return done; +} + + + +void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETERS, sample_t *dst) +{ + int VOLUME_VARIABLES; + SRCTYPE *src; + long pos; + int subpos; + int quality; + SRCTYPE *x; + + if (!resampler || resampler->dir == 0) { MIX_ZEROS(=); return; } + ASSERT(resampler->dir == -1 || resampler->dir == 1); + + if (process_pickup(resampler)) { MIX_ZEROS(=); return; } + + SET_VOLUME_VARIABLES; + + if (VOLUMES_ARE_ZERO) { MIX_ZEROS(=); return; } + + init_cubic(); + + quality = resampler->quality; + + src = resampler->src; + pos = resampler->pos; + subpos = resampler->subpos; + x = resampler->X; + + if (resampler->dir < 0) { + HEAVYASSERT(pos >= resampler->start); + if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing, backwards */ + PEEK_ALIAS; + } else if (quality <= DUMB_RQ_LINEAR) { + /* Linear interpolation, backwards */ + MIX_LINEAR(=, 0, 2, 1); + } else if (quality <= DUMB_RQ_CUBIC) { + /* Cubic interpolation, backwards */ + MIX_CUBIC(=, 0, src, x, pos, 2, 1, 0); + } else { + /* FIR resampling, backwards */ + PEEK_FIR; + } + } else { + HEAVYASSERT(pos < resampler->end); + if (quality <= DUMB_RQ_ALIASING) { + /* Aliasing */ + PEEK_ALIAS; + } else if (quality <= DUMB_RQ_LINEAR) { + /* Linear interpolation, forwards */ + MIX_LINEAR(=, 0, 1, 2); + } else if (quality <= DUMB_RQ_CUBIC) { + /* Cubic interpolation, forwards */ + MIX_CUBIC(=, 0, x, src, 0, 1, 2, pos); + } else { + /* FIR resampling, forwards */ + PEEK_FIR; + } + } +} + + + +#undef MIX_ZEROS +#undef MIX_CUBIC +#undef MIX_LINEAR +#undef MIX_ALIAS +#undef MIX_FIR +#undef PEEK_ALIAS +#undef PEEK_FIR +#undef VOLUMES_ARE_ZERO +#undef SET_VOLUME_VARIABLES +#undef RETURN_VOLUME_VARIABLES +#undef VOLUME_VARIABLES +#undef VOLUME_PARAMETERS +#undef SUFFIX3 diff --git a/Frameworks/Dumb/dumb/src/helpers/resample.c b/Frameworks/Dumb/dumb/src/helpers/resample.c index 3baafd0da..cb3683266 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resample.c +++ b/Frameworks/Dumb/dumb/src/helpers/resample.c @@ -1,385 +1,410 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * resample.c - Resampling helpers. / / \ \ - * | < / \_ - * By Bob and entheh. | \/ /\ / - * \_ / > / - * In order to find a good trade-off between | \ / / - * speed and accuracy in this code, some tests | ' / - * were carried out regarding the behaviour of \__/ - * long long ints with gcc. The following code - * was tested: - * - * int a, b, c; - * c = ((long long)a * b) >> 16; - * - * DJGPP GCC Version 3.0.3 generated the following assembly language code for - * the multiplication and scaling, leaving the 32-bit result in EAX. - * - * movl -8(%ebp), %eax ; read one int into EAX - * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX - * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX - * - * Note that a 32*32->64 multiplication is performed, allowing for high - * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), - * so it is a minor concern when four multiplications are being performed - * (the cubic resampler). On the Pentium MMX and earlier, it takes four or - * more cycles, so this method is unsuitable for use in the low-quality - * resamplers. - * - * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, - * defined in dumb.h. We may investigate later what code MSVC generates, but - * if it seems too slow then we suggest you use a good compiler. - * - * FIXME: these comments are somewhat out of date now. - */ - -#include -#include "dumb.h" - - - -/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is - * called when it should be. There will be a considerable performance hit, - * since at least one condition has to be tested for every sample generated. - */ -#ifdef HEAVYDEBUG -#define HEAVYASSERT(cond) ASSERT(cond) -#else -#define HEAVYASSERT(cond) -#endif - - - -/* A global variable for controlling resampling quality wherever a local - * specification doesn't override it. The following values are valid: - * - * 0 - DUMB_RQ_ALIASING - fastest - * 1 - DUMB_RQ_LINEAR - * 2 - DUMB_RQ_CUBIC - nicest - * - * Values outside the range 0-2 will behave the same as the nearest - * value within the range. - */ -int dumb_resampling_quality = DUMB_RQ_CUBIC; - - - -//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16)) -//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14) -#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32)) -#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32)) - - - -/* Executes the content 'iterator' times. - * Clobbers the 'iterator' variable. - * The loop is unrolled by four. - */ -#define LOOP4(iterator, CONTENT) \ -{ \ - if ((iterator) & 2) { \ - CONTENT; \ - CONTENT; \ - } \ - if ((iterator) & 1) { \ - CONTENT; \ - } \ - (iterator) >>= 2; \ - while (iterator) { \ - CONTENT; \ - CONTENT; \ - CONTENT; \ - CONTENT; \ - (iterator)--; \ - } \ -} - - - -#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */ -#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */ - -#define X PASTE(x.x, SRCBITS) - - - -/* Cubic resampler: look-up tables - * - * a = 1.5*x1 - 1.5*x2 + 0.5*x3 - 0.5*x0 - * b = 2*x2 + x0 - 2.5*x1 - 0.5*x3 - * c = 0.5*x2 - 0.5*x0 - * d = x1 - * - * x = a*t*t*t + b*t*t + c*t + d - * = (-0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3) * t*t*t + - * ( 1*x0 - 2.5*x1 + 2 *x2 - 0.5*x3) * t*t + - * (-0.5*x0 + 0.5*x2 ) * t + - * ( 1*x1 ) - * = (-0.5*t*t*t + 1 *t*t - 0.5*t ) * x0 + - * ( 1.5*t*t*t - 2.5*t*t + 1) * x1 + - * (-1.5*t*t*t + 2 *t*t + 0.5*t ) * x2 + - * ( 0.5*t*t*t - 0.5*t*t ) * x3 - * = A0(t) * x0 + A1(t) * x1 + A2(t) * x2 + A3(t) * x3 - * - * A0, A1, A2 and A3 stay within the range [-1,1]. - * In the tables, they are scaled with 14 fractional bits. - * - * Turns out we don't need to store A2 and A3; they are symmetrical to A1 and A0. - * - * TODO: A0 and A3 stay very small indeed. Consider different scale/resolution? - */ - -static short cubicA0[1025], cubicA1[1025]; - -static void init_cubic(void) -{ - unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */ - static int done = 0; - if (done) return; - done = 1; - for (t = 0; t < 1025; t++) { - /* int casts to pacify warnings about negating unsigned values */ - cubicA0[t] = -(int)( t*t*t >> 17) + (int)( t*t >> 6) - (int)(t << 3); - cubicA1[t] = (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7) + (int)(1 << 14); - } -} - - - -/* Create resamplers for 24-in-32-bit source samples. */ - -/* #define SUFFIX - * MSVC warns if we try to paste a null SUFFIX, so instead we define - * special macros for the function names that don't bother doing the - * corresponding paste. The more generic definitions are further down. - */ -#define process_pickup PASTE(process_pickup, SUFFIX2) -#define dumb_resample PASTE(PASTE(dumb_resample, SUFFIX2), SUFFIX3) -#define dumb_resample_get_current_sample PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX2), SUFFIX3) - -#define SRCTYPE sample_t -#define SRCBITS 24 -#define ALIAS(x, vol) MULSC(x, vol) -#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos)) -/* -#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ - a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \ - b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \ - c = (x2 - x0) >> 1; \ -} -#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol) -*/ -#define CUBIC(x0, x1, x2, x3) ( \ - MULSC(x0, cubicA0[subpos >> 6] << 2) + \ - MULSC(x1, cubicA1[subpos >> 6] << 2) + \ - MULSC(x2, cubicA1[1 + (subpos >> 6 ^ 1023)] << 2) + \ - MULSC(x3, cubicA0[1 + (subpos >> 6 ^ 1023)] << 2)) -#define CUBICVOL(x, vol) MULSC(x, vol) -#include "resample.inc" - -/* Undefine the simplified macros. */ -#undef dumb_resample_get_current_sample -#undef dumb_resample -#undef process_pickup - - -/* Now define the proper ones that use SUFFIX. */ -#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX) -#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX) -#define process_pickup PASTE(PASTE(process_pickup, SUFFIX), SUFFIX2) -#define dumb_resample PASTE(PASTE(PASTE(dumb_resample, SUFFIX), SUFFIX2), SUFFIX3) -#define dumb_resample_get_current_sample PASTE(PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX), SUFFIX2), SUFFIX3) -#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX) - -/* Create resamplers for 16-bit source samples. */ -#define SUFFIX _16 -#define SRCTYPE short -#define SRCBITS 16 -#define ALIAS(x, vol) (x * vol >> 8) -#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos)) -/* -#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ - a = (3 * (x1 - x2) + (x3 - x0)) << 7; \ - b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \ - c = (x2 - x0) << 7; \ -} -#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol) -*/ -#define CUBIC(x0, x1, x2, x3) ( \ - x0 * cubicA0[subpos >> 6] + \ - x1 * cubicA1[subpos >> 6] + \ - x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \ - x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) -#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 10) >> 32) -#include "resample.inc" - -/* Create resamplers for 8-bit source samples. */ -#define SUFFIX _8 -#define SRCTYPE signed char -#define SRCBITS 8 -#define ALIAS(x, vol) (x * vol) -#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos) -/* -#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ - a = 3 * (x1 - x2) + (x3 - x0); \ - b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \ - c = (x2 - x0) << 15; \ -} -#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol) -*/ -#define CUBIC(x0, x1, x2, x3) (( \ - x0 * cubicA0[subpos >> 6] + \ - x1 * cubicA1[subpos >> 6] + \ - x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \ - x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) << 6) -#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 12) >> 32) -#include "resample.inc" - - -#undef dumb_reset_resampler -#undef dumb_start_resampler -#undef process_pickup -#undef dumb_resample -#undef dumb_resample_get_current_sample -#undef dumb_end_resampler - - - -void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end) -{ - if (n == 8) - dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end); - else if (n == 16) - dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end); - else - dumb_reset_resampler(resampler, src, src_channels, pos, start, end); -} - - - -DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end) -{ - if (n == 8) - return dumb_start_resampler_8(src, src_channels, pos, start, end); - else if (n == 16) - return dumb_start_resampler_16(src, src_channels, pos, start, end); - else - return dumb_start_resampler(src, src_channels, pos, start, end); -} - - - -long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta) -{ - if (n == 8) - return dumb_resample_8_1_1(resampler, dst, dst_size, volume, delta); - else if (n == 16) - return dumb_resample_16_1_1(resampler, dst, dst_size, volume, delta); - else - return dumb_resample_1_1(resampler, dst, dst_size, volume, delta); -} - - - -long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta) -{ - if (n == 8) - return dumb_resample_8_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); - else if (n == 16) - return dumb_resample_16_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); - else - return dumb_resample_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); -} - - - -long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta) -{ - if (n == 8) - return dumb_resample_8_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); - else if (n == 16) - return dumb_resample_16_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); - else - return dumb_resample_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); -} - - - -long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta) -{ - if (n == 8) - return dumb_resample_8_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); - else if (n == 16) - return dumb_resample_16_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); - else - return dumb_resample_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); -} - - - -void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, float volume, sample_t *dst) -{ - if (n == 8) - dumb_resample_get_current_sample_8_1_1(resampler, volume, dst); - else if (n == 16) - dumb_resample_get_current_sample_16_1_1(resampler, volume, dst); - else - dumb_resample_get_current_sample_1_1(resampler, volume, dst); -} - - - -void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst) -{ - if (n == 8) - dumb_resample_get_current_sample_8_1_2(resampler, volume_left, volume_right, dst); - else if (n == 16) - dumb_resample_get_current_sample_16_1_2(resampler, volume_left, volume_right, dst); - else - dumb_resample_get_current_sample_1_2(resampler, volume_left, volume_right, dst); -} - - - -void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst) -{ - if (n == 8) - dumb_resample_get_current_sample_8_2_1(resampler, volume_left, volume_right, dst); - else if (n == 16) - dumb_resample_get_current_sample_16_2_1(resampler, volume_left, volume_right, dst); - else - dumb_resample_get_current_sample_2_1(resampler, volume_left, volume_right, dst); -} - - - -void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst) -{ - if (n == 8) - dumb_resample_get_current_sample_8_2_2(resampler, volume_left, volume_right, dst); - else if (n == 16) - dumb_resample_get_current_sample_16_2_2(resampler, volume_left, volume_right, dst); - else - dumb_resample_get_current_sample_2_2(resampler, volume_left, volume_right, dst); -} - - - -void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler) -{ - if (n == 8) - dumb_end_resampler_8(resampler); - else if (n == 16) - dumb_end_resampler_16(resampler); - else - dumb_end_resampler(resampler); -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resample.c - Resampling helpers. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + +#include +#include "dumb.h" + +#include "internal/blip_buf.h" +#include "internal/lanczos_resampler.h" + + + +/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is + * called when it should be. There will be a considerable performance hit, + * since at least one condition has to be tested for every sample generated. + */ +#ifdef HEAVYDEBUG +#define HEAVYASSERT(cond) ASSERT(cond) +#else +#define HEAVYASSERT(cond) +#endif + + + +/* Make MSVC shut the hell up about if ( upd ) UPDATE_VOLUME() conditions being constant */ +#ifdef _MSC_VER +#pragma warning(disable:4127 4701) +#endif + + + +/* A global variable for controlling resampling quality wherever a local + * specification doesn't override it. The following values are valid: + * + * 0 - DUMB_RQ_ALIASING - fastest + * 1 - DUMB_RQ_LINEAR + * 2 - DUMB_RQ_CUBIC + * 3 - DUMB_RQ_FIR - nicest + * + * Values outside the range 0-3 will behave the same as the nearest + * value within the range. + */ +int dumb_resampling_quality = DUMB_RQ_CUBIC; + + + +//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16)) +//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14) +#define MULSCV(a, b) ((int)((LONG_LONG)(a) * (b) >> 32)) +#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32)) +#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32)) + + + +/* Executes the content 'iterator' times. + * Clobbers the 'iterator' variable. + * The loop is unrolled by four. + */ +#if 0 +#define LOOP4(iterator, CONTENT) \ +{ \ + if ((iterator) & 2) { \ + CONTENT; \ + CONTENT; \ + } \ + if ((iterator) & 1) { \ + CONTENT; \ + } \ + (iterator) >>= 2; \ + while (iterator) { \ + CONTENT; \ + CONTENT; \ + CONTENT; \ + CONTENT; \ + (iterator)--; \ + } \ +} +#else +#define LOOP4(iterator, CONTENT) \ +{ \ + while ( (iterator)-- ) \ + { \ + CONTENT; \ + } \ +} +#endif + +#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */ +#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */ + +#define X PASTE(x.x, SRCBITS) + + + +/* Cubic resampler: look-up tables + * + * a = 1.5*x1 - 1.5*x2 + 0.5*x3 - 0.5*x0 + * b = 2*x2 + x0 - 2.5*x1 - 0.5*x3 + * c = 0.5*x2 - 0.5*x0 + * d = x1 + * + * x = a*t*t*t + b*t*t + c*t + d + * = (-0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3) * t*t*t + + * ( 1*x0 - 2.5*x1 + 2 *x2 - 0.5*x3) * t*t + + * (-0.5*x0 + 0.5*x2 ) * t + + * ( 1*x1 ) + * = (-0.5*t*t*t + 1 *t*t - 0.5*t ) * x0 + + * ( 1.5*t*t*t - 2.5*t*t + 1) * x1 + + * (-1.5*t*t*t + 2 *t*t + 0.5*t ) * x2 + + * ( 0.5*t*t*t - 0.5*t*t ) * x3 + * = A0(t) * x0 + A1(t) * x1 + A2(t) * x2 + A3(t) * x3 + * + * A0, A1, A2 and A3 stay within the range [-1,1]. + * In the tables, they are scaled with 14 fractional bits. + * + * Turns out we don't need to store A2 and A3; they are symmetrical to A1 and A0. + * + * TODO: A0 and A3 stay very small indeed. Consider different scale/resolution? + */ + +static short cubicA0[1025], cubicA1[1025]; + +/*static*/ void init_cubic(void) +{ + unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */ + static int done = 0; + if (done) return; + done = 1; + for (t = 0; t < 1025; t++) { + /* int casts to pacify warnings about negating unsigned values */ + cubicA0[t] = -(int)( t*t*t >> 17) + (int)( t*t >> 6) - (int)(t << 3); + cubicA1[t] = (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7) + (int)(1 << 14); + } + + lanczos_init(); +} + + + +/* Create resamplers for 24-in-32-bit source samples. */ + +/* #define SUFFIX + * MSVC warns if we try to paste a null SUFFIX, so instead we define + * special macros for the function names that don't bother doing the + * corresponding paste. The more generic definitions are further down. + */ +#define process_pickup PASTE(process_pickup, SUFFIX2) +#define dumb_resample PASTE(PASTE(dumb_resample, SUFFIX2), SUFFIX3) +#define dumb_resample_get_current_sample PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX2), SUFFIX3) + +#define SRCTYPE sample_t +#define SRCBITS 24 +#define ALIAS(x) (x >> 8) +#define FIR(x) (x >> 8) +#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos)) +/* +#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ + a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \ + b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \ + c = (x2 - x0) >> 1; \ +} +#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol) +*/ +#define CUBIC(x0, x1, x2, x3) ( \ + MULSC(x0, cubicA0[subpos >> 6] << 2) + \ + MULSC(x1, cubicA1[subpos >> 6] << 2) + \ + MULSC(x2, cubicA1[1 + (subpos >> 6 ^ 1023)] << 2) + \ + MULSC(x3, cubicA0[1 + (subpos >> 6 ^ 1023)] << 2)) +#define CUBICVOL(x, vol) MULSC(x, vol) +#include "resample.inc" + +/* Undefine the simplified macros. */ +#undef dumb_resample_get_current_sample +#undef dumb_resample +#undef process_pickup + + +/* Now define the proper ones that use SUFFIX. */ +#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX) +#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX) +#define process_pickup PASTE(PASTE(process_pickup, SUFFIX), SUFFIX2) +#define dumb_resample PASTE(PASTE(PASTE(dumb_resample, SUFFIX), SUFFIX2), SUFFIX3) +#define dumb_resample_get_current_sample PASTE(PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX), SUFFIX2), SUFFIX3) +#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX) + +/* Create resamplers for 16-bit source samples. */ +#define SUFFIX _16 +#define SRCTYPE short +#define SRCBITS 16 +#define ALIAS(x) (x) +#define FIR(x) (x) +#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos)) +/* +#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ + a = (3 * (x1 - x2) + (x3 - x0)) << 7; \ + b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \ + c = (x2 - x0) << 7; \ +} +#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol) +*/ +#define CUBIC(x0, x1, x2, x3) ( \ + x0 * cubicA0[subpos >> 6] + \ + x1 * cubicA1[subpos >> 6] + \ + x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \ + x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) +#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 10) >> 32) +#include "resample.inc" + +/* Create resamplers for 8-bit source samples. */ +#define SUFFIX _8 +#define SRCTYPE signed char +#define SRCBITS 8 +#define ALIAS(x) (x << 8) +#define FIR(x) (x << 8) +#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos) +/* +#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \ + a = 3 * (x1 - x2) + (x3 - x0); \ + b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \ + c = (x2 - x0) << 15; \ +} +#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol) +*/ +#define CUBIC(x0, x1, x2, x3) (( \ + x0 * cubicA0[subpos >> 6] + \ + x1 * cubicA1[subpos >> 6] + \ + x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \ + x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) << 6) +#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 12) >> 32) +#include "resample.inc" + + +#undef dumb_reset_resampler +#undef dumb_start_resampler +#undef process_pickup +#undef dumb_resample +#undef dumb_resample_get_current_sample +#undef dumb_end_resampler + + + +void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end, int quality) +{ + if (n == 8) + dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end, quality); + else if (n == 16) + dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end, quality); + else + dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality); +} + + + +DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end, int quality) +{ + if (n == 8) + return dumb_start_resampler_8(src, src_channels, pos, start, end, quality); + else if (n == 16) + return dumb_start_resampler_16(src, src_channels, pos, start, end, quality); + else + return dumb_start_resampler(src, src_channels, pos, start, end, quality); +} + + + +long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume, float delta) +{ + if (n == 8) + return dumb_resample_8_1_1(resampler, dst, dst_size, volume, delta); + else if (n == 16) + return dumb_resample_16_1_1(resampler, dst, dst_size, volume, delta); + else + return dumb_resample_1_1(resampler, dst, dst_size, volume, delta); +} + + + +long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta) +{ + if (n == 8) + return dumb_resample_8_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else if (n == 16) + return dumb_resample_16_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else + return dumb_resample_1_2(resampler, dst, dst_size, volume_left, volume_right, delta); +} + + + +long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta) +{ + if (n == 8) + return dumb_resample_8_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); + else if (n == 16) + return dumb_resample_16_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); + else + return dumb_resample_2_1(resampler, dst, dst_size, volume_left, volume_right, delta); +} + + + +long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, float delta) +{ + if (n == 8) + return dumb_resample_8_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else if (n == 16) + return dumb_resample_16_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); + else + return dumb_resample_2_2(resampler, dst, dst_size, volume_left, volume_right, delta); +} + + + +void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_1_1(resampler, volume, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_1_1(resampler, volume, dst); + else + dumb_resample_get_current_sample_1_1(resampler, volume, dst); +} + + + +void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_1_2(resampler, volume_left, volume_right, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_1_2(resampler, volume_left, volume_right, dst); + else + dumb_resample_get_current_sample_1_2(resampler, volume_left, volume_right, dst); +} + + + +void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_2_1(resampler, volume_left, volume_right, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_2_1(resampler, volume_left, volume_right, dst); + else + dumb_resample_get_current_sample_2_1(resampler, volume_left, volume_right, dst); +} + + + +void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right, sample_t *dst) +{ + if (n == 8) + dumb_resample_get_current_sample_8_2_2(resampler, volume_left, volume_right, dst); + else if (n == 16) + dumb_resample_get_current_sample_16_2_2(resampler, volume_left, volume_right, dst); + else + dumb_resample_get_current_sample_2_2(resampler, volume_left, volume_right, dst); +} + + + +void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler) +{ + if (n == 8) + dumb_end_resampler_8(resampler); + else if (n == 16) + dumb_end_resampler_16(resampler); + else + dumb_end_resampler(resampler); +} diff --git a/Frameworks/Dumb/dumb/src/helpers/resample.inc b/Frameworks/Dumb/dumb/src/helpers/resample.inc index 7a80423ac..bf520559d 100644 --- a/Frameworks/Dumb/dumb/src/helpers/resample.inc +++ b/Frameworks/Dumb/dumb/src/helpers/resample.inc @@ -1,167 +1,403 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * resample.inc - Resampling helper template. / / \ \ - * | < / \_ - * By Bob and entheh. | \/ /\ / - * \_ / > / - * In order to find a good trade-off between | \ / / - * speed and accuracy in this code, some tests | ' / - * were carried out regarding the behaviour of \__/ - * long long ints with gcc. The following code - * was tested: - * - * int a, b, c; - * c = ((long long)a * b) >> 16; - * - * DJGPP GCC Version 3.0.3 generated the following assembly language code for - * the multiplication and scaling, leaving the 32-bit result in EAX. - * - * movl -8(%ebp), %eax ; read one int into EAX - * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX - * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX - * - * Note that a 32*32->64 multiplication is performed, allowing for high - * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), - * so it is a minor concern when four multiplications are being performed - * (the cubic resampler). On the Pentium MMX and earlier, it takes four or - * more cycles, so this method is unsuitable for use in the low-quality - * resamplers. - * - * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, - * defined in dumb.h. We may investigate later what code MSVC generates, but - * if it seems too slow then we suggest you use a good compiler. - * - * FIXME: these comments are somewhat out of date now. - */ - - - -void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_channels, long pos, long start, long end) -{ - int i; - resampler->src = src; - resampler->pos = pos; - resampler->subpos = 0; - resampler->start = start; - resampler->end = end; - resampler->dir = 1; - resampler->pickup = NULL; - resampler->pickup_data = NULL; - resampler->min_quality = 0; - resampler->max_quality = DUMB_RQ_N_LEVELS - 1; - for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0; - resampler->overshot = -1; -} - - - -DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, long pos, long start, long end) -{ - DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler)); - if (!resampler) return NULL; - dumb_reset_resampler(resampler, src, src_channels, pos, start, end); - return resampler; -} - - - -/* Create mono source resampler. */ -#define SUFFIX2 _1 -#define SRC_CHANNELS 1 -#define DIVIDE_BY_SRC_CHANNELS(x) (x) -#define COPYSRC(dstarray, dstindex, srcarray, srcindex) (dstarray)[dstindex] = (srcarray)[srcindex] -#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) (dstarray)[dstindex] = condition ? (srcarray)[srcindex] : 0 -#define MONO_DEST_VOLUME_PARAMETERS float volume -#define MONO_DEST_VOLUME_VARIABLES vol -#define MONO_DEST_VOLUME_ZEROS 0 -#define SET_MONO_DEST_VOLUME_VARIABLES vol = (int)floor(volume * 65536.0 + 0.5) -#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0) -#define MONO_DEST_MIX_ALIAS(op, offset) *dst++ op ALIAS(x[offset], vol) -#define STEREO_DEST_MIX_ALIAS(op, offset) { \ - int xm = x[offset]; \ - *dst++ op ALIAS(xm, lvol); \ - *dst++ op ALIAS(xm, rvol); \ -} -#define MONO_DEST_MIX_LINEAR(op, o0, o1) *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol) -#define STEREO_DEST_MIX_LINEAR(op, o0, o1) { \ - int xm = LINEAR(x[o0], x[o1]); \ - *dst++ op MULSC(xm, lvol); \ - *dst++ op MULSC(xm, rvol); \ -} -#define MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) *dst++ op CUBICVOL(CUBIC(x0[o0], x[o1], x[o2], x3[o3]), vol) -#define STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) { \ - int xm = CUBIC(x0[o0], x[o1], x[o2], x3[o3]); \ - *dst++ op CUBICVOL(xm, lvol); \ - *dst++ op CUBICVOL(xm, rvol); \ -} -#include "resamp2.inc" - -/* Create stereo source resampler. */ -#define SUFFIX2 _2 -#define SRC_CHANNELS 2 -#define DIVIDE_BY_SRC_CHANNELS(x) ((x) >> 1) -#define COPYSRC(dstarray, dstindex, srcarray, srcindex) { \ - (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \ - (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \ -} -#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) { \ - if (condition) { \ - (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \ - (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \ - } else { \ - (dstarray)[(dstindex)*2] = 0; \ - (dstarray)[(dstindex)*2+1] = 0; \ - } \ -} -#define MONO_DEST_VOLUME_PARAMETERS float volume_left, float volume_right -#define MONO_DEST_VOLUME_VARIABLES lvol, rvol -#define MONO_DEST_VOLUME_ZEROS 0, 0 -#define SET_MONO_DEST_VOLUME_VARIABLES { \ - lvol = (int)floor(volume_left * 65536.0 + 0.5); \ - rvol = (int)floor(volume_right * 65536.0 + 0.5); \ -} -#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && rvol == 0) -#define MONO_DEST_MIX_ALIAS(op, offset) *dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol) -#define STEREO_DEST_MIX_ALIAS(op, offset) { \ - *dst++ op ALIAS(x[(offset)*2], lvol); \ - *dst++ op ALIAS(x[(offset)*2+1], rvol); \ -} -#define MONO_DEST_MIX_LINEAR(op, o0, o1) *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol) -#define STEREO_DEST_MIX_LINEAR(op, o0, o1) { \ - *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol); \ - *dst++ op MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \ -} -#define MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) *dst++ op \ - CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol) + \ - CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol) -#define STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) { \ - *dst++ op CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol); \ - *dst++ op CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \ -} -#include "resamp2.inc" - - - -void dumb_end_resampler(DUMB_RESAMPLER *resampler) -{ - if (resampler) - free(resampler); -} - - - -#undef CUBICVOL -#undef CUBIC -#undef LINEAR -#undef ALIAS -#undef SRCBITS -#undef SRCTYPE -#undef SUFFIX +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * resample.inc - Resampling helper template. / / \ \ + * | < / \_ + * By Bob and entheh. | \/ /\ / + * \_ / > / + * In order to find a good trade-off between | \ / / + * speed and accuracy in this code, some tests | ' / + * were carried out regarding the behaviour of \__/ + * long long ints with gcc. The following code + * was tested: + * + * int a, b, c; + * c = ((long long)a * b) >> 16; + * + * DJGPP GCC Version 3.0.3 generated the following assembly language code for + * the multiplication and scaling, leaving the 32-bit result in EAX. + * + * movl -8(%ebp), %eax ; read one int into EAX + * imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX + * shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX + * + * Note that a 32*32->64 multiplication is performed, allowing for high + * accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally), + * so it is a minor concern when four multiplications are being performed + * (the cubic resampler). On the Pentium MMX and earlier, it takes four or + * more cycles, so this method is unsuitable for use in the low-quality + * resamplers. + * + * Since "long long" is a gcc-specific extension, we use LONG_LONG instead, + * defined in dumb.h. We may investigate later what code MSVC generates, but + * if it seems too slow then we suggest you use a good compiler. + * + * FIXME: these comments are somewhat out of date now. + */ + + + +void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_channels, long pos, long start, long end, int quality) +{ + int i; + resampler->src = src; + resampler->pos = pos; + resampler->subpos = 0; + resampler->start = start; + resampler->end = end; + resampler->dir = 1; + resampler->pickup = NULL; + resampler->pickup_data = NULL; + if (quality < 0) + { + resampler->quality = 0; + } + else if (quality > DUMB_RQ_N_LEVELS - 1) + { + resampler->quality = DUMB_RQ_N_LEVELS - 1; + } + else + { + resampler->quality = quality; + } + for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0; + resampler->overshot = -1; + resampler->last_clock = 0; + resampler->last_amp[0] = 0; + resampler->last_amp[1] = 0; + blip_clear(resampler->blip_buffer[0]); + blip_clear(resampler->blip_buffer[1]); + resampler->fir_resampler_ratio = 0; + lanczos_resampler_clear(resampler->fir_resampler[0]); + lanczos_resampler_clear(resampler->fir_resampler[1]); +} + + + +DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, long pos, long start, long end, int quality) +{ + DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler)); + if (!resampler) return NULL; + resampler->blip_buffer[0] = blip_new( 256 ); + if (!resampler->blip_buffer[0]) + { + free(resampler); + return NULL; + } + resampler->blip_buffer[1] = blip_new( 256 ); + if (!resampler->blip_buffer[1]) + { + free(resampler->blip_buffer[0]); + free(resampler); + return NULL; + } + blip_set_rates(resampler->blip_buffer[0], 65536, 1); + blip_set_rates(resampler->blip_buffer[1], 65536, 1); + dumb_reset_resampler(resampler, src, src_channels, pos, start, end, quality); + return resampler; +} + + + +#define UPDATE_VOLUME( pvol, vol ) { \ + if (pvol) { \ + vol##r += vol##d; \ + if ((vol##d < 0 && vol##r <= vol##t) || \ + (vol##d > 0 && vol##r >= vol##t)) { \ + pvol->volume = pvol->target; \ + pvol = NULL; \ + vol = MULSCV( vol##t, vol##m ); \ + } else { \ + vol = MULSCV( vol##r, vol##m ); \ + } \ + } \ +} + + + +/* Create mono source resampler. */ +#define SUFFIX2 _1 +#define SRC_CHANNELS 1 +#define DIVIDE_BY_SRC_CHANNELS(x) (x) +#define COPYSRC(dstarray, dstindex, srcarray, srcindex) (dstarray)[dstindex] = (srcarray)[srcindex] +#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) (dstarray)[dstindex] = condition ? (srcarray)[srcindex] : 0 +#define MONO_DEST_VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume +#define MONO_DEST_VOLUME_VARIABLES vol, volr, vold, volt, volm +#define MONO_DEST_VOLUME_ZEROS 0 +#define SET_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume ) { \ + volr = (int)(volume->volume * 16777216.0); \ + vold = (int)(volume->delta * 16777216.0); \ + volt = (int)(volume->target * 16777216.0); \ + volm = (int)(volume->mix * 16777216.0); \ + vol = MULSCV( volr, volm ); \ + if ( volr == volt ) volume = NULL; \ + } else { \ + vol = 0; \ + vold = 0; \ + volt = 0; \ + volm = 0; \ + } \ +} +#define RETURN_MONO_DEST_VOLUME_VARIABLES if ( volume ) volume->volume = (float)volr / 16777216.0f +#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0 && volt == 0) +#define POKE_ALIAS(offset) { \ + int delta = ALIAS(x[offset]) - resampler->last_amp[0]; \ + resampler->last_amp[0] += delta; \ + if ( delta ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, delta ); \ + resampler->last_clock += inv_dt; \ +} +#define POKE_FIR(offset) { \ + lanczos_resampler_write_sample( resampler->fir_resampler[0], FIR(x[offset]) ); \ +} +#define MONO_DEST_PEEK_ALIAS *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), vol ) +#define MONO_DEST_PEEK_FIR *dst = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), vol ) +#define MONO_DEST_MIX_FIR { \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), vol ); \ + UPDATE_VOLUME( volume, vol ); \ +} +#define ADVANCE_FIR lanczos_resampler_remove_sample( resampler->fir_resampler[0] ) +#define MONO_DEST_MIX_ALIAS(count) { \ + int n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + LOOP4( count, \ + *dst++ += MULSC( blip_samples[n], vol ); \ + n++; \ + UPDATE_VOLUME( volume, vol ); \ + ); \ +} +#define STEREO_DEST_PEEK_ALIAS { \ + int sample = blip_peek_sample( resampler->blip_buffer[0] ); \ + *dst++ = MULSC( sample, lvol ); \ + *dst++ = MULSC( sample, rvol ); \ +} +#define STEREO_DEST_PEEK_FIR { \ + int sample = lanczos_resampler_get_sample( resampler->fir_resampler[0] ); \ + *dst++ = MULSC( sample, lvol ); \ + *dst++ = MULSC( sample, rvol ); \ +} +#define STEREO_DEST_MIX_FIR { \ + int sample = lanczos_resampler_get_sample( resampler->fir_resampler[0] ); \ + *dst++ += MULSC( sample, lvol ); \ + *dst++ += MULSC( sample, rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_ALIAS(count) { \ + int sample, n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + LOOP4( count, \ + sample = blip_samples[n++]; \ + *dst++ += MULSC( sample, lvol ); \ + *dst++ += MULSC( sample, rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ + ); \ +} +#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol); \ + if ( upd ) UPDATE_VOLUME( volume, vol ); \ +} +#define STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + int xm = LINEAR(x[o0], x[o1]); \ + *dst++ op MULSC(xm, lvol); \ + *dst++ op MULSC(xm, rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + *dst++ op CUBICVOL(CUBIC(x0[o0], x[o1], x[o2], x3[o3]), vol); \ + if ( upd ) UPDATE_VOLUME( volume, vol ); \ +} +#define STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + int xm = CUBIC(x0[o0], x[o1], x[o2], x3[o3]); \ + *dst++ op CUBICVOL(xm, lvol); \ + *dst++ op CUBICVOL(xm, rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#include "resamp2.inc" + +/* Create stereo source resampler. */ +#define SUFFIX2 _2 +#define SRC_CHANNELS 2 +#define DIVIDE_BY_SRC_CHANNELS(x) ((x) >> 1) +#define COPYSRC(dstarray, dstindex, srcarray, srcindex) { \ + (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \ + (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \ +} +#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) { \ + if (condition) { \ + (dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \ + (dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \ + } else { \ + (dstarray)[(dstindex)*2] = 0; \ + (dstarray)[(dstindex)*2+1] = 0; \ + } \ +} + +#define MONO_DEST_VOLUME_PARAMETERS DUMB_VOLUME_RAMP_INFO * volume_left, DUMB_VOLUME_RAMP_INFO * volume_right +#define MONO_DEST_VOLUME_VARIABLES lvol, lvolr, lvold, lvolt, lvolm, rvol, rvolr, rvold, rvolt, rvolm +#define MONO_DEST_VOLUME_ZEROS 0, 0 +#define SET_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume_left ) { \ + lvolr = (int)(volume_left->volume * 16777216.0); \ + lvold = (int)(volume_left->delta * 16777216.0); \ + lvolt = (int)(volume_left->target * 16777216.0); \ + lvolm = (int)(volume_left->mix * 16777216.0); \ + lvol = MULSCV( lvolr, lvolm ); \ + if ( lvolr == lvolt ) volume_left = NULL; \ + } else { \ + lvol = 0; \ + lvold = 0; \ + lvolt = 0; \ + lvolm = 0; \ + } \ + if ( volume_right ) { \ + rvolr = (int)(volume_right->volume * 16777216.0); \ + rvold = (int)(volume_right->delta * 16777216.0); \ + rvolt = (int)(volume_right->target * 16777216.0); \ + rvolm = (int)(volume_right->mix * 16777216.0); \ + rvol = MULSCV( rvolr, rvolm ); \ + if ( rvolr == rvolt ) volume_right = NULL; \ + } else { \ + rvol = 0; \ + rvold = 0; \ + rvolt = 0; \ + rvolm = 0; \ + } \ +} +#define RETURN_MONO_DEST_VOLUME_VARIABLES { \ + if ( volume_left ) volume_left->volume = (float)lvolr / 16777216.0f; \ + if ( volume_right ) volume_right->volume = (float)rvolr / 16777216.0f; \ +} +#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && lvolt == 0 && rvol == 0 && rvolt == 0) +#define POKE_ALIAS(offset) { \ + int deltal = ALIAS(x[(offset)*2+0]) - resampler->last_amp[0]; \ + int deltar = ALIAS(x[(offset)*2+1]) - resampler->last_amp[1]; \ + resampler->last_amp[0] += deltal; \ + resampler->last_amp[1] += deltar; \ + if ( deltal ) blip_add_delta( resampler->blip_buffer[0], resampler->last_clock, deltal ); \ + if ( deltar ) blip_add_delta( resampler->blip_buffer[1], resampler->last_clock, deltar ); \ + resampler->last_clock += inv_dt; \ +} +#define POKE_FIR(offset) { \ + lanczos_resampler_write_sample( resampler->fir_resampler[0], FIR(x[(offset)*2+0]) ); \ + lanczos_resampler_write_sample( resampler->fir_resampler[1], FIR(x[(offset)*2+1]) ); \ +} +#define MONO_DEST_PEEK_ALIAS { \ + *dst = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ) + \ + MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \ +} +#define MONO_DEST_PEEK_FIR { \ + *dst = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \ + MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ +} +#define MONO_DEST_MIX_FIR { \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ) + \ + MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#define ADVANCE_FIR { \ + lanczos_resampler_remove_sample( resampler->fir_resampler[0] ); \ + lanczos_resampler_remove_sample( resampler->fir_resampler[1] ); \ +} +#define MONO_DEST_MIX_ALIAS(count) { \ + int n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \ + LOOP4( count, \ + *dst++ += MULSC( blip_samples[n], lvol ) + MULSC( blip_samples[256+n], rvol ); \ + n++; \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ + ); \ +} +#define STEREO_DEST_PEEK_ALIAS { \ + *dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[0] ), lvol ); \ + *dst++ = MULSC( blip_peek_sample( resampler->blip_buffer[1] ), rvol ); \ +} +#define STEREO_DEST_PEEK_FIR { \ + *dst++ = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \ + *dst++ = MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ +} +#define STEREO_DEST_MIX_FIR { \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[0] ), lvol ); \ + *dst++ += MULSC( lanczos_resampler_get_sample( resampler->fir_resampler[1] ), rvol ); \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_ALIAS(count) { \ + int n = 0; \ + resampler->last_clock -= count * 65536; \ + blip_end_frame( resampler->blip_buffer[0], count * 65536 ); \ + blip_end_frame( resampler->blip_buffer[1], count * 65536 ); \ + blip_read_samples( resampler->blip_buffer[0], blip_samples, count ); \ + blip_read_samples( resampler->blip_buffer[1], blip_samples + 256, count ); \ + LOOP4( count, \ + *dst++ += MULSC( blip_samples[n], lvol); \ + *dst++ += MULSC( blip_samples[256+n], rvol); \ + n++; \ + UPDATE_VOLUME( volume_left, lvol ); \ + UPDATE_VOLUME( volume_right, rvol ); \ + ); \ +} +#define MONO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_LINEAR(op, upd, o0, o1) { \ + *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol); \ + *dst++ op MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define MONO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + *dst++ op \ + CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol) + \ + CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#define STEREO_DEST_MIX_CUBIC(op, upd, x0, x3, o0, o1, o2, o3) { \ + *dst++ op CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol); \ + *dst++ op CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \ + if ( upd ) UPDATE_VOLUME( volume_left, lvol ); \ + if ( upd ) UPDATE_VOLUME( volume_right, rvol ); \ +} +#include "resamp2.inc" + + + +void dumb_end_resampler(DUMB_RESAMPLER *resampler) +{ + if (resampler) + free(resampler); +} + + + +#undef CUBICVOL +#undef CUBIC +#undef LINEAR +#undef ALIAS +#undef FIR +#undef SRCBITS +#undef SRCTYPE +#undef SUFFIX diff --git a/Frameworks/Dumb/dumb/src/helpers/riff.c b/Frameworks/Dumb/dumb/src/helpers/riff.c new file mode 100644 index 000000000..22e0f9136 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/riff.c @@ -0,0 +1,85 @@ +#include "dumb.h" +#include "internal/riff.h" + +#include + +struct riff * riff_parse( DUMBFILE * f, long offset, long size, unsigned proper ) +{ + unsigned stream_size; + struct riff * stream; + + if ( size < 8 ) return 0; + + if ( dumbfile_seek(f, offset, DFS_SEEK_SET) ) return 0; + if ( dumbfile_mgetl(f) != DUMB_ID('R','I','F','F') ) return 0; + + stream_size = dumbfile_igetl(f); + if ( stream_size + 8 > size ) return 0; + if ( stream_size < 4 ) return 0; + + stream = (struct riff *) malloc( sizeof( struct riff ) ); + if ( ! stream ) return 0; + + stream->type = dumbfile_mgetl(f); + stream->chunk_count = 0; + stream->chunks = 0; + + stream_size -= 4; + + while ( stream_size && !dumbfile_error(f) ) + { + struct riff_chunk * chunk; + if ( stream_size < 8 ) break; + stream->chunks = ( struct riff_chunk * ) realloc( stream->chunks, ( stream->chunk_count + 1 ) * sizeof( struct riff_chunk ) ); + if ( ! stream->chunks ) break; + chunk = stream->chunks + stream->chunk_count; + chunk->type = dumbfile_mgetl(f); + chunk->size = dumbfile_igetl(f); + chunk->offset = dumbfile_pos(f); + stream_size -= 8; + if ( stream_size < chunk->size ) break; + if ( chunk->type == DUMB_ID('R','I','F','F') ) + { + chunk->nested = riff_parse( f, chunk->offset - 8, chunk->size + 8, proper ); + if ( ! chunk->nested ) break; + } + else + { + chunk->nested = 0; + } + dumbfile_seek(f, chunk->offset + chunk->size, DFS_SEEK_SET); + stream_size -= chunk->size; + if ( proper && ( chunk->size & 1 ) ) + { + dumbfile_skip(f, 1); + -- stream_size; + } + ++stream->chunk_count; + } + + if ( stream_size ) + { + riff_free( stream ); + stream = 0; + } + + return stream; +} + +void riff_free( struct riff * stream ) +{ + if ( stream ) + { + if ( stream->chunks ) + { + unsigned i; + for ( i = 0; i < stream->chunk_count; ++i ) + { + struct riff_chunk * chunk = stream->chunks + i; + if ( chunk->nested ) riff_free( chunk->nested ); + } + free( stream->chunks ); + } + free( stream ); + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/sampbuf.c b/Frameworks/Dumb/dumb/src/helpers/sampbuf.c index 6a80f1fac..488db4490 100644 --- a/Frameworks/Dumb/dumb/src/helpers/sampbuf.c +++ b/Frameworks/Dumb/dumb/src/helpers/sampbuf.c @@ -1,64 +1,64 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * sampbuf.c - Helper for allocating sample / / \ \ - * buffers. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include "dumb.h" - - - -/* DEPRECATED */ -sample_t **create_sample_buffer(int n_channels, long length) -{ - int i; - sample_t **samples = malloc(n_channels * sizeof(*samples)); - if (!samples) return NULL; - samples[0] = malloc(n_channels * length * sizeof(*samples[0])); - if (!samples[0]) { - free(samples); - return NULL; - } - for (i = 1; i < n_channels; i++) samples[i] = samples[i-1] + length; - return samples; -} - - - -sample_t **allocate_sample_buffer(int n_channels, long length) -{ - int i; - sample_t **samples = malloc(((n_channels + 1) >> 1) * sizeof(*samples)); - if (!samples) return NULL; - samples[0] = malloc(n_channels * length * sizeof(*samples[0])); - if (!samples[0]) { - free(samples); - return NULL; - } - for (i = 1; i < (n_channels + 1) >> 1; i++) samples[i] = samples[i-1] + length*2; - return samples; -} - - - -void destroy_sample_buffer(sample_t **samples) -{ - if (samples) { - free(samples[0]); - free(samples); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * sampbuf.c - Helper for allocating sample / / \ \ + * buffers. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include "dumb.h" + + + +/* DEPRECATED */ +sample_t **create_sample_buffer(int n_channels, long length) +{ + int i; + sample_t **samples = malloc(n_channels * sizeof(*samples)); + if (!samples) return NULL; + samples[0] = malloc(n_channels * length * sizeof(*samples[0])); + if (!samples[0]) { + free(samples); + return NULL; + } + for (i = 1; i < n_channels; i++) samples[i] = samples[i-1] + length; + return samples; +} + + + +sample_t **allocate_sample_buffer(int n_channels, long length) +{ + int i; + sample_t **samples = malloc(((n_channels + 1) >> 1) * sizeof(*samples)); + if (!samples) return NULL; + samples[0] = malloc(n_channels * length * sizeof(*samples[0])); + if (!samples[0]) { + free(samples); + return NULL; + } + for (i = 1; i < (n_channels + 1) >> 1; i++) samples[i] = samples[i-1] + length*2; + return samples; +} + + + +void destroy_sample_buffer(sample_t **samples) +{ + if (samples) { + free(samples[0]); + free(samples); + } +} diff --git a/Frameworks/Dumb/dumb/src/helpers/silence.c b/Frameworks/Dumb/dumb/src/helpers/silence.c index 4d5fdcf4d..794ae8315 100644 --- a/Frameworks/Dumb/dumb/src/helpers/silence.c +++ b/Frameworks/Dumb/dumb/src/helpers/silence.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * silence.c - Silencing helper. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include "dumb.h" - - - -void dumb_silence(sample_t *samples, long length) -{ - memset(samples, 0, length * sizeof(*samples)); -} - +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * silence.c - Silencing helper. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include "dumb.h" + + + +void dumb_silence(sample_t *samples, long length) +{ + memset(samples, 0, length * sizeof(*samples)); +} + diff --git a/Frameworks/Dumb/dumb/src/helpers/stdfile.c b/Frameworks/Dumb/dumb/src/helpers/stdfile.c index 2f02539a9..ec042ecac 100644 --- a/Frameworks/Dumb/dumb/src/helpers/stdfile.c +++ b/Frameworks/Dumb/dumb/src/helpers/stdfile.c @@ -1,93 +1,146 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * stdfile.c - stdio file input module. / / \ \ - * | < / \_ - * By entheh. | \/ /\ / - * \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" - - - -static void *dumb_stdfile_open(const char *filename) -{ - return fopen(filename, "rb"); -} - - - -static int dumb_stdfile_skip(void *f, long n) -{ - return fseek(f, n, SEEK_CUR); -} - - - -static int dumb_stdfile_getc(void *f) -{ - return fgetc(f); -} - - - -static long dumb_stdfile_getnc(char *ptr, long n, void *f) -{ - return fread(ptr, 1, n, f); -} - - - -static void dumb_stdfile_close(void *f) -{ - fclose(f); -} - - - -static DUMBFILE_SYSTEM stdfile_dfs = { - &dumb_stdfile_open, - &dumb_stdfile_skip, - &dumb_stdfile_getc, - &dumb_stdfile_getnc, - &dumb_stdfile_close -}; - - - -void dumb_register_stdfiles(void) -{ - register_dumbfile_system(&stdfile_dfs); -} - - - -static DUMBFILE_SYSTEM stdfile_dfs_leave_open = { - NULL, - &dumb_stdfile_skip, - &dumb_stdfile_getc, - &dumb_stdfile_getnc, - NULL -}; - - - -DUMBFILE *dumbfile_open_stdfile(FILE *p) -{ - DUMBFILE *d = dumbfile_open_ex(p, &stdfile_dfs_leave_open); - - return d; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * stdfile.c - stdio file input module. / / \ \ + * | < / \_ + * By entheh. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" + + + +typedef struct dumb_stdfile +{ + FILE * file; + long size; +} dumb_stdfile; + + + +static void *dumb_stdfile_open(const char *filename) +{ + dumb_stdfile * file = ( dumb_stdfile * ) malloc( sizeof(dumb_stdfile) ); + if ( !file ) return 0; + file->file = fopen(filename, "rb"); + fseek(file->file, 0, SEEK_END); + file->size = ftell(file->file); + fseek(file->file, 0, SEEK_SET); + return file; +} + + + +static int dumb_stdfile_skip(void *f, long n) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fseek(file->file, n, SEEK_CUR); +} + + + +static int dumb_stdfile_getc(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fgetc(file->file); +} + + + +static long dumb_stdfile_getnc(char *ptr, long n, void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fread(ptr, 1, n, file->file); +} + + + +static void dumb_stdfile_close(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + fclose(file->file); + free(f); +} + + + +static void dumb_stdfile_noclose(void *f) +{ + free(f); +} + + + +static int dumb_stdfile_seek(void *f, long n) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return fseek(file->file, n, SEEK_SET); +} + + + +static long dumb_stdfile_get_size(void *f) +{ + dumb_stdfile * file = ( dumb_stdfile * ) f; + return file->size; +} + + + +static const DUMBFILE_SYSTEM stdfile_dfs = { + &dumb_stdfile_open, + &dumb_stdfile_skip, + &dumb_stdfile_getc, + &dumb_stdfile_getnc, + &dumb_stdfile_close, + &dumb_stdfile_seek, + &dumb_stdfile_get_size +}; + + + +void dumb_register_stdfiles(void) +{ + register_dumbfile_system(&stdfile_dfs); +} + + + +static const DUMBFILE_SYSTEM stdfile_dfs_leave_open = { + NULL, + &dumb_stdfile_skip, + &dumb_stdfile_getc, + &dumb_stdfile_getnc, + &dumb_stdfile_noclose, + &dumb_stdfile_seek, + &dumb_stdfile_get_size +}; + + + +DUMBFILE *dumbfile_open_stdfile(FILE *p) +{ + dumb_stdfile * file = ( dumb_stdfile * ) malloc( sizeof(dumb_stdfile) ); + DUMBFILE *d; + if ( !file ) return 0; + file->file = p; + fseek(p, 0, SEEK_END); + file->size = ftell(p); + fseek(p, 0, SEEK_SET); + d = dumbfile_open_ex(file, &stdfile_dfs_leave_open); + + return d; +} diff --git a/Frameworks/Dumb/dumb/src/helpers/tarray.c b/Frameworks/Dumb/dumb/src/helpers/tarray.c new file mode 100644 index 000000000..f3ba422d8 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/helpers/tarray.c @@ -0,0 +1,175 @@ +#include "internal/tarray.h" + +#include + + /* + Structures which contain the play times of each pattern and row combination in the song, + not guaranteed to be valid for the whole song until the loop status is no longer zero. + The initial count and restart count will both be zero on song start, then both will be + incremented until the song loops. Restart count will be reset to zero on loop for all + rows which have a time equal to or greater than the loop start point, so time keeping + functions will know which timestamp the song is currently located at. + + Timestamp lists are guaranteed to be allocated in blocks of 16 timestamps at a time. + */ + + /* + We don't need full timekeeping because the player loop only wants the first play time + of the loop start order/row. We also don't really want full timekeeping because it + involves a lot of memory allocations, which is also slow. + */ + +#undef FULL_TIMEKEEPING + +typedef struct DUMB_IT_ROW_TIME +{ + unsigned int count, restart_count; +#ifndef FULL_TIMEKEEPING + LONG_LONG first_time; +#else + LONG_LONG * times; +#endif +} DUMB_IT_ROW_TIME; + +void * timekeeping_array_create(size_t size) +{ + size_t * _size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * size ); + if ( _size ) { + *_size = size; + } + return _size; +} + +void timekeeping_array_destroy(void * array) +{ +#ifdef FULL_TIMEKEEPING + size_t i; + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + for (i = 0; i < *size; i++) { + if (s[i].times) free(s[i].times); + } +#endif + + free(array); +} + +void * timekeeping_array_dup(void * array) +{ + size_t i; + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + size_t * new_size = (size_t *) calloc( 1, sizeof(size_t) + sizeof(DUMB_IT_ROW_TIME) * *size ); + if ( new_size ) { + DUMB_IT_ROW_TIME * new_s = (DUMB_IT_ROW_TIME *)(new_size + 1); + + *new_size = *size; + + for (i = 0; i < *size; i++) { + new_s[i].count = s[i].count; + new_s[i].restart_count = s[i].restart_count; + +#ifndef FULL_TIMEKEEPING + new_s[i].first_time = s[i].first_time; +#else + if ( s[i].times ) { + size_t time_count = ( s[i].count + 15 ) & ~15; + new_s[i].times = (LONG_LONG *) malloc( sizeof(LONG_LONG) * time_count ); + if ( new_s[i].times == (void *)0 ) { + timekeeping_array_destroy( new_size ); + return (void *) 0; + } + memcpy( new_s[i].times, s[i].times, sizeof(LONG_LONG) * s[i].count ); + } +#endif + } + } + + return new_size; +} + +void timekeeping_array_reset(void * array, size_t loop_start) +{ + size_t i; + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + DUMB_IT_ROW_TIME * s_loop_start = s + loop_start; + LONG_LONG loop_start_time; + + if ( loop_start >= *size || s_loop_start->count < 1 ) return; + +#ifndef FULL_TIMEKEEPING + loop_start_time = s_loop_start->first_time; +#else + loop_start_time = s_loop_start->times[0]; +#endif + + for ( i = 0; i < *size; i++ ) { +#ifndef FULL_TIMEKEEPING + if ( s[i].count && s[i].first_time >= loop_start_time ) { +#else + if ( s[i].count && s[i].times[0] >= loop_start_time ) { +#endif + s[i].restart_count = 0; + } + } +} + +void timekeeping_array_push(void * array, size_t index, LONG_LONG time) +{ +#ifdef FULL_TIMEKEEPING + size_t i; + size_t time_count; +#endif + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size) return; + +#ifndef FULL_TIMEKEEPING + if ( !s[index].count++ ) + s[index].first_time = time; +#else + time_count = ( s[index].count + 16 ) & ~15; + + s[index].times = (LONG_LONG *) realloc( s[index].times, sizeof(LONG_LONG) * time_count ); + + s[index].times[s[index].count++] = time; +#endif +} + +void timekeeping_array_bump(void * array, size_t index) +{ + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size) return; + + s[index].restart_count++; +} + +unsigned int timekeeping_array_get_count(void * array, size_t index) +{ + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size) return 0; + + return s[index].count; +} + +LONG_LONG timekeeping_array_get_item(void * array, size_t index) +{ + size_t * size = (size_t *) array; + DUMB_IT_ROW_TIME * s = (DUMB_IT_ROW_TIME *)(size + 1); + + if (index >= *size || s[index].restart_count >= s[index].count) return 0; + +#ifndef FULL_TIMEKEEPING + return s[index].first_time; +#else + return s[index].times[s[index].restart_count]; +#endif +} diff --git a/Frameworks/Dumb/dumb/src/it/itload.c b/Frameworks/Dumb/dumb/src/it/itload.c index a26f5e102..300042330 100644 --- a/Frameworks/Dumb/dumb/src/it/itload.c +++ b/Frameworks/Dumb/dumb/src/it/itload.c @@ -1,42 +1,43 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itload.c - Code to read an Impulse Tracker / / \ \ - * file, opening and closing it for | < / \_ - * you. | \/ /\ / - * \_ / > / - * By entheh. Don't worry Bob, you're credited | \ / / - * in itread.c! | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/it.h" - - - -/* dumb_load_it_quick(): loads an IT file into a DUH struct, returning a - * pointer to the DUH struct. When you have finished with it, you must pass - * the pointer to unload_duh() so that the memory can be freed. - */ -DUH *dumb_load_it_quick(const char *filename) -{ - DUH *duh; - DUMBFILE *f = dumbfile_open(filename); - - if (!f) - return NULL; - - duh = dumb_read_it_quick(f); - - dumbfile_close(f); - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itload.c - Code to read an Impulse Tracker / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. Don't worry Bob, you're credited | \ / / + * in itread.c! | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_it_quick(): loads an IT file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must pass + * the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_it_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_it_quick(f); + + dumbfile_close(f); + + return duh; +} + diff --git a/Frameworks/Dumb/dumb/src/it/itload2.c b/Frameworks/Dumb/dumb/src/it/itload2.c index 2dd65a71e..15cff1d0a 100644 --- a/Frameworks/Dumb/dumb/src/it/itload2.c +++ b/Frameworks/Dumb/dumb/src/it/itload2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itload2.c - Function to read an Impulse Tracker / / \ \ - * file, opening and closing it for | < / \_ - * you, and do an initial run-through. | \/ /\ / - * \_ / > / - * Split off from itload.c by entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_load_it(const char *filename) -{ - DUH *duh = dumb_load_it_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itload2.c - Function to read an Impulse Tracker / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * Split off from itload.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_it(const char *filename) +{ + DUH *duh = dumb_load_it_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/itmisc.c b/Frameworks/Dumb/dumb/src/it/itmisc.c index fc62c188f..c891bd457 100644 --- a/Frameworks/Dumb/dumb/src/it/itmisc.c +++ b/Frameworks/Dumb/dumb/src/it/itmisc.c @@ -1,247 +1,247 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itmisc.c - Miscellaneous functions relating / / \ \ - * to module files. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/it.h" - - - -DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh) -{ - return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT); -} - - - -const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->song_message : NULL; -} - - - -int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->n_orders : 0; -} - - - -int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->n_samples : 0; -} - - - -int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->n_instruments : 0; -} - - - -const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i) -{ - ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples); - return sd->sample[i].name; -} - - - -const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i) -{ - ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples); - return sd->sample[i].filename; -} - - - -const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i) -{ - ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments); - return sd->instrument[i].name; -} - - - -const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i) -{ - ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments); - return sd->instrument[i].filename; -} - - - -int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->global_volume : 0; -} - - - -void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv) -{ - if (sd) sd->global_volume = gv; -} - - - -int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->mixing_volume : 0; -} - - - -void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv) -{ - if (sd) sd->mixing_volume = mv; -} - - - -int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->speed : 0; -} - - - -void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed) -{ - if (sd) sd->speed = speed; -} - - - -int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd) -{ - return sd ? sd->tempo : 0; -} - - - -void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo) -{ - if (sd) sd->tempo = tempo; -} - - - -int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel) -{ - ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS); - return sd ? sd->channel_volume[channel] : 0; -} - -void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume) -{ - ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS); - if (sd) sd->channel_volume[channel] = volume; -} - - - -int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->order : -1; -} - - - -int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->row : -1; -} - - - -int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->globalvolume : 0; -} - - - -void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv) -{ - if (sr) sr->globalvolume = gv; -} - - - -int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->tempo : 0; -} - - - -void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo) -{ - if (sr) sr->tempo = tempo; -} - - - -int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr) -{ - return sr ? sr->speed : 0; -} - - - -void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed) -{ - if (sr) sr->speed = speed; -} - - - -int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel) -{ - return sr ? sr->channel[channel].channelvolume : 0; -} - - - -void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume) -{ - if (sr) sr->channel[channel].channelvolume = volume; -} - - - -void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted) -{ - if (sr) { - if (muted) - sr->channel[channel].flags |= IT_CHANNEL_MUTED; - else - sr->channel[channel].flags &= ~IT_CHANNEL_MUTED; - } -} - - - -int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel) -{ - return sr ? (sr->channel[channel].flags & IT_CHANNEL_MUTED) != 0 : 0; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itmisc.c - Miscellaneous functions relating / / \ \ + * to module files. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh) +{ + return duh_get_raw_sigdata(duh, -1, SIGTYPE_IT); +} + + + +const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->song_message : NULL; +} + + + +int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_orders : 0; +} + + + +int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_samples : 0; +} + + + +int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->n_instruments : 0; +} + + + +const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples); + return sd->sample[i].name; +} + + + +const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples); + return sd->sample[i].filename; +} + + + +const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments); + return sd->instrument[i].name; +} + + + +const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i) +{ + ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments); + return sd->instrument[i].filename; +} + + + +int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->global_volume : 0; +} + + + +void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv) +{ + if (sd) sd->global_volume = gv; +} + + + +int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->mixing_volume : 0; +} + + + +void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv) +{ + if (sd) sd->mixing_volume = mv; +} + + + +int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->speed : 0; +} + + + +void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed) +{ + if (sd) sd->speed = speed; +} + + + +int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd) +{ + return sd ? sd->tempo : 0; +} + + + +void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo) +{ + if (sd) sd->tempo = tempo; +} + + + +int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel) +{ + ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS); + return sd ? sd->channel_volume[channel] : 0; +} + +void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume) +{ + ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS); + if (sd) sd->channel_volume[channel] = volume; +} + + + +int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->order : -1; +} + + + +int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->row : -1; +} + + + +int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->globalvolume : 0; +} + + + +void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv) +{ + if (sr) sr->globalvolume = gv; +} + + + +int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->tempo : 0; +} + + + +void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo) +{ + if (sr) sr->tempo = tempo; +} + + + +int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr) +{ + return sr ? sr->speed : 0; +} + + + +void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed) +{ + if (sr) sr->speed = speed; +} + + + +int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel) +{ + return sr ? sr->channel[channel].channelvolume : 0; +} + + + +void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume) +{ + if (sr) sr->channel[channel].channelvolume = volume; +} + + + +void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted) +{ + if (sr) { + if (muted) + sr->channel[channel].flags |= IT_CHANNEL_MUTED; + else + sr->channel[channel].flags &= ~IT_CHANNEL_MUTED; + } +} + + + +int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel) +{ + return sr ? (sr->channel[channel].flags & IT_CHANNEL_MUTED) != 0 : 0; +} diff --git a/Frameworks/Dumb/dumb/src/it/itorder.c b/Frameworks/Dumb/dumb/src/it/itorder.c index 6959f0544..c3fe51cbe 100644 --- a/Frameworks/Dumb/dumb/src/it/itorder.c +++ b/Frameworks/Dumb/dumb/src/it/itorder.c @@ -1,63 +1,63 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itorder.c - Code to fix invalid patterns in / / \ \ - * the pattern table. | < / \_ - * | \/ /\ / - * By Julien Cugniere. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - - - -#include - -#include "dumb.h" -#include "internal/it.h" - - - -/* This function ensures that any pattern mentioned in the order table but - * not present in the pattern table is treated as an empty 64 rows pattern. - * This is done by adding such a dummy pattern at the end of the pattern - * table, and redirect invalid orders to it. - * Patterns 254 and 255 are left untouched, unless the signal is an XM. - */ -int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata) -{ - int i; - int found_some = 0; - - int first_invalid = sigdata->n_patterns; - int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253; - - for (i = 0; i < sigdata->n_orders; i++) { - if (sigdata->order[i] >= first_invalid && sigdata->order[i] <= last_invalid) { - sigdata->order[i] = sigdata->n_patterns; - found_some = 1; - } - } - - if (found_some) { - IT_PATTERN *new_pattern = realloc(sigdata->pattern, sizeof(*sigdata->pattern) * (sigdata->n_patterns + 1)); - if (!new_pattern) - return -1; - - new_pattern[sigdata->n_patterns].n_rows = 64; - new_pattern[sigdata->n_patterns].n_entries = 0; - new_pattern[sigdata->n_patterns].entry = NULL; - sigdata->pattern = new_pattern; - sigdata->n_patterns++; - } - - return 0; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itorder.c - Code to fix invalid patterns in / / \ \ + * the pattern table. | < / \_ + * | \/ /\ / + * By Julien Cugniere. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + + + +#include + +#include "dumb.h" +#include "internal/it.h" + + + +/* This function ensures that any pattern mentioned in the order table but + * not present in the pattern table is treated as an empty 64 rows pattern. + * This is done by adding such a dummy pattern at the end of the pattern + * table, and redirect invalid orders to it. + * Patterns 254 and 255 are left untouched, unless the signal is an XM. + */ +int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata) +{ + int i; + int found_some = 0; + + int first_invalid = sigdata->n_patterns; + int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253; + + for (i = 0; i < sigdata->n_orders; i++) { + if (sigdata->order[i] >= first_invalid && sigdata->order[i] <= last_invalid) { + sigdata->order[i] = sigdata->n_patterns; + found_some = 1; + } + } + + if (found_some) { + IT_PATTERN *new_pattern = realloc(sigdata->pattern, sizeof(*sigdata->pattern) * (sigdata->n_patterns + 1)); + if (!new_pattern) + return -1; + + new_pattern[sigdata->n_patterns].n_rows = 64; + new_pattern[sigdata->n_patterns].n_entries = 0; + new_pattern[sigdata->n_patterns].entry = NULL; + sigdata->pattern = new_pattern; + sigdata->n_patterns++; + } + + return 0; +} diff --git a/Frameworks/Dumb/dumb/src/it/itread.c b/Frameworks/Dumb/dumb/src/it/itread.c index 532fb65fe..0ed0b1b9a 100644 --- a/Frameworks/Dumb/dumb/src/it/itread.c +++ b/Frameworks/Dumb/dumb/src/it/itread.c @@ -1,1202 +1,1411 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itread.c - Code to read an Impulse Tracker / / \ \ - * module from an open file. | < / \_ - * | \/ /\ / - * Based on the loader from an IT player by Bob. \_ / > / - * Adapted for DUMB by entheh. | \ / / - * | ' / - * \__/ - */ - -#include -#include //might not be necessary later; required for memset - -#include "dumb.h" -#include "internal/it.h" - - - -#define INVESTIGATE_OLD_INSTRUMENTS - - - -static int it_seek(DUMBFILE *f, long offset) -{ - long pos = dumbfile_pos(f); - - if (pos > offset) - return -1; - - if (pos < offset) - if (dumbfile_skip(f, offset - pos)) - return -1; - - return 0; -} - - - -typedef unsigned char byte; -typedef unsigned short word; -typedef unsigned long dword; - - - -static unsigned char *sourcebuf = NULL; -static unsigned char *sourcepos = NULL; -static unsigned char *sourceend; -static int rembits = 0; - - - -static int readblock(DUMBFILE *f) -{ - long size; - int c; - - size = dumbfile_igetw(f); - if (size < 0) - return size; - - sourcebuf = malloc(size); - if (!sourcebuf) - return -1; - - c = dumbfile_getnc((char *)sourcebuf, size, f); - if (c < size) { - free(sourcebuf); - sourcebuf = NULL; - return -1; - } - - sourcepos = sourcebuf; - sourceend = sourcebuf + size; - rembits = 8; - return 0; -} - - - -static void freeblock(void) -{ - free(sourcebuf); - sourcebuf = NULL; -} - - - -static int readbits(int bitwidth) -{ - int val = 0; - int b = 0; - - if (sourcepos >= sourceend) return val; - - while (bitwidth > rembits) { - val |= *sourcepos++ << b; - if (sourcepos >= sourceend) return val; - b += rembits; - bitwidth -= rembits; - rembits = 8; - } - - val |= (*sourcepos & ((1 << bitwidth) - 1)) << b; - *sourcepos >>= bitwidth; - rembits -= bitwidth; - - return val; -} - - - -/** WARNING - do we even need to pass `right`? */ -/** WARNING - why bother memsetting at all? The whole array is written... */ -// if we do memset, dumb_silence() would be neater... -static int decompress8(DUMBFILE *f, signed char *data, int len, int cmwt) -{ - int blocklen, blockpos; - byte bitwidth; - word val; - char d1, d2; - - memset(data, 0, len * sizeof(*data)); - - while (len > 0) { - //Read a block of compressed data: - if (readblock(f)) - return -1; - //Set up a few variables - blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes - blockpos = 0; - bitwidth = 9; - d1 = d2 = 0; - //Start the decompression: - while (blockpos < blocklen) { - //Read a value: - val = (word)readbits(bitwidth); - //Check for bit width change: - - if (bitwidth < 7) { //Method 1: - if (val == (1 << (bitwidth - 1))) { - val = (word)readbits(3) + 1; - bitwidth = (val < bitwidth) ? val : val + 1; - continue; - } - } - else if (bitwidth < 9) { //Method 2 - byte border = (0xFF >> (9 - bitwidth)) - 4; - - if (val > border && val <= (border + 8)) { - val -= border; - bitwidth = (val < bitwidth) ? val : val + 1; - continue; - } - } - else if (bitwidth == 9) { //Method 3 - if (val & 0x100) { - bitwidth = (val + 1) & 0xFF; - continue; - } - } - else { //Illegal width, abort ? - freeblock(); - return -1; - } - - //Expand the value to signed byte: - { - char v; //The sample value: - if (bitwidth < 8) { - byte shift = 8 - bitwidth; - v = (val << shift); - v >>= shift; - } - else - v = (char)val; - - //And integrate the sample value - //(It always has to end with integration doesn't it ? ;-) - d1 += v; - d2 += d1; - } - - //Store ! - /* Version 2.15 was an unofficial version with hacked compression - * code. Yay, better compression :D - */ - *data++ = cmwt == 0x215 ? d2 : d1; - len--; - blockpos++; - } - freeblock(); - } - return 0; -} - - - -static int decompress16(DUMBFILE *f, short *data, int len, int cmwt) -{ - int blocklen, blockpos; - byte bitwidth; - long val; - short d1, d2; - - memset(data, 0, len * sizeof(*data)); - - while (len > 0) { - //Read a block of compressed data: - if (readblock(f)) - return -1; - //Set up a few variables - blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes - blockpos = 0; - bitwidth = 17; - d1 = d2 = 0; - //Start the decompression: - while (blockpos < blocklen) { - val = readbits(bitwidth); - //Check for bit width change: - - if (bitwidth < 7) { //Method 1: - if (val == (1 << (bitwidth - 1))) { - val = readbits(4) + 1; - bitwidth = (val < bitwidth) ? val : val + 1; - continue; - } - } - else if (bitwidth < 17) { //Method 2 - word border = (0xFFFF >> (17 - bitwidth)) - 8; - - if (val > border && val <= (border + 16)) { - val -= border; - bitwidth = val < bitwidth ? val : val + 1; - continue; - } - } - else if (bitwidth == 17) { //Method 3 - if (val & 0x10000) { - bitwidth = (val + 1) & 0xFF; - continue; - } - } - else { //Illegal width, abort ? - freeblock(); - return -1; - } - - //Expand the value to signed byte: - { - short v; //The sample value: - if (bitwidth < 16) { - byte shift = 16 - bitwidth; - v = (short)(val << shift); - v >>= shift; - } - else - v = (short)val; - - //And integrate the sample value - //(It always has to end with integration doesn't it ? ;-) - d1 += v; - d2 += d1; - } - - //Store ! - /* Version 2.15 was an unofficial version with hacked compression - * code. Yay, better compression :D - */ - *data++ = cmwt == 0x215 ? d2 : d1; - len--; - blockpos++; - } - freeblock(); - } - return 0; -} - - - -static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f) -{ - int n; - - envelope->flags = dumbfile_getc(f); - envelope->n_nodes = dumbfile_getc(f); - envelope->loop_start = dumbfile_getc(f); - envelope->loop_end = dumbfile_getc(f); - envelope->sus_loop_start = dumbfile_getc(f); - envelope->sus_loop_end = dumbfile_getc(f); - for (n = 0; n < envelope->n_nodes; n++) { - envelope->node_y[n] = dumbfile_getc(f); - envelope->node_t[n] = dumbfile_igetw(f); - } - dumbfile_skip(f, 75 - envelope->n_nodes * 3 + 1); - - if (envelope->n_nodes <= 0) - envelope->flags &= ~IT_ENVELOPE_ON; - else { - if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; - if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; - } - - return dumbfile_error(f); -} - - - -static int it_read_old_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f) -{ - int n; - - if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) - return -1; - - dumbfile_getnc(instrument->filename, 13, f); - instrument->filename[13] = 0; - - instrument->volume_envelope.flags = dumbfile_getc(f); - instrument->volume_envelope.loop_start = dumbfile_getc(f); - instrument->volume_envelope.loop_end = dumbfile_getc(f); - instrument->volume_envelope.sus_loop_start = dumbfile_getc(f); - instrument->volume_envelope.sus_loop_end = dumbfile_getc(f); - - /* Skip two unused bytes. */ - dumbfile_skip(f, 2); - - /* In the old instrument format, fadeout ranges from 0 to 64, and is - * subtracted at intervals from a value starting at 512. In the new - * format, all these values are doubled. Therefore we double when loading - * from the old instrument format - that way we don't have to think about - * it later. - */ - instrument->fadeout = dumbfile_igetw(f) << 1; - instrument->new_note_action = dumbfile_getc(f); - instrument->dup_check_type = dumbfile_getc(f); - instrument->dup_check_action = DCA_NOTE_CUT; // This might be wrong! - /** WARNING - what is the duplicate check action for old-style instruments? */ - - /* Skip Tracker Version and Number of Samples. These are only used in - * separate instrument files. Also skip unused byte. - */ - dumbfile_skip(f, 4); - - dumbfile_getnc(instrument->name, 26, f); - instrument->name[26] = 0; - - /* Skip unused bytes following the Instrument Name. */ - dumbfile_skip(f, 6); - - instrument->pp_separation = 0; - instrument->pp_centre = 60; - instrument->global_volume = 128; - /** WARNING - should global_volume be 64 or something? */ - instrument->default_pan = 32; - /** WARNING - should default_pan be 128, meaning don`t use? */ - instrument->random_volume = 0; - instrument->random_pan = 0; - - for (n = 0; n < 120; n++) { - instrument->map_note[n] = dumbfile_getc(f); - instrument->map_sample[n] = dumbfile_getc(f); - } - - /* Skip "Volume envelope (200 bytes)". */ - // - need to know better what this is for though. - dumbfile_skip(f, 200); - -#ifdef INVESTIGATE_OLD_INSTRUMENTS - fprintf(stderr, "Inst %02d Env:", n); -#endif - - for (n = 0; n < 25; n++) - { - instrument->volume_envelope.node_t[n] = dumbfile_getc(f); - instrument->volume_envelope.node_y[n] = dumbfile_getc(f); - -#ifdef INVESTIGATE_OLD_INSTRUMENTS - fprintf(stderr, " %d,%d", - instrument->volume_envelope.node_t[n], - instrument->volume_envelope.node_y[n]); -#endif - - // This loop is unfinished, as we can probably escape from it before - // the end if we want to. Hence the otherwise useless dumbfile_skip() - // call below. - } - dumbfile_skip(f, 50 - (n << 1)); - instrument->volume_envelope.n_nodes = n; - -#ifdef INVESTIGATE_OLD_INSTRUMENTS - fprintf(stderr, "\n"); -#endif - - if (dumbfile_error(f)) - return -1; - - { - IT_ENVELOPE *envelope = &instrument->volume_envelope; - if (envelope->n_nodes <= 0) - envelope->flags &= ~IT_ENVELOPE_ON; - else { - if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; - if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; - } - } - - instrument->filter_cutoff = 127; - instrument->filter_resonance = 0; - - instrument->pan_envelope.flags = 0; - instrument->pitch_envelope.flags = 0; - - return 0; -} - - - -static int it_read_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f) -{ - int n; - - if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) - return -1; - - dumbfile_getnc(instrument->filename, 13, f); - instrument->filename[13] = 0; - - instrument->new_note_action = dumbfile_getc(f); - instrument->dup_check_type = dumbfile_getc(f); - instrument->dup_check_action = dumbfile_getc(f); - instrument->fadeout = dumbfile_igetw(f); - instrument->pp_separation = dumbfile_getc(f); - instrument->pp_centre = dumbfile_getc(f); - instrument->global_volume = dumbfile_getc(f); - instrument->default_pan = dumbfile_getc(f); - instrument->random_volume = dumbfile_getc(f); - instrument->random_pan = dumbfile_getc(f); - - /* Skip Tracker Version and Number of Samples. These are only used in - * separate instrument files. Also skip unused byte. - */ - dumbfile_skip(f, 4); - - dumbfile_getnc(instrument->name, 26, f); - instrument->name[26] = 0; - - instrument->filter_cutoff = dumbfile_getc(f); - instrument->filter_resonance = dumbfile_getc(f); - - /* Skip MIDI Channel, Program and Bank. */ - dumbfile_skip(f, 4); - - for (n = 0; n < 120; n++) { - instrument->map_note[n] = dumbfile_getc(f); - instrument->map_sample[n] = dumbfile_getc(f); - } - - if (dumbfile_error(f)) - return -1; - - if (it_read_envelope(&instrument->volume_envelope, f)) return -1; - if (it_read_envelope(&instrument->pan_envelope, f)) return -1; - if (it_read_envelope(&instrument->pitch_envelope, f)) return -1; - - return 0; -} - - - -static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f) -{ - if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE) - return -1; - - dumbfile_getnc(sample->filename, 13, f); - sample->filename[13] = 0; - - sample->global_volume = dumbfile_getc(f); - sample->flags = dumbfile_getc(f); - sample->default_volume = dumbfile_getc(f); - - dumbfile_getnc(sample->name, 26, f); - sample->name[26] = 0; - - *convert = dumbfile_getc(f); - sample->default_pan = dumbfile_getc(f); - sample->length = dumbfile_igetl(f); - sample->loop_start = dumbfile_igetl(f); - sample->loop_end = dumbfile_igetl(f); - sample->C5_speed = dumbfile_igetl(f); - sample->sus_loop_start = dumbfile_igetl(f); - sample->sus_loop_end = dumbfile_igetl(f); - -#ifdef STEREO_SAMPLES_COUNT_AS_TWO - if (sample->flags & IT_SAMPLE_STEREO) { - sample->length >>= 1; - sample->loop_start >>= 1; - sample->loop_end >>= 1; - sample->C5_speed >>= 1; - sample->sus_loop_start >>= 1; - sample->sus_loop_end >>= 1; - } -#endif - - if (sample->flags & IT_SAMPLE_EXISTS) { - if (sample->length <= 0) - sample->flags &= ~IT_SAMPLE_EXISTS; - else { - if ((unsigned int)sample->loop_end > (unsigned int)sample->length) - sample->flags &= ~IT_SAMPLE_LOOP; - else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) - sample->flags &= ~IT_SAMPLE_LOOP; - - if ((unsigned int)sample->sus_loop_end > (unsigned int)sample->length) - sample->flags &= ~IT_SAMPLE_SUS_LOOP; - else if ((unsigned int)sample->sus_loop_start >= (unsigned int)sample->sus_loop_end) - sample->flags &= ~IT_SAMPLE_SUS_LOOP; - - /* We may be able to truncate the sample to save memory. */ - if (sample->flags & IT_SAMPLE_LOOP) { - if ((sample->flags & IT_SAMPLE_SUS_LOOP) && sample->sus_loop_end >= sample->loop_end) - sample->length = sample->sus_loop_end; - else - sample->length = sample->loop_end; - } - } - } - - *offset = dumbfile_igetl(f); - - sample->vibrato_speed = dumbfile_getc(f); - sample->vibrato_depth = dumbfile_getc(f); - sample->vibrato_rate = dumbfile_getc(f); - sample->vibrato_waveform = dumbfile_getc(f); - - return dumbfile_error(f); -} - - - -static long it_read_sample_data(int cmwt, IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f) -{ - long n; - - long datasize = sample->length; - if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1; - - sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); - if (!sample->data) - return -1; - - if (sample->flags & 8) { - /* If the sample is packed, then we must unpack it. */ - - /** WARNING - unresolved business here... test with ModPlug? */ - - if (sample->flags & IT_SAMPLE_STEREO) - exit(37); // TODO: if this ever happens, maybe sample->length should be doubled below? - -/* -//#ifndef STEREO_SAMPLES_COUNT_AS_TWO - ASSERT(!(sample->flags & IT_SAMPLE_STEREO)); -//#endif -*/ - if (sample->flags & IT_SAMPLE_16BIT) - decompress16(f, sample->data, datasize, cmwt); - else - decompress8(f, sample->data, datasize, cmwt); - } else if (sample->flags & IT_SAMPLE_16BIT) { - if (convert & 2) - for (n = 0; n < datasize; n++) - ((short *)sample->data)[n] = dumbfile_mgetw(f); - else - for (n = 0; n < datasize; n++) - ((short *)sample->data)[n] = dumbfile_igetw(f); - } else - for (n = 0; n < datasize; n++) - ((signed char *)sample->data)[n] = dumbfile_getc(f); - - if (dumbfile_error(f)) - return -1; - - if (!(convert & 1)) { - /* Convert to signed. */ - if (sample->flags & IT_SAMPLE_16BIT) - for (n = 0; n < datasize; n++) - ((short *)sample->data)[n] ^= 0x8000; - else - for (n = 0; n < datasize; n++) - ((signed char *)sample->data)[n] ^= 0x80; - } - - /* NOT SUPPORTED: - * - * convert & 4 - Samples stored as delta values - * convert & 16 - Samples stored as TX-Wave 12-bit values - * convert & 32 - Left/Right/All Stereo prompt - */ - - return 0; -} - - - -#define DETECT_DUPLICATE_CHANNELS -#ifdef DETECT_DUPLICATE_CHANNELS -#include -#endif -static int it_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer) -{ - unsigned char cmask[DUMB_IT_N_CHANNELS]; - unsigned char cnote[DUMB_IT_N_CHANNELS]; - unsigned char cinstrument[DUMB_IT_N_CHANNELS]; - unsigned char cvolpan[DUMB_IT_N_CHANNELS]; - unsigned char ceffect[DUMB_IT_N_CHANNELS]; - unsigned char ceffectvalue[DUMB_IT_N_CHANNELS]; -#ifdef DETECT_DUPLICATE_CHANNELS - IT_ENTRY *dupentry[DUMB_IT_N_CHANNELS]; -#endif - - int n_entries = 0; - int buflen; - int bufpos = 0; - - IT_ENTRY *entry; - - unsigned char channel; - unsigned char mask; - - memset(cmask, 0, sizeof(cmask)); - memset(cnote, 0, sizeof(cnote)); - memset(cinstrument, 0, sizeof(cinstrument)); - memset(cvolpan, 0, sizeof(cvolpan)); - memset(ceffect, 0, sizeof(ceffect)); - memset(ceffectvalue, 0, sizeof(ceffectvalue)); -#ifdef DETECT_DUPLICATE_CHANNELS - { - int i; - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL; - } -#endif - - buflen = dumbfile_igetw(f); - pattern->n_rows = dumbfile_igetw(f); - - /* Skip four unused bytes. */ - dumbfile_skip(f, 4); - - if (dumbfile_error(f)) - return -1; - - /* Read in the pattern data. */ - dumbfile_getnc(buffer, buflen, f); - - if (dumbfile_error(f)) - return -1; - - /* Scan the pattern data, and work out how many entries we need room for. */ - while (bufpos < buflen) { - unsigned char b = buffer[bufpos++]; - - if (b == 0) { - /* End of row */ - n_entries++; - continue; - } - - channel = (b - 1) & 63; - - if (b & 128) - cmask[channel] = mask = buffer[bufpos++]; - else - mask = cmask[channel]; - - { - static const unsigned char used[16] = {0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5}; - n_entries += (mask != 0); - bufpos += used[mask & 15]; - } - } - - pattern->n_entries = n_entries; - - pattern->entry = malloc(n_entries * sizeof(*pattern->entry)); - - if (!pattern->entry) - return -1; - - bufpos = 0; - memset(cmask, 0, sizeof(cmask)); - - entry = pattern->entry; - - while (bufpos < buflen) { - unsigned char b = buffer[bufpos++]; - - if (b == 0) { - /* End of row */ - IT_SET_END_ROW(entry); - entry++; -#ifdef DETECT_DUPLICATE_CHANNELS - { - int i; - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL; - } -#endif - continue; - } - - channel = (b - 1) & 63; - - if (b & 128) - cmask[channel] = mask = buffer[bufpos++]; - else - mask = cmask[channel]; - - if (mask) { - entry->mask = (mask & 15) | (mask >> 4); - entry->channel = channel; - - if (mask & IT_ENTRY_NOTE) - cnote[channel] = entry->note = buffer[bufpos++]; - else if (mask & (IT_ENTRY_NOTE << 4)) - entry->note = cnote[channel]; - - if (mask & IT_ENTRY_INSTRUMENT) - cinstrument[channel] = entry->instrument = buffer[bufpos++]; - else if (mask & (IT_ENTRY_INSTRUMENT << 4)) - entry->instrument = cinstrument[channel]; - - if (mask & IT_ENTRY_VOLPAN) - cvolpan[channel] = entry->volpan = buffer[bufpos++]; - else if (mask & (IT_ENTRY_VOLPAN << 4)) - entry->volpan = cvolpan[channel]; - - if (mask & IT_ENTRY_EFFECT) { - ceffect[channel] = entry->effect = buffer[bufpos++]; - ceffectvalue[channel] = entry->effectvalue = buffer[bufpos++]; - } else { - entry->effect = ceffect[channel]; - entry->effectvalue = ceffectvalue[channel]; - } - -#ifdef DETECT_DUPLICATE_CHANNELS - if (dupentry[channel]) { - FILE *f = fopen("dupentry.txt", "a"); - if (!f) abort(); - fprintf(f, "Two events on channel %d:", channel); - fprintf(f, " Event #1:"); - if (dupentry[channel]->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", dupentry[channel]->note ); else fprintf(f, " ..."); - if (dupentry[channel]->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", dupentry[channel]->instrument); else fprintf(f, " ..."); - if (dupentry[channel]->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", dupentry[channel]->volpan ); else fprintf(f, " ..."); - if (dupentry[channel]->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + dupentry[channel]->effect, dupentry[channel]->effectvalue); else fprintf(f, " ...\n"); - fprintf(f, " Event #2:"); - if (entry->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", entry->note ); else fprintf(f, " ..."); - if (entry->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", entry->instrument); else fprintf(f, " ..."); - if (entry->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", entry->volpan ); else fprintf(f, " ..."); - if (entry->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + entry->effect, entry->effectvalue); else fprintf(f, " ...\n"); - fclose(f); - } - dupentry[channel] = entry; -#endif - - entry++; - } - } - - ASSERT(entry == pattern->entry + n_entries); - - return 0; -} - - - -/* Currently we assume the sample data are stored after the sample headers in - * module files. This assumption may be unjustified; let me know if you have - * trouble. - */ - -#define IT_COMPONENT_SONG_MESSAGE 1 -#define IT_COMPONENT_INSTRUMENT 2 -#define IT_COMPONENT_PATTERN 3 -#define IT_COMPONENT_SAMPLE 4 - -typedef struct IT_COMPONENT -{ - unsigned char type; - unsigned char n; - long offset; - short sampfirst; /* component[sampfirst] = first sample data after this */ - short sampnext; /* sampnext is used to create linked lists of sample data */ -} -IT_COMPONENT; - - - -static int it_component_compare(const void *e1, const void *e2) -{ - return ((const IT_COMPONENT *)e1)->offset - - ((const IT_COMPONENT *)e2)->offset; -} - - - -static sigdata_t *it_load_sigdata(DUMBFILE *f) -{ - DUMB_IT_SIGDATA *sigdata; - - int cwt, cmwt; - int special; - int message_length, message_offset; - - IT_COMPONENT *component; - int n_components = 0; - - unsigned char sample_convert[256]; - - int n; - - unsigned char *buffer; - - if (dumbfile_mgetl(f) != IT_SIGNATURE) - return NULL; - - sigdata = malloc(sizeof(*sigdata)); - - if (!sigdata) - return NULL; - - sigdata->song_message = NULL; - sigdata->order = NULL; - sigdata->instrument = NULL; - sigdata->sample = NULL; - sigdata->pattern = NULL; - sigdata->midi = NULL; - sigdata->checkpoint = NULL; - - dumbfile_getnc(sigdata->name, 26, f); - sigdata->name[26] = 0; - - /* Skip pattern row highlight info. */ - dumbfile_skip(f, 2); - - sigdata->n_orders = dumbfile_igetw(f); - sigdata->n_instruments = dumbfile_igetw(f); - sigdata->n_samples = dumbfile_igetw(f); - sigdata->n_patterns = dumbfile_igetw(f); - - cwt = dumbfile_igetw(f); - cmwt = dumbfile_igetw(f); - - sigdata->flags = dumbfile_igetw(f); - special = dumbfile_igetw(f); - - sigdata->global_volume = dumbfile_getc(f); - sigdata->mixing_volume = dumbfile_getc(f); - sigdata->speed = dumbfile_getc(f); - if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? - sigdata->tempo = dumbfile_getc(f); - sigdata->pan_separation = dumbfile_getc(f); /** WARNING: use this */ - - /* Skip Pitch Wheel Depth */ - dumbfile_skip(f, 1); - - message_length = dumbfile_igetw(f); - message_offset = dumbfile_igetl(f); - - /* Skip Reserved. */ - dumbfile_skip(f, 4); - - dumbfile_getnc(sigdata->channel_pan, DUMB_IT_N_CHANNELS, f); - dumbfile_getnc(sigdata->channel_volume, DUMB_IT_N_CHANNELS, f); - - if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - sigdata->order = malloc(sigdata->n_orders); - if (!sigdata->order) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (sigdata->n_instruments) { - sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); - if (!sigdata->instrument) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - } - - if (sigdata->n_samples) { - sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); - if (!sigdata->sample) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (n = 0; n < sigdata->n_samples; n++) - sigdata->sample[n].data = NULL; - } - - if (sigdata->n_patterns) { - sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); - if (!sigdata->pattern) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (n = 0; n < sigdata->n_patterns; n++) - sigdata->pattern[n].entry = NULL; - } - - dumbfile_getnc(sigdata->order, sigdata->n_orders, f); - sigdata->restart_position = 0; - - component = malloc(769 * sizeof(*component)); - if (!component) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (special & 1) { - component[n_components].type = IT_COMPONENT_SONG_MESSAGE; - component[n_components].offset = message_offset; - component[n_components].sampfirst = -1; - n_components++; - } - - for (n = 0; n < sigdata->n_instruments; n++) { - component[n_components].type = IT_COMPONENT_INSTRUMENT; - component[n_components].n = n; - component[n_components].offset = dumbfile_igetl(f); - component[n_components].sampfirst = -1; - n_components++; - } - - for (n = 0; n < sigdata->n_samples; n++) { - component[n_components].type = IT_COMPONENT_SAMPLE; - component[n_components].n = n; - component[n_components].offset = dumbfile_igetl(f); - component[n_components].sampfirst = -1; - n_components++; - } - - for (n = 0; n < sigdata->n_patterns; n++) { - long offset = dumbfile_igetl(f); - if (offset) { - component[n_components].type = IT_COMPONENT_PATTERN; - component[n_components].n = n; - component[n_components].offset = offset; - component[n_components].sampfirst = -1; - n_components++; - } else { - /* Empty 64-row pattern */ - sigdata->pattern[n].n_rows = 64; - sigdata->pattern[n].n_entries = 0; - } - } - - if (dumbfile_error(f)) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (!(sigdata->flags & 128) != !(special & 8)) { - fprintf(stderr, "Flags Bit 7 (\"Request embedded MIDI configuration\"): %s\n", sigdata->flags & 128 ? "=SET=" : "clear"); - fprintf(stderr, "Special Bit 3 (\"MIDI configuration embedded\") : %s\n", special & 8 ? "=SET=" : "clear"); - fprintf(stderr, "entheh would like to investigate this IT file.\n"); - fprintf(stderr, "Please contact him! entheh@users.sf.net\n"); - } - - if (special & 8) { - /* MIDI configuration is embedded. */ - unsigned char mididata[32]; - int i; - sigdata->midi = malloc(sizeof(*sigdata->midi)); - if (!sigdata->midi) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - // Should we be happy with this outcome in some situations? - } - // What are we skipping? - i = dumbfile_igetw(f); - if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - /* Read embedded MIDI configuration */ - // What are the first 9 commands for? - if (dumbfile_skip(f, 32*9)) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (i = 0; i < 16; i++) { - unsigned char len = 0; - int j, leftdigit = -1; - if (dumbfile_getnc(mididata, 32, f) < 32) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - sigdata->midi->SFmacroz[i] = 0; - for (j = 0; j < 32; j++) { - if (leftdigit >= 0) { - if (mididata[j] == 0) { - sigdata->midi->SFmacro[i][len++] = leftdigit; - break; - } else if (mididata[j] == ' ') - sigdata->midi->SFmacro[i][len++] = leftdigit; - else if (mididata[j] >= '0' && mididata[j] <= '9') - sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0'); - else if (mididata[j] >= 'A' && mididata[j] <= 'F') - sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA); - leftdigit = -1; - } else if (mididata[j] == 0) - break; - else if (mididata[j] == 'z') - sigdata->midi->SFmacroz[i] |= 1 << len++; - else if (mididata[j] >= '0' && mididata[j] <= '9') - leftdigit = mididata[j] - '0'; - else if (mididata[j] >= 'A' && mididata[j] <= 'F') - leftdigit = mididata[j] - 'A' + 0xA; - } - sigdata->midi->SFmacrolen[i] = len; - } - for (i = 0; i < 128; i++) { - unsigned char len = 0; - int j, leftdigit = -1; - dumbfile_getnc(mididata, 32, f); - for (j = 0; j < 32; j++) { - if (leftdigit >= 0) { - if (mididata[j] == 0) { - sigdata->midi->Zmacro[i][len++] = leftdigit; - break; - } else if (mididata[j] == ' ') - sigdata->midi->Zmacro[i][len++] = leftdigit; - else if (mididata[j] >= '0' && mididata[j] <= '9') - sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0'); - else if (mididata[j] >= 'A' && mididata[j] <= 'F') - sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA); - leftdigit = -1; - } else if (mididata[j] == 0) - break; - else if (mididata[j] >= '0' && mididata[j] <= '9') - leftdigit = mididata[j] - '0'; - else if (mididata[j] >= 'A' && mididata[j] <= 'F') - leftdigit = mididata[j] - 'A' + 0xA; - } - sigdata->midi->Zmacrolen[i] = len; - } - } - - sigdata->flags &= IT_REAL_FLAGS; - - qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare); - - buffer = malloc(65536); - if (!buffer) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - for (n = 0; n < n_components; n++) { - long offset; - int m; - - if (it_seek(f, component[n].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - switch (component[n].type) { - - case IT_COMPONENT_SONG_MESSAGE: - sigdata->song_message = malloc(message_length + 1); - if (sigdata->song_message) { - if (dumbfile_getnc(sigdata->song_message, message_length, f) < message_length) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - sigdata->song_message[message_length] = 0; - } - break; - - case IT_COMPONENT_INSTRUMENT: - if (cmwt < 0x200) - m = it_read_old_instrument(&sigdata->instrument[component[n].n], f); - else - m = it_read_instrument(&sigdata->instrument[component[n].n], f); - - if (m) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - break; - - case IT_COMPONENT_PATTERN: - if (it_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - break; - - case IT_COMPONENT_SAMPLE: - if (it_read_sample_header(&sigdata->sample[component[n].n], &sample_convert[component[n].n], &offset, f)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) { - short *sample; - - for (m = n + 1; m < n_components; m++) - if (component[m].offset > offset) - break; - m--; - - sample = &component[m].sampfirst; - - while (*sample >= 0 && component[*sample].offset <= offset) - sample = &component[*sample].sampnext; - - component[n].sampnext = *sample; - *sample = n; - - component[n].offset = offset; - } - } - - m = component[n].sampfirst; - - while (m >= 0) { - if (it_seek(f, component[m].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (it_read_sample_data(cmwt, &sigdata->sample[component[m].n], sample_convert[component[m].n], f)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - m = component[m].sampnext; - } - } - - free(buffer); - free(component); - - _dumb_it_fix_invalid_orders(sigdata); - - return sigdata; -} - - - -DUH *dumb_read_it_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_load_sigdata(f); - printf("sigdata: %i\n", sigdata); - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itread.c - Code to read an Impulse Tracker / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * Based on the loader from an IT player by Bob. \_ / > / + * Adapted for DUMB by entheh. | \ / / + * | ' / + * \__/ + */ + +#include +#include //might not be necessary later; required for memset + +#include "dumb.h" +#include "internal/it.h" + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + + +#define INVESTIGATE_OLD_INSTRUMENTS + + + +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned long dword; + +typedef struct readblock_crap readblock_crap; + +struct readblock_crap { + unsigned char *sourcebuf; + unsigned char *sourcepos; + unsigned char *sourceend; + int rembits; +}; + + +static int readblock(DUMBFILE *f, readblock_crap * crap) +{ + long size; + int c; + + size = dumbfile_igetw(f); + if (size < 0) + return size; + + crap->sourcebuf = malloc(size); + if (!crap->sourcebuf) + return -1; + + c = dumbfile_getnc((char *)crap->sourcebuf, size, f); + if (c < size) { + free(crap->sourcebuf); + crap->sourcebuf = NULL; + return -1; + } + + crap->sourcepos = crap->sourcebuf; + crap->sourceend = crap->sourcebuf + size; + crap->rembits = 8; + return 0; +} + + + +static void freeblock(readblock_crap * crap) +{ + free(crap->sourcebuf); + crap->sourcebuf = NULL; +} + + + +static int readbits(int bitwidth, readblock_crap * crap) +{ + int val = 0; + int b = 0; + + if (crap->sourcepos >= crap->sourceend) return val; + + while (bitwidth > crap->rembits) { + val |= *crap->sourcepos++ << b; + if (crap->sourcepos >= crap->sourceend) return val; + b += crap->rembits; + bitwidth -= crap->rembits; + crap->rembits = 8; + } + + val |= (*crap->sourcepos & ((1 << bitwidth) - 1)) << b; + *crap->sourcepos >>= bitwidth; + crap->rembits -= bitwidth; + + return val; +} + + + +/** WARNING - do we even need to pass `right`? */ +/** WARNING - why bother memsetting at all? The whole array is written... */ +// if we do memset, dumb_silence() would be neater... +static int decompress8(DUMBFILE *f, signed char *data, int len, int it215, int stereo) +{ + int blocklen, blockpos; + byte bitwidth; + word val; + char d1, d2; + readblock_crap crap; + + memset(&crap, 0, sizeof(crap)); + + for (blocklen = 0, blockpos = 0; blocklen < len; blocklen++, blockpos += 1 + stereo) + data[ blockpos ] = 0; + + while (len > 0) { + //Read a block of compressed data: + if (readblock(f, &crap)) + return -1; + //Set up a few variables + blocklen = (len < 0x8000) ? len : 0x8000; //Max block length is 0x8000 bytes + blockpos = 0; + bitwidth = 9; + d1 = d2 = 0; + //Start the decompression: + while (blockpos < blocklen) { + //Read a value: + val = (word)readbits(bitwidth, &crap); + //Check for bit width change: + + if (bitwidth < 7) { //Method 1: + if (val == (1 << (bitwidth - 1))) { + val = (word)readbits(3, &crap) + 1; + bitwidth = (val < bitwidth) ? val : val + 1; + continue; + } + } + else if (bitwidth < 9) { //Method 2 + byte border = (0xFF >> (9 - bitwidth)) - 4; + + if (val > border && val <= (border + 8)) { + val -= border; + bitwidth = (val < bitwidth) ? val : val + 1; + continue; + } + } + else if (bitwidth == 9) { //Method 3 + if (val & 0x100) { + bitwidth = (val + 1) & 0xFF; + continue; + } + } + else { //Illegal width, abort ? + freeblock(&crap); + return -1; + } + + //Expand the value to signed byte: + { + char v; //The sample value: + if (bitwidth < 8) { + byte shift = 8 - bitwidth; + v = (val << shift); + v >>= shift; + } + else + v = (char)val; + + //And integrate the sample value + //(It always has to end with integration doesn't it ? ;-) + d1 += v; + d2 += d1; + } + + //Store ! + /* Version 2.15 was an unofficial version with hacked compression + * code. Yay, better compression :D + */ + *data++ = it215 ? d2 : d1; + data += stereo; + len--; + blockpos++; + } + freeblock(&crap); + } + return 0; +} + + + +static int decompress16(DUMBFILE *f, short *data, int len, int it215, int stereo) +{ + int blocklen, blockpos; + byte bitwidth; + long val; + short d1, d2; + readblock_crap crap; + + memset(&crap, 0, sizeof(crap)); + + for ( blocklen = 0, blockpos = 0; blocklen < len; blocklen++, blockpos += 1 + stereo ) + data[ blockpos ] = 0; + + while (len > 0) { + //Read a block of compressed data: + if (readblock(f, &crap)) + return -1; + //Set up a few variables + blocklen = (len < 0x4000) ? len : 0x4000; // Max block length is 0x4000 bytes + blockpos = 0; + bitwidth = 17; + d1 = d2 = 0; + //Start the decompression: + while (blockpos < blocklen) { + val = readbits(bitwidth, &crap); + //Check for bit width change: + + if (bitwidth < 7) { //Method 1: + if (val == (1 << (bitwidth - 1))) { + val = readbits(4, &crap) + 1; + bitwidth = (val < bitwidth) ? val : val + 1; + continue; + } + } + else if (bitwidth < 17) { //Method 2 + word border = (0xFFFF >> (17 - bitwidth)) - 8; + + if (val > border && val <= (border + 16)) { + val -= border; + bitwidth = val < bitwidth ? val : val + 1; + continue; + } + } + else if (bitwidth == 17) { //Method 3 + if (val & 0x10000) { + bitwidth = (val + 1) & 0xFF; + continue; + } + } + else { //Illegal width, abort ? + freeblock(&crap); + return -1; + } + + //Expand the value to signed byte: + { + short v; //The sample value: + if (bitwidth < 16) { + byte shift = 16 - bitwidth; + v = (short)(val << shift); + v >>= shift; + } + else + v = (short)val; + + //And integrate the sample value + //(It always has to end with integration doesn't it ? ;-) + d1 += v; + d2 += d1; + } + + //Store ! + /* Version 2.15 was an unofficial version with hacked compression + * code. Yay, better compression :D + */ + *data++ = it215 ? d2 : d1; + data += stereo; + len--; + blockpos++; + } + freeblock(&crap); + } + return 0; +} + + + +static int it_read_envelope(IT_ENVELOPE *envelope, DUMBFILE *f) +{ + int n; + + envelope->flags = dumbfile_getc(f); + envelope->n_nodes = dumbfile_getc(f); + envelope->loop_start = dumbfile_getc(f); + envelope->loop_end = dumbfile_getc(f); + envelope->sus_loop_start = dumbfile_getc(f); + envelope->sus_loop_end = dumbfile_getc(f); + if (envelope->n_nodes > 25) + envelope->n_nodes = 25; + for (n = 0; n < envelope->n_nodes; n++) { + envelope->node_y[n] = dumbfile_getc(f); + envelope->node_t[n] = dumbfile_igetw(f); + } + dumbfile_skip(f, 75 - envelope->n_nodes * 3 + 1); + + if (envelope->n_nodes <= 0) + envelope->flags &= ~IT_ENVELOPE_ON; + else { + if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; + if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; + } + + return dumbfile_error(f); +} + + + +static int it_read_old_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f) +{ + int n; + + /*if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) + return -1;*/ + // XXX + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->filename, 13, f); + instrument->filename[13] = 0; + + instrument->volume_envelope.flags = dumbfile_getc(f); + instrument->volume_envelope.loop_start = dumbfile_getc(f); + instrument->volume_envelope.loop_end = dumbfile_getc(f); + instrument->volume_envelope.sus_loop_start = dumbfile_getc(f); + instrument->volume_envelope.sus_loop_end = dumbfile_getc(f); + + /* Skip two unused bytes. */ + dumbfile_skip(f, 2); + + /* In the old instrument format, fadeout ranges from 0 to 64, and is + * subtracted at intervals from a value starting at 512. In the new + * format, all these values are doubled. Therefore we double when loading + * from the old instrument format - that way we don't have to think about + * it later. + */ + instrument->fadeout = dumbfile_igetw(f) << 1; + instrument->new_note_action = dumbfile_getc(f); + instrument->dup_check_type = dumbfile_getc(f); + instrument->dup_check_action = DCA_NOTE_CUT; // This might be wrong! + /** WARNING - what is the duplicate check action for old-style instruments? */ + + /* Skip Tracker Version and Number of Samples. These are only used in + * separate instrument files. Also skip unused byte. + */ + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->name, 26, f); + instrument->name[26] = 0; + + /* Skip unused bytes following the Instrument Name. */ + dumbfile_skip(f, 6); + + instrument->pp_separation = 0; + instrument->pp_centre = 60; + instrument->global_volume = 128; + /** WARNING - should global_volume be 64 or something? */ + instrument->default_pan = 32; + /** WARNING - should default_pan be 128, meaning don`t use? */ + instrument->random_volume = 0; + instrument->random_pan = 0; + + for (n = 0; n < 120; n++) { + instrument->map_note[n] = dumbfile_getc(f); + instrument->map_sample[n] = dumbfile_getc(f); + } + + /* Skip "Volume envelope (200 bytes)". */ + // - need to know better what this is for though. + dumbfile_skip(f, 200); + +#ifdef INVESTIGATE_OLD_INSTRUMENTS + fprintf(stderr, "Inst %02d Env:", n); +#endif + + for (n = 0; n < 25; n++) + { + instrument->volume_envelope.node_t[n] = dumbfile_getc(f); + instrument->volume_envelope.node_y[n] = dumbfile_getc(f); + +#ifdef INVESTIGATE_OLD_INSTRUMENTS + fprintf(stderr, " %d,%d", + instrument->volume_envelope.node_t[n], + instrument->volume_envelope.node_y[n]); +#endif + + // This loop is unfinished, as we can probably escape from it before + // the end if we want to. Hence the otherwise useless dumbfile_skip() + // call below. + } + dumbfile_skip(f, 50 - (n << 1)); + instrument->volume_envelope.n_nodes = n; + +#ifdef INVESTIGATE_OLD_INSTRUMENTS + fprintf(stderr, "\n"); +#endif + + if (dumbfile_error(f)) + return -1; + + { + IT_ENVELOPE *envelope = &instrument->volume_envelope; + if (envelope->n_nodes <= 0) + envelope->flags &= ~IT_ENVELOPE_ON; + else { + if (envelope->loop_end >= envelope->n_nodes || envelope->loop_start > envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; + if (envelope->sus_loop_end >= envelope->n_nodes || envelope->sus_loop_start > envelope->sus_loop_end) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; + } + } + + instrument->filter_cutoff = 127; + instrument->filter_resonance = 0; + + instrument->pan_envelope.flags = 0; + instrument->pitch_envelope.flags = 0; + + return 0; +} + + + +static int it_read_instrument(IT_INSTRUMENT *instrument, DUMBFILE *f, int maxlen) +{ + int n, len; + + /*if (dumbfile_mgetl(f) != IT_INSTRUMENT_SIGNATURE) + return -1;*/ + // XXX + + if (maxlen) len = dumbfile_pos(f); + + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->filename, 13, f); + instrument->filename[13] = 0; + + instrument->new_note_action = dumbfile_getc(f); + instrument->dup_check_type = dumbfile_getc(f); + instrument->dup_check_action = dumbfile_getc(f); + instrument->fadeout = dumbfile_igetw(f); + instrument->pp_separation = dumbfile_getc(f); + instrument->pp_centre = dumbfile_getc(f); + instrument->global_volume = dumbfile_getc(f); + instrument->default_pan = dumbfile_getc(f); + instrument->random_volume = dumbfile_getc(f); + instrument->random_pan = dumbfile_getc(f); + + /* Skip Tracker Version and Number of Samples. These are only used in + * separate instrument files. Also skip unused byte. + */ + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)instrument->name, 26, f); + instrument->name[26] = 0; + + instrument->filter_cutoff = dumbfile_getc(f); + instrument->filter_resonance = dumbfile_getc(f); + + /* Skip MIDI Channel, Program and Bank. */ + //dumbfile_skip(f, 4); + /*instrument->output = dumbfile_getc(f); + if ( instrument->output > 16 ) { + instrument->output -= 128; + } else { + instrument->output = 0; + } + dumbfile_skip(f, 3);*/ + dumbfile_skip(f, 4); + + for (n = 0; n < 120; n++) { + instrument->map_note[n] = dumbfile_getc(f); + instrument->map_sample[n] = dumbfile_getc(f); + } + + if (dumbfile_error(f)) + return -1; + + if (it_read_envelope(&instrument->volume_envelope, f)) return -1; + if (it_read_envelope(&instrument->pan_envelope, f)) return -1; + if (it_read_envelope(&instrument->pitch_envelope, f)) return -1; + + if (maxlen) { + len = dumbfile_pos(f) - len; + if ( maxlen - len < 124 ) return 0; + } + + if ( dumbfile_mgetl(f) == IT_MPTX_SIGNATURE ) { + for ( n = 0; n < 120; n++ ) { + instrument->map_sample[ n ] += dumbfile_getc( f ) << 8; + } + + if (dumbfile_error(f)) + return -1; + } + + /*if ( dumbfile_mgetl(f) == IT_INSM_SIGNATURE ) { + long end = dumbfile_igetl(f); + end += dumbfile_pos(f); + while ( dumbfile_pos(f) < end ) { + int chunkid = dumbfile_igetl(f); + switch ( chunkid ) { + case DUMB_ID('P','L','U','G'): + instrument->output = dumbfile_getc(f); + break; + default: + chunkid = chunkid / 0x100 + dumbfile_getc(f) * 0x1000000; + break; + } + } + + if (dumbfile_error(f)) + return -1; + }*/ + + return 0; +} + + + +static int it_read_sample_header(IT_SAMPLE *sample, unsigned char *convert, long *offset, DUMBFILE *f) +{ + /* XXX + if (dumbfile_mgetl(f) != IT_SAMPLE_SIGNATURE) + return -1;*/ + int hax = 0; + long s = dumbfile_mgetl(f); + if (s != IT_SAMPLE_SIGNATURE) { + if ( s == ( IT_SAMPLE_SIGNATURE >> 16 ) ) { + s <<= 16; + s |= dumbfile_mgetw(f); + if ( s != IT_SAMPLE_SIGNATURE ) + return -1; + hax = 1; + } + } + + dumbfile_getnc((char *)sample->filename, 13, f); + sample->filename[13] = 0; + + sample->global_volume = dumbfile_getc(f); + sample->flags = dumbfile_getc(f); + sample->default_volume = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->name, 26, f); + sample->name[26] = 0; + + *convert = dumbfile_getc(f); + sample->default_pan = dumbfile_getc(f); + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + sample->C5_speed = dumbfile_igetl(f); + sample->sus_loop_start = dumbfile_igetl(f); + sample->sus_loop_end = dumbfile_igetl(f); + +#ifdef STEREO_SAMPLES_COUNT_AS_TWO + if (sample->flags & IT_SAMPLE_STEREO) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + sample->C5_speed >>= 1; + sample->sus_loop_start >>= 1; + sample->sus_loop_end >>= 1; + } +#endif + + if (sample->flags & IT_SAMPLE_EXISTS) { + if (sample->length <= 0) + sample->flags &= ~IT_SAMPLE_EXISTS; + else { + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_LOOP; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + + if ((unsigned int)sample->sus_loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_SUS_LOOP; + else if ((unsigned int)sample->sus_loop_start >= (unsigned int)sample->sus_loop_end) + sample->flags &= ~IT_SAMPLE_SUS_LOOP; + + /* We may be able to truncate the sample to save memory. */ + if (sample->flags & IT_SAMPLE_LOOP && + *convert != 0xFF) { /* not truncating compressed samples, for now... */ + if ((sample->flags & IT_SAMPLE_SUS_LOOP) && sample->sus_loop_end >= sample->loop_end) + sample->length = sample->sus_loop_end; + else + sample->length = sample->loop_end; + } + } + } + + *offset = dumbfile_igetl(f); + + sample->vibrato_speed = dumbfile_getc(f); + sample->vibrato_depth = dumbfile_getc(f); + if ( ! hax ) { + sample->vibrato_rate = dumbfile_getc(f); + sample->vibrato_waveform = dumbfile_getc(f); + } else { + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; + } + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + +long _dumb_it_read_sample_data_adpcm4(IT_SAMPLE *sample, DUMBFILE *f) +{ + long n, len, delta; + signed char * ptr, * end; + signed char compression_table[16]; + if (dumbfile_getnc((char *)compression_table, 16, f) != 16) + return -1; + ptr = (signed char *) sample->data; + delta = 0; + + end = ptr + sample->length; + len = (sample->length + 1) / 2; + for (n = 0; n < len; n++) { + int b = dumbfile_getc(f); + if (b < 0) return -1; + delta += compression_table[b & 0x0F]; + *ptr++ = delta; + if (ptr >= end) break; + delta += compression_table[b >> 4]; + *ptr++ = delta; + } + + return 0; +} + + +static long it_read_sample_data(IT_SAMPLE *sample, unsigned char convert, DUMBFILE *f) +{ + long n; + + long datasize = sample->length; + if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1; + + sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + if (!(sample->flags & IT_SAMPLE_16BIT) && (convert == 0xFF)) { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + } else if (sample->flags & 8) { + /* If the sample is packed, then we must unpack it. */ + + /* Behavior as defined by greasemonkey's munch.py and observed by XMPlay and OpenMPT */ + + if (sample->flags & IT_SAMPLE_STEREO) { + if (sample->flags & IT_SAMPLE_16BIT) { + decompress16(f, (short *) sample->data, datasize >> 1, convert & 4, 1); + decompress16(f, (short *) sample->data + 1, datasize >> 1, convert & 4, 1); + } else { + decompress8(f, (signed char *) sample->data, datasize >> 1, convert & 4, 1); + decompress8(f, (signed char *) sample->data + 1, datasize >> 1, convert & 4, 1); + } + } else { + if (sample->flags & IT_SAMPLE_16BIT) + decompress16(f, (short *) sample->data, datasize, convert & 4, 0); + else + decompress8(f, (signed char *) sample->data, datasize, convert & 4, 0); + } + } else if (sample->flags & IT_SAMPLE_16BIT) { + if (sample->flags & IT_SAMPLE_STEREO) { + if (convert & 2) { + for (n = 0; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_mgetw(f); + for (n = 1; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_mgetw(f); + } else { + for (n = 0; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + for (n = 1; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + } + } else { + if (convert & 2) + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] = dumbfile_mgetw(f); + else + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] = dumbfile_igetw(f); + } + } else { + if (sample->flags & IT_SAMPLE_STEREO) { + for (n = 0; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + for (n = 1; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + } else + for (n = 0; n < datasize; n++) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + } + + if (dumbfile_error(f)) + return -1; + + if (!(convert & 1)) { + /* Convert to signed. */ + if (sample->flags & IT_SAMPLE_16BIT) + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] ^= 0x8000; + else + for (n = 0; n < datasize; n++) + ((signed char *)sample->data)[n] ^= 0x80; + } + + /* NOT SUPPORTED: + * + * convert & 4 - Samples stored as delta values + * convert & 16 - Samples stored as TX-Wave 12-bit values + * convert & 32 - Left/Right/All Stereo prompt + */ + + return 0; +} + + + +//#define DETECT_DUPLICATE_CHANNELS +#ifdef DETECT_DUPLICATE_CHANNELS +#include +#endif +static int it_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer) +{ + unsigned char cmask[DUMB_IT_N_CHANNELS]; + unsigned char cnote[DUMB_IT_N_CHANNELS]; + unsigned char cinstrument[DUMB_IT_N_CHANNELS]; + unsigned char cvolpan[DUMB_IT_N_CHANNELS]; + unsigned char ceffect[DUMB_IT_N_CHANNELS]; + unsigned char ceffectvalue[DUMB_IT_N_CHANNELS]; +#ifdef DETECT_DUPLICATE_CHANNELS + IT_ENTRY *dupentry[DUMB_IT_N_CHANNELS]; +#endif + + int n_entries = 0; + int buflen; + int bufpos = 0; + + IT_ENTRY *entry; + + unsigned char channel; + unsigned char mask; + + memset(cmask, 0, sizeof(cmask)); + memset(cnote, 0, sizeof(cnote)); + memset(cinstrument, 0, sizeof(cinstrument)); + memset(cvolpan, 0, sizeof(cvolpan)); + memset(ceffect, 0, sizeof(ceffect)); + memset(ceffectvalue, 0, sizeof(ceffectvalue)); +#ifdef DETECT_DUPLICATE_CHANNELS + { + int i; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL; + } +#endif + + buflen = dumbfile_igetw(f); + pattern->n_rows = dumbfile_igetw(f); + + /* Skip four unused bytes. */ + dumbfile_skip(f, 4); + + if (dumbfile_error(f)) + return -1; + + /* Read in the pattern data. */ + dumbfile_getnc((char *)buffer, buflen, f); + + if (dumbfile_error(f)) + return -1; + + /* Scan the pattern data, and work out how many entries we need room for. */ + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + + if (b == 0) { + /* End of row */ + n_entries++; + continue; + } + + channel = (b - 1) & 63; + + if (b & 128) + cmask[channel] = mask = buffer[bufpos++]; + else + mask = cmask[channel]; + + { + static const unsigned char used[16] = {0, 1, 1, 2, 1, 2, 2, 3, 2, 3, 3, 4, 3, 4, 4, 5}; + n_entries += (mask != 0); + bufpos += used[mask & 15]; + } + } + + pattern->n_entries = n_entries; + + pattern->entry = malloc(n_entries * sizeof(*pattern->entry)); + + if (!pattern->entry) + return -1; + + bufpos = 0; + memset(cmask, 0, sizeof(cmask)); + + entry = pattern->entry; + + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + + if (b == 0) { + /* End of row */ + IT_SET_END_ROW(entry); + entry++; +#ifdef DETECT_DUPLICATE_CHANNELS + { + int i; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) dupentry[i] = NULL; + } +#endif + continue; + } + + channel = (b - 1) & 63; + + if (b & 128) + cmask[channel] = mask = buffer[bufpos++]; + else + mask = cmask[channel]; + + if (mask) { + entry->mask = (mask & 15) | (mask >> 4); + entry->channel = channel; + + if (mask & IT_ENTRY_NOTE) + cnote[channel] = entry->note = buffer[bufpos++]; + else if (mask & (IT_ENTRY_NOTE << 4)) + entry->note = cnote[channel]; + + if (mask & IT_ENTRY_INSTRUMENT) + cinstrument[channel] = entry->instrument = buffer[bufpos++]; + else if (mask & (IT_ENTRY_INSTRUMENT << 4)) + entry->instrument = cinstrument[channel]; + + if (mask & IT_ENTRY_VOLPAN) + cvolpan[channel] = entry->volpan = buffer[bufpos++]; + else if (mask & (IT_ENTRY_VOLPAN << 4)) + entry->volpan = cvolpan[channel]; + + if (mask & IT_ENTRY_EFFECT) { + ceffect[channel] = entry->effect = buffer[bufpos++]; + ceffectvalue[channel] = entry->effectvalue = buffer[bufpos++]; + } else { + entry->effect = ceffect[channel]; + entry->effectvalue = ceffectvalue[channel]; + } + +#ifdef DETECT_DUPLICATE_CHANNELS + if (dupentry[channel]) { + FILE *f = fopen("dupentry.txt", "a"); + if (!f) abort(); + fprintf(f, "Two events on channel %d:", channel); + fprintf(f, " Event #1:"); + if (dupentry[channel]->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", dupentry[channel]->note ); else fprintf(f, " ..."); + if (dupentry[channel]->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", dupentry[channel]->instrument); else fprintf(f, " ..."); + if (dupentry[channel]->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", dupentry[channel]->volpan ); else fprintf(f, " ..."); + if (dupentry[channel]->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + dupentry[channel]->effect, dupentry[channel]->effectvalue); else fprintf(f, " ...\n"); + fprintf(f, " Event #2:"); + if (entry->mask & IT_ENTRY_NOTE ) fprintf(f, " %03d", entry->note ); else fprintf(f, " ..."); + if (entry->mask & IT_ENTRY_INSTRUMENT) fprintf(f, " %03d", entry->instrument); else fprintf(f, " ..."); + if (entry->mask & IT_ENTRY_VOLPAN ) fprintf(f, " %03d", entry->volpan ); else fprintf(f, " ..."); + if (entry->mask & IT_ENTRY_EFFECT) fprintf(f, " %c%02X\n", 'A' - 1 + entry->effect, entry->effectvalue); else fprintf(f, " ...\n"); + fclose(f); + } + dupentry[channel] = entry; +#endif + + entry++; + } + } + + ASSERT(entry == pattern->entry + n_entries); + + return 0; +} + + + +/* Currently we assume the sample data are stored after the sample headers in + * module files. This assumption may be unjustified; let me know if you have + * trouble. + */ + +#define IT_COMPONENT_SONG_MESSAGE 1 +#define IT_COMPONENT_INSTRUMENT 2 +#define IT_COMPONENT_PATTERN 3 +#define IT_COMPONENT_SAMPLE 4 + +typedef struct IT_COMPONENT +{ + unsigned char type; + unsigned short n; + long offset; + short sampfirst; /* component[sampfirst] = first sample data after this */ + short sampnext; /* sampnext is used to create linked lists of sample data */ +} +IT_COMPONENT; + + + +static int it_component_compare(const void *e1, const void *e2) +{ + return ((const IT_COMPONENT *)e1)->offset - + ((const IT_COMPONENT *)e2)->offset; +} + + + +static sigdata_t *it_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + + int cwt, cmwt; + int special; + int message_length, message_offset; + + IT_COMPONENT *component; + int n_components = 0; + + unsigned char sample_convert[4096]; + + int n; + + unsigned char *buffer; + + if (dumbfile_mgetl(f) != IT_SIGNATURE) + { + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + + if (!sigdata) + { + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + dumbfile_getnc((char *)sigdata->name, 26, f); + sigdata->name[26] = 0; + + /* Skip pattern row highlight info. */ + dumbfile_skip(f, 2); + + sigdata->n_orders = dumbfile_igetw(f); + sigdata->n_instruments = dumbfile_igetw(f); + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + + cwt = dumbfile_igetw(f); + cmwt = dumbfile_igetw(f); + + sigdata->flags = dumbfile_igetw(f); + special = dumbfile_igetw(f); + + sigdata->global_volume = dumbfile_getc(f); + sigdata->mixing_volume = dumbfile_getc(f); + sigdata->speed = dumbfile_getc(f); + if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? + sigdata->tempo = dumbfile_getc(f); + sigdata->pan_separation = dumbfile_getc(f); /** WARNING: use this */ + + /* Skip Pitch Wheel Depth */ + dumbfile_skip(f, 1); + + message_length = dumbfile_igetw(f); + message_offset = dumbfile_igetl(f); + + /* Skip Reserved. */ + dumbfile_skip(f, 4); + + dumbfile_getnc((char *)sigdata->channel_pan, DUMB_IT_N_CHANNELS, f); + dumbfile_getnc((char *)sigdata->channel_volume, DUMB_IT_N_CHANNELS, f); + + // XXX sample count + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_instruments > 256 || sigdata->n_samples > 4000 || sigdata->n_patterns > 256) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_instruments) { + sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); + if (!sigdata->instrument) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + sigdata->restart_position = 0; + + component = malloc(769 * sizeof(*component)); + if (!component) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (special & 1) { + component[n_components].type = IT_COMPONENT_SONG_MESSAGE; + component[n_components].offset = message_offset; + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_instruments; n++) { + component[n_components].type = IT_COMPONENT_INSTRUMENT; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetl(f); + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_samples; n++) { + component[n_components].type = IT_COMPONENT_SAMPLE; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetl(f); + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + long offset = dumbfile_igetl(f); + if (offset) { + component[n_components].type = IT_COMPONENT_PATTERN; + component[n_components].n = n; + component[n_components].offset = offset; + component[n_components].sampfirst = -1; + n_components++; + } else { + /* Empty 64-row pattern */ + sigdata->pattern[n].n_rows = 64; + sigdata->pattern[n].n_entries = 0; + } + } + + if (dumbfile_error(f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + /* + if (!(sigdata->flags & 128) != !(special & 8)) { + fprintf(stderr, "Flags Bit 7 (\"Request embedded MIDI configuration\"): %s\n", sigdata->flags & 128 ? "=SET=" : "clear"); + fprintf(stderr, "Special Bit 3 (\"MIDI configuration embedded\") : %s\n", special & 8 ? "=SET=" : "clear"); + fprintf(stderr, "entheh would like to investigate this IT file.\n"); + fprintf(stderr, "Please contact him! entheh@users.sf.net\n"); + } + */ + + if (special & 8) { + /* MIDI configuration is embedded. */ + unsigned char mididata[32]; + int i; + sigdata->midi = malloc(sizeof(*sigdata->midi)); + if (!sigdata->midi) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + // Should we be happy with this outcome in some situations? + } + // What are we skipping? + i = dumbfile_igetw(f); + if (dumbfile_error(f) || dumbfile_skip(f, 8*i)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + /* Read embedded MIDI configuration */ + // What are the first 9 commands for? + if (dumbfile_skip(f, 32*9)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < 16; i++) { + unsigned char len = 0; + int j, leftdigit = -1; + if (dumbfile_getnc((char *)mididata, 32, f) < 32) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->midi->SFmacroz[i] = 0; + for (j = 0; j < 32; j++) { + if (leftdigit >= 0) { + if (mididata[j] == 0) { + sigdata->midi->SFmacro[i][len++] = leftdigit; + break; + } else if (mididata[j] == ' ') + sigdata->midi->SFmacro[i][len++] = leftdigit; + else if (mididata[j] >= '0' && mididata[j] <= '9') + sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0'); + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + sigdata->midi->SFmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA); + leftdigit = -1; + } else if (mididata[j] == 0) + break; + else if (mididata[j] == 'z') + sigdata->midi->SFmacroz[i] |= 1 << len++; + else if (mididata[j] >= '0' && mididata[j] <= '9') + leftdigit = mididata[j] - '0'; + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + leftdigit = mididata[j] - 'A' + 0xA; + } + sigdata->midi->SFmacrolen[i] = len; + } + for (i = 0; i < 128; i++) { + unsigned char len = 0; + int j, leftdigit = -1; + dumbfile_getnc((char *)mididata, 32, f); + for (j = 0; j < 32; j++) { + if (leftdigit >= 0) { + if (mididata[j] == 0) { + sigdata->midi->Zmacro[i][len++] = leftdigit; + break; + } else if (mididata[j] == ' ') + sigdata->midi->Zmacro[i][len++] = leftdigit; + else if (mididata[j] >= '0' && mididata[j] <= '9') + sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - '0'); + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + sigdata->midi->Zmacro[i][len++] = (leftdigit << 4) | (mididata[j] - 'A' + 0xA); + leftdigit = -1; + } else if (mididata[j] == 0) + break; + else if (mididata[j] >= '0' && mididata[j] <= '9') + leftdigit = mididata[j] - '0'; + else if (mididata[j] >= 'A' && mididata[j] <= 'F') + leftdigit = mididata[j] - 'A' + 0xA; + } + sigdata->midi->Zmacrolen[i] = len; + } + } + + sigdata->flags &= IT_REAL_FLAGS; + + qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare); + + buffer = malloc(65536); + if (!buffer) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < n_components; n++) { + long offset; + int m; + + /* XXX */ + if ( component[n].offset == 0 ) { + switch (component[n].type) { + case IT_COMPONENT_INSTRUMENT: + memset( &sigdata->instrument[component[n].n], 0, sizeof(IT_INSTRUMENT) ); + break; + case IT_COMPONENT_SAMPLE: + memset( &sigdata->sample[component[n].n], 0, sizeof(IT_SAMPLE) ); + break; + case IT_COMPONENT_PATTERN: + { + IT_PATTERN * p = &sigdata->pattern[component[n].n]; + p->entry = 0; + p->n_rows = 64; + p->n_entries = 0; + } + break; + } + continue; + } + + if (dumbfile_seek(f, component[n].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + switch (component[n].type) { + + case IT_COMPONENT_SONG_MESSAGE: + if ( n < n_components ) { + message_length = min( message_length, component[n+1].offset - component[n].offset ); + } + sigdata->song_message = malloc(message_length + 1); + if (sigdata->song_message) { + if (dumbfile_getnc((char *)sigdata->song_message, message_length, f) < message_length) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->song_message[message_length] = 0; + } + break; + + case IT_COMPONENT_INSTRUMENT: + if (cmwt < 0x200) + m = it_read_old_instrument(&sigdata->instrument[component[n].n], f); + else + m = it_read_instrument(&sigdata->instrument[component[n].n], f, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0); + + if (m) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case IT_COMPONENT_PATTERN: + if (it_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case IT_COMPONENT_SAMPLE: + if (it_read_sample_header(&sigdata->sample[component[n].n], &sample_convert[component[n].n], &offset, f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) { + short *sample; + + for (m = n + 1; m < n_components; m++) + if (component[m].offset > offset) + break; + m--; + + sample = &component[m].sampfirst; + + while (*sample >= 0 && component[*sample].offset <= offset) + sample = &component[*sample].sampnext; + + component[n].sampnext = *sample; + *sample = n; + + component[n].offset = offset; + } + } + + m = component[n].sampfirst; + + while (m >= 0) { + if (dumbfile_seek(f, component[m].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_read_sample_data(&sigdata->sample[component[m].n], sample_convert[component[m].n], f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + m = component[m].sampnext; + } + } + + for ( n = 0; n < 10; n++ ) + { + if ( dumbfile_getc( f ) == 'X' ) + { + if ( dumbfile_getc( f ) == 'T' ) + { + if ( dumbfile_getc( f ) == 'P' ) + { + if ( dumbfile_getc( f ) == 'M' ) + { + break; + } + } + } + } + } + + if ( !dumbfile_error( f ) && n < 10 ) + { + unsigned int mptx_id = dumbfile_igetl( f ); + while ( !dumbfile_error( f ) && mptx_id != DUMB_ID('M','P','T','S') ) + { + unsigned int size = dumbfile_igetw( f ); + switch (mptx_id) + { + /* TODO: Add instrument extension readers */ + + default: + dumbfile_skip(f, size * sigdata->n_instruments); + break; + } + + mptx_id = dumbfile_igetl( f ); + } + + mptx_id = dumbfile_igetl( f ); + while ( !dumbfile_error(f) && dumbfile_pos(f) < dumbfile_get_size(f) ) + { + unsigned int size = dumbfile_igetw( f ); + switch (mptx_id) + { + /* TODO: Add more song extension readers */ + + case DUMB_ID('D','T','.','.'): + if ( size == 2 ) + sigdata->tempo = dumbfile_igetw( f ); + else if ( size == 4 ) + sigdata->tempo = dumbfile_igetl( f ); + break; + + default: + dumbfile_skip(f, size); + break; + } + mptx_id = dumbfile_igetl( f ); + } + } + + free(buffer); + free(component); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *dumb_read_it_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "IT"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/itread2.c b/Frameworks/Dumb/dumb/src/it/itread2.c index e152737e4..202b8bb96 100644 --- a/Frameworks/Dumb/dumb/src/it/itread2.c +++ b/Frameworks/Dumb/dumb/src/it/itread2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itread2.c - Function to read an Impulse Tracker / / \ \ - * module from an open file and do an | < / \_ - * initial run-through. | \/ /\ / - * \_ / > / - * Split off from itread.c by entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_read_it(DUMBFILE *f) -{ - DUH *duh = dumb_read_it_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itread2.c - Function to read an Impulse Tracker / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * Split off from itread.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_it(DUMBFILE *f) +{ + DUH *duh = dumb_read_it_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/itrender.c b/Frameworks/Dumb/dumb/src/it/itrender.c index b74b84526..1a9e2f905 100644 --- a/Frameworks/Dumb/dumb/src/it/itrender.c +++ b/Frameworks/Dumb/dumb/src/it/itrender.c @@ -1,3739 +1,6308 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itrender.c - Code to render an Impulse Tracker / / \ \ - * module. | < / \_ - * | \/ /\ / - * Written - painstakingly - by entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include - -#include "dumb.h" -#include "internal/it.h" - - - -static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel) -{ - IT_PLAYING *dst; - - if (!src) return NULL; - - dst = malloc(sizeof(*dst)); - if (!dst) return NULL; - - dst->flags = src->flags; - - ASSERT(src->channel); - dst->channel = &dstchannel[src->channel - srcchannel]; - dst->sample = src->sample; - dst->instrument = src->instrument; - dst->env_instrument = src->env_instrument; - - dst->sampnum = src->sampnum; - dst->instnum = src->instnum; - - dst->channel_volume = src->channel_volume; - - dst->volume = src->volume; - dst->pan = src->pan; - - dst->note = src->note; - - dst->filter_cutoff = src->filter_cutoff; - dst->filter_resonance = src->filter_resonance; - - dst->true_filter_cutoff = src->true_filter_cutoff; - dst->true_filter_resonance = src->true_filter_resonance; - - dst->vibrato_speed = src->vibrato_speed; - dst->vibrato_depth = src->vibrato_depth; - dst->vibrato_n = src->vibrato_n; - dst->vibrato_time = src->vibrato_time; - - dst->tremolo_speed = src->tremolo_speed; - dst->tremolo_depth = src->tremolo_depth; - dst->tremolo_time = src->tremolo_time; - - dst->sample_vibrato_time = src->sample_vibrato_time; - dst->sample_vibrato_depth = src->sample_vibrato_depth; - - dst->slide = src->slide; - dst->delta = src->delta; - - dst->volume_envelope = src->volume_envelope; - dst->pan_envelope = src->pan_envelope; - dst->pitch_envelope = src->pitch_envelope; - - dst->fadeoutcount = src->fadeoutcount; - - dst->filter_state[0] = src->filter_state[0]; - dst->filter_state[1] = src->filter_state[1]; - - dst->resampler = src->resampler; - dst->resampler.pickup_data = dst; - dst->time_lost = src->time_lost; - - return dst; -} - - - -static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) -{ - dst->flags = src->flags; - - dst->volume = src->volume; - dst->volslide = src->volslide; - dst->xm_volslide = src->xm_volslide; - dst->panslide = src->panslide; - - dst->pan = src->pan; - dst->truepan = src->truepan; - - dst->channelvolume = src->channelvolume; - dst->channelvolslide = src->channelvolslide; - - dst->instrument = src->instrument; - dst->note = src->note; - - dst->SFmacro = src->SFmacro; - - dst->filter_cutoff = src->filter_cutoff; - dst->filter_resonance = src->filter_resonance; - - dst->key_off_count = src->key_off_count; - dst->note_cut_count = src->note_cut_count; - dst->note_delay_count = src->note_delay_count; - dst->note_delay_entry = src->note_delay_entry; - - dst->arpeggio = src->arpeggio; - dst->retrig = src->retrig; - dst->xm_retrig = src->xm_retrig; - dst->retrig_tick = src->retrig_tick; - - dst->tremor_time = src->tremor_time; - - dst->portamento = src->portamento; - dst->toneporta = src->toneporta; - dst->destnote = src->destnote; - - dst->sample = src->sample; - dst->truenote = src->truenote; - - dst->midi_state = src->midi_state; - - dst->lastvolslide = src->lastvolslide; - dst->lastDKL = src->lastDKL; - dst->lastEF = src->lastEF; - dst->lastG = src->lastG; - dst->lastHspeed = src->lastHspeed; - dst->lastHdepth = src->lastHdepth; - dst->lastRspeed = src->lastRspeed; - dst->lastRdepth = src->lastRdepth; - dst->lastI = src->lastI; - dst->lastJ = src->lastJ; - dst->lastN = src->lastN; - dst->lastO = src->lastO; - dst->high_offset = src->high_offset; - dst->lastP = src->lastP; - dst->lastQ = src->lastQ; - dst->lastS = src->lastS; - dst->pat_loop_row = src->pat_loop_row; - dst->pat_loop_count = src->pat_loop_count; - dst->pat_loop_end_row = src->pat_loop_end_row; - dst->lastW = src->lastW; - - dst->xm_lastE1 = src->xm_lastE1; - dst->xm_lastE2 = src->xm_lastE2; - dst->xm_lastEA = src->xm_lastEA; - dst->xm_lastEB = src->xm_lastEB; - dst->xm_lastX1 = src->xm_lastX1; - dst->xm_lastX2 = src->xm_lastX2; - - dst->playing = dup_playing(src->playing, dst, src); -} - - - -/* Allocate the new callbacks first, then pass them to this function! - * It will free them on failure. - */ -static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_channels, IT_CALLBACKS *callbacks) -{ - DUMB_IT_SIGRENDERER *dst; - int i; - - if (!src) { - if (callbacks) free(callbacks); - return NULL; - } - - dst = malloc(sizeof(*dst)); - if (!dst) { - if (callbacks) free(callbacks); - return NULL; - } - - dst->sigdata = src->sigdata; - - dst->n_channels = n_channels; - - dst->globalvolume = src->globalvolume; - dst->globalvolslide = src->globalvolslide; - - dst->tempo = src->tempo; - dst->temposlide = src->temposlide; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) - dup_channel(&dst->channel[i], &src->channel[i]); - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) - dst->playing[i] = dup_playing(src->playing[i], dst->channel, src->channel); - - dst->tick = src->tick; - dst->speed = src->speed; - dst->rowcount = src->rowcount; - - dst->order = src->order; - dst->row = src->row; - dst->processorder = src->processorder; - dst->processrow = src->processrow; - dst->breakrow = src->breakrow; - dst->pat_loop_row = src->pat_loop_row; - - dst->n_rows = src->n_rows; - - dst->entry_start = src->entry_start; - dst->entry = src->entry; - dst->entry_end = src->entry_end; - - dst->time_left = src->time_left; - dst->sub_time_left = src->sub_time_left; - - dst->click_remover = NULL; - - dst->callbacks = callbacks; - - return dst; -} - - - -static IT_MIDI default_midi = { - /* unsigned char SFmacro[16][16]; */ - { - {0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - }, - /* unsigned char SFmacrolen[16]; */ - {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - /* unsigned short SFmacroz[16]; */ - /* Bitfield; bit 0 set = z in first position */ - { - 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 - }, - /* unsigned char Zmacro[128][16]; */ - { - {0xF0, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xF0, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} - }, - /* unsigned char Zmacrolen[128]; */ - { - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - } -}; - - - -static void it_reset_filter_state(IT_FILTER_STATE *state) -{ - state->currsample = 0; - state->prevsample = 0; -} - - - -#define LOG10 2.30258509299 - -/* IMPORTANT: This function expects one extra sample in 'src' so it can apply - * click removal. It reads size samples, starting from src[0], and writes its - * output starting at dst[pos]. The pos parameter is required for getting - * click removal right. - */ -static void it_filter(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance) -{ - sample_t currsample = state->currsample; - sample_t prevsample = state->prevsample; - - float a, b, c; - - long datasize; - - { - float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24< 2.0f) d = 2.0f; - d = (loss - d) * inv_angle; - e = inv_angle * inv_angle; - a = 1.0f / (1.0f + d + e); - c = -e * a; - b = 1.0f - a - c; -#else - a = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss); - c = -(inv_angle*inv_angle) * a; - b = 1.0f - a - c; -#endif - } - - dst += pos * step; - datasize = size * step; - -#define INT_FILTERS -#ifdef INT_FILTERS -#define MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32)) -#define SCALEB 12 - { - int ai = (int)(a * (1 << (16+SCALEB))); - int bi = (int)(b * (1 << (16+SCALEB))); - int ci = (int)(c * (1 << (16+SCALEB))); - int i; - - if (cr) { - sample_t startstep = MULSCA(src[0], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); - dumb_record_click(cr, pos, startstep); - } - - for (i = 0; i < datasize; i += step) { - { - sample_t newsample = MULSCA(src[i], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); - prevsample = currsample; - currsample = newsample; - } - dst[i] += currsample; - } - - if (cr) { - sample_t endstep = MULSCA(src[datasize], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); - dumb_record_click(cr, pos + size, -endstep); - } - } -#else -#error This version is broken - it does not use step, and state should contain floats for it - if (cr) { - float startstep = src[0]*a + currsample*b + prevsample*c; - dumb_record_click(cr, pos, (sample_t)startstep); - } - - { - int i = size % 3; - while (i > 0) { - { - float newsample = *src++*a + currsample*b + prevsample*c; - prevsample = currsample; - currsample = newsample; - } - *dst++ += (sample_t)currsample; - i--; - } - i = size / 3; - while (i > 0) { - float newsample; - /* Gotta love unrolled loops! */ - *dst++ += (sample_t)(newsample = *src++*a + currsample*b + prevsample*c); - *dst++ += (sample_t)(prevsample = *src++*a + newsample*b + currsample*c); - *dst++ += (sample_t)(currsample = *src++*a + prevsample*b + newsample*c); - i--; - } - } - - if (cr) { - float endstep = src[datasize]*a + currsample*b + prevsample*c; - dumb_record_click(cr, pos + size, -(sample_t)endstep); - } -#endif - - state->currsample = currsample; - state->prevsample = prevsample; -} - -#undef LOG10 - - - -static signed char it_sine[256] = { - 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, - 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, - 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, - 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, - 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, - 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, - 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, - 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, - -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, - -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, - -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, - -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, - -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, - -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, - -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2 -}; - - - -#if 0 -/** WARNING: use these! */ -/** JULIEN: Plus for XM compatibility it could be interesting to rename - * it_sawtooth[] to it_rampdown[], and add an it_rampup[]. - * Also, still for XM compat', twood be good if it was possible to tell the - * the player not to retrig' the waveform on a new instrument. - * Both of these are only for completness though, as I don't think it would - * be very noticeable ;) - */ -/** ENTHEH: IT also has the 'don't retrig' thingy :) */ - -static signed char it_sawtooth[256] = { - 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, - 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, - 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, - 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, - 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, - 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, - 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, - 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, - 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, - -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, - -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, - -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, - -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, - -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, - -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, - -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64 -}; - - - -static signed char it_squarewave[256] = { - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -#endif - - - -static void reset_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer) -{ - int i; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - channel->key_off_count = 0; - channel->note_cut_count = 0; - channel->note_delay_count = 0; - } -} - - - -static void reset_effects(DUMB_IT_SIGRENDERER *sigrenderer) -{ - int i; - - sigrenderer->globalvolslide = 0; - sigrenderer->temposlide = 0; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - channel->volslide = 0; - channel->xm_volslide = 0; - channel->panslide = 0; - channel->channelvolslide = 0; - channel->arpeggio = 0; - channel->retrig = 0; - if (channel->xm_retrig) { - channel->xm_retrig = 0; - channel->retrig_tick = 0; - } - channel->tremor_time &= 127; - channel->portamento = 0; - channel->toneporta = 0; - if (channel->playing) { - channel->playing->vibrato_n = 0; - channel->playing->tremolo_speed = 0; - channel->playing->tremolo_depth = 0; - } - } -} - - - -static void update_tremor(IT_CHANNEL *channel) -{ - if ((channel->tremor_time & 128) && channel->playing) { - if (channel->tremor_time == 128) - channel->tremor_time = (channel->lastI >> 4) | 192; - else if (channel->tremor_time == 192) - channel->tremor_time = (channel->lastI & 15) | 128; - else - channel->tremor_time--; - } -} - - - -static void it_pickup_loop(DUMB_RESAMPLER *resampler, void *data) -{ - resampler->pos -= resampler->end - resampler->start; - ((IT_PLAYING *)data)->time_lost += resampler->end - resampler->start; -} - - - -static void it_pickup_pingpong_loop(DUMB_RESAMPLER *resampler, void *data) -{ - if (resampler->dir < 0) { - resampler->pos = (resampler->start << 1) - 1 - resampler->pos; - resampler->subpos ^= 65535; - resampler->dir = 1; - ((IT_PLAYING *)data)->time_lost += (resampler->end - resampler->start) << 1; - } else { - resampler->pos = (resampler->end << 1) - 1 - resampler->pos; - resampler->subpos ^= 65535; - resampler->dir = -1; - } -} - - - -static void it_pickup_stop_at_end(DUMB_RESAMPLER *resampler, void *data) -{ - (void)data; - - if (resampler->dir < 0) { - resampler->pos = (resampler->start << 1) - 1 - resampler->pos; - resampler->subpos ^= 65535; - /* By rights, time_lost would be updated here. However, there is no - * need at this point; it will not be used. - * - * ((IT_PLAYING *)data)->time_lost += (resampler->src_end - resampler->src_start) << 1; - */ - resampler->dir = 1; - } else - resampler->dir = 0; -} - - - -static void it_playing_update_resamplers(IT_PLAYING *playing) -{ - if ((playing->sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { - playing->resampler.start = playing->sample->sus_loop_start; - playing->resampler.end = playing->sample->sus_loop_end; - if (playing->sample->flags & IT_SAMPLE_PINGPONG_SUS_LOOP) - playing->resampler.pickup = &it_pickup_pingpong_loop; - else - playing->resampler.pickup = &it_pickup_loop; - } else if (playing->sample->flags & IT_SAMPLE_LOOP) { - playing->resampler.start = playing->sample->loop_start; - playing->resampler.end = playing->sample->loop_end; - if (playing->sample->flags & IT_SAMPLE_PINGPONG_LOOP) - playing->resampler.pickup = &it_pickup_pingpong_loop; - else - playing->resampler.pickup = &it_pickup_loop; - } else { - if (playing->sample->flags & IT_SAMPLE_SUS_LOOP) - playing->resampler.start = playing->sample->sus_loop_start; - else - playing->resampler.start = 0; - playing->resampler.end = playing->sample->length; - playing->resampler.pickup = &it_pickup_stop_at_end; - } - ASSERT(playing->resampler.pickup_data == playing); -} - - - -/* This should be called whenever the sample or sample position changes. */ -static void it_playing_reset_resamplers(IT_PLAYING *playing, long pos) -{ - int bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; - int channels = playing->sample->flags & IT_SAMPLE_STEREO ? 2 : 1; - dumb_reset_resampler_n(bits, &playing->resampler, playing->sample->data, channels, pos, 0, 0); - playing->resampler.pickup_data = playing; - playing->time_lost = 0; - playing->flags &= ~IT_PLAYING_DEAD; - it_playing_update_resamplers(playing); -} - - - -static void update_retrig(IT_CHANNEL *channel) -{ - if (channel->xm_retrig) { - channel->retrig_tick--; - if (channel->retrig_tick <= 0) { - if (channel->playing) it_playing_reset_resamplers(channel->playing, 0); - channel->retrig_tick = channel->xm_retrig; - } - } else if (channel->retrig & 0x0F) { - channel->retrig_tick--; - if (channel->retrig_tick <= 0) { - if (channel->retrig < 0x10) { - } else if (channel->retrig < 0x20) { - channel->volume--; - if (channel->volume > 64) channel->volume = 0; - } else if (channel->retrig < 0x30) { - channel->volume -= 2; - if (channel->volume > 64) channel->volume = 0; - } else if (channel->retrig < 0x40) { - channel->volume -= 4; - if (channel->volume > 64) channel->volume = 0; - } else if (channel->retrig < 0x50) { - channel->volume -= 8; - if (channel->volume > 64) channel->volume = 0; - } else if (channel->retrig < 0x60) { - channel->volume -= 16; - if (channel->volume > 64) channel->volume = 0; - } else if (channel->retrig < 0x70) { - channel->volume <<= 1; - channel->volume /= 3; - } else if (channel->retrig < 0x80) { - channel->volume >>= 1; - } else if (channel->retrig < 0x90) { - } else if (channel->retrig < 0xA0) { - channel->volume++; - if (channel->volume > 64) channel->volume = 64; - } else if (channel->retrig < 0xB0) { - channel->volume += 2; - if (channel->volume > 64) channel->volume = 64; - } else if (channel->retrig < 0xC0) { - channel->volume += 4; - if (channel->volume > 64) channel->volume = 64; - } else if (channel->retrig < 0xD0) { - channel->volume += 8; - if (channel->volume > 64) channel->volume = 64; - } else if (channel->retrig < 0xE0) { - channel->volume += 16; - if (channel->volume > 64) channel->volume = 64; - } else if (channel->retrig < 0xF0) { - channel->volume *= 3; - channel->volume >>= 1; - if (channel->volume > 64) channel->volume = 64; - } else { - channel->volume <<= 1; - if (channel->volume > 64) channel->volume = 64; - } - if (channel->playing) it_playing_reset_resamplers(channel->playing, 0); - channel->retrig_tick = channel->retrig & 0x0F; - } - } -} - - - -static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer) -{ - int i; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - IT_PLAYING *playing = channel->playing; - - if (playing) { - playing->vibrato_time += playing->vibrato_n * - (playing->vibrato_speed << 2); - playing->tremolo_time += playing->tremolo_speed << 2; - } - } -} - - - -static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) -{ - int i; - - if (sigrenderer->globalvolslide) { - sigrenderer->globalvolume += sigrenderer->globalvolslide; - if (sigrenderer->globalvolume > 128) { - if (sigrenderer->globalvolslide >= 0) - sigrenderer->globalvolume = 128; - else - sigrenderer->globalvolume = 0; - } - } - - if (sigrenderer->temposlide) { - sigrenderer->tempo += sigrenderer->temposlide; - if (sigrenderer->tempo < 32) { - if (sigrenderer->temposlide >= 0) - sigrenderer->tempo = 255; - else - sigrenderer->tempo = 32; - } - } - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - IT_PLAYING *playing = channel->playing; - - if (channel->xm_volslide) { - channel->volume += channel->xm_volslide; - if (channel->volume > 64) { - if (channel->xm_volslide >= 0) - channel->volume = 64; - else - channel->volume = 0; - } - } - - if (channel->volslide) { - channel->volume += channel->volslide; - if (channel->volume > 64) { - if (channel->volslide >= 0) - channel->volume = 64; - else - channel->volume = 0; - } - } - - if (channel->panslide && !IT_IS_SURROUND(channel->pan)) { - if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { - if (channel->panslide == -128) - channel->truepan = 32; - else - channel->truepan = MID(32, channel->truepan + channel->panslide*64, 32+255*64); - } else { - channel->pan += channel->panslide; - if (channel->pan > 64) { - if (channel->panslide >= 0) - channel->pan = 64; - else - channel->pan = 0; - } - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - } - } - - if (channel->channelvolslide) { - channel->channelvolume += channel->channelvolslide; - if (channel->channelvolume > 64) { - if (channel->channelvolslide >= 0) - channel->channelvolume = 64; - else - channel->channelvolume = 0; - } - if (channel->playing) - channel->playing->channel_volume = channel->channelvolume; - } - - update_tremor(channel); - - channel->arpeggio = (channel->arpeggio << 4) | (channel->arpeggio >> 8); - channel->arpeggio &= 0xFFF; - - update_retrig(channel); - - if (playing) { - playing->slide += channel->portamento; - - if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) { - if (channel->toneporta && channel->destnote < 120) { - int currpitch = ((playing->note - 60) << 8) + playing->slide; - int destpitch = (channel->destnote - 60) << 8; - if (currpitch > destpitch) { - currpitch -= channel->toneporta; - if (currpitch < destpitch) { - currpitch = destpitch; - channel->destnote = IT_NOTE_OFF; - } - } else if (currpitch < destpitch) { - currpitch += channel->toneporta; - if (currpitch > destpitch) { - currpitch = destpitch; - channel->destnote = IT_NOTE_OFF; - } - } - playing->slide = currpitch - ((playing->note - 60) << 8); - } - } else { - if (channel->toneporta && channel->destnote < 120) { - float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR); - - float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); - /* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */ - - float deltaslid = deltanote - playing->slide * amiga_multiplier; - - float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote); - if (deltaslid < destdelta) { - playing->slide -= channel->toneporta; - deltaslid = deltanote - playing->slide * amiga_multiplier; - if (deltaslid > destdelta) { - playing->note = channel->destnote; - playing->slide = 0; - channel->destnote = IT_NOTE_OFF; - } - } else { - playing->slide += channel->toneporta; - deltaslid = deltanote - playing->slide * amiga_multiplier; - if (deltaslid < destdelta) { - playing->note = channel->destnote; - playing->slide = 0; - channel->destnote = IT_NOTE_OFF; - } - } - } - } - } - } - - update_smooth_effects(sigrenderer); -} - - - -// This function should be renamed; it doesn't do the 'Update Pattern Variables' operation ittech.txt describes -/* Returns 1 if a pattern loop is happening. */ -static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) -{ - IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - - if (entry->mask & IT_ENTRY_EFFECT) { - switch (entry->effect) { - case IT_JUMP_TO_ORDER: - sigrenderer->breakrow = 0; - sigrenderer->processorder = entry->effectvalue - 1; - sigrenderer->processrow = 0xFFFE; - break; - - case IT_S: - { - unsigned char effectvalue = entry->effectvalue; - if (effectvalue == 0) - effectvalue = channel->lastS; - channel->lastS = effectvalue; - switch (effectvalue >> 4) { - //case IT_S7: - case IT_S_PATTERN_LOOP: - { - unsigned char v = effectvalue & 15; - if (v == 0) - channel->pat_loop_row = sigrenderer->processrow; - else { - if (channel->pat_loop_count == 0) { - channel->pat_loop_count = v; - sigrenderer->breakrow = channel->pat_loop_row; - if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { - /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */ - if (sigrenderer->processrow < 0xFFFE) { - /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */ - if (sigrenderer->processrow < channel->pat_loop_end_row) - sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */ - else - sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */ - channel->pat_loop_end_row = sigrenderer->processrow; - sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */ - } - } else { - /* IT files do this regardless of other flow control effects seen here. */ - sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */ - sigrenderer->processrow = 0xFFFE; - } - return 1; - } else if (--channel->pat_loop_count) { - sigrenderer->breakrow = channel->pat_loop_row; - if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { - /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */ - if (sigrenderer->processrow < 0xFFFE) { - /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */ - if (sigrenderer->processrow < channel->pat_loop_end_row) - sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */ - else - sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */ - channel->pat_loop_end_row = sigrenderer->processrow; - sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */ - } - } else { - /* IT files do this regardless of other flow control effects seen here. */ - sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */ - sigrenderer->processrow = 0xFFFE; - } - return 1; - } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { - channel->pat_loop_end_row = 0; - // TODO - /* Findings: - - If a pattern loop completes successfully, and then the pattern terminates, then the next pattern will start on the row corresponding to the E60. - - If a pattern loop doesn't do any loops, and then the pattern terminates, then the next pattern will start on the first row. - - If a break appears to the left of the pattern loop, it jumps into the relevant position in the next pattern, and that's it. - - If a break appears to the right of the pattern loop, it jumps to the start of the next pattern, and that's it. - - If we jump, then effect a loop using an old E60, and then the pattern ends, the next pattern starts on the row corresponding to the E60. - - Theory: breakrow is not cleared when it's a pattern loop effect! - */ - //if (sigrenderer->processrow < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :( - // sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */ - } else - channel->pat_loop_row = sigrenderer->processrow + 1; - } - } - break; - case IT_S_PATTERN_DELAY: - sigrenderer->rowcount = 1 + (effectvalue & 15); - break; - } - } - } - } - - return 0; -} - - - -/* This function guarantees that channel->sample will always be valid if it - * is nonzero. In other words, to check if it is valid, simply check if it is - * nonzero. - */ -static void instrument_to_sample(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) -{ - if (sigdata->flags & IT_USE_INSTRUMENTS) { - if (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments) { - if (channel->note < 120) { - channel->sample = sigdata->instrument[channel->instrument-1].map_sample[channel->note]; - channel->truenote = sigdata->instrument[channel->instrument-1].map_note[channel->note]; - } else - channel->sample = 0; - } else - channel->sample = 0; - } else { - channel->sample = channel->instrument; - channel->truenote = channel->note; - } - if (!(channel->sample >= 1 && channel->sample <= sigdata->n_samples && (sigdata->sample[channel->sample-1].flags & IT_SAMPLE_EXISTS))) - channel->sample = 0; -} - - - -static void fix_sample_looping(IT_PLAYING *playing) -{ - if ((playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) == - (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) { - if (playing->resampler.dir < 0) { - playing->resampler.pos = (playing->sample->sus_loop_end << 1) - 1 - playing->resampler.pos; - playing->resampler.subpos ^= 65535; - playing->resampler.dir = 1; - } - - playing->resampler.pos += playing->time_lost; - } -} - - - -static void it_compatible_gxx_retrigger(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) -{ - channel->playing->volume_envelope.next_node = 0; - channel->playing->volume_envelope.tick = 0; - channel->playing->pan_envelope.next_node = 0; - channel->playing->pan_envelope.tick = 0; - channel->playing->pitch_envelope.next_node = 0; - channel->playing->pitch_envelope.tick = 0; - channel->playing->fadeoutcount = 1024; - // Should we remove IT_PLAYING_BACKGROUND? Test with sample with sustain loop... - channel->playing->flags &= ~(IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING | IT_PLAYING_DEAD); - it_playing_update_resamplers(channel->playing); - - if (channel->sample) - if (sigdata->flags & IT_USE_INSTRUMENTS) - channel->playing->env_instrument = &sigdata->instrument[channel->instrument-1]; -} - - - -static void it_note_off(IT_PLAYING *playing) -{ - if (playing) { - playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF; - fix_sample_looping(playing); - it_playing_update_resamplers(playing); - if (playing->instrument) - if ((playing->instrument->volume_envelope.flags & (IT_ENVELOPE_ON | IT_ENVELOPE_LOOP_ON)) != IT_ENVELOPE_ON) - playing->flags |= IT_PLAYING_FADING; - } -} - - - -static void xm_note_off(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) -{ - if (channel->playing) { - if (!(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON)) - //if (!(entry->mask & IT_ENTRY_INSTRUMENT)) - // dunno what that was there for ... - channel->volume = 0; - channel->playing->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING; - it_playing_update_resamplers(channel->playing); - } -} - - - -static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - unsigned char nna; - int i; - - if (channel->playing) { -#ifdef INVALID_NOTES_CAUSE_NOTE_CUT - if (channel->note == IT_NOTE_OFF) - nna = NNA_NOTE_OFF; - else if (channel->note >= 120 || !channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD)) - nna = NNA_NOTE_CUT; - else - nna = channel->playing->instrument->new_note_action; -#else - if (channel->note == IT_NOTE_CUT) - nna = NNA_NOTE_CUT; - if (channel->note >= 120) - nna = NNA_NOTE_OFF; - else if (!channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD)) - nna = NNA_NOTE_CUT; - else - nna = channel->playing->instrument->new_note_action; -#endif - - switch (nna) { - case NNA_NOTE_CUT: - free(channel->playing); - channel->playing = NULL; - break; - case NNA_NOTE_OFF: - it_note_off(channel->playing); - break; - case NNA_NOTE_FADE: - channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING; - break; - } - } - - if (channel->sample == 0 || channel->note >= 120) - return; - - channel->destnote = IT_NOTE_OFF; - - if (channel->playing) { - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { - if (!sigrenderer->playing[i]) { - sigrenderer->playing[i] = channel->playing; - channel->playing = NULL; - break; - } - } -/** WARNING - come up with some more heuristics for replacing old notes */ -#if 0 - if (channel->playing) { - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { - if (sigrenderer->playing[i]->flags & IT_PLAYING_BACKGROUND) { - write_seqtime(); - sequence_c(SEQUENCE_STOP_SIGNAL); - sequence_c(i); - channel->VChannel = &module->VChannel[i]; - break; - } - } - } -#endif - } - - if (channel->playing) - free(channel->playing); - - channel->playing = malloc(sizeof(*channel->playing)); - - if (!channel->playing) - return; - - channel->playing->flags = 0; - channel->playing->channel = channel; - channel->playing->sample = &sigdata->sample[channel->sample-1]; - if (sigdata->flags & IT_USE_INSTRUMENTS) - channel->playing->instrument = &sigdata->instrument[channel->instrument-1]; - else - channel->playing->instrument = NULL; - channel->playing->env_instrument = channel->playing->instrument; - channel->playing->sampnum = channel->sample; - channel->playing->instnum = channel->instrument; - channel->playing->channel_volume = channel->channelvolume; - channel->playing->note = channel->truenote; - channel->playing->filter_cutoff = 127; - channel->playing->filter_resonance = 0; - channel->playing->true_filter_cutoff = 127 << 8; - channel->playing->true_filter_resonance = 0; - channel->playing->vibrato_speed = 0; - channel->playing->vibrato_depth = 0; - channel->playing->vibrato_n = 0; - channel->playing->vibrato_time = 0; - channel->playing->tremolo_speed = 0; - channel->playing->tremolo_depth = 0; - channel->playing->tremolo_time = 0; - channel->playing->sample_vibrato_time = 0; - channel->playing->sample_vibrato_depth = 0; - channel->playing->slide = 0; - channel->playing->volume_envelope.next_node = 0; - channel->playing->volume_envelope.tick = 0; - channel->playing->pan_envelope.next_node = 0; - channel->playing->pan_envelope.tick = 0; - channel->playing->pitch_envelope.next_node = 0; - channel->playing->pitch_envelope.tick = 0; - channel->playing->fadeoutcount = 1024; - it_reset_filter_state(&channel->playing->filter_state[0]); - it_reset_filter_state(&channel->playing->filter_state[1]); - it_playing_reset_resamplers(channel->playing, 0); - - /** WARNING - is everything initialised? */ -} - - - -static void get_default_volpan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) -{ - if (channel->sample == 0) - return; - - channel->volume = sigdata->sample[channel->sample-1].default_volume; - - if (sigdata->flags & IT_WAS_AN_XM) { - if (!(sigdata->flags & IT_WAS_A_MOD)) - channel->truepan = 32 + sigdata->sample[channel->sample-1].default_pan*64; - return; - } - - { - int pan = sigdata->sample[channel->sample-1].default_pan; - if (pan >= 128 && pan <= 192) { - channel->pan = pan - 128; - return; - } - } - - if (sigdata->flags & IT_USE_INSTRUMENTS) { - IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1]; - if (instrument->default_pan <= 64) - channel->pan = instrument->default_pan; - if (instrument->filter_cutoff >= 128) - channel->filter_cutoff = instrument->filter_cutoff - 128; - if (instrument->filter_resonance >= 128) - channel->filter_resonance = instrument->filter_resonance - 128; - } -} - - - -static void get_true_pan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) -{ - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - - if (!IT_IS_SURROUND_SHIFTED(channel->truepan) && (sigdata->flags & IT_USE_INSTRUMENTS)) { - IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1]; - int truepan = channel->truepan; - truepan += (channel->note - instrument->pp_centre) * instrument->pp_separation << (IT_ENVELOPE_SHIFT - 3); - channel->truepan = (unsigned short)MID(0, truepan, 64 << IT_ENVELOPE_SHIFT); - } -} - - - -static void post_process_it_volpan(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) -{ - IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - - if (entry->mask & IT_ENTRY_VOLPAN) { - if (entry->volpan <= 84) { - /* Volume */ - /* Fine volume slide up */ - /* Fine volume slide down */ - } else if (entry->volpan <= 94) { - /* Volume slide up */ - unsigned char v = entry->volpan - 85; - if (v == 0) - v = channel->lastvolslide; - channel->lastvolslide = v; - /* = effect Dx0 where x == entry->volpan - 85 */ - channel->volslide = v; - } else if (entry->volpan <= 104) { - /* Volume slide down */ - unsigned char v = entry->volpan - 95; - if (v == 0) - v = channel->lastvolslide; - channel->lastvolslide = v; - /* = effect D0x where x == entry->volpan - 95 */ - channel->volslide = -v; - } else if (entry->volpan <= 114) { - /* Portamento down */ - unsigned char v = (entry->volpan - 105) << 2; - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - channel->portamento -= v << 4; - } else if (entry->volpan <= 124) { - /* Portamento up */ - unsigned char v = (entry->volpan - 115) << 2; - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - channel->portamento += v << 4; - } else if (entry->volpan <= 202) { - /* Pan */ - /* Tone Portamento */ - } else if (entry->volpan <= 212) { - /* Vibrato */ - /* This is unaffected by IT_OLD_EFFECTS. However, if v == 0, then any doubling of depth that happened before (with Hxy in the effect column) will be preserved. */ - unsigned char v = entry->volpan - 203; - if (v == 0) - v = channel->lastHdepth; - else { - v <<= 2; - channel->lastHdepth = v; - } - if (channel->playing) { - channel->playing->vibrato_speed = channel->lastHspeed; - channel->playing->vibrato_depth = v; - channel->playing->vibrato_n++; - } - } - } -} - - - -static void it_send_midi(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel, unsigned char midi_byte) -{ - if (sigrenderer->callbacks->midi) - if ((*sigrenderer->callbacks->midi)(sigrenderer->callbacks->midi_data, channel - sigrenderer->channel, midi_byte)) - return; - - switch (channel->midi_state) { - case 4: /* Ready to receive resonance parameter */ - if (midi_byte < 0x80) channel->filter_resonance = midi_byte; - channel->midi_state = 0; - break; - case 3: /* Ready to receive cutoff parameter */ - if (midi_byte < 0x80) channel->filter_cutoff = midi_byte; - channel->midi_state = 0; - break; - case 2: /* Ready for byte specifying which parameter will follow */ - if (midi_byte == 0) /* Cutoff */ - channel->midi_state = 3; - else if (midi_byte == 1) /* Resonance */ - channel->midi_state = 4; - else - channel->midi_state = 0; - break; - default: /* Counting initial F0 bytes */ - switch (midi_byte) { - case 0xF0: - channel->midi_state++; - break; - case 0xFA: - case 0xFC: - case 0xFF: - /* Reset filter parameters for all channels */ - { - int i; - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - sigrenderer->channel[i].filter_cutoff = 127; - sigrenderer->channel[i].filter_resonance = 0; - //// should we be resetting channel[i].playing->filter_* here? - } - } - /* Fall through */ - default: - channel->midi_state = 0; - break; - } - } -} - - - -static void xm_envelope_calculate_value(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) -{ - if (pe->next_node <= 0) - pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT; - else if (pe->next_node >= envelope->n_nodes) - pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; - else { - int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; - int ts = envelope->node_t[pe->next_node-1]; - int te = envelope->node_t[pe->next_node]; - - if (ts == te) - pe->value = ys; - else { - int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; - int t = pe->tick; - - pe->value = ys + (ye - ys) * (t - ts) / (te - ts); - } - } -} - - - -/* Returns 1 if a callback caused termination of playback. */ -static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - - IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - - if (entry->mask & IT_ENTRY_EFFECT) { - switch (entry->effect) { -/* -Notes about effects (as compared to other module formats) - -C This is now in *HEX*. (Used to be in decimal in ST3) -E/F/G/H/U You need to check whether the song uses Amiga/Linear slides. -H/U Vibrato in Impulse Tracker is two times finer than in - any other tracker and is updated EVERY tick. - If "Old Effects" is *ON*, then the vibrato is played in the - normal manner (every non-row tick and normal depth) -E/F/G These commands ALL share the same memory. -Oxx Offsets to samples are to the 'xx00th' SAMPLE. (ie. for - 16 bit samples, the offset is xx00h*2) - Oxx past the sample end will be ignored, unless "Old Effects" - is ON, in which case the Oxx will play from the end of the - sample. -Yxy This uses a table 4 times larger (hence 4 times slower) than - vibrato or tremelo. If the waveform is set to random, then - the 'speed' part of the command is interpreted as a delay. -*/ - case IT_SET_SPEED: - if (entry->effectvalue) - sigrenderer->tick = sigrenderer->speed = entry->effectvalue; - else if (sigdata->flags & IT_WAS_AN_XM) { - sigrenderer->speed = 0; - if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) - return 1; - } - break; - - case IT_BREAK_TO_ROW: - if (ignore_cxx) break; - sigrenderer->breakrow = entry->effectvalue; - sigrenderer->processrow = 0xFFFE; - break; - - case IT_VOLSLIDE_VIBRATO: - if (channel->playing) { - channel->playing->vibrato_speed = channel->lastHspeed; - channel->playing->vibrato_depth = channel->lastHdepth; - channel->playing->vibrato_n++; - } - /* Fall through and process volume slide. */ - case IT_VOLUME_SLIDE: - case IT_VOLSLIDE_TONEPORTA: - /* The tone portamento component is handled elsewhere. */ - { - unsigned char v = entry->effectvalue; - if (!(sigdata->flags & IT_WAS_A_MOD)) { - if (v == 0) - v = channel->lastDKL; - channel->lastDKL = v; - } - if ((v & 0x0F) == 0) { /* Dx0 */ - channel->volslide = v >> 4; - if (channel->volslide == 15 && !(sigdata->flags & IT_WAS_AN_XM)) { - channel->volume += 15; - if (channel->volume > 64) channel->volume = 64; - } - } else if ((v & 0xF0) == 0) { /* D0x */ - channel->volslide = -v; - if (channel->volslide == -15 && !(sigdata->flags & IT_WAS_AN_XM)) { - channel->volume -= 15; - if (channel->volume > 64) channel->volume = 0; - } - } else if ((v & 0x0F) == 0x0F) { /* DxF */ - channel->volume += v >> 4; - if (channel->volume > 64) channel->volume = 64; - } else if ((v & 0xF0) == 0xF0) { /* DFx */ - channel->volume -= v & 15; - if (channel->volume > 64) channel->volume = 0; - } - } - break; - case IT_XM_FINE_VOLSLIDE_DOWN: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->xm_lastEB; - channel->xm_lastEB = v; - channel->volume -= v; - if (channel->volume > 64) channel->volume = 0; - } - break; - case IT_XM_FINE_VOLSLIDE_UP: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->xm_lastEA; - channel->xm_lastEA = v; - channel->volume += v; - if (channel->volume > 64) channel->volume = 64; - } - break; - case IT_PORTAMENTO_DOWN: - { - unsigned char v = entry->effectvalue; - if (sigdata->flags & IT_WAS_AN_XM) { - if (!(sigdata->flags & IT_WAS_A_MOD)) { - if (v == 0xF0) - v |= channel->xm_lastE2; - else if (v >= 0xF0) - channel->xm_lastE2 = v & 15; - else if (v == 0xE0) - v |= channel->xm_lastX2; - else - channel->xm_lastX2 = v & 15; - } - } else { - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if (channel->playing) { - if ((v & 0xF0) == 0xF0) - channel->playing->slide -= (v & 15) << 4; - else if ((v & 0xF0) == 0xE0) - channel->playing->slide -= (v & 15) << 2; - else - channel->portamento -= v << 4; - } - } - break; - case IT_PORTAMENTO_UP: - { - unsigned char v = entry->effectvalue; - if (sigdata->flags & IT_WAS_AN_XM) { - if (!(sigdata->flags & IT_WAS_A_MOD)) { - if (v == 0xF0) - v |= channel->xm_lastE1; - else if (v >= 0xF0) - channel->xm_lastE1 = v & 15; - else if (v == 0xE0) - v |= channel->xm_lastX1; - else - channel->xm_lastX1 = v & 15; - } - } else { - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if (channel->playing) { - if ((v & 0xF0) == 0xF0) - channel->playing->slide += (v & 15) << 4; - else if ((v & 0xF0) == 0xE0) - channel->playing->slide += (v & 15) << 2; - else - channel->portamento += v << 4; - } - } - break; - case IT_XM_PORTAMENTO_DOWN: - { - unsigned char v = entry->effectvalue; - if (!(sigdata->flags & IT_WAS_A_MOD)) { - if (v == 0) - v = channel->lastJ; - channel->lastJ = v; - } - if (channel->playing) - channel->portamento -= v << 4; - } - break; - case IT_XM_PORTAMENTO_UP: - { - unsigned char v = entry->effectvalue; - if (!(sigdata->flags & IT_WAS_A_MOD)) { - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if (channel->playing) - channel->portamento += v << 4; - } - break; - case IT_XM_KEY_OFF: - channel->key_off_count = entry->effectvalue; - if (!channel->key_off_count) xm_note_off(sigdata, channel); - break; - case IT_VIBRATO: - { - unsigned char speed = entry->effectvalue >> 4; - unsigned char depth = entry->effectvalue & 15; - if (speed == 0) - speed = channel->lastHspeed; - channel->lastHspeed = speed; - if (depth == 0) - depth = channel->lastHdepth; - else { - if (sigdata->flags & IT_OLD_EFFECTS) - depth <<= 3; - else - depth <<= 2; - channel->lastHdepth = depth; - } - if (channel->playing) { - channel->playing->vibrato_speed = speed; - channel->playing->vibrato_depth = depth; - channel->playing->vibrato_n++; - } - } - break; - case IT_TREMOR: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->lastI; - else if (!(sigdata->flags & IT_OLD_EFFECTS)) { - if (v & 0xF0) v -= 0x10; - if (v & 0x0F) v -= 0x01; - } - channel->lastI = v; - channel->tremor_time |= 128; - } - update_tremor(channel); - break; - case IT_ARPEGGIO: - { - unsigned char v = entry->effectvalue; - /* XM files have no memory for arpeggio (000 = no effect) - * and we use lastJ for portamento down instead. - */ - if (!(sigdata->flags & IT_WAS_AN_XM)) { - if (v == 0) - v = channel->lastJ; - channel->lastJ = v; - } - channel->arpeggio = v; - } - break; - case IT_SET_CHANNEL_VOLUME: - if (sigdata->flags & IT_WAS_AN_XM) - channel->volume = MIN(entry->effectvalue, 64); - else if (entry->effectvalue <= 64) - channel->channelvolume = entry->effectvalue; -#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM - else - channel->channelvolume = 64; -#endif - if (channel->playing) - channel->playing->channel_volume = channel->channelvolume; - break; - case IT_CHANNEL_VOLUME_SLIDE: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->lastN; - channel->lastN = v; - if ((v & 0x0F) == 0) { /* Nx0 */ - channel->channelvolslide = v >> 4; - } else if ((v & 0xF0) == 0) { /* N0x */ - channel->channelvolslide = -v; - } else { - if ((v & 0x0F) == 0x0F) { /* NxF */ - channel->channelvolume += v >> 4; - if (channel->channelvolume > 64) channel->channelvolume = 64; - } else if ((v & 0xF0) == 0xF0) { /* NFx */ - channel->channelvolume -= v & 15; - if (channel->channelvolume > 64) channel->channelvolume = 0; - } else - break; - if (channel->playing) - channel->playing->channel_volume = channel->channelvolume; - } - } - break; - case IT_SET_SAMPLE_OFFSET: - { - unsigned char v = entry->effectvalue; - if (sigdata->flags & IT_WAS_A_MOD) { - if (v == 0) break; - } else { - if (v == 0) - v = channel->lastO; - channel->lastO = v; - } - /* Note: we set the offset even if tone portamento is - * specified. Impulse Tracker does the same. - */ - if (entry->mask & IT_ENTRY_NOTE) { - if (channel->playing) { - int offset = ((int)channel->high_offset << 16) | ((int)v << 8); - IT_PLAYING *playing = channel->playing; - IT_SAMPLE *sample = playing->sample; - int end; - if ((sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) - end = sample->sus_loop_end; - else if (sample->flags & IT_SAMPLE_LOOP) - end = sample->loop_end; - else - end = sample->length; - if (offset < end) - it_playing_reset_resamplers(playing, offset); - else if (sigdata->flags & IT_OLD_EFFECTS) - it_playing_reset_resamplers(playing, end); - } - } - } - break; - case IT_PANNING_SLIDE: - /** JULIEN: guess what? the docs are wrong! (how unusual ;) - * Pxy seems to memorize its previous value... and there - * might be other mistakes like that... (sigh!) - */ - /** ENTHEH: umm... but... the docs say that Pxy memorises its - * value... don't they? :o - */ - { - unsigned char v = entry->effectvalue; - int p = channel->truepan; - if (sigdata->flags & IT_WAS_AN_XM) - p >>= 6; - else { - p = (p + 128) >> 8; - channel->pan = p; - } - if (v == 0) - v = channel->lastP; - channel->lastP = v; - if ((v & 0x0F) == 0) { /* Px0 */ - channel->panslide = v >> 4; - } else if ((v & 0xF0) == 0) { /* P0x */ - channel->panslide = -v; - } else if ((v & 0x0F) == 0x0F) { /* PxF */ - p += v >> 4; - } else if ((v & 0xF0) == 0xF0) { /* PFx */ - p -= v & 15; - } - if (sigdata->flags & IT_WAS_AN_XM) - channel->truepan = 32 + MID(0, p, 255) * 64; - else if (!IT_IS_SURROUND(channel->pan)) { - channel->pan = p; - channel->truepan = p << 8; - } - } - break; - case IT_RETRIGGER_NOTE: - { - unsigned char v = entry->effectvalue; - if (sigdata->flags & IT_WAS_AN_XM) { - if ((v & 0x0F) == 0) v |= channel->lastQ & 0x0F; - if ((v & 0xF0) == 0) v |= channel->lastQ & 0xF0; - } else { - if (v == 0) - v = channel->lastQ; - } - channel->lastQ = v; - if ((v & 0x0F) == 0) v |= 0x01; - channel->retrig = v; - if (entry->mask & IT_ENTRY_NOTE) { - channel->retrig_tick = v & 0x0F; - /* Emulate a bug */ - if (sigdata->flags & IT_WAS_AN_XM) - update_retrig(channel); - } else - update_retrig(channel); - } - break; - case IT_XM_RETRIGGER_NOTE: - channel->retrig_tick = channel->xm_retrig = entry->effectvalue; - if (entry->effectvalue == 0) - if (channel->playing) it_playing_reset_resamplers(channel->playing, 0); - break; - case IT_TREMOLO: - { - unsigned char speed = entry->effectvalue >> 4; - unsigned char depth = entry->effectvalue & 15; - if (speed == 0) - speed = channel->lastRspeed; - channel->lastRspeed = speed; - if (depth == 0) - depth = channel->lastRdepth; - channel->lastRdepth = depth; - if (channel->playing) { - channel->playing->tremolo_speed = speed; - channel->playing->tremolo_depth = depth; - } - } - break; - case IT_S: - { - /* channel->lastS was set in update_pattern_variables(). */ - unsigned char effectvalue = channel->lastS; - switch (effectvalue >> 4) { - //case IT_S_SET_FILTER: - //case IT_S_SET_GLISSANDO_CONTROL: - //case IT_S_FINETUNE: - //case IT_S_SET_VIBRATO_WAVEFORM: - //case IT_S_SET_TREMOLO_WAVEFORM: - //case IT_S_SET_PANBRELLO_WAVEFORM: - /* Waveforms for commands S3x, S4x and S5x: - * 0: Sine wave - * 1: Ramp down - * 2: Square wave - * 3: Random wave - */ - case IT_S_FINE_PATTERN_DELAY: - sigrenderer->tick += effectvalue & 15; - break; - //case IT_S7: - case IT_S_SET_PAN: - ASSERT(!(sigdata->flags & IT_WAS_AN_XM)); - channel->pan = - ((effectvalue & 15) << 2) | - ((effectvalue & 15) >> 2); - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - break; - case IT_S_SET_SURROUND_SOUND: - if ((effectvalue & 15) == 1) { - channel->pan = IT_SURROUND; - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - } - break; - case IT_S_SET_HIGH_OFFSET: - channel->high_offset = effectvalue & 15; - break; - //case IT_S_PATTERN_LOOP: - case IT_S_DELAYED_NOTE_CUT: - channel->note_cut_count = effectvalue & 15; - if (!channel->note_cut_count) { - if (sigdata->flags & IT_WAS_AN_XM) - channel->volume = 0; - else - channel->note_cut_count = 1; - } - break; - case IT_S_SET_MIDI_MACRO: - channel->SFmacro = effectvalue & 15; - break; - } - } - break; - case IT_SET_SONG_TEMPO: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->lastW; - channel->lastW = v; - if (v < 0x10) - sigrenderer->temposlide = -v; - else if (v < 0x20) - sigrenderer->temposlide = v & 15; - else - sigrenderer->tempo = v; - } - break; - case IT_FINE_VIBRATO: - { - unsigned char speed = entry->effectvalue >> 4; - unsigned char depth = entry->effectvalue & 15; - if (speed == 0) - speed = channel->lastHspeed; - channel->lastHspeed = speed; - if (depth == 0) - depth = channel->lastHdepth; - else { - if (sigdata->flags & IT_OLD_EFFECTS) - depth <<= 1; - channel->lastHdepth = depth; - } - if (channel->playing) { - channel->playing->vibrato_speed = speed; - channel->playing->vibrato_depth = depth; - channel->playing->vibrato_n++; - } - } - break; - case IT_SET_GLOBAL_VOLUME: - if (entry->effectvalue <= 128) - sigrenderer->globalvolume = entry->effectvalue; -#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM - else - sigrenderer->globalvolume = 128; -#endif - break; - case IT_GLOBAL_VOLUME_SLIDE: - { - unsigned char v = entry->effectvalue; - if (v == 0) - v = channel->lastW; - channel->lastW = v; - if ((v & 0x0F) == 0) { /* Wx0 */ - sigrenderer->globalvolslide = - (sigdata->flags & IT_WAS_AN_XM) ? (v >> 4)*2 : (v >> 4); - } else if ((v & 0xF0) == 0) { /* W0x */ - sigrenderer->globalvolslide = - (sigdata->flags & IT_WAS_AN_XM) ? (-v)*2 : (-v); - } else if ((v & 0x0F) == 0x0F) { /* WxF */ - sigrenderer->globalvolume += v >> 4; - if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 128; - } else if ((v & 0xF0) == 0xF0) { /* WFx */ - sigrenderer->globalvolume -= v & 15; - if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 0; - } - } - break; - case IT_SET_PANNING: - if (sigdata->flags & IT_WAS_AN_XM) - channel->truepan = 32 + entry->effectvalue*64; - else { - channel->pan = (entry->effectvalue + 2) >> 2; - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - } - break; - //case IT_PANBRELLO: - case IT_MIDI_MACRO: - { - IT_MIDI *midi = sigdata->midi ? sigdata->midi : &default_midi; - if (entry->effectvalue >= 0x80) { - int n = midi->Zmacrolen[entry->effectvalue-0x80]; - int i; - for (i = 0; i < n; i++) - it_send_midi(sigrenderer, channel, midi->Zmacro[entry->effectvalue-0x80][i]); - } else { - int n = midi->SFmacrolen[channel->SFmacro]; - int i, j; - for (i = 0, j = 1; i < n; i++, j <<= 1) - it_send_midi(sigrenderer, channel, - (unsigned char)(midi->SFmacroz[channel->SFmacro] & j ? - entry->effectvalue : midi->SFmacro[channel->SFmacro][i])); - } - } - break; - case IT_XM_SET_ENVELOPE_POSITION: - if (channel->playing && channel->playing->env_instrument) { - IT_ENVELOPE *envelope = &channel->playing->env_instrument->volume_envelope; - if (envelope->flags & IT_ENVELOPE_ON) { - IT_PLAYING_ENVELOPE *pe = &channel->playing->volume_envelope; - pe->tick = entry->effectvalue; - if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) - pe->tick = envelope->node_t[envelope->n_nodes-1]; - pe->next_node = 0; - while (pe->tick > envelope->node_t[pe->next_node]) pe->next_node++; - xm_envelope_calculate_value(envelope, pe); - } - } - break; - } - } - - if (!(sigdata->flags & IT_WAS_AN_XM)) - post_process_it_volpan(sigrenderer, entry); - - return 0; -} - - - -static int process_it_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - - // When tone portamento and instrument are specified: - // If Gxx is off: - // - same sample, do nothing but portamento - // - diff sample, retrigger all but keep current note+slide + do porta - // - if instrument is invalid, nothing; if sample is invalid, cut - // If Gxx is on: - // - same sample or new sample invalid, retrigger envelopes and initialise note value for portamento to 'seek' to - // - diff sample/inst, start using new envelopes - // When tone portamento is specified alone, sample won't change. - // TODO: consider what happens with instrument alone after all this... - - if (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) { - if (entry->mask & IT_ENTRY_INSTRUMENT) - channel->instrument = entry->instrument; - instrument_to_sample(sigdata, channel); - if (channel->note < 120) { - if ((sigdata->flags & IT_USE_INSTRUMENTS) && channel->sample == 0) - return 1; - if (entry->mask & IT_ENTRY_INSTRUMENT) - get_default_volpan(sigdata, channel); - } else - it_retrigger_note(sigrenderer, channel); /* Stop the note */ - } - - /** WARNING: This is not ideal, since channel->playing might not get allocated owing to lack of memory... */ - if (((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) || - ((entry->mask & IT_ENTRY_EFFECT) && (entry->effect == IT_TONE_PORTAMENTO || entry->effect == IT_VOLSLIDE_TONEPORTA))) - { - if (channel->playing && (entry->mask & IT_ENTRY_INSTRUMENT)) { - if (sigdata->flags & IT_COMPATIBLE_GXX) - it_compatible_gxx_retrigger(sigdata, channel); - else if ((!(sigdata->flags & IT_USE_INSTRUMENTS) || - (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments)) && - channel->sample != channel->playing->sampnum) - { - unsigned char note = channel->playing->note; - int slide = channel->playing->slide; - it_retrigger_note(sigrenderer, channel); - if (channel->playing) { - channel->playing->note = note; - channel->playing->slide = slide; - // Should we be preserving sample_vibrato_time? depth? - } - } - } - - if ((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) { - /* Tone Portamento in the volume column */ - static const unsigned char slidetable[] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255}; - unsigned char v = slidetable[entry->volpan - 193]; - if (sigdata->flags & IT_COMPATIBLE_GXX) { - if (v == 0) - v = channel->lastG; - channel->lastG = v; - } else { - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { - if (channel->sample) - channel->destnote = channel->truenote; - else - channel->destnote = channel->note; - } - channel->toneporta = v << 4; - } else { - /* Tone Portamento in the effect column */ - unsigned char v; - if (entry->effect == IT_TONE_PORTAMENTO) - v = entry->effectvalue; - else - v = 0; - if (sigdata->flags & IT_COMPATIBLE_GXX) { - if (v == 0) - v = channel->lastG; - channel->lastG = v; - } else { - if (v == 0) - v = channel->lastEF; - channel->lastEF = v; - } - if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { - if (channel->sample) - channel->destnote = channel->truenote; - else - channel->destnote = channel->note; - } - channel->toneporta = v << 4; - } - if (channel->playing) goto skip_start_note; - } - - if ((entry->mask & IT_ENTRY_NOTE) || - ((entry->mask & IT_ENTRY_INSTRUMENT) && (!channel->playing || entry->instrument != channel->playing->instnum))) - { - if (channel->note < 120) { - get_true_pan(sigdata, channel); - it_retrigger_note(sigrenderer, channel); - } - } - - skip_start_note: - - if (entry->mask & IT_ENTRY_VOLPAN) { - if (entry->volpan <= 64) { - /* Volume */ - channel->volume = entry->volpan; - } else if (entry->volpan <= 74) { - /* Fine volume slide up */ - unsigned char v = entry->volpan - 65; - if (v == 0) - v = channel->lastvolslide; - channel->lastvolslide = v; - /* = effect DxF where x == entry->volpan - 65 */ - channel->volume += v; - if (channel->volume > 64) channel->volume = 64; - } else if (entry->volpan <= 84) { - /* Fine volume slide down */ - unsigned char v = entry->volpan - 75; - if (v == 0) - v = channel->lastvolslide; - channel->lastvolslide = v; - /* = effect DFx where x == entry->volpan - 75 */ - channel->volume -= v; - if (channel->volume > 64) channel->volume = 0; - } else if (entry->volpan < 128) { - /* Volume slide up */ - /* Volume slide down */ - /* Portamento down */ - /* Portamento up */ - } else if (entry->volpan <= 192) { - /* Pan */ - channel->pan = entry->volpan - 128; - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - } - /* else */ - /* Tone Portamento */ - /* Vibrato */ - } - return 0; -} - - - -static void retrigger_xm_envelopes(IT_PLAYING *playing) -{ - playing->volume_envelope.next_node = 0; - playing->volume_envelope.tick = -1; - playing->pan_envelope.next_node = 0; - playing->pan_envelope.tick = -1; - playing->fadeoutcount = 1024; -} - - - -static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - - if (entry->mask & IT_ENTRY_INSTRUMENT) { - channel->instrument = entry->instrument; - instrument_to_sample(sigdata, channel); - if (channel->playing) { - /* Retrigger vol/pan envelopes if enabled, and cancel fadeout. - * Also reset vol/pan to that of _original_ instrument. - */ - channel->playing->flags &= ~(IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING); - it_playing_update_resamplers(channel->playing); - - channel->volume = channel->playing->sample->default_volume; - if (!(sigdata->flags & IT_WAS_A_MOD)) - channel->truepan = 32 + channel->playing->sample->default_pan*64; - - retrigger_xm_envelopes(channel->playing); - } - } - - if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) && - (entry->mask & IT_ENTRY_NOTE)) - { - if (!(entry->mask & IT_ENTRY_INSTRUMENT)) - instrument_to_sample(sigdata, channel); - - if (channel->note >= 120) - xm_note_off(sigdata, channel); - else if (channel->sample == 0) { - /** If we get here, one of the following is the case: - ** 1. The instrument has never been specified on this channel. - ** 2. The specified instrument is invalid. - ** 3. The instrument has no sample mapped to the selected note. - ** What should happen? - ** - ** Experimentation shows that any existing note stops and cannot - ** be brought back. A subsequent instrument change fixes that. - **/ - if (channel->playing) { - free(channel->playing); - channel->playing = NULL; - } - return; - } else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { - /* Don't retrigger note; portamento in the volume column. */ - } else if (channel->playing && - (entry->mask & IT_ENTRY_EFFECT) && - (entry->effect == IT_TONE_PORTAMENTO || - entry->effect == IT_VOLSLIDE_TONEPORTA)) { - /* Don't retrigger note; portamento in the effects column. */ - } else { - channel->destnote = IT_NOTE_OFF; - - if (!channel->playing) { - channel->playing = malloc(sizeof(*channel->playing)); - if (!channel->playing) - return; - // Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone. - retrigger_xm_envelopes(channel->playing); - } - - channel->playing->flags = 0; - channel->playing->channel = channel; - channel->playing->sample = &sigdata->sample[channel->sample-1]; - if (sigdata->flags & IT_USE_INSTRUMENTS) - channel->playing->instrument = &sigdata->instrument[channel->instrument-1]; - else - channel->playing->instrument = NULL; - channel->playing->env_instrument = channel->playing->instrument; - channel->playing->sampnum = channel->sample; - channel->playing->instnum = channel->instrument; - channel->playing->channel_volume = channel->channelvolume; - channel->playing->note = channel->truenote; - channel->playing->filter_cutoff = 127; - channel->playing->filter_resonance = 0; - channel->playing->true_filter_cutoff = 127 << 8; - channel->playing->true_filter_resonance = 0; - channel->playing->vibrato_speed = 0; - channel->playing->vibrato_depth = 0; - channel->playing->vibrato_n = 0; - channel->playing->vibrato_time = 0; - channel->playing->tremolo_speed = 0; - channel->playing->tremolo_depth = 0; - channel->playing->tremolo_time = 0; - channel->playing->sample_vibrato_time = 0; - channel->playing->sample_vibrato_depth = 0; - channel->playing->slide = 0; - it_reset_filter_state(&channel->playing->filter_state[0]); // Are these - it_reset_filter_state(&channel->playing->filter_state[1]); // necessary? - it_playing_reset_resamplers(channel->playing, 0); - - /** WARNING - is everything initialised? */ - } - } - - if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) && - (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) == (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) - { - if (channel->playing) retrigger_xm_envelopes(channel->playing); - get_default_volpan(sigdata, channel); - } - - if ((entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { - /* Tone Portamento */ - unsigned char v = (entry->volpan & 15) << 4; - if (v == 0) - v = channel->lastG; - channel->lastG = v; - if (entry->mask & IT_ENTRY_NOTE) - if (channel->sample) - channel->destnote = channel->truenote; - channel->toneporta = v << 4; - } else if ((entry->mask & IT_ENTRY_EFFECT) && - (entry->effect == IT_TONE_PORTAMENTO || - entry->effect == IT_VOLSLIDE_TONEPORTA)) { - unsigned char v; - if (entry->effect == IT_TONE_PORTAMENTO) - v = entry->effectvalue; - else - v = 0; - if (v == 0) - v = channel->lastG; - channel->lastG = v; - if (entry->mask & IT_ENTRY_NOTE) - if (channel->sample) - channel->destnote = channel->truenote; - channel->toneporta = v << 4; - } - - if (entry->mask & IT_ENTRY_VOLPAN) { - int effect = entry->volpan >> 4; - int value = entry->volpan & 15; - switch (effect) { - case 0x6: /* Volume slide down */ - channel->xm_volslide = -value; - break; - case 0x7: /* Volume slide up */ - channel->xm_volslide = value; - break; - case 0x8: /* Fine volume slide down */ - channel->volume -= value; - if (channel->volume > 64) channel->volume = 0; - break; - case 0x9: /* Fine volume slide up */ - channel->volume += value; - if (channel->volume > 64) channel->volume = 64; - break; - case 0xA: /* Set vibrato speed */ - if (value) - channel->lastHspeed = value; - if (channel->playing) - channel->playing->vibrato_speed = channel->lastHspeed; - break; - case 0xB: /* Vibrato */ - if (value) - channel->lastHdepth = value << 2; /** WARNING: correct ? */ - if (channel->playing) { - channel->playing->vibrato_depth = channel->lastHdepth; - channel->playing->vibrato_speed = channel->lastHspeed; - channel->playing->vibrato_n++; - } - break; - case 0xC: /* Set panning */ - channel->truepan = 32 + value*(17*64); - break; - case 0xD: /* Pan slide left */ - /* -128 is a special case for emulating a 'feature' in FT2. - * As soon as effects are processed, it goes hard left. - */ - channel->panslide = value ? -value : -128; - break; - case 0xE: /* Pan slide Right */ - channel->panslide = value; - break; - case 0xF: /* Tone porta */ - break; - default: /* Volume */ - channel->volume = entry->volpan - 0x10; - break; - } - } -} - - - -/* This function assumes !IT_IS_END_ROW(entry). */ -static int process_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - - if (sigdata->flags & IT_WAS_AN_XM) - process_xm_note_data(sigrenderer, entry); - else - if (process_it_note_data(sigrenderer, entry)) return 0; - - return process_effects(sigrenderer, entry, ignore_cxx); -} - - - -static int process_entry(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) -{ - IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; - - if (entry->mask & IT_ENTRY_NOTE) - channel->note = entry->note; - - if ((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_S) { - /* channel->lastS was set in update_pattern_variables(). */ - unsigned char effectvalue = channel->lastS; - if (effectvalue >> 4 == IT_S_NOTE_DELAY) { - channel->note_delay_count = effectvalue & 15; - if (channel->note_delay_count == 0) - channel->note_delay_count = 1; - channel->note_delay_entry = entry; - return 0; - } - } - - return process_note_data(sigrenderer, entry, ignore_cxx); -} - - - -static void update_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer) -{ - int i; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - - if (channel->key_off_count) { - channel->key_off_count--; - if (channel->key_off_count == 0) - xm_note_off(sigrenderer->sigdata, channel); - } else if (channel->note_cut_count) { - channel->note_cut_count--; - if (channel->note_cut_count == 0) { - if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) - channel->volume = 0; - else if (channel->playing) { - free(channel->playing); - channel->playing = NULL; - } - } - } else if (channel->note_delay_count) { - channel->note_delay_count--; - if (channel->note_delay_count == 0) - process_note_data(sigrenderer, channel->note_delay_entry, 0); - /* Don't bother checking the return value; if the note - * was delayed, there can't have been a speed=0. - */ - } - } -} - - - -static int envelope_get_y(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) -{ -#if 1 - (void)envelope; //TODO: remove the parameter - return pe->value; -#else - int ys, ye; - int ts, te; - int t; - - if (pe->next_node <= 0) - return envelope->node_y[0] << IT_ENVELOPE_SHIFT; - - if (pe->next_node >= envelope->n_nodes) - return envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; - - ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; - ts = envelope->node_t[pe->next_node-1]; - te = envelope->node_t[pe->next_node]; - - if (ts == te) - return ys; - - ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; - - t = pe->tick; - - return ys + (ye - ys) * (t - ts) / (te - ts); -#endif -} - - - -#if 0 -static int it_envelope_end(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) -{ - if (pe->next_node >= envelope->n_nodes) - return 1; - - if (pe->tick < envelope->node_t[pe->next_node]) return 0; - - if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && - envelope->loop_end >= pe->next_node && - envelope->node_t[envelope->loop_end] <= pe->tick) return 0; - - if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && - !(playing->flags & IT_PLAYING_SUSTAINOFF) && - envelope->sus_loop_end >= pe->next_node && - envelope->node_t[envelope->sus_loop_end] <= pe->tick) return 0; - - if (envelope->node_t[envelope->n_nodes-1] <= pe->tick) return 1; - - return 0; -} -#endif - - - -/* Returns 1 when fading should be initiated for a volume envelope. */ -static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) -{ - if (!(envelope->flags & IT_ENVELOPE_ON)) - return 0; - - ASSERT(envelope->n_nodes > 0); - - if (pe->next_node <= 0) - pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT; - else if (pe->next_node >= envelope->n_nodes) { - pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; - return 1; - } else { - int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; - int ts = envelope->node_t[pe->next_node-1]; - int te = envelope->node_t[pe->next_node]; - - if (ts == te) - pe->value = ys; - else { - int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; - int t = pe->tick; - - pe->value = ys + (ye - ys) * (t - ts) / (te - ts); - } - } - - pe->tick++; - while (pe->tick >= envelope->node_t[pe->next_node]) { - pe->next_node++; - if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { - if (pe->next_node > envelope->sus_loop_end) { - pe->next_node = envelope->sus_loop_start; - ASSERT(pe->next_node < envelope->n_nodes); - pe->tick = envelope->node_t[envelope->sus_loop_start]; - return 0; - } - } else if (envelope->flags & IT_ENVELOPE_LOOP_ON) { - if (pe->next_node > envelope->loop_end) { - pe->next_node = envelope->loop_start; - ASSERT(pe->next_node < envelope->n_nodes); - pe->tick = envelope->node_t[envelope->loop_start]; - return 0; - } - } - if (pe->next_node >= envelope->n_nodes) - return 0; - } - return 0; -} - - - -static void update_it_envelopes(IT_PLAYING *playing) -{ - IT_ENVELOPE *envelope = &playing->env_instrument->volume_envelope; - IT_PLAYING_ENVELOPE *pe = &playing->volume_envelope; - - if (update_it_envelope(playing, envelope, pe)) { - playing->flags |= IT_PLAYING_FADING; - if (pe->value == 0) - playing->flags |= IT_PLAYING_DEAD; - } - - update_it_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope); - update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope); -} - - - -static int xm_envelope_is_sustaining(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) -{ - if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) - if (envelope->sus_loop_start < envelope->n_nodes) - if (pe->tick == envelope->node_t[envelope->sus_loop_start]) - return 1; - return 0; -} - - - -static void update_xm_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) -{ - if (!(envelope->flags & IT_ENVELOPE_ON)) - return; - - if (xm_envelope_is_sustaining(playing, envelope, pe)) - return; - - if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) - return; - - pe->tick++; - - /* pe->next_node must be kept up to date for envelope_get_y(). */ - while (pe->tick > envelope->node_t[pe->next_node]) - pe->next_node++; - - if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && envelope->loop_end < envelope->n_nodes) { - if (pe->tick == envelope->node_t[envelope->loop_end]) { - pe->next_node = MID(0, envelope->loop_start, envelope->n_nodes - 1); - pe->tick = envelope->node_t[pe->next_node]; - } - } - - xm_envelope_calculate_value(envelope, pe); -} - - - -static void update_xm_envelopes(IT_PLAYING *playing) -{ - update_xm_envelope(playing, &playing->env_instrument->volume_envelope, &playing->volume_envelope); - update_xm_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope); -} - - - -static void update_fadeout(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing) -{ - if (playing->flags & IT_PLAYING_FADING) { - playing->fadeoutcount -= playing->env_instrument->fadeout; - if (playing->fadeoutcount <= 0) { - playing->fadeoutcount = 0; - if (!(sigdata->flags & IT_WAS_AN_XM)) - playing->flags |= IT_PLAYING_DEAD; - } - } -} - - - -static void process_playing(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing) -{ - if (playing->instrument) { - if (sigdata->flags & IT_WAS_AN_XM) - update_xm_envelopes(playing); - else - update_it_envelopes(playing); - update_fadeout(sigdata, playing); - } - - //Calculate final volume if required - //Calculate final pan if required - - if (sigdata->flags & IT_WAS_AN_XM) { - /* 'depth' is used to store the tick number for XM files. */ - if (playing->sample_vibrato_depth < playing->sample->vibrato_rate) - playing->sample_vibrato_depth++; - } else { - playing->sample_vibrato_depth += playing->sample->vibrato_rate; - if (playing->sample_vibrato_depth > playing->sample->vibrato_depth << 8) - playing->sample_vibrato_depth = playing->sample->vibrato_depth << 8; - } - - playing->sample_vibrato_time += playing->sample->vibrato_speed; -} - - - -static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - int i; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; - IT_PLAYING *playing = channel->playing; - - if (playing) { - int vibrato_shift = it_sine[playing->vibrato_time]; - vibrato_shift *= playing->vibrato_n; - vibrato_shift *= playing->vibrato_depth; - vibrato_shift >>= 4; - - if (sigdata->flags & IT_OLD_EFFECTS) - vibrato_shift = -vibrato_shift; - - playing->volume = channel->volume; - playing->pan = channel->truepan; - - if (sigdata->flags & IT_LINEAR_SLIDES) { - int currpitch = ((playing->note - 60) << 8) + playing->slide - + vibrato_shift; - - /* We add a feature here, which is that of keeping the pitch - * within range. Otherwise it crashes. Trust me. It happened. - * The limit 32768 gives almost 11 octaves either way. - */ - if (currpitch < -32768) - currpitch = -32768; - else if (currpitch > 32767) - currpitch = 32767; - - playing->delta = (float)pow(DUMB_PITCH_BASE, currpitch); - playing->delta *= playing->sample->C5_speed / 65536.0f; - } else { - int slide = playing->slide + vibrato_shift; - - playing->delta = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); - /* playing->delta is 1.0 for C-5, 0.5 for C-6, etc. */ - - playing->delta *= 1.0f / playing->sample->C5_speed; - - playing->delta -= slide / AMIGA_DIVISOR; - - if (playing->delta < (1.0f / 65536.0f) / 32768.0f) { - // Should XM notes die if Amiga slides go out of range? - playing->flags |= IT_PLAYING_DEAD; - continue; - } - - playing->delta = (1.0f / 65536.0f) / playing->delta; - } - - playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio >> 8); - - playing->filter_cutoff = channel->filter_cutoff; - playing->filter_resonance = channel->filter_resonance; - } - } - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - if (sigrenderer->channel[i].playing) { - process_playing(sigdata, sigrenderer->channel[i].playing); - if (!(sigdata->flags & IT_WAS_AN_XM)) { - //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { - // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. - if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) { - free(sigrenderer->channel[i].playing); - sigrenderer->channel[i].playing = NULL; - } - } - } - } - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { - if (sigrenderer->playing[i]) { - process_playing(sigdata, sigrenderer->playing[i]); - if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { - free(sigrenderer->playing[i]); - sigrenderer->playing[i] = NULL; - } - } - } -} - - - -static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) -{ - DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; - - // Set note vol/freq to vol/freq set for each channel - - if (sigrenderer->speed && --sigrenderer->tick == 0) { - reset_tick_counts(sigrenderer); - sigrenderer->tick = sigrenderer->speed; - sigrenderer->rowcount--; - if (sigrenderer->rowcount == 0) { - sigrenderer->rowcount = 1; - - sigrenderer->processrow++; - - if (sigrenderer->processrow >= sigrenderer->n_rows) { - IT_PATTERN *pattern; - int n; - int processorder = sigrenderer->processorder; - - if (sigrenderer->processrow == 0xFFFE + 1) { /* It was incremented above! */ - sigrenderer->processrow = sigrenderer->breakrow; - sigrenderer->breakrow = 0; - for (n = 0; n < DUMB_IT_N_CHANNELS; n++) sigrenderer->channel[n].pat_loop_end_row = 0; - } else - sigrenderer->processrow = sigrenderer->breakrow; - - if (sigrenderer->processorder == 0xFFFF) - sigrenderer->processorder = sigrenderer->order - 1; - - for (;;) { - sigrenderer->processorder++; - - if (sigrenderer->processorder >= sigdata->n_orders) { - sigrenderer->processorder = sigdata->restart_position; - if (sigrenderer->processorder >= sigdata->n_orders) { - /* Restarting beyond end. We'll loop for now. */ - sigrenderer->processorder = -1; - continue; - } - } - - n = sigdata->order[sigrenderer->processorder]; - - if (n < sigdata->n_patterns) - break; - -#ifdef INVALID_ORDERS_END_SONG - if (n != IT_ORDER_SKIP) - sigrenderer->processorder = -1; -#else - if (n == IT_ORDER_END) - sigrenderer->processorder = -1; -#endif - } - - pattern = &sigdata->pattern[n]; - - n = sigrenderer->n_rows; - sigrenderer->n_rows = pattern->n_rows; - - if (sigrenderer->processrow >= sigrenderer->n_rows) - sigrenderer->processrow = 0; - -/** WARNING - everything pertaining to a new pattern initialised? */ - - sigrenderer->entry = sigrenderer->entry_start = pattern->entry; - sigrenderer->entry_end = sigrenderer->entry + pattern->n_entries; - - /* If n_rows was 0, we're only just starting. Don't do anything weird here. */ - if (n && (processorder == 0xFFFF ? sigrenderer->order > sigrenderer->processorder : sigrenderer->order >= sigrenderer->processorder)) { - if (sigrenderer->callbacks->loop) { - if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) - return 1; - if (sigrenderer->speed == 0) - goto speed0; /* I love goto */ - } - } - sigrenderer->order = sigrenderer->processorder; - - n = sigrenderer->processrow; - while (n) { - while (sigrenderer->entry < sigrenderer->entry_end) { - if (IT_IS_END_ROW(sigrenderer->entry)) { - sigrenderer->entry++; - break; - } - sigrenderer->entry++; - } - n--; - } - sigrenderer->row = sigrenderer->processrow; - } else { - if (sigrenderer->entry) { - while (sigrenderer->entry < sigrenderer->entry_end) { - if (IT_IS_END_ROW(sigrenderer->entry)) { - sigrenderer->entry++; - break; - } - sigrenderer->entry++; - } - sigrenderer->row++; - } else { - sigrenderer->entry = sigrenderer->entry_start; - sigrenderer->row = 0; - } - } - - reset_effects(sigrenderer); - - { - IT_ENTRY *entry = sigrenderer->entry; - int ignore_cxx = 0; - - while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) - ignore_cxx |= update_pattern_variables(sigrenderer, entry++); - - entry = sigrenderer->entry; - - while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) - if (process_entry(sigrenderer, entry++, sigdata->flags & IT_WAS_AN_XM ? 0 : ignore_cxx)) - return 1; - } - - if (!(sigdata->flags & IT_OLD_EFFECTS)) - update_smooth_effects(sigrenderer); - } else { - { - IT_ENTRY *entry = sigrenderer->entry; - - while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) - process_effects(sigrenderer, entry++, 0); - /* Don't bother checking the return value; if there - * was a pattern delay, there can't be a speed=0. - */ - } - - update_effects(sigrenderer); - } - } else { - speed0: - update_effects(sigrenderer); - update_tick_counts(sigrenderer); - } - - process_all_playing(sigrenderer); - - { - LONG_LONG t = sigrenderer->sub_time_left + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; - sigrenderer->time_left += (int)(t >> 16); - sigrenderer->sub_time_left = (int)t & 65535; - } - - return 0; -} - - - -int dumb_it_max_to_mix = 64; - - - -static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume) -{ - if (volume != 0) { - int vol; - - if (playing->channel->flags & IT_CHANNEL_MUTED) - return 0; - - if ((playing->channel->tremor_time & 192) == 128) - return 0; - - vol = it_sine[playing->tremolo_time]; - vol *= playing->tremolo_depth; - - vol = (playing->volume << 5) + vol; - - if (vol <= 0) - return 0; - - if (vol > 64 << 5) - vol = 64 << 5; - - volume *= vol; /* 64 << 5 */ - volume *= playing->sample->global_volume; /* 64 */ - volume *= playing->channel_volume; /* 64 */ - volume *= sigrenderer->globalvolume; /* 128 */ - volume *= sigrenderer->sigdata->mixing_volume; /* 128 */ - volume *= 1.0f / ((64 << 5) * 64.0f * 64.0f * 128.0f * 128.0f); - - if (volume && playing->instrument) { - if (playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_ON) { - volume *= envelope_get_y(&playing->env_instrument->volume_envelope, &playing->volume_envelope); - volume *= 1.0f / (64 << IT_ENVELOPE_SHIFT); - } - volume *= playing->instrument->global_volume; /* 128 */ - volume *= playing->fadeoutcount; /* 1024 */ - volume *= 1.0f / (128.0f * 1024.0f); - } - } - - return volume; -} - - - -static int apply_pan_envelope(IT_PLAYING *playing) -{ - int pan = playing->pan; - if (pan <= 64 << IT_ENVELOPE_SHIFT && playing->env_instrument && (playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_ON)) { - int p = envelope_get_y(&playing->env_instrument->pan_envelope, &playing->pan_envelope); - if (pan > 32 << IT_ENVELOPE_SHIFT) - p *= (64 << IT_ENVELOPE_SHIFT) - pan; - else - p *= pan; - pan += p >> (5 + IT_ENVELOPE_SHIFT); - } - return pan; -} - - - -/* Note: if a click remover is provided, and store_end_sample is set, then - * the end point will be computed twice. This situation should not arise. - */ -static long render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix) -{ - int bits; - - int pan; - float span; /* separated pan, range -1 to 1; garbage for surround */ - - long size_rendered; - - if (playing->flags & IT_PLAYING_DEAD) - return 0; - - if (*left_to_mix <= 0) - volume = 0; - - bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; - - pan = apply_pan_envelope(playing); - span = (pan - (32<<8)) * sigrenderer->sigdata->pan_separation * (1.0f / ((32<<8) * 128)); - - if (volume == 0) { - if (playing->sample->flags & IT_SAMPLE_STEREO) - size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta); - else - size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta); - } else { - if (sigrenderer->n_channels == 2) { - float lvol, rvol; - lvol = volume; - if (!IT_IS_SURROUND_SHIFTED(pan)) lvol *= 1.0f - span; - rvol = -lvol; - if (!IT_IS_SURROUND_SHIFTED(pan)) rvol += 2.0f * volume; - if (playing->sample->flags & IT_SAMPLE_STEREO) { - if (sigrenderer->click_remover) { - sample_t click[2]; - dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click); - dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); - dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); - } - size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta); - if (store_end_sample) { - sample_t click[2]; - dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click); - samples[0][(pos + size_rendered) * 2] = click[0]; - samples[0][(pos + size_rendered) * 2 + 1] = click[1]; - } - if (sigrenderer->click_remover) { - sample_t click[2]; - dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click); - dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); - dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); - } - } else { - if (sigrenderer->click_remover) { - sample_t click[2]; - dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click); - dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); - dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); - } - size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta); - if (store_end_sample) { - sample_t click[2]; - dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click); - samples[0][(pos + size_rendered) * 2] = click[0]; - samples[0][(pos + size_rendered) * 2 + 1] = click[1]; - } - if (sigrenderer->click_remover) { - sample_t click[2]; - dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click); - dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); - dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); - } - } - } else { - if (playing->sample->flags & IT_SAMPLE_STEREO) { - float lvol, rvol; - lvol = 0.5f * volume; - if (!IT_IS_SURROUND_SHIFTED(pan)) lvol *= 1.0f - span; - rvol = lvol; - if (!IT_IS_SURROUND_SHIFTED(pan)) rvol = volume - rvol; - if (sigrenderer->click_remover) { - sample_t click; - dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click); - dumb_record_click(sigrenderer->click_remover[0], pos, click); - } - size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, lvol, rvol, delta); - if (store_end_sample) - dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &samples[0][pos + size_rendered]); - if (sigrenderer->click_remover) { - sample_t click; - dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click); - dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); - } - } else { - if (sigrenderer->click_remover) { - sample_t click; - dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, volume, &click); - dumb_record_click(sigrenderer->click_remover[0], pos, click); - } - size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, volume, delta); - if (store_end_sample) - dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, volume, &samples[0][pos + size_rendered]); - if (sigrenderer->click_remover) { - sample_t click; - dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, volume, &click); - dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); - } - } - } - (*left_to_mix)--; - } - - if (playing->resampler.dir == 0) - playing->flags |= IT_PLAYING_DEAD; - - return size_rendered; -} - - - -typedef struct IT_TO_MIX -{ - IT_PLAYING *playing; - float volume; -} -IT_TO_MIX; - - - -static int it_to_mix_compare(const void *e1, const void *e2) -{ - if (((const IT_TO_MIX *)e1)->volume > ((const IT_TO_MIX *)e2)->volume) - return -1; - - if (((const IT_TO_MIX *)e1)->volume < ((const IT_TO_MIX *)e2)->volume) - return 1; - - return 0; -} - - - -static void apply_pitch_modifications(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing, float *delta, int *cutoff) -{ - { - int sample_vibrato_shift = it_sine[playing->sample_vibrato_time]; - - if (sigdata->flags & IT_WAS_AN_XM) { - int depth = playing->sample->vibrato_depth; /* True depth */ - if (playing->sample->vibrato_rate) { - depth *= playing->sample_vibrato_depth; /* Tick number */ - depth /= playing->sample->vibrato_rate; /* XM sweep */ - } - sample_vibrato_shift *= depth; - } else - sample_vibrato_shift *= playing->sample_vibrato_depth >> 8; - - sample_vibrato_shift >>= 4; - - *delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift); - } - - if (playing->env_instrument && - (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_ON)) - { - int p = envelope_get_y(&playing->env_instrument->pitch_envelope, &playing->pitch_envelope); - if (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_PITCH_IS_FILTER) - *cutoff = (*cutoff * (p+(32<> (6 + IT_ENVELOPE_SHIFT); - else - *delta *= (float)pow(DUMB_PITCH_BASE, p >> (IT_ENVELOPE_SHIFT - 7)); - } -} - - - -static void render(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long size, sample_t **samples) -{ - int i; - - int n_to_mix = 0; - IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS]; - int left_to_mix = dumb_it_max_to_mix; - - sample_t **samples_to_filter = NULL; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { - to_mix[n_to_mix].playing = sigrenderer->channel[i].playing; - to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->channel[i].playing, volume); - n_to_mix++; - } - } - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { - if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */ - to_mix[n_to_mix].playing = sigrenderer->playing[i]; - to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->playing[i], volume); - n_to_mix++; - } - } - - if (volume != 0) - qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare); - - for (i = 0; i < n_to_mix; i++) { - IT_PLAYING *playing = to_mix[i].playing; - float note_delta = delta * playing->delta; - int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; - - apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); - - if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { - playing->true_filter_cutoff = cutoff; - playing->true_filter_resonance = playing->filter_resonance; - } - - if (to_mix[i].volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { - if (!samples_to_filter) { - samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); - if (!samples_to_filter) { - render_playing(sigrenderer, playing, 0, note_delta, pos, size, NULL, 0, &left_to_mix); - continue; - } - } - { - long size_rendered; - DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; - dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1)); - sigrenderer->click_remover = NULL; - size_rendered = render_playing(sigrenderer, playing, to_mix[i].volume, note_delta, 0, size, samples_to_filter, 1, &left_to_mix); - sigrenderer->click_remover = cr; - if (sigrenderer->n_channels == 2) { - it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0], pos, samples_to_filter[0], size_rendered, - 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); - it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[0]+1, pos, samples_to_filter[0]+1, size_rendered, - 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); - } else { - it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0], pos, samples_to_filter[0], size_rendered, - 1, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); - } - // FIXME: filtering is not prevented by low left_to_mix! - // FIXME: change 'warning' to 'FIXME' everywhere - } - } else { - it_reset_filter_state(&playing->filter_state[0]); - it_reset_filter_state(&playing->filter_state[1]); - render_playing(sigrenderer, playing, to_mix[i].volume, note_delta, pos, size, samples, 0, &left_to_mix); - } - } - - destroy_sample_buffer(samples_to_filter); - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - if (sigrenderer->channel[i].playing) { - //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { - // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. - if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) { - free(sigrenderer->channel[i].playing); - sigrenderer->channel[i].playing = NULL; - } - } - } - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { - if (sigrenderer->playing[i]) { - if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { - free(sigrenderer->playing[i]); - sigrenderer->playing[i] = NULL; - } - } - } -} - - - -static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder, IT_CALLBACKS *callbacks, DUMB_CLICK_REMOVER **cr) -{ - DUMB_IT_SIGRENDERER *sigrenderer; - int i; - - if (startorder > sigdata->n_orders) { - free(callbacks); - dumb_destroy_click_remover_array(n_channels, cr); - return NULL; - } - - sigrenderer = malloc(sizeof(*sigrenderer)); - if (!sigrenderer) { - free(callbacks); - dumb_destroy_click_remover_array(n_channels, cr); - return NULL; - } - - sigrenderer->callbacks = callbacks; - sigrenderer->click_remover = cr; - - sigrenderer->sigdata = sigdata; - sigrenderer->n_channels = n_channels; - sigrenderer->globalvolume = sigdata->global_volume; - sigrenderer->tempo = sigdata->tempo; - - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { - IT_CHANNEL *channel = &sigrenderer->channel[i]; -#if IT_CHANNEL_MUTED != 1 -#error this is wrong -#endif - channel->flags = sigdata->channel_pan[i] >> 7; - channel->volume = (sigdata->flags & IT_WAS_AN_XM) ? 0 : 64; - channel->pan = sigdata->channel_pan[i] & 0x7F; - channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; - channel->channelvolume = sigdata->channel_volume[i]; - channel->instrument = 0; - channel->note = 0; - channel->SFmacro = 0; - channel->filter_cutoff = 127; - channel->filter_resonance = 0; - channel->xm_retrig = 0; - channel->retrig_tick = 0; - channel->tremor_time = 0; - channel->midi_state = 0; - channel->lastvolslide = 0; - channel->lastDKL = 0; - channel->lastEF = 0; - channel->lastG = 0; - channel->lastHspeed = 0; - channel->lastHdepth = 0; - channel->lastRspeed = 0; - channel->lastRdepth = 0; - channel->lastI = 0; - channel->lastJ = 0; - channel->lastN = 0; - channel->lastO = 0; - channel->high_offset = 0; - channel->lastP = 0; - channel->lastQ = 0; - channel->lastS = 0; - channel->pat_loop_row = 0; - channel->pat_loop_count = 0; - channel->pat_loop_end_row = 0; - channel->lastW = 0; - channel->xm_lastE1 = 0; - channel->xm_lastE2 = 0; - channel->xm_lastEA = 0; - channel->xm_lastEB = 0; - channel->xm_lastX1 = 0; - channel->xm_lastX2 = 0; - channel->playing = NULL; - } - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) - sigrenderer->playing[i] = NULL; - - sigrenderer->speed = sigdata->speed; - - sigrenderer->processrow = 0xFFFE; - sigrenderer->n_rows = 0; - sigrenderer->breakrow = 0; - sigrenderer->pat_loop_row = -1; - sigrenderer->rowcount = 1; - sigrenderer->order = startorder; - sigrenderer->row = 0; - sigrenderer->processorder = startorder - 1; - sigrenderer->tick = 1; - - { - int order; - for (order = 0; order < sigdata->n_orders; order++) { - int n = sigdata->order[order]; - if (n < sigdata->n_patterns) goto found_valid_order; -#ifdef INVALID_ORDERS_END_SONG - if (n != IT_ORDER_SKIP) -#else - if (n == IT_ORDER_END) -#endif - break; - } - /* If we get here, there were no valid orders in the song. */ - _dumb_it_end_sigrenderer(sigrenderer); - return NULL; - } - found_valid_order: - - sigrenderer->time_left = 0; - sigrenderer->sub_time_left = 0; - - return sigrenderer; -} - - - -void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) -{ - if (sigrenderer) { - sigrenderer->callbacks->loop = callback; - sigrenderer->callbacks->loop_data = data; - } -} - - - -void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) -{ - if (sigrenderer) { - sigrenderer->callbacks->xm_speed_zero = callback; - sigrenderer->callbacks->xm_speed_zero_data = data; - } -} - - - -void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data) -{ - if (sigrenderer) { - sigrenderer->callbacks->midi = callback; - sigrenderer->callbacks->midi_data = data; - } -} - - - -static IT_CALLBACKS *create_callbacks(void) -{ - IT_CALLBACKS *callbacks = malloc(sizeof(*callbacks)); - if (!callbacks) return NULL; - callbacks->loop = NULL; - callbacks->xm_speed_zero = NULL; - callbacks->midi = NULL; - return callbacks; -} - - - -static DUMB_IT_SIGRENDERER *dumb_it_init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder) -{ - IT_CALLBACKS *callbacks; - - if (!sigdata) return NULL; - - callbacks = create_callbacks(); - if (!callbacks) return NULL; - - return init_sigrenderer(sigdata, n_channels, startorder, callbacks, - dumb_create_click_remover_array(n_channels)); -} - - - -DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder) -{ - DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh); - DUMB_IT_SIGRENDERER *itsr = dumb_it_init_sigrenderer(itsd, n_channels, startorder); - return duh_encapsulate_it_sigrenderer(itsr, n_channels, 0); -} - - - -static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_channels, long pos) -{ - DUMB_IT_SIGDATA *sigdata = vsigdata; - DUMB_IT_SIGRENDERER *sigrenderer; - - (void)duh; - - { - IT_CALLBACKS *callbacks = create_callbacks(); - if (!callbacks) return NULL; - - if (sigdata->checkpoint) { - IT_CHECKPOINT *checkpoint = sigdata->checkpoint; - while (checkpoint->next && checkpoint->next->time < pos) - checkpoint = checkpoint->next; - sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, n_channels, callbacks); - if (!sigrenderer) return NULL; - sigrenderer->click_remover = dumb_create_click_remover_array(n_channels); - pos -= checkpoint->time; - } else { - sigrenderer = init_sigrenderer(sigdata, n_channels, 0, callbacks, - dumb_create_click_remover_array(n_channels)); - if (!sigrenderer) return NULL; - } - } - - while (pos >= sigrenderer->time_left) { - render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL); - - pos -= sigrenderer->time_left; - sigrenderer->time_left = 0; - - if (process_tick(sigrenderer)) { - _dumb_it_end_sigrenderer(sigrenderer); - return NULL; - } - } - - render(sigrenderer, 0, 1.0f, 0, pos, NULL); - sigrenderer->time_left -= pos; - - return sigrenderer; -} - - - -static long it_sigrenderer_get_samples( - sigrenderer_t *vsigrenderer, - float volume, float delta, - long size, sample_t **samples -) -{ - DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; - long pos; - int dt; - long todo; - LONG_LONG t; - - if (sigrenderer->order < 0) return 0; // problematic - - pos = 0; - dt = (int)(delta * 65536.0f + 0.5f); - - /* When samples is finally used in render_playing(), it won't be used if - * volume is 0. - */ - if (!samples) volume = 0; - - for (;;) { - todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); - - if (todo >= size) - break; - - render(sigrenderer, volume, delta, pos, todo, samples); - - pos += todo; - size -= todo; - - t = sigrenderer->sub_time_left - (LONG_LONG)todo * dt; - sigrenderer->sub_time_left = (long)t & 65535; - sigrenderer->time_left += (long)(t >> 16); - - if (process_tick(sigrenderer)) { - sigrenderer->order = -1; - sigrenderer->row = -1; - return pos; - } - } - - render(sigrenderer, volume, delta, pos, size, samples); - - pos += size; - - t = sigrenderer->sub_time_left - (LONG_LONG)size * dt; - sigrenderer->sub_time_left = (long)t & 65535; - sigrenderer->time_left += (long)(t >> 16); - - if (samples) - dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta); - - return pos; -} - - - -static void it_sigrenderer_get_current_sample(sigrenderer_t *vsigrenderer, float volume, sample_t *samples) -{ - DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; - (void)volume; // for consideration: in any of these such functions, is 'volume' going to be required? - dumb_click_remover_get_offset_array(sigrenderer->n_channels, sigrenderer->click_remover, samples); -} - - - -void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) -{ - DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; - - int i; - - if (sigrenderer) { - for (i = 0; i < DUMB_IT_N_CHANNELS; i++) - if (sigrenderer->channel[i].playing) - free(sigrenderer->channel[i].playing); - - for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) - if (sigrenderer->playing[i]) - free(sigrenderer->playing[i]); - - dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover); - - if (sigrenderer->callbacks) - free(sigrenderer->callbacks); - - free(vsigrenderer); - } -} - - - -DUH_SIGTYPE_DESC _dumb_sigtype_it = { - SIGTYPE_IT, - NULL, - &it_start_sigrenderer, - NULL, - &it_sigrenderer_get_samples, - &it_sigrenderer_get_current_sample, - &_dumb_it_end_sigrenderer, - &_dumb_it_unload_sigdata -}; - - - -DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos) -{ - return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos); -} - - - -DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer) -{ - return duh_get_raw_sigrenderer(sigrenderer, SIGTYPE_IT); -} - - - -/* Values of 64 or more will access NNA channels here. */ -void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state) -{ - IT_PLAYING *playing; - int t; /* temporary var for holding accurate pan and filter cutoff */ - float delta; - ASSERT(channel < DUMB_IT_TOTAL_CHANNELS); - if (!sr) { state->sample = 0; return; } - if (channel >= DUMB_IT_N_CHANNELS) { - playing = sr->playing[channel - DUMB_IT_N_CHANNELS]; - if (!playing) { state->sample = 0; return; } - } else { - playing = sr->channel[channel].playing; - if (!playing) { state->sample = 0; return; } - } - - if (playing->flags & IT_PLAYING_DEAD) { state->sample = 0; return; } - - state->channel = playing->channel - sr->channel; - state->sample = playing->sampnum; - state->volume = calculate_volume(sr, playing, 1.0f); - - t = apply_pan_envelope(playing); - state->pan = (unsigned char)((t + 128) >> IT_ENVELOPE_SHIFT); - state->subpan = (signed char)t; - - delta = playing->delta * 65536.0f; - t = playing->filter_cutoff << IT_ENVELOPE_SHIFT; - apply_pitch_modifications(sr->sigdata, playing, &delta, &t); - state->freq = (int)delta; - if (t == 127 << IT_ENVELOPE_SHIFT && playing->filter_resonance == 0) { - state->filter_resonance = playing->true_filter_resonance; - t = playing->true_filter_cutoff; - } else - state->filter_resonance = playing->filter_resonance; - state->filter_cutoff = (unsigned char)(t >> 8); - state->filter_subcutoff = (unsigned char)t; -} - - - -int dumb_it_callback_terminate(void *data) -{ - (void)data; - return 1; -} - - - -int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte) -{ - (void)data; - (void)channel; - (void)midi_byte; - return 1; -} - - - -#define IT_CHECKPOINT_INTERVAL (30 * 65536) /* Half a minute */ - - - -/* Returns the length of the module, up until it first loops. */ -long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata) -{ - IT_CHECKPOINT *checkpoint; - if (!sigdata) return 0; - checkpoint = sigdata->checkpoint; - while (checkpoint) { - IT_CHECKPOINT *next = checkpoint->next; - _dumb_it_end_sigrenderer(checkpoint->sigrenderer); - free(checkpoint); - checkpoint = next; - } - sigdata->checkpoint = NULL; - checkpoint = malloc(sizeof(*checkpoint)); - if (!checkpoint) return 0; - checkpoint->time = 0; - checkpoint->sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, 0); - if (!checkpoint->sigrenderer) { - free(checkpoint); - return 0; - } - checkpoint->sigrenderer->callbacks->loop = &dumb_it_callback_terminate; - checkpoint->sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate; - sigdata->checkpoint = checkpoint; - - for (;;) { - long l; - DUMB_IT_SIGRENDERER *sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, 0, checkpoint->sigrenderer->callbacks); - checkpoint->sigrenderer->callbacks = NULL; - if (!sigrenderer) { - checkpoint->next = NULL; - return checkpoint->time; - } - - l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL); - if (l < IT_CHECKPOINT_INTERVAL) { - _dumb_it_end_sigrenderer(sigrenderer); - checkpoint->next = NULL; - return checkpoint->time + l; - } - - checkpoint->next = malloc(sizeof(*checkpoint->next)); - if (!checkpoint->next) { - _dumb_it_end_sigrenderer(sigrenderer); - return checkpoint->time + IT_CHECKPOINT_INTERVAL; - } - - checkpoint->next->time = checkpoint->time + IT_CHECKPOINT_INTERVAL; - checkpoint = checkpoint->next; - checkpoint->sigrenderer = sigrenderer; - } -} - - - -void dumb_it_do_initial_runthrough(DUH *duh) -{ - if (duh) { - DUMB_IT_SIGDATA *sigdata = duh_get_it_sigdata(duh); - - if (sigdata) - duh_set_length(duh, dumb_it_build_checkpoints(sigdata)); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itrender.c - Code to render an Impulse Tracker / / \ \ + * module. | < / \_ + * | \/ /\ / + * Written - painstakingly - by entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/dumb.h" +#include "internal/it.h" +#include "internal/lpc.h" + +#include "internal/blip_buf.h" +#include "internal/lanczos_resampler.h" + +// #define BIT_ARRAY_BULLSHIT + +#define END_RAMPING +#define RAMP_DOWN + +static IT_PLAYING *new_playing() +{ + IT_PLAYING * r = (IT_PLAYING*) malloc(sizeof(*r)); + if (r) + { + r->resampler.blip_buffer[0] = blip_new( 256 ); + if ( !r->resampler.blip_buffer[0] ) + { + free( r ); + return NULL; + } + r->resampler.blip_buffer[1] = blip_new( 256 ); + if ( !r->resampler.blip_buffer[1] ) + { + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + blip_set_rates(r->resampler.blip_buffer[0], 65536, 1); + blip_set_rates(r->resampler.blip_buffer[1], 65536, 1); + r->resampler.fir_resampler_ratio = 0.0; + r->resampler.fir_resampler[0] = lanczos_resampler_create(); + if ( !r->resampler.fir_resampler[0] ) { + free( r->resampler.blip_buffer[1] ); + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + r->resampler.fir_resampler[1] = lanczos_resampler_create(); + if ( !r->resampler.fir_resampler[1] ) { + lanczos_resampler_delete( r->resampler.fir_resampler[0] ); + free( r->resampler.blip_buffer[1] ); + free( r->resampler.blip_buffer[0] ); + free( r ); + return NULL; + } + } + return r; +} + +static void free_playing(IT_PLAYING * r) +{ + lanczos_resampler_delete( r->resampler.fir_resampler[1] ); + lanczos_resampler_delete( r->resampler.fir_resampler[0] ); + blip_delete( r->resampler.blip_buffer[1] ); + blip_delete( r->resampler.blip_buffer[0] ); + free( r ); +} + +static IT_PLAYING *dup_playing(IT_PLAYING *src, IT_CHANNEL *dstchannel, IT_CHANNEL *srcchannel) +{ + IT_PLAYING *dst; + + if (!src) return NULL; + + dst = malloc(sizeof(*dst)); + if (!dst) return NULL; + + dst->flags = src->flags; + dst->resampling_quality = src->resampling_quality; + + ASSERT(src->channel); + dst->channel = &dstchannel[src->channel - srcchannel]; + dst->sample = src->sample; + dst->instrument = src->instrument; + dst->env_instrument = src->env_instrument; + + dst->sampnum = src->sampnum; + dst->instnum = src->instnum; + +#ifdef END_RAMPING + dst->declick_stage = src->declick_stage; + dst->declick_volume = src->declick_volume; +#endif + + dst->float_volume[0] = src->float_volume[0]; + dst->float_volume[1] = src->float_volume[1]; + + dst->ramp_volume[0] = src->ramp_volume[0]; + dst->ramp_volume[1] = src->ramp_volume[1]; + + dst->ramp_delta[0] = src->ramp_delta[0]; + dst->ramp_delta[1] = src->ramp_delta[1]; + + dst->channel_volume = src->channel_volume; + + dst->volume = src->volume; + dst->pan = src->pan; + + dst->volume_offset = src->volume_offset; + dst->panning_offset = src->panning_offset; + + dst->note = src->note; + + dst->enabled_envelopes = src->enabled_envelopes; + + dst->filter_cutoff = src->filter_cutoff; + dst->filter_resonance = src->filter_resonance; + + dst->true_filter_cutoff = src->true_filter_cutoff; + dst->true_filter_resonance = src->true_filter_resonance; + + dst->vibrato_speed = src->vibrato_speed; + dst->vibrato_depth = src->vibrato_depth; + dst->vibrato_n = src->vibrato_n; + dst->vibrato_time = src->vibrato_time; + dst->vibrato_waveform = src->vibrato_waveform; + + dst->tremolo_speed = src->tremolo_speed; + dst->tremolo_depth = src->tremolo_depth; + dst->tremolo_time = src->tremolo_time; + dst->tremolo_waveform = src->tremolo_waveform; + + dst->panbrello_speed = src->panbrello_speed; + dst->panbrello_depth = src->panbrello_depth; + dst->panbrello_time = src->panbrello_time; + dst->panbrello_waveform = src->panbrello_waveform; + dst->panbrello_random = src->panbrello_random; + + dst->sample_vibrato_time = src->sample_vibrato_time; + dst->sample_vibrato_waveform = src->sample_vibrato_waveform; + dst->sample_vibrato_depth = src->sample_vibrato_depth; + + dst->slide = src->slide; + dst->delta = src->delta; + dst->finetune = src->finetune; + + dst->volume_envelope = src->volume_envelope; + dst->pan_envelope = src->pan_envelope; + dst->pitch_envelope = src->pitch_envelope; + + dst->fadeoutcount = src->fadeoutcount; + + dst->filter_state[0] = src->filter_state[0]; + dst->filter_state[1] = src->filter_state[1]; + + dst->resampler = src->resampler; + dst->resampler.pickup_data = dst; + dst->resampler.blip_buffer[0] = blip_dup( src->resampler.blip_buffer[0] ); + if ( !dst->resampler.blip_buffer[0] ) + { + free( dst ); + return NULL; + } + dst->resampler.blip_buffer[1] = blip_dup( src->resampler.blip_buffer[1] ); + if ( !dst->resampler.blip_buffer[1] ) + { + blip_delete( dst->resampler.blip_buffer[0] ); + free( dst ); + return NULL; + } + dst->resampler.fir_resampler_ratio = src->resampler.fir_resampler_ratio; + dst->resampler.fir_resampler[0] = lanczos_resampler_dup( src->resampler.fir_resampler[0] ); + if ( !dst->resampler.fir_resampler[0] ) { + blip_delete( dst->resampler.blip_buffer[1] ); + blip_delete( dst->resampler.blip_buffer[0] ); + free( dst ); + return NULL; + } + dst->resampler.fir_resampler[1] = lanczos_resampler_dup( src->resampler.fir_resampler[1] ); + if ( !dst->resampler.fir_resampler[1] ) { + lanczos_resampler_delete( dst->resampler.fir_resampler[0] ); + blip_delete( dst->resampler.blip_buffer[1] ); + blip_delete( dst->resampler.blip_buffer[0] ); + free( dst ); + return NULL; + } + dst->time_lost = src->time_lost; + + //dst->output = src->output; + + return dst; +} + + + +static void dup_channel(IT_CHANNEL *dst, IT_CHANNEL *src) +{ + dst->flags = src->flags; + + dst->volume = src->volume; + dst->volslide = src->volslide; + dst->xm_volslide = src->xm_volslide; + dst->panslide = src->panslide; + + dst->pan = src->pan; + dst->truepan = src->truepan; + + dst->channelvolume = src->channelvolume; + dst->channelvolslide = src->channelvolslide; + + dst->instrument = src->instrument; + dst->note = src->note; + + dst->SFmacro = src->SFmacro; + + dst->filter_cutoff = src->filter_cutoff; + dst->filter_resonance = src->filter_resonance; + + dst->key_off_count = src->key_off_count; + dst->note_cut_count = src->note_cut_count; + dst->note_delay_count = src->note_delay_count; + dst->note_delay_entry = src->note_delay_entry; + + dst->new_note_action = src->new_note_action; + + dst->arpeggio_table = src->arpeggio_table; + memcpy(dst->arpeggio_offsets, src->arpeggio_offsets, sizeof(dst->arpeggio_offsets)); + dst->retrig = src->retrig; + dst->xm_retrig = src->xm_retrig; + dst->retrig_tick = src->retrig_tick; + + dst->tremor_time = src->tremor_time; + + dst->vibrato_waveform = src->vibrato_waveform; + dst->tremolo_waveform = src->tremolo_waveform; + dst->panbrello_waveform = src->panbrello_waveform; + + dst->portamento = src->portamento; + dst->toneporta = src->toneporta; + dst->toneslide = src->toneslide; + dst->toneslide_tick = src->toneslide_tick; + dst->last_toneslide_tick = src->last_toneslide_tick; + dst->ptm_toneslide = src->ptm_toneslide; + dst->ptm_last_toneslide = src->ptm_last_toneslide; + dst->okt_toneslide = src->okt_toneslide; + dst->destnote = src->destnote; + + dst->glissando = src->glissando; + + dst->sample = src->sample; + dst->truenote = src->truenote; + + dst->midi_state = src->midi_state; + + dst->lastvolslide = src->lastvolslide; + dst->lastDKL = src->lastDKL; + dst->lastEF = src->lastEF; + dst->lastG = src->lastG; + dst->lastHspeed = src->lastHspeed; + dst->lastHdepth = src->lastHdepth; + dst->lastRspeed = src->lastRspeed; + dst->lastRdepth = src->lastRdepth; + dst->lastYspeed = src->lastYspeed; + dst->lastYdepth = src->lastYdepth; + dst->lastI = src->lastI; + dst->lastJ = src->lastJ; + dst->lastN = src->lastN; + dst->lastO = src->lastO; + dst->high_offset = src->high_offset; + dst->lastP = src->lastP; + dst->lastQ = src->lastQ; + dst->lastS = src->lastS; + dst->pat_loop_row = src->pat_loop_row; + dst->pat_loop_count = src->pat_loop_count; + dst->pat_loop_end_row = src->pat_loop_end_row; + dst->lastW = src->lastW; + + dst->xm_lastE1 = src->xm_lastE1; + dst->xm_lastE2 = src->xm_lastE2; + dst->xm_lastEA = src->xm_lastEA; + dst->xm_lastEB = src->xm_lastEB; + dst->xm_lastX1 = src->xm_lastX1; + dst->xm_lastX2 = src->xm_lastX2; + + dst->inv_loop_delay = src->inv_loop_delay; + dst->inv_loop_speed = src->inv_loop_speed; + dst->inv_loop_offset = src->inv_loop_offset; + + dst->playing = dup_playing(src->playing, dst, src); + +#ifdef BIT_ARRAY_BULLSHIT + dst->played_patjump = bit_array_dup(src->played_patjump); + dst->played_patjump_order = src->played_patjump_order; +#endif + + //dst->output = src->output; +} + + + +/* Allocate the new callbacks first, then pass them to this function! + * It will free them on failure. + */ +static DUMB_IT_SIGRENDERER *dup_sigrenderer(DUMB_IT_SIGRENDERER *src, int n_channels, IT_CALLBACKS *callbacks) +{ + DUMB_IT_SIGRENDERER *dst; + int i; + + if (!src) { + if (callbacks) free(callbacks); + return NULL; + } + + dst = malloc(sizeof(*dst)); + if (!dst) { + if (callbacks) free(callbacks); + return NULL; + } + + dst->sigdata = src->sigdata; + + dst->n_channels = n_channels; + + dst->resampling_quality = src->resampling_quality; + + dst->globalvolume = src->globalvolume; + dst->globalvolslide = src->globalvolslide; + + dst->tempo = src->tempo; + dst->temposlide = src->temposlide; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) + dup_channel(&dst->channel[i], &src->channel[i]); + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + dst->playing[i] = dup_playing(src->playing[i], dst->channel, src->channel); + + dst->tick = src->tick; + dst->speed = src->speed; + dst->rowcount = src->rowcount; + + dst->order = src->order; + dst->row = src->row; + dst->processorder = src->processorder; + dst->processrow = src->processrow; + dst->breakrow = src->breakrow; + + dst->restart_position = src->restart_position; + + dst->n_rows = src->n_rows; + + dst->entry_start = src->entry_start; + dst->entry = src->entry; + dst->entry_end = src->entry_end; + + dst->time_left = src->time_left; + dst->sub_time_left = src->sub_time_left; + + dst->click_remover = NULL; + + dst->callbacks = callbacks; + +#ifdef BIT_ARRAY_BULLSHIT + dst->played = bit_array_dup(src->played); + + dst->looped = src->looped; + dst->time_played = src->time_played; + dst->row_timekeeper = timekeeping_array_dup(src->row_timekeeper); +#endif + + dst->gvz_time = src->gvz_time; + dst->gvz_sub_time = src->gvz_sub_time; + + //dst->max_output = src->max_output; + + return dst; +} + + + +static const IT_MIDI default_midi = { + /* unsigned char SFmacro[16][16]; */ + { + {0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + /* unsigned char SFmacrolen[16]; */ + {4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + /* unsigned short SFmacroz[16]; */ + /* Bitfield; bit 0 set = z in first position */ + { + 0x0008, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 + }, + /* unsigned char Zmacro[128][16]; */ + { + {0xF0, 0xF0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xF0, 0xF0, 0x01, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} + }, + /* unsigned char Zmacrolen[128]; */ + { + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + } +}; + + + +static void it_reset_filter_state(IT_FILTER_STATE *state) +{ + state->currsample = 0; + state->prevsample = 0; +} + + + +#define LOG10 2.30258509299 + +/* IMPORTANT: This function expects one extra sample in 'src' so it can apply + * click removal. It reads size samples, starting from src[0], and writes its + * output starting at dst[pos]. The pos parameter is required for getting + * click removal right. + */ + +static void it_filter_int(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance) +{ + sample_t currsample = state->currsample; + sample_t prevsample = state->prevsample; + + float a, b, c; + + long datasize; + + { + float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24< 2.0f) d = 2.0f; + d = (loss - d) * inv_angle; + e = inv_angle * inv_angle; + a = 1.0f / (1.0f + d + e); + c = -e * a; + b = 1.0f - a - c; +#else + a = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss); + c = -(inv_angle*inv_angle) * a; + b = 1.0f - a - c; +#endif + } + + dst += pos * step; + datasize = size * step; + +#define INT_FILTERS +#ifdef INT_FILTERS +#define MULSCA(a, b) ((int)((LONG_LONG)((a) << 4) * (b) >> 32)) +#define SCALEB 12 + { + int ai = (int)(a * (1 << (16+SCALEB))); + int bi = (int)(b * (1 << (16+SCALEB))); + int ci = (int)(c * (1 << (16+SCALEB))); + int i; + + if (cr) { + sample_t startstep = MULSCA(src[0], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos, startstep); + } + + for (i = 0; i < datasize; i += step) { + { + sample_t newsample = MULSCA(src[i], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + prevsample = currsample; + currsample = newsample; + } + dst[i] += currsample; + } + + if (cr) { + sample_t endstep = MULSCA(src[datasize], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos + size, -endstep); + } + } +#else +#error This version is broken - it does not use step, and state should contain floats for it + if (cr) { + float startstep = src[0]*a + currsample*b + prevsample*c; + dumb_record_click(cr, pos, (sample_t)startstep); + } + + { + int i = size % 3; + while (i > 0) { + { + float newsample = *src++*a + currsample*b + prevsample*c; + prevsample = currsample; + currsample = newsample; + } + *dst++ += (sample_t)currsample; + i--; + } + i = size / 3; + while (i > 0) { + float newsample; + /* Gotta love unrolled loops! */ + *dst++ += (sample_t)(newsample = *src++*a + currsample*b + prevsample*c); + *dst++ += (sample_t)(prevsample = *src++*a + newsample*b + currsample*c); + *dst++ += (sample_t)(currsample = *src++*a + prevsample*b + newsample*c); + i--; + } + } + + if (cr) { + float endstep = src[datasize]*a + currsample*b + prevsample*c; + dumb_record_click(cr, pos + size, -(sample_t)endstep); + } +#endif + + state->currsample = currsample; + state->prevsample = prevsample; +} + +#if defined(_USE_SSE) +#include + +static void it_filter_sse(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance) +{ + __m128 data, impulse; + __m128 temp1, temp2; + + sample_t currsample = state->currsample; + sample_t prevsample = state->prevsample; + + float imp[4]; + + //profiler( filter_sse ); On ClawHammer Athlon64 3200+, ~12000 cycles, ~500 for that x87 setup code (as opposed to ~25500 for the original integer code) + + long datasize; + + { + float inv_angle = (float)(sampfreq * pow(0.5, 0.25 + cutoff*(1.0/(24< 2.0f) d = 2.0f; + d = (loss - d) * inv_angle; + e = inv_angle * inv_angle; + imp[0] = 1.0f / (1.0f + d + e); + imp[2] = -e * imp[0]; + imp[1] = 1.0f - imp[0] - imp[2]; +#else + imp[0] = 1.0f / (inv_angle*inv_angle + inv_angle*loss + loss); + imp[2] = -(inv_angle*inv_angle) * imp[0]; + imp[1] = 1.0f - imp[0] - imp[2]; +#endif + imp[3] = 0.0f; + } + + dst += pos * step; + datasize = size * step; + + { + int ai, bi, ci, i; + + if (cr) { + sample_t startstep; + ai = (int)(imp[0] * (1 << (16+SCALEB))); + bi = (int)(imp[1] * (1 << (16+SCALEB))); + ci = (int)(imp[2] * (1 << (16+SCALEB))); + startstep = MULSCA(src[0], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos, startstep); + } + + temp1 = _mm_setzero_ps(); + data = _mm_cvtsi32_ss( temp1, currsample ); + temp2 = _mm_cvtsi32_ss( temp1, prevsample ); + impulse = _mm_loadu_ps( (const float *) &imp ); + data = _mm_shuffle_ps( data, temp2, _MM_SHUFFLE(1, 0, 0, 1) ); + + for (i = 0; i < datasize; i += step) { + temp1 = _mm_cvtsi32_ss( data, src [i] ); + temp1 = _mm_mul_ps( temp1, impulse ); + temp2 = _mm_movehl_ps( temp2, temp1 ); + temp1 = _mm_add_ps( temp1, temp2 ); + temp2 = temp1; + temp2 = _mm_shuffle_ps( temp2, temp1, _MM_SHUFFLE(0, 0, 0, 1) ); + temp1 = _mm_add_ps( temp1, temp2 ); + temp1 = _mm_shuffle_ps( temp1, data, _MM_SHUFFLE(2, 1, 0, 0) ); + data = temp1; + dst [i] += _mm_cvtss_si32( temp1 ); + } + + currsample = _mm_cvtss_si32( temp1 ); + temp1 = _mm_shuffle_ps( temp1, data, _MM_SHUFFLE(0, 0, 0, 2) ); + prevsample = _mm_cvtss_si32( temp1 ); + + if (cr) { + sample_t endstep = MULSCA(src[datasize], ai) + MULSCA(currsample, bi) + MULSCA(prevsample, ci); + dumb_record_click(cr, pos + size, -endstep); + } + } + + state->currsample = currsample; + state->prevsample = prevsample; +} +#endif + +#undef LOG10 + +int _dumb_it_use_sse = 0; + +static void it_filter(DUMB_CLICK_REMOVER *cr, IT_FILTER_STATE *state, sample_t *dst, long pos, sample_t *src, long size, int step, int sampfreq, int cutoff, int resonance) +{ +#if defined(_USE_SSE) + if ( _dumb_it_use_sse ) it_filter_sse( cr, state, dst, pos, src, size, step, sampfreq, cutoff, resonance ); + else +#endif + it_filter_int( cr, state, dst, pos, src, size, step, sampfreq, cutoff, resonance ); +} + + + +static const signed char it_sine[256] = { + 0, 2, 3, 5, 6, 8, 9, 11, 12, 14, 16, 17, 19, 20, 22, 23, + 24, 26, 27, 29, 30, 32, 33, 34, 36, 37, 38, 39, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 56, 57, 58, 59, + 59, 60, 60, 61, 61, 62, 62, 62, 63, 63, 63, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, + 59, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, + 45, 44, 43, 42, 41, 39, 38, 37, 36, 34, 33, 32, 30, 29, 27, 26, + 24, 23, 22, 20, 19, 17, 16, 14, 12, 11, 9, 8, 6, 5, 3, 2, + 0, -2, -3, -5, -6, -8, -9,-11,-12,-14,-16,-17,-19,-20,-22,-23, + -24,-26,-27,-29,-30,-32,-33,-34,-36,-37,-38,-39,-41,-42,-43,-44, + -45,-46,-47,-48,-49,-50,-51,-52,-53,-54,-55,-56,-56,-57,-58,-59, + -59,-60,-60,-61,-61,-62,-62,-62,-63,-63,-63,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-63,-63,-63,-62,-62,-62,-61,-61,-60,-60, + -59,-59,-58,-57,-56,-56,-55,-54,-53,-52,-51,-50,-49,-48,-47,-46, + -45,-44,-43,-42,-41,-39,-38,-37,-36,-34,-33,-32,-30,-29,-27,-26, + -24,-23,-22,-20,-19,-17,-16,-14,-12,-11, -9, -8, -6, -5, -3, -2 +}; + + + +#if 1 +/** WARNING: use these! */ +/** JULIEN: Plus for XM compatibility it could be interesting to rename + * it_sawtooth[] to it_rampdown[], and add an it_rampup[]. + * Also, still for XM compat', twood be good if it was possible to tell the + * the player not to retrig' the waveform on a new instrument. + * Both of these are only for completness though, as I don't think it would + * be very noticeable ;) + */ +/** ENTHEH: IT also has the 'don't retrig' thingy :) */ +static const signed char it_sawtooth[256] = { + 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, + 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, + 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, + 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, + 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, + 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, + 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, + 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, + -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, + -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, + -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, + -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, + -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, + -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, + -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64 +}; + +static const signed char it_squarewave[256] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const signed char it_xm_ramp[256] = { + 0, -1, -1, -2, -2, -3, -3, -4, -4, -5, -5, -6, -6, -7, -7, -8, + -8, -9, -9,-10,-10,-11,-11,-12,-12,-13,-13,-14,-14,-15,-15,-16, + -16,-17,-17,-18,-18,-19,-19,-20,-20,-21,-21,-22,-22,-23,-23,-24, + -24,-25,-25,-26,-26,-27,-27,-28,-28,-29,-29,-30,-30,-31,-31,-32, + -32,-33,-33,-34,-34,-35,-35,-36,-36,-37,-37,-38,-38,-39,-39,-40, + -40,-41,-41,-42,-42,-43,-43,-44,-44,-45,-45,-46,-46,-47,-47,-48, + -48,-49,-49,-50,-50,-51,-51,-52,-52,-53,-53,-54,-54,-55,-55,-56, + -56,-57,-57,-58,-58,-59,-59,-60,-60,-61,-61,-62,-62,-63,-63,-64, + 64, 63, 63, 62, 62, 61, 61, 60, 60, 59, 59, 58, 58, 57, 57, 56, + 56, 55, 55, 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, 49, 49, 48, + 48, 47, 47, 46, 46, 45, 45, 44, 44, 43, 43, 42, 42, 41, 41, 40, + 40, 39, 39, 38, 38, 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, + 32, 31, 31, 30, 30, 29, 29, 28, 28, 27, 27, 26, 26, 25, 25, 24, + 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 16, + 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, + 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0 +}; + +static const signed char it_xm_squarewave[256] = { + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64, + -64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64,-64 +}; + +#endif + + + +static void reset_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + channel->key_off_count = 0; + channel->note_cut_count = 0; + channel->note_delay_count = 0; + } +} + + + +static const unsigned char arpeggio_mod[32] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1}; +static const unsigned char arpeggio_xm[32] = {0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}; +static const unsigned char arpeggio_okt_3[32] = {1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 0}; +static const unsigned char arpeggio_okt_4[32] = {0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1, 0, 2, 0, 1}; +static const unsigned char arpeggio_okt_5[32] = {2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2, 0, 2, 2}; + + + +static void reset_channel_effects(IT_CHANNEL *channel) +{ + channel->volslide = 0; + channel->xm_volslide = 0; + channel->panslide = 0; + channel->channelvolslide = 0; + channel->arpeggio_table = &arpeggio_mod; + memset(channel->arpeggio_offsets, 0, sizeof(channel->arpeggio_offsets)); + channel->retrig = 0; + if (channel->xm_retrig) { + channel->xm_retrig = 0; + channel->retrig_tick = 0; + } + channel->tremor_time &= 127; + channel->portamento = 0; + channel->toneporta = 0; + if (channel->ptm_toneslide) { + channel->ptm_last_toneslide = channel->ptm_toneslide; + channel->last_toneslide_tick = channel->toneslide_tick; + } else + channel->ptm_last_toneslide = 0; + channel->ptm_toneslide = 0; + channel->toneslide_tick = 0; + channel->okt_toneslide = 0; + if (channel->playing) { + channel->playing->vibrato_n = 0; + channel->playing->tremolo_speed = 0; + channel->playing->tremolo_depth = 0; + channel->playing->panbrello_speed = 0; + } +} + +static void reset_effects(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + sigrenderer->globalvolslide = 0; + sigrenderer->temposlide = 0; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + reset_channel_effects(&sigrenderer->channel[i]); + } +} + + + +static void update_tremor(IT_CHANNEL *channel) +{ + if ((channel->tremor_time & 128) && channel->playing) { + if (channel->tremor_time == 128) + channel->tremor_time = (channel->lastI >> 4) | 192; + else if (channel->tremor_time == 192) + channel->tremor_time = (channel->lastI & 15) | 128; + else + channel->tremor_time--; + } +} + + + +static void it_pickup_loop(DUMB_RESAMPLER *resampler, void *data) +{ + resampler->pos -= resampler->end - resampler->start; + ((IT_PLAYING *)data)->time_lost += resampler->end - resampler->start; +} + + + +static void it_pickup_pingpong_loop(DUMB_RESAMPLER *resampler, void *data) +{ + if (resampler->dir < 0) { + resampler->pos = (resampler->start << 1) - 1 - resampler->pos; + resampler->subpos ^= 65535; + resampler->dir = 1; + ((IT_PLAYING *)data)->time_lost += (resampler->end - resampler->start) << 1; + } else { + resampler->pos = (resampler->end << 1) - 1 - resampler->pos; + resampler->subpos ^= 65535; + resampler->dir = -1; + } +} + + + +static void it_pickup_stop_at_end(DUMB_RESAMPLER *resampler, void *data) +{ + (void)data; + + if (resampler->dir < 0) { + resampler->pos = (resampler->start << 1) - 1 - resampler->pos; + resampler->subpos ^= 65535; + /* By rights, time_lost would be updated here. However, there is no + * need at this point; it will not be used. + * + * ((IT_PLAYING *)data)->time_lost += (resampler->src_end - resampler->src_start) << 1; + */ + resampler->dir = 1; + } else + resampler->dir = 0; +} + + + +static void it_pickup_stop_after_reverse(DUMB_RESAMPLER *resampler, void *data) +{ + (void)data; + + resampler->dir = 0; +} + + + +static void it_playing_update_resamplers(IT_PLAYING *playing) +{ + if ((playing->sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { + playing->resampler.start = playing->sample->sus_loop_start; + playing->resampler.end = playing->sample->sus_loop_end; + if (playing->resampler.start == playing->resampler.end) + playing->resampler.pickup = &it_pickup_stop_at_end; + else if (playing->sample->flags & IT_SAMPLE_PINGPONG_SUS_LOOP) + playing->resampler.pickup = &it_pickup_pingpong_loop; + else + playing->resampler.pickup = &it_pickup_loop; + } else if (playing->sample->flags & IT_SAMPLE_LOOP) { + playing->resampler.start = playing->sample->loop_start; + playing->resampler.end = playing->sample->loop_end; + if (playing->resampler.start == playing->resampler.end) + playing->resampler.pickup = &it_pickup_stop_at_end; + else if (playing->sample->flags & IT_SAMPLE_PINGPONG_LOOP) + playing->resampler.pickup = &it_pickup_pingpong_loop; + else + playing->resampler.pickup = &it_pickup_loop; + } else if (playing->flags & IT_PLAYING_REVERSE) { + playing->resampler.start = 0; + playing->resampler.end = playing->sample->length; + playing->resampler.dir = -1; + playing->resampler.pickup = &it_pickup_stop_after_reverse; + } else { + if (playing->sample->flags & IT_SAMPLE_SUS_LOOP) + playing->resampler.start = playing->sample->sus_loop_start; + else + playing->resampler.start = 0; + playing->resampler.end = playing->sample->length; + playing->resampler.pickup = &it_pickup_stop_at_end; + } + ASSERT(playing->resampler.pickup_data == playing); +} + + + +/* This should be called whenever the sample or sample position changes. */ +static void it_playing_reset_resamplers(IT_PLAYING *playing, long pos) +{ + int bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; + int quality = playing->resampling_quality; + int channels = playing->sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality) + quality = playing->sample->max_resampling_quality; + dumb_reset_resampler_n(bits, &playing->resampler, playing->sample->data, channels, pos, 0, 0, quality); + playing->resampler.pickup_data = playing; + playing->time_lost = 0; + playing->flags &= ~IT_PLAYING_DEAD; + it_playing_update_resamplers(playing); +} + +static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel); + +/* Should we only be retriggering short samples on XM? */ + +static void update_retrig(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) +{ + if (channel->xm_retrig) { + channel->retrig_tick--; + if (channel->retrig_tick <= 0) { + if (channel->playing) { + it_playing_reset_resamplers(channel->playing, 0); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel); + channel->retrig_tick = channel->xm_retrig; + } + } else if (channel->retrig & 0x0F) { + channel->retrig_tick--; + if (channel->retrig_tick <= 0) { + if (channel->retrig < 0x10) { + } else if (channel->retrig < 0x20) { + channel->volume--; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x30) { + channel->volume -= 2; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x40) { + channel->volume -= 4; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x50) { + channel->volume -= 8; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x60) { + channel->volume -= 16; + if (channel->volume > 64) channel->volume = 0; + } else if (channel->retrig < 0x70) { + channel->volume <<= 1; + channel->volume /= 3; + } else if (channel->retrig < 0x80) { + channel->volume >>= 1; + } else if (channel->retrig < 0x90) { + } else if (channel->retrig < 0xA0) { + channel->volume++; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xB0) { + channel->volume += 2; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xC0) { + channel->volume += 4; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xD0) { + channel->volume += 8; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xE0) { + channel->volume += 16; + if (channel->volume > 64) channel->volume = 64; + } else if (channel->retrig < 0xF0) { + channel->volume *= 3; + channel->volume >>= 1; + if (channel->volume > 64) channel->volume = 64; + } else { + channel->volume <<= 1; + if (channel->volume > 64) channel->volume = 64; + } + if (channel->playing) { + it_playing_reset_resamplers(channel->playing, 0); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } else if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) it_retrigger_note(sigrenderer, channel); + channel->retrig_tick = channel->retrig & 0x0F; + } + } +} + + +static void update_smooth_effects_playing(IT_PLAYING *playing) +{ + playing->vibrato_time += playing->vibrato_n * + (playing->vibrato_speed << 2); + playing->tremolo_time += playing->tremolo_speed << 2; + playing->panbrello_time += playing->panbrello_speed; + if (playing->panbrello_waveform == 3) + playing->panbrello_random = (rand() % 129) - 64; +} + +static void update_smooth_effects(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + IT_PLAYING *playing = channel->playing; + + if (playing) { + update_smooth_effects_playing(playing); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING *playing = sigrenderer->playing[i]; + + if (playing) { + update_smooth_effects_playing(playing); + } + } +} + + +static const unsigned char pt_tab_invloop[16] = +{ + 0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D, + 0x0F, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80 +}; + +static void update_invert_loop(IT_CHANNEL *channel, IT_SAMPLE *sample) +{ + channel->inv_loop_delay += pt_tab_invloop[channel->inv_loop_speed]; + if (channel->inv_loop_delay >= 0x80) + { + channel->inv_loop_delay = 0; + + if (sample && ((sample->flags & (IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP)) == (IT_SAMPLE_EXISTS | IT_SAMPLE_LOOP)) && !(sample->flags & (IT_SAMPLE_STEREO | IT_SAMPLE_16BIT))) + { + if (sample->loop_end - sample->loop_start >= 4) + { + channel->inv_loop_offset++; + if (channel->inv_loop_offset >= (sample->loop_end - sample->loop_start)) channel->inv_loop_offset = 0; + + ((char *)sample->data)[sample->loop_start + channel->inv_loop_offset] ^= 0xFF; + } + } + } +} + + +static void update_playing_effects(IT_PLAYING *playing) +{ + IT_CHANNEL *channel = playing->channel; + + if (channel->channelvolslide) { + playing->channel_volume = channel->channelvolume; + } + + if (channel->okt_toneslide) { + if (channel->okt_toneslide--) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + } + } else if (channel->ptm_toneslide) { + if (--channel->toneslide_tick == 0) { + channel->toneslide_tick = channel->ptm_toneslide; + if (playing) { + playing->note += channel->toneslide; + if (playing->note >= 120) { + if (channel->toneslide < 0) playing->note = 0; + else playing->note = 119; + } + if (channel->playing == playing) { + channel->note = channel->truenote = playing->note; + } + if (channel->toneslide_retrig) { + it_playing_reset_resamplers(playing, 0); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + } +} + + +static void update_effects(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + if (sigrenderer->globalvolslide) { + sigrenderer->globalvolume += sigrenderer->globalvolslide; + if (sigrenderer->globalvolume > 128) { + if (sigrenderer->globalvolslide >= 0) + sigrenderer->globalvolume = 128; + else + sigrenderer->globalvolume = 0; + } + } + + if (sigrenderer->temposlide) { + sigrenderer->tempo += sigrenderer->temposlide; + if (sigrenderer->tempo < 32) { + if (sigrenderer->temposlide >= 0) + sigrenderer->tempo = 255; + else + sigrenderer->tempo = 32; + } + } + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + IT_PLAYING *playing = channel->playing; + + if (channel->xm_volslide) { + channel->volume += channel->xm_volslide; + if (channel->volume > 64) { + if (channel->xm_volslide >= 0) + channel->volume = 64; + else + channel->volume = 0; + } + } + + if (channel->volslide) { + int clip = (sigrenderer->sigdata->flags & IT_WAS_AN_S3M) ? 63 : 64; + channel->volume += channel->volslide; + if (channel->volume > clip) { + if (channel->volslide >= 0) + channel->volume = clip; + else + channel->volume = 0; + } + } + + if (channel->panslide) { + if (sigrenderer->sigdata->flags & IT_WAS_AN_XM) { + if (IT_IS_SURROUND(channel->pan)) + { + channel->pan = 32; + channel->truepan = 32 + 128 * 64; + } + if (channel->panslide == -128) + channel->truepan = 32; + else + channel->truepan = MID(32, channel->truepan + channel->panslide*64, 32+255*64); + } else { + if (IT_IS_SURROUND(channel->pan)) + { + channel->pan = 32; + } + channel->pan += channel->panslide; + if (channel->pan > 64) { + if (channel->panslide >= 0) + channel->pan = 64; + else + channel->pan = 0; + } + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + } + + if (channel->channelvolslide) { + channel->channelvolume += channel->channelvolslide; + if (channel->channelvolume > 64) { + if (channel->channelvolslide >= 0) + channel->channelvolume = 64; + else + channel->channelvolume = 0; + } + } + + update_tremor(channel); + + update_retrig(sigrenderer, channel); + + if (channel->inv_loop_speed) update_invert_loop(channel, playing ? playing->sample : NULL); + + if (playing) { + playing->slide += channel->portamento; + + if (sigrenderer->sigdata->flags & IT_LINEAR_SLIDES) { + if (channel->toneporta && channel->destnote < 120) { + int currpitch = ((playing->note - 60) << 8) + playing->slide; + int destpitch = (channel->destnote - 60) << 8; + if (currpitch > destpitch) { + currpitch -= channel->toneporta; + if (currpitch < destpitch) { + currpitch = destpitch; + channel->destnote = IT_NOTE_OFF; + } + } else if (currpitch < destpitch) { + currpitch += channel->toneporta; + if (currpitch > destpitch) { + currpitch = destpitch; + channel->destnote = IT_NOTE_OFF; + } + } + playing->slide = currpitch - ((playing->note - 60) << 8); + } + } else { + if (channel->toneporta && channel->destnote < 120) { + float amiga_multiplier = playing->sample->C5_speed * (1.0f / AMIGA_DIVISOR); + + float deltanote = (float)pow(DUMB_SEMITONE_BASE, 60 - playing->note); + /* deltanote is 1.0 for C-5, 0.5 for C-6, etc. */ + + float deltaslid = deltanote - playing->slide * amiga_multiplier; + + float destdelta = (float)pow(DUMB_SEMITONE_BASE, 60 - channel->destnote); + if (deltaslid < destdelta) { + playing->slide -= channel->toneporta; + deltaslid = deltanote - playing->slide * amiga_multiplier; + if (deltaslid > destdelta) { + playing->note = channel->destnote; + playing->slide = 0; + channel->destnote = IT_NOTE_OFF; + } + } else { + playing->slide += channel->toneporta; + deltaslid = deltanote - playing->slide * amiga_multiplier; + if (deltaslid < destdelta) { + playing->note = channel->destnote; + playing->slide = 0; + channel->destnote = IT_NOTE_OFF; + } + } + } + } + + update_playing_effects(playing); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING *playing = sigrenderer->playing[i]; + if (playing) update_playing_effects(playing); + } + + update_smooth_effects(sigrenderer); +} + + +static void it_note_off(IT_PLAYING *playing); + +// This function should be renamed; it doesn't do the 'Update Pattern Variables' operation ittech.txt describes +/* Returns 1 if a pattern loop is happening. */ +static int update_pattern_variables(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_EFFECT) { + switch (entry->effect) { + case IT_JUMP_TO_ORDER: + /* XXX jump and break in same row */ + if ( ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) && + ! ( sigrenderer->processrow & 0x800 ) ) { + sigrenderer->processrow = 0xFFFE & ~0xC00; + } else { + sigrenderer->breakrow = 0; + sigrenderer->processrow = 0xFFFE & ~0x400; + } + sigrenderer->processorder = entry->effectvalue - 1; + break; + + case IT_S: + { + unsigned char effectvalue = entry->effectvalue; + if (sigrenderer->sigdata->flags & IT_WAS_AN_S3M) { + if (effectvalue == 0) + effectvalue = channel->lastDKL; + channel->lastDKL = effectvalue; + } else { + if (effectvalue == 0) + effectvalue = channel->lastS; + } + channel->lastS = effectvalue; + switch (effectvalue >> 4) { + case IT_S_PATTERN_LOOP: + { + unsigned char v = effectvalue & 15; + if (v == 0) { +#ifdef BIT_ARRAY_BULLSHIT + if (!channel->played_patjump) + channel->played_patjump = bit_array_create(256); + else { + if ( channel->played_patjump_order != 0xFFFE && channel->played_patjump_order != sigrenderer->order ) + bit_array_merge(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); + //if (channel->played_patjump_order != sigrenderer->order) + bit_array_reset(channel->played_patjump); + } + channel->played_patjump_order = sigrenderer->order; +#endif + channel->pat_loop_row = sigrenderer->processrow; + } else { + if (channel->pat_loop_count == 0) { +#ifdef BIT_ARRAY_BULLSHIT + /* wft, uninitialized and no start marker yet... */ + if (channel->played_patjump_order == 0xFFFE) { + int n; + bit_array_destroy(channel->played_patjump); + channel->played_patjump = bit_array_create(256); + for (n = channel->pat_loop_row; n <= sigrenderer->row; n++) + bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + n); + channel->played_patjump_order = sigrenderer->order; + } else if (channel->played_patjump_order == sigrenderer->order) { + bit_array_set(channel->played_patjump, sigrenderer->row); + bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); + //bit_array_reset(channel->played_patjump); + } +#endif + channel->pat_loop_count = v; + sigrenderer->breakrow = channel->pat_loop_row; + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { + /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */ + if ((sigrenderer->processrow|0xC00) < 0xFFFE) { + /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */ + if (sigrenderer->processrow < channel->pat_loop_end_row) + sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */ + else + sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */ + channel->pat_loop_end_row = sigrenderer->processrow; + sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */ + } + } else { + /* IT files do this regardless of other flow control effects seen here. */ + sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */ + sigrenderer->processrow = 0xFFFE; + } + return 1; + } else if (--channel->pat_loop_count) { +#ifdef BIT_ARRAY_BULLSHIT + if (channel->played_patjump_order == sigrenderer->order) { + bit_array_set(channel->played_patjump, sigrenderer->row); + bit_array_mask(sigrenderer->played, channel->played_patjump, channel->played_patjump_order * 256); + //bit_array_reset(channel->played_patjump); + } +#endif + sigrenderer->breakrow = channel->pat_loop_row; + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { + /* For XM files, if a loop occurs by itself, keep breakrow set for when the pattern ends - fun bug in FT2! */ + if ((sigrenderer->processrow|0xC00) < 0xFFFE) { + /* Infinite pattern loops are possible, so we check whether the pattern loop we're hitting now is earlier than the last one we hit. */ + if (sigrenderer->processrow < channel->pat_loop_end_row) + sigrenderer->processorder = 0xFFFE; /* suspect infinite loop, so trigger loop callback */ + else + sigrenderer->processorder = 0xFFFF; /* don't trigger loop callback */ + channel->pat_loop_end_row = sigrenderer->processrow; + sigrenderer->processrow = 0xFFFF; /* special case: don't reset breakrow or pat_loop_end_row */ + } + } else { + /* IT files do this regardless of other flow control effects seen here. */ + sigrenderer->processorder = 0xFFFF; /* special case: don't trigger loop callback */ + sigrenderer->processrow = 0xFFFE; + } + return 1; + } else if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { + channel->pat_loop_end_row = 0; + // TODO + /* Findings: + - If a pattern loop completes successfully, and then the pattern terminates, then the next pattern will start on the row corresponding to the E60. + - If a pattern loop doesn't do any loops, and then the pattern terminates, then the next pattern will start on the first row. + - If a break appears to the left of the pattern loop, it jumps into the relevant position in the next pattern, and that's it. + - If a break appears to the right of the pattern loop, it jumps to the start of the next pattern, and that's it. + - If we jump, then effect a loop using an old E60, and then the pattern ends, the next pattern starts on the row corresponding to the E60. + - Theory: breakrow is not cleared when it's a pattern loop effect! + */ + if ((sigrenderer->processrow | 0xC00) < 0xFFFE) // I have no idea if this is correct or not - FT2 is so weird :( + sigrenderer->breakrow = channel->pat_loop_row; /* emulate bug in FT2 */ + } else + channel->pat_loop_row = sigrenderer->processrow + 1; +#ifdef BIT_ARRAY_BULLSHIT + /*channel->played_patjump_order |= 0x8000;*/ + if (channel->played_patjump_order == sigrenderer->order) { + bit_array_destroy(channel->played_patjump); + channel->played_patjump = 0; + channel->played_patjump_order = 0xFFFE; + } + bit_array_clear(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); +#endif + } + } + break; + case IT_S_PATTERN_DELAY: + sigrenderer->rowcount = 1 + (effectvalue & 15); + break; + } + } + } + } + + return 0; +} + + + +/* This function guarantees that channel->sample will always be valid if it + * is nonzero. In other words, to check if it is valid, simply check if it is + * nonzero. + */ +static void instrument_to_sample(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + if (sigdata->flags & IT_USE_INSTRUMENTS) { + if (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments) { + if (channel->note < 120) { + channel->sample = sigdata->instrument[channel->instrument-1].map_sample[channel->note]; + channel->truenote = sigdata->instrument[channel->instrument-1].map_note[channel->note]; + } else + channel->sample = 0; + } else + channel->sample = 0; + } else { + channel->sample = channel->instrument; + channel->truenote = channel->note; + } + if (!(channel->sample >= 1 && channel->sample <= sigdata->n_samples && (sigdata->sample[channel->sample-1].flags & IT_SAMPLE_EXISTS) && sigdata->sample[channel->sample-1].C5_speed)) + channel->sample = 0; +} + + + +static void fix_sample_looping(IT_PLAYING *playing) +{ + if ((playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) == + (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP)) { + if (playing->resampler.dir < 0) { + playing->resampler.pos = (playing->sample->sus_loop_end << 1) - 1 - playing->resampler.pos; + playing->resampler.subpos ^= 65535; + playing->resampler.dir = 1; + } + + playing->resampler.pos += playing->time_lost; + // XXX what + playing->time_lost = 0; + } +} + + + +static void it_compatible_gxx_retrigger(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + int flags = 0; + if (channel->sample) { + if (sigdata->flags & IT_USE_INSTRUMENTS) { + if (!(channel->playing->flags & IT_PLAYING_SUSTAINOFF)) { + if (channel->playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_CARRY) + flags |= 1; + if (channel->playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_CARRY) + flags |= 2; + if (channel->playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_CARRY) + flags |= 4; + } + } + } + if (!(flags & 1)) { + channel->playing->volume_envelope.next_node = 0; + channel->playing->volume_envelope.tick = 0; + } + if (!(flags & 2)) { + channel->playing->pan_envelope.next_node = 0; + channel->playing->pan_envelope.tick = 0; + } + if (!(flags & 4)) { + channel->playing->pitch_envelope.next_node = 0; + channel->playing->pitch_envelope.tick = 0; + } + channel->playing->fadeoutcount = 1024; + // Should we remove IT_PLAYING_BACKGROUND? Test with sample with sustain loop... + channel->playing->flags &= ~(IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING | IT_PLAYING_DEAD); + it_playing_update_resamplers(channel->playing); + + if (!flags && channel->sample) + if (sigdata->flags & IT_USE_INSTRUMENTS) + channel->playing->env_instrument = &sigdata->instrument[channel->instrument-1]; +} + + + +static void it_note_off(IT_PLAYING *playing) +{ + if (playing) { + playing->enabled_envelopes |= IT_ENV_VOLUME; + playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_SUSTAINOFF; + fix_sample_looping(playing); + it_playing_update_resamplers(playing); + if (playing->instrument) + if ((playing->instrument->volume_envelope.flags & (IT_ENVELOPE_ON | IT_ENVELOPE_LOOP_ON)) != IT_ENVELOPE_ON) + playing->flags |= IT_PLAYING_FADING; + } +} + + + +static void xm_note_off(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + if (channel->playing) { + if (!channel->instrument || channel->instrument > sigdata->n_instruments || + !(sigdata->instrument[channel->instrument-1].volume_envelope.flags & IT_ENVELOPE_ON)) + //if (!(entry->mask & IT_ENTRY_INSTRUMENT)) + // dunno what that was there for ... + channel->volume = 0; + channel->playing->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING; + it_playing_update_resamplers(channel->playing); + } +} + + +static void recalculate_it_envelope_node(IT_PLAYING_ENVELOPE *pe, IT_ENVELOPE *e) +{ + int envpos = pe->tick; + unsigned int pt = e->n_nodes - 1; + unsigned int i; + for (i = 0; i < (unsigned int)(e->n_nodes - 1); ++i) + { + if (envpos <= e->node_t[i]) + { + pt = i; + break; + } + } + pe->next_node = pt; +} + + +static void recalculate_it_envelope_nodes(IT_PLAYING *playing) +{ + recalculate_it_envelope_node(&playing->volume_envelope, &playing->env_instrument->volume_envelope); + recalculate_it_envelope_node(&playing->pan_envelope, &playing->env_instrument->pitch_envelope); + recalculate_it_envelope_node(&playing->pitch_envelope, &playing->env_instrument->pitch_envelope); +} + + +static void it_retrigger_note(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel) +{ + int vol_env_tick = 0; + int pan_env_tick = 0; + int pitch_env_tick = 0; + + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + unsigned char nna = ~0; + int i, envelopes_copied = 0; + + if (channel->playing) { + if (channel->note == IT_NOTE_CUT) + nna = NNA_NOTE_CUT; + else if (channel->note == IT_NOTE_OFF) + nna = NNA_NOTE_OFF; + else if (channel->note > 120) + nna = NNA_NOTE_FADE; + else if (!channel->playing->instrument || (channel->playing->flags & IT_PLAYING_DEAD)) + nna = NNA_NOTE_CUT; + else if (channel->new_note_action != 0xFF) + { + nna = channel->new_note_action; + } + else + nna = channel->playing->instrument->new_note_action; + + if (!(channel->playing->flags & IT_PLAYING_SUSTAINOFF) && nna != NNA_NOTE_CUT) + { + vol_env_tick = channel->playing->volume_envelope.tick; + pan_env_tick = channel->playing->pan_envelope.tick; + pitch_env_tick = channel->playing->pitch_envelope.tick; + envelopes_copied = 1; + } + + switch (nna) { + case NNA_NOTE_CUT: +#ifdef RAMP_DOWN + channel->playing->declick_stage = 2; +#else + free_playing(channel->playing); + channel->playing = NULL; +#endif + break; + case NNA_NOTE_OFF: + it_note_off(channel->playing); + break; + case NNA_NOTE_FADE: + channel->playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING; + break; + } + } + + channel->new_note_action = 0xFF; + + if (channel->sample == 0 || channel->note > 120) + return; + + channel->destnote = IT_NOTE_OFF; + + if (channel->playing) { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + + if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS) + { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && playing->channel == channel && playing->instrument->dup_check_type) { + int match = 1; + switch (playing->instrument->dup_check_type) + { + case DCT_NOTE: + match = (channel->truenote == playing->note); + case DCT_SAMPLE: + match = match && (channel->sample == playing->sampnum); + case DCT_INSTRUMENT: + match = match && (channel->instrument == playing->instnum); + break; + } + + if (match) + { + switch (playing->instrument->dup_check_action) + { + case DCA_NOTE_CUT: +#ifdef RAMP_DOWN + playing->declick_stage = 2; +#else + free_playing(playing); + sigrenderer->playing[i] = NULL; +#endif + if (channel->playing == playing) channel->playing = NULL; + break; + case DCA_NOTE_OFF: + if (!(playing->flags & IT_PLAYING_SUSTAINOFF)) + it_note_off(playing); + break; + case DCA_NOTE_FADE: + playing->flags |= IT_PLAYING_BACKGROUND | IT_PLAYING_FADING; + break; + } + } + } + } + } + +/** WARNING - come up with some more heuristics for replacing old notes */ +#if 0 + if (channel->playing) { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]->flags & IT_PLAYING_BACKGROUND) { + write_seqtime(); + sequence_c(SEQUENCE_STOP_SIGNAL); + sequence_c(i); + channel->VChannel = &module->VChannel[i]; + break; + } + } + } +#endif + } + + if (channel->playing) + free_playing(channel->playing); + + channel->playing = new_playing(); + + if (!channel->playing) + return; + + if (!envelopes_copied && sigdata->flags & IT_USE_INSTRUMENTS && nna != NNA_NOTE_CUT) { + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + if (playing->flags & IT_PLAYING_SUSTAINOFF) continue; + vol_env_tick = playing->volume_envelope.tick; + pan_env_tick = playing->pan_envelope.tick; + pitch_env_tick = playing->pitch_envelope.tick; + envelopes_copied = 1; + break; + } + } + + channel->playing->flags = 0; + channel->playing->resampling_quality = sigrenderer->resampling_quality; + channel->playing->channel = channel; + channel->playing->sample = &sigdata->sample[channel->sample-1]; + if (sigdata->flags & IT_USE_INSTRUMENTS) + channel->playing->instrument = &sigdata->instrument[channel->instrument-1]; + else + channel->playing->instrument = NULL; + channel->playing->env_instrument = channel->playing->instrument; + channel->playing->sampnum = channel->sample; + channel->playing->instnum = channel->instrument; +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + channel->playing->channel_volume = channel->channelvolume; + channel->playing->ramp_volume[0] = 31337.0; /* special */ + channel->playing->note = channel->truenote; + channel->playing->enabled_envelopes = 0; + channel->playing->volume_offset = 0; + channel->playing->panning_offset = 0; + //channel->playing->output = channel->output; + if (sigdata->flags & IT_USE_INSTRUMENTS) { + IT_PLAYING * playing = channel->playing; + IT_INSTRUMENT * instrument = playing->instrument; + if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME; + if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING; + if (instrument->pitch_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PITCH; + if (instrument->random_volume) playing->volume_offset = (rand() % (instrument->random_volume * 2 + 1)) - instrument->random_volume; + if (instrument->random_pan) playing->panning_offset = (rand() % (instrument->random_pan * 2 + 1)) - instrument->random_pan; + //if (instrument->output) playing->output = instrument->output; + } + channel->playing->filter_cutoff = 127; + channel->playing->filter_resonance = 0; + channel->playing->true_filter_cutoff = 127 << 8; + channel->playing->true_filter_resonance = 0; + channel->playing->vibrato_speed = 0; + channel->playing->vibrato_depth = 0; + channel->playing->vibrato_n = 0; + channel->playing->vibrato_time = 0; + channel->playing->vibrato_waveform = channel->vibrato_waveform; + channel->playing->tremolo_speed = 0; + channel->playing->tremolo_depth = 0; + channel->playing->tremolo_time = 0; + channel->playing->tremolo_waveform = channel->tremolo_waveform; + channel->playing->panbrello_speed = 0; + channel->playing->panbrello_depth = 0; + channel->playing->panbrello_time = 0; + channel->playing->panbrello_waveform = channel->panbrello_waveform; + channel->playing->panbrello_random = 0; + channel->playing->sample_vibrato_time = 0; + channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform; + channel->playing->sample_vibrato_depth = 0; + channel->playing->slide = 0; + channel->playing->finetune = channel->playing->sample->finetune; + + if (sigdata->flags & IT_USE_INSTRUMENTS) + { + if (envelopes_copied && channel->playing->env_instrument->volume_envelope.flags & IT_ENVELOPE_CARRY) { + channel->playing->volume_envelope.tick = vol_env_tick; + } else { + channel->playing->volume_envelope.tick = 0; + } + if (envelopes_copied && channel->playing->env_instrument->pan_envelope.flags & IT_ENVELOPE_CARRY) { + channel->playing->pan_envelope.tick = pan_env_tick; + } else { + channel->playing->pan_envelope.tick = 0; + } + if (envelopes_copied && channel->playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_CARRY) { + channel->playing->pitch_envelope.tick = pitch_env_tick; + } else { + channel->playing->pitch_envelope.tick = 0; + } + recalculate_it_envelope_nodes(channel->playing); + } + channel->playing->fadeoutcount = 1024; + it_reset_filter_state(&channel->playing->filter_state[0]); + it_reset_filter_state(&channel->playing->filter_state[1]); + it_playing_reset_resamplers(channel->playing, 0); + + /** WARNING - is everything initialised? */ +} + + + +static void get_default_volpan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + if (channel->sample == 0) + return; + + channel->volume = sigdata->sample[channel->sample-1].default_volume; + + if (sigdata->flags & IT_WAS_AN_XM) { + if (!(sigdata->flags & IT_WAS_A_MOD)) + channel->truepan = 32 + sigdata->sample[channel->sample-1].default_pan*64; + return; + } + + { + int pan = sigdata->sample[channel->sample-1].default_pan; + if (pan >= 128 && pan <= 192) { + channel->pan = pan - 128; + return; + } + } + + if (sigdata->flags & IT_USE_INSTRUMENTS) { + IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1]; + if (instrument->default_pan <= 64) + channel->pan = instrument->default_pan; + if (instrument->filter_cutoff >= 128) + channel->filter_cutoff = instrument->filter_cutoff - 128; + if (instrument->filter_resonance >= 128) + channel->filter_resonance = instrument->filter_resonance - 128; + } +} + + + +static void get_true_pan(DUMB_IT_SIGDATA *sigdata, IT_CHANNEL *channel) +{ + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + + if (channel->sample && !IT_IS_SURROUND_SHIFTED(channel->truepan) && (sigdata->flags & IT_USE_INSTRUMENTS)) { + IT_INSTRUMENT *instrument = &sigdata->instrument[channel->instrument-1]; + int truepan = channel->truepan; + truepan += (channel->note - instrument->pp_centre) * instrument->pp_separation << (IT_ENVELOPE_SHIFT - 3); + channel->truepan = (unsigned short)MID(0, truepan, 64 << IT_ENVELOPE_SHIFT); + } +} + + + +static void post_process_it_volpan(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_VOLPAN) { + if (entry->volpan <= 84) { + /* Volume */ + /* Fine volume slide up */ + /* Fine volume slide down */ + } else if (entry->volpan <= 94) { + /* Volume slide up */ + unsigned char v = entry->volpan - 85; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect Dx0 where x == entry->volpan - 85 */ + channel->volslide = v; + } else if (entry->volpan <= 104) { + /* Volume slide down */ + unsigned char v = entry->volpan - 95; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect D0x where x == entry->volpan - 95 */ + channel->volslide = -v; + } else if (entry->volpan <= 114) { + /* Portamento down */ + unsigned char v = (entry->volpan - 105) << 2; + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + channel->portamento -= v << 4; + } else if (entry->volpan <= 124) { + /* Portamento up */ + unsigned char v = (entry->volpan - 115) << 2; + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + channel->portamento += v << 4; + } else if (entry->volpan <= 202) { + /* Pan */ + /* Tone Portamento */ + } else if (entry->volpan <= 212) { + /* Vibrato */ + /* This is unaffected by IT_OLD_EFFECTS. However, if v == 0, then any doubling of depth that happened before (with Hxy in the effect column) will be preserved. */ + unsigned char v = entry->volpan - 203; + if (v == 0) + v = channel->lastHdepth; + else { + v <<= 2; + channel->lastHdepth = v; + } + if (channel->playing) { + channel->playing->vibrato_speed = channel->lastHspeed; + channel->playing->vibrato_depth = v; + channel->playing->vibrato_n++; + } + } + } +} + + + +static void it_send_midi(DUMB_IT_SIGRENDERER *sigrenderer, IT_CHANNEL *channel, unsigned char midi_byte) +{ + if (sigrenderer->callbacks->midi) + if ((*sigrenderer->callbacks->midi)(sigrenderer->callbacks->midi_data, channel - sigrenderer->channel, midi_byte)) + return; + + switch (channel->midi_state) { + case 4: /* Ready to receive resonance parameter */ + if (midi_byte < 0x80) channel->filter_resonance = midi_byte; + channel->midi_state = 0; + break; + case 3: /* Ready to receive cutoff parameter */ + if (midi_byte < 0x80) channel->filter_cutoff = midi_byte; + channel->midi_state = 0; + break; + case 2: /* Ready for byte specifying which parameter will follow */ + if (midi_byte == 0) /* Cutoff */ + channel->midi_state = 3; + else if (midi_byte == 1) /* Resonance */ + channel->midi_state = 4; + else + channel->midi_state = 0; + break; + default: /* Counting initial F0 bytes */ + switch (midi_byte) { + case 0xF0: + channel->midi_state++; + break; + case 0xFA: + case 0xFC: + case 0xFF: + /* Reset filter parameters for all channels */ + { + int i; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + sigrenderer->channel[i].filter_cutoff = 127; + sigrenderer->channel[i].filter_resonance = 0; + //// should we be resetting channel[i].playing->filter_* here? + } + } + /* Fall through */ + default: + channel->midi_state = 0; + break; + } + } +} + + + +static void xm_envelope_calculate_value(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if (pe->next_node <= 0) + pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT; + else if (pe->next_node >= envelope->n_nodes) + pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; + else { + int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; + int ts = envelope->node_t[pe->next_node-1]; + int te = envelope->node_t[pe->next_node]; + + if (ts == te) + pe->value = ys; + else { + int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; + int t = pe->tick; + + pe->value = ys + (ye - ys) * (t - ts) / (te - ts); + } + } +} + + + +extern const char xm_convert_vibrato[]; + +const char mod_convert_vibrato[] = { + IT_VIBRATO_SINE, + IT_VIBRATO_RAMP_UP, /* this will be inverted by IT_OLD_EFFECTS */ + IT_VIBRATO_XM_SQUARE, + IT_VIBRATO_XM_SQUARE +}; + +/* Returns 1 if a callback caused termination of playback. */ +static int process_effects(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_PLAYING *playing; + int i; + + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_EFFECT) { + switch (entry->effect) { +/* +Notes about effects (as compared to other module formats) + +C This is now in *HEX*. (Used to be in decimal in ST3) +E/F/G/H/U You need to check whether the song uses Amiga/Linear slides. +H/U Vibrato in Impulse Tracker is two times finer than in + any other tracker and is updated EVERY tick. + If "Old Effects" is *ON*, then the vibrato is played in the + normal manner (every non-row tick and normal depth) +E/F/G These commands ALL share the same memory. +Oxx Offsets to samples are to the 'xx00th' SAMPLE. (ie. for + 16 bit samples, the offset is xx00h*2) + Oxx past the sample end will be ignored, unless "Old Effects" + is ON, in which case the Oxx will play from the end of the + sample. +Yxy This uses a table 4 times larger (hence 4 times slower) than + vibrato or tremelo. If the waveform is set to random, then + the 'speed' part of the command is interpreted as a delay. +*/ + case IT_SET_SPEED: + if (entry->effectvalue) + { + /*if (entry->effectvalue == 255) + if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) + return 1;*/ + if (sigdata->flags & IT_WAS_AN_STM) { + int n = entry->effectvalue; + if (n >= 32) { + sigrenderer->tick = sigrenderer->speed = n; + } + } else { + sigrenderer->tick = sigrenderer->speed = entry->effectvalue; + } + } + else if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) { +#ifdef BIT_ARRAY_BULLSHIT + bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); +#endif + sigrenderer->speed = 0; +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->looped = 1; +#endif + if (sigrenderer->callbacks->xm_speed_zero && (*sigrenderer->callbacks->xm_speed_zero)(sigrenderer->callbacks->xm_speed_zero_data)) + return 1; + } + break; + + case IT_BREAK_TO_ROW: + if (ignore_cxx) break; + sigrenderer->breakrow = entry->effectvalue; + /* XXX jump and break on the same row */ + if ( ( ( sigrenderer->processrow | 0xC00 ) == 0xFFFE ) && + ! ( sigrenderer->processrow & 0x400 ) ) { + sigrenderer->processrow = 0xFFFE & ~0xC00; + } else { + sigrenderer->processorder = sigrenderer->order; + sigrenderer->processrow = 0xFFFE & ~0x800; + } + break; + + case IT_VOLSLIDE_VIBRATO: + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = channel->lastHspeed; + playing->vibrato_depth = channel->lastHdepth; + playing->vibrato_n++; + } + } + /* Fall through and process volume slide. */ + case IT_VOLUME_SLIDE: + case IT_VOLSLIDE_TONEPORTA: + /* The tone portamento component is handled elsewhere. */ + { + unsigned char v = entry->effectvalue; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } + if (!(sigdata->flags & IT_WAS_AN_XM)) { + int clip = (sigdata->flags & IT_WAS_AN_S3M) ? 63 : 64; + if ((v & 0x0F) == 0x0F) { + if (!(v & 0xF0)) { + channel->volslide = -15; + channel->volume -= 15; + if (channel->volume > clip) channel->volume = 0; + } else { + channel->volume += v >> 4; + if (channel->volume > clip) channel->volume = clip; + } + } else if ((v & 0xF0) == 0xF0) { + if (!(v & 0x0F)) { + channel->volslide = 15; + channel->volume += 15; + if (channel->volume > clip) channel->volume = clip; + } else { + channel->volume -= v & 15; + if (channel->volume > clip) channel->volume = 0; + } + } else if (!(v & 0x0F)) { + channel->volslide = v >> 4; + } else { + channel->volslide = -(v & 15); + } + } else { + if ((v & 0x0F) == 0) { /* Dx0 */ + channel->volslide = v >> 4; + } else if ((v & 0xF0) == 0) { /* D0x */ + channel->volslide = -v; + } else if ((v & 0x0F) == 0x0F) { /* DxF */ + channel->volume += v >> 4; + if (channel->volume > 64) channel->volume = 64; + } else if ((v & 0xF0) == 0xF0) { /* DFx */ + channel->volume -= v & 15; + if (channel->volume > 64) channel->volume = 0; + } + } + } + break; + case IT_XM_FINE_VOLSLIDE_DOWN: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->xm_lastEB; + channel->xm_lastEB = v; + channel->volume -= v; + if (channel->volume > 64) channel->volume = 0; + } + break; + case IT_XM_FINE_VOLSLIDE_UP: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->xm_lastEA; + channel->xm_lastEA = v; + channel->volume += v; + if (channel->volume > 64) channel->volume = 64; + } + break; + case IT_PORTAMENTO_DOWN: + { + unsigned char v = entry->effectvalue; + if (sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_669)) { + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0xF0) + v |= channel->xm_lastE2; + else if (v >= 0xF0) + channel->xm_lastE2 = v & 15; + else if (v == 0xE0) + v |= channel->xm_lastX2; + else + channel->xm_lastX2 = v & 15; + } + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + if ((v & 0xF0) == 0xF0) + playing->slide -= (v & 15) << 4; + else if ((v & 0xF0) == 0xE0) + playing->slide -= (v & 15) << 2; + else if (i < 0 && sigdata->flags & IT_WAS_A_669) + channel->portamento -= v << 3; + else if (i < 0) + channel->portamento -= v << 4; + } + } + } + break; + case IT_PORTAMENTO_UP: + { + unsigned char v = entry->effectvalue; + if (sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_669)) { + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0xF0) + v |= channel->xm_lastE1; + else if (v >= 0xF0) + channel->xm_lastE1 = v & 15; + else if (v == 0xE0) + v |= channel->xm_lastX1; + else + channel->xm_lastX1 = v & 15; + } + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + if ((v & 0xF0) == 0xF0) + playing->slide += (v & 15) << 4; + else if ((v & 0xF0) == 0xE0) + playing->slide += (v & 15) << 2; + else if (i < 0 && sigdata->flags & IT_WAS_A_669) + channel->portamento += v << 3; + else if (i < 0) + channel->portamento += v << 4; + } + } + } + break; + case IT_XM_PORTAMENTO_DOWN: + { + unsigned char v = entry->effectvalue; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0) + v = channel->lastJ; + channel->lastJ = v; + } + if (channel->playing) + channel->portamento -= v << 4; + } + break; + case IT_XM_PORTAMENTO_UP: + { + unsigned char v = entry->effectvalue; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + if (channel->playing) + channel->portamento += v << 4; + } + break; + case IT_XM_KEY_OFF: + channel->key_off_count = entry->effectvalue; + if (!channel->key_off_count) xm_note_off(sigdata, channel); + break; + case IT_VIBRATO: + { + if (entry->effectvalue || !(sigdata->flags & IT_WAS_A_669)) { + unsigned char speed = entry->effectvalue >> 4; + unsigned char depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastHspeed; + channel->lastHspeed = speed; + if (depth == 0) + depth = channel->lastHdepth; + else { + if (sigdata->flags & IT_OLD_EFFECTS && !(sigdata->flags & IT_WAS_A_MOD)) + depth <<= 3; + else + depth <<= 2; + channel->lastHdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = speed; + playing->vibrato_depth = depth; + playing->vibrato_n++; + } + } + } + } + break; + case IT_TREMOR: + { + unsigned char v = entry->effectvalue; + if (v == 0) { + if (sigdata->flags & IT_WAS_AN_S3M) + v = channel->lastDKL; + else + v = channel->lastI; + } + else if (!(sigdata->flags & IT_OLD_EFFECTS)) { + if (v & 0xF0) v -= 0x10; + if (v & 0x0F) v -= 0x01; + } + if (sigdata->flags & IT_WAS_AN_S3M) + channel->lastDKL = v; + else + channel->lastI = v; + channel->tremor_time |= 128; + } + update_tremor(channel); + break; + case IT_ARPEGGIO: + { + unsigned char v = entry->effectvalue; + /* XM files have no memory for arpeggio (000 = no effect) + * and we use lastJ for portamento down instead. + */ + if (!(sigdata->flags & IT_WAS_AN_XM)) { + if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastJ; + channel->lastJ = v; + } + } + channel->arpeggio_offsets[0] = 0; + channel->arpeggio_offsets[1] = (v & 0xF0) >> 4; + channel->arpeggio_offsets[2] = (v & 0x0F); + channel->arpeggio_table = ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD))==IT_WAS_AN_XM) ? &arpeggio_xm : &arpeggio_mod; + } + break; + case IT_SET_CHANNEL_VOLUME: + if (sigdata->flags & IT_WAS_AN_XM) + channel->volume = MIN(entry->effectvalue, 64); + else if (entry->effectvalue <= 64) + channel->channelvolume = entry->effectvalue; +#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM + else + channel->channelvolume = 64; +#endif + if (channel->playing) + channel->playing->channel_volume = channel->channelvolume; + break; + case IT_CHANNEL_VOLUME_SLIDE: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastN; + channel->lastN = v; + if ((v & 0x0F) == 0) { /* Nx0 */ + channel->channelvolslide = v >> 4; + } else if ((v & 0xF0) == 0) { /* N0x */ + channel->channelvolslide = -v; + } else { + if ((v & 0x0F) == 0x0F) { /* NxF */ + channel->channelvolume += v >> 4; + if (channel->channelvolume > 64) channel->channelvolume = 64; + } else if ((v & 0xF0) == 0xF0) { /* NFx */ + channel->channelvolume -= v & 15; + if (channel->channelvolume > 64) channel->channelvolume = 0; + } else + break; + if (channel->playing) + channel->playing->channel_volume = channel->channelvolume; + } + } + break; + case IT_SET_SAMPLE_OFFSET: + { + unsigned char v = entry->effectvalue; + /*if (sigdata->flags & IT_WAS_A_MOD) { + if (v == 0) break; + } else*/ { + if (v == 0) + v = channel->lastO; + channel->lastO = v; + } + /* Note: we set the offset even if tone portamento is + * specified. Impulse Tracker does the same. + */ + if (entry->mask & IT_ENTRY_NOTE) { + if (channel->playing) { + int offset = ((int)channel->high_offset << 16) | ((int)v << 8); + IT_PLAYING *playing = channel->playing; + IT_SAMPLE *sample = playing->sample; + int end; + if ((sample->flags & IT_SAMPLE_SUS_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) + end = sample->sus_loop_end; + else if (sample->flags & IT_SAMPLE_LOOP) + end = sample->loop_end; + else + end = sample->length; + if ((sigdata->flags & IT_WAS_A_PTM) && (sample->flags & IT_SAMPLE_16BIT)) + offset >>= 1; + if (offset < end) { + it_playing_reset_resamplers(playing, offset); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } else if (sigdata->flags & IT_OLD_EFFECTS) { + it_playing_reset_resamplers(playing, end); +#ifdef END_RAMPING + playing->declick_stage = 0; + playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + } + break; + case IT_PANNING_SLIDE: + /** JULIEN: guess what? the docs are wrong! (how unusual ;) + * Pxy seems to memorize its previous value... and there + * might be other mistakes like that... (sigh!) + */ + /** ENTHEH: umm... but... the docs say that Pxy memorises its + * value... don't they? :o + */ + { + unsigned char v = entry->effectvalue; + int p = channel->truepan; + if (sigdata->flags & IT_WAS_AN_XM) + { + if (IT_IS_SURROUND(channel->pan)) + { + channel->pan = 32; + p = 32 + 128 * 64; + } + p >>= 6; + } + else { + if (IT_IS_SURROUND(channel->pan)) p = 32 << 8; + p = (p + 128) >> 8; + channel->pan = p; + } + if (v == 0) + v = channel->lastP; + channel->lastP = v; + if ((v & 0x0F) == 0) { /* Px0 */ + channel->panslide = -(v >> 4); + } else if ((v & 0xF0) == 0) { /* P0x */ + channel->panslide = v; + } else if ((v & 0x0F) == 0x0F) { /* PxF */ + p -= v >> 4; + } else if ((v & 0xF0) == 0xF0) { /* PFx */ + p += v & 15; + } + if (sigdata->flags & IT_WAS_AN_XM) + channel->truepan = 32 + MID(0, p, 255) * 64; + else { + if (p < 0) p = 0; + else if (p > 64) p = 64; + channel->pan = p; + channel->truepan = p << 8; + } + } + break; + case IT_RETRIGGER_NOTE: + { + unsigned char v = entry->effectvalue; + if (sigdata->flags & IT_WAS_AN_XM) { + if ((v & 0x0F) == 0) v |= channel->lastQ & 0x0F; + if ((v & 0xF0) == 0) v |= channel->lastQ & 0xF0; + channel->lastQ = v; + } else if (sigdata->flags & IT_WAS_AN_S3M) { + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + } else { + if (v == 0) + v = channel->lastQ; + channel->lastQ = v; + } + if ((v & 0x0F) == 0) v |= 0x01; + channel->retrig = v; + if (entry->mask & IT_ENTRY_NOTE) { + channel->retrig_tick = v & 0x0F; + /* Emulate a bug */ + if (sigdata->flags & IT_WAS_AN_XM) + update_retrig(sigrenderer, channel); + } else + update_retrig(sigrenderer, channel); + } + break; + case IT_XM_RETRIGGER_NOTE: + channel->retrig_tick = channel->xm_retrig = entry->effectvalue; + if (entry->effectvalue == 0) + if (channel->playing) { + it_playing_reset_resamplers(channel->playing, 0); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } + break; + case IT_TREMOLO: + { + unsigned char speed, depth; + if (sigdata->flags & IT_WAS_AN_S3M) { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastDKL; + channel->lastDKL = v; + speed = v >> 4; + depth = v & 15; + } else { + speed = entry->effectvalue >> 4; + depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastRspeed; + channel->lastRspeed = speed; + if (depth == 0) + depth = channel->lastRdepth; + channel->lastRdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->tremolo_speed = speed; + playing->tremolo_depth = depth; + } + } + } + break; + case IT_S: + { + /* channel->lastS was set in update_pattern_variables(). */ + unsigned char effectvalue = channel->lastS; + switch (effectvalue >> 4) { + //case IT_S_SET_FILTER: + /* Waveforms for commands S3x, S4x and S5x: + * 0: Sine wave + * 1: Ramp down + * 2: Square wave + * 3: Random wave + */ + case IT_S_SET_GLISSANDO_CONTROL: + channel->glissando = effectvalue & 15; + break; + + case IT_S_FINETUNE: + if (channel->playing) { + channel->playing->finetune = ((int)(effectvalue & 15) - 8) << 5; + } + break; + + case IT_S_SET_VIBRATO_WAVEFORM: + { + int waveform = effectvalue & 3; + if (sigdata->flags & IT_WAS_A_MOD) waveform = mod_convert_vibrato[waveform]; + else if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; + channel->vibrato_waveform = waveform; + if (channel->playing) { + channel->playing->vibrato_waveform = waveform; + if (!(effectvalue & 4)) + channel->playing->vibrato_time = 0; + } + } + break; + case IT_S_SET_TREMOLO_WAVEFORM: + { + int waveform = effectvalue & 3; + if (sigdata->flags & IT_WAS_A_MOD) waveform = mod_convert_vibrato[waveform]; + else if (sigdata->flags & IT_WAS_AN_XM) waveform = xm_convert_vibrato[waveform]; + channel->tremolo_waveform = waveform; + if (channel->playing) { + channel->playing->tremolo_waveform = waveform; + if (!(effectvalue & 4)) + channel->playing->tremolo_time = 0; + } + } + break; + case IT_S_SET_PANBRELLO_WAVEFORM: + channel->panbrello_waveform = effectvalue & 3; + if (channel->playing) { + channel->playing->panbrello_waveform = effectvalue & 3; + if (!(effectvalue & 4)) + channel->playing->panbrello_time = 0; + } + break; + + case IT_S_FINE_PATTERN_DELAY: + sigrenderer->tick += effectvalue & 15; + break; +#if 1 + case IT_S7: + { + if (sigrenderer->sigdata->flags & IT_USE_INSTRUMENTS) + { + int i; + switch (effectvalue & 15) + { + case 0: /* cut background notes */ + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && channel == playing->channel) + { +#ifdef RAMP_DOWN + playing->declick_stage = 2; +#else + free_playing(playing); + sigrenderer->playing[i] = NULL; +#endif + if (channel->playing == playing) channel->playing = NULL; + } + } + break; + case 1: /* release background notes */ + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && channel == playing->channel && !(playing->flags & IT_PLAYING_SUSTAINOFF)) + { + it_note_off(playing); + } + } + break; + case 2: /* fade background notes */ + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + { + IT_PLAYING * playing = sigrenderer->playing[i]; + if (playing && channel == playing->channel) + { + //playing->flags &= IT_PLAYING_SUSTAINOFF; + playing->flags |= IT_PLAYING_FADING; + } + } + break; + case 3: + channel->new_note_action = NNA_NOTE_CUT; + break; + case 4: + channel->new_note_action = NNA_NOTE_CONTINUE; + break; + case 5: + channel->new_note_action = NNA_NOTE_OFF; + break; + case 6: + channel->new_note_action = NNA_NOTE_FADE; + break; + + case 7: + if (channel->playing) + channel->playing->enabled_envelopes &= ~IT_ENV_VOLUME; + break; + case 8: + if (channel->playing) + channel->playing->enabled_envelopes |= IT_ENV_VOLUME; + break; + + case 9: + if (channel->playing) + channel->playing->enabled_envelopes &= ~IT_ENV_PANNING; + break; + case 10: + if (channel->playing) + channel->playing->enabled_envelopes |= IT_ENV_PANNING; + break; + + case 11: + if (channel->playing) + channel->playing->enabled_envelopes &= ~IT_ENV_PITCH; + break; + case 12: + if (channel->playing) + channel->playing->enabled_envelopes |= IT_ENV_PITCH; + break; + } + } + } + break; +#endif + case IT_S_SET_PAN: + //ASSERT(!(sigdata->flags & IT_WAS_AN_XM)); + channel->pan = + ((effectvalue & 15) << 2) | + ((effectvalue & 15) >> 2); + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + + if (channel->playing) + channel->playing->panbrello_depth = 0; + break; + case IT_S_SET_SURROUND_SOUND: + if ((effectvalue & 15) == 15) { + if (channel->playing && channel->playing->sample && + !(channel->playing->sample->flags & (IT_SAMPLE_LOOP | IT_SAMPLE_SUS_LOOP))) { + channel->playing->flags |= IT_PLAYING_REVERSE; + it_playing_reset_resamplers( channel->playing, channel->playing->sample->length - 1 ); + } + } else if ((effectvalue & 15) == 1) { + channel->pan = IT_SURROUND; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + if (channel->playing) + channel->playing->panbrello_depth = 0; + break; + case IT_S_SET_HIGH_OFFSET: + channel->high_offset = effectvalue & 15; + break; + //case IT_S_PATTERN_LOOP: + case IT_S_DELAYED_NOTE_CUT: + channel->note_cut_count = effectvalue & 15; + if (!channel->note_cut_count) { + if (sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM)) + channel->volume = 0; + else + channel->note_cut_count = 1; + } + break; + case IT_S_SET_MIDI_MACRO: + if ((sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == (IT_WAS_AN_XM | IT_WAS_A_MOD)) { + channel->inv_loop_speed = effectvalue & 15; + update_invert_loop(channel, channel->playing ? channel->playing->sample : NULL); + } else channel->SFmacro = effectvalue & 15; + break; + } + } + break; + case IT_SET_SONG_TEMPO: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastW; + channel->lastW = v; + if (v < 0x10) + sigrenderer->temposlide = -v; + else if (v < 0x20) + sigrenderer->temposlide = v & 15; + else + sigrenderer->tempo = v; + } + break; + case IT_FINE_VIBRATO: + { + unsigned char speed = entry->effectvalue >> 4; + unsigned char depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastHspeed; + channel->lastHspeed = speed; + if (depth == 0) + depth = channel->lastHdepth; + else { + if (sigdata->flags & IT_OLD_EFFECTS) + depth <<= 1; + channel->lastHdepth = depth; + } + for (i = -1; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (i < 0) playing = channel->playing; + else { + playing = sigrenderer->playing[i]; + if (!playing || playing->channel != channel) continue; + } + if (playing) { + playing->vibrato_speed = speed; + playing->vibrato_depth = depth; + playing->vibrato_n++; + } + } + } + break; + case IT_SET_GLOBAL_VOLUME: + if ((sigdata->flags & IT_WAS_AN_S3M) && (entry->effectvalue > 64)) + break; + if (entry->effectvalue <= 128) + sigrenderer->globalvolume = entry->effectvalue; +#ifdef VOLUME_OUT_OF_RANGE_SETS_MAXIMUM + else + sigrenderer->globalvolume = 128; +#endif + break; + case IT_GLOBAL_VOLUME_SLIDE: + { + unsigned char v = entry->effectvalue; + if (v == 0) + v = channel->lastW; + channel->lastW = v; + if ((v & 0x0F) == 0) { /* Wx0 */ + sigrenderer->globalvolslide = + (sigdata->flags & IT_WAS_AN_XM) ? (v >> 4)*2 : (v >> 4); + } else if ((v & 0xF0) == 0) { /* W0x */ + sigrenderer->globalvolslide = + (sigdata->flags & IT_WAS_AN_XM) ? (-v)*2 : (-v); + } else if ((v & 0x0F) == 0x0F) { /* WxF */ + sigrenderer->globalvolume += v >> 4; + if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 128; + } else if ((v & 0xF0) == 0xF0) { /* WFx */ + sigrenderer->globalvolume -= v & 15; + if (sigrenderer->globalvolume > 128) sigrenderer->globalvolume = 0; + } + } + break; + case IT_SET_PANNING: + if (sigdata->flags & IT_WAS_AN_XM) { + channel->truepan = 32 + entry->effectvalue*64; + } else { + if (sigdata->flags & IT_WAS_AN_S3M) + channel->pan = (entry->effectvalue + 1) >> 1; + else + channel->pan = (entry->effectvalue + 2) >> 2; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + if (channel->playing) + channel->playing->panbrello_depth = 0; + break; + case IT_PANBRELLO: + { + unsigned char speed = entry->effectvalue >> 4; + unsigned char depth = entry->effectvalue & 15; + if (speed == 0) + speed = channel->lastYspeed; + channel->lastYspeed = speed; + if (depth == 0) + depth = channel->lastYdepth; + channel->lastYdepth = depth; + if (channel->playing) { + channel->playing->panbrello_speed = speed; + channel->playing->panbrello_depth = depth; + } + } + break; + case IT_MIDI_MACRO: + { + const IT_MIDI *midi = sigdata->midi ? sigdata->midi : &default_midi; + if (entry->effectvalue >= 0x80) { + int n = midi->Zmacrolen[entry->effectvalue-0x80]; + int i; + for (i = 0; i < n; i++) + it_send_midi(sigrenderer, channel, midi->Zmacro[entry->effectvalue-0x80][i]); + } else { + int n = midi->SFmacrolen[channel->SFmacro]; + int i, j; + for (i = 0, j = 1; i < n; i++, j <<= 1) + it_send_midi(sigrenderer, channel, + (unsigned char)(midi->SFmacroz[channel->SFmacro] & j ? + entry->effectvalue : midi->SFmacro[channel->SFmacro][i])); + } + } + break; + case IT_XM_SET_ENVELOPE_POSITION: + if (channel->playing && channel->playing->env_instrument) { + IT_ENVELOPE *envelope = &channel->playing->env_instrument->volume_envelope; + if (envelope->flags & IT_ENVELOPE_ON) { + IT_PLAYING_ENVELOPE *pe = &channel->playing->volume_envelope; + pe->tick = entry->effectvalue; + if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) + pe->tick = envelope->node_t[envelope->n_nodes-1]; + pe->next_node = 0; + while (pe->tick > envelope->node_t[pe->next_node]) pe->next_node++; + xm_envelope_calculate_value(envelope, pe); + } + } + break; + + /* uggly plain portamento for now */ + case IT_PTM_NOTE_SLIDE_DOWN: + case IT_PTM_NOTE_SLIDE_DOWN_RETRIG: + { + channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_DOWN_RETRIG); + + if (channel->ptm_last_toneslide) { + channel->toneslide_tick = channel->last_toneslide_tick; + + if (--channel->toneslide_tick == 0) { + channel->truenote += channel->toneslide; + if (channel->truenote >= 120) { + if (channel->toneslide < 0) channel->truenote = 0; + else channel->truenote = 119; + } + channel->note += channel->toneslide; + if (channel->note >= 120) { + if (channel->toneslide < 0) channel->note = 0; + else channel->note = 119; + } + + if (channel->playing) { + if (channel->sample) channel->playing->note = channel->truenote; + else channel->playing->note = channel->note; + it_playing_reset_resamplers(channel->playing, 0); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + + channel->ptm_last_toneslide = 0; + + channel->toneslide = -(entry->effectvalue & 15); + channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4; + channel->toneslide_tick += channel->ptm_toneslide; + } + break; + case IT_PTM_NOTE_SLIDE_UP: + case IT_PTM_NOTE_SLIDE_UP_RETRIG: + { + channel->toneslide_retrig = (entry->effect == IT_PTM_NOTE_SLIDE_UP_RETRIG); + + if (channel->ptm_last_toneslide) { + channel->toneslide_tick = channel->last_toneslide_tick; + + if (--channel->toneslide_tick == 0) { + channel->truenote += channel->toneslide; + if (channel->truenote >= 120) { + if (channel->toneslide < 0) channel->truenote = 0; + else channel->truenote = 119; + } + channel->note += channel->toneslide; + if (channel->note >= 120) { + if (channel->toneslide < 0) channel->note = 0; + else channel->note = 119; + } + + if (channel->playing) { + if (channel->sample) channel->playing->note = channel->truenote; + else channel->playing->note = channel->note; + it_playing_reset_resamplers(channel->playing, 0); +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + } + } + } + + channel->ptm_last_toneslide = 0; + + channel->toneslide = -(entry->effectvalue & 15); + channel->ptm_toneslide = (entry->effectvalue & 0xF0) >> 4; + channel->toneslide_tick += channel->ptm_toneslide; + } + break; + + case IT_OKT_NOTE_SLIDE_DOWN: + case IT_OKT_NOTE_SLIDE_DOWN_ROW: + channel->toneslide = -entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_DOWN) ? 255 : 1; + break; + + case IT_OKT_NOTE_SLIDE_UP: + case IT_OKT_NOTE_SLIDE_UP_ROW: + channel->toneslide = entry->effectvalue; + channel->okt_toneslide = (entry->effect == IT_OKT_NOTE_SLIDE_UP) ? 255 : 1; + break; + + case IT_OKT_ARPEGGIO_3: + case IT_OKT_ARPEGGIO_4: + case IT_OKT_ARPEGGIO_5: + { + channel->arpeggio_offsets[0] = 0; + channel->arpeggio_offsets[1] = -(entry->effectvalue >> 4); + channel->arpeggio_offsets[2] = entry->effectvalue & 0x0F; + + switch (entry->effect) + { + case IT_OKT_ARPEGGIO_3: + channel->arpeggio_table = &arpeggio_okt_3; + break; + + case IT_OKT_ARPEGGIO_4: + channel->arpeggio_table = &arpeggio_okt_4; + break; + + case IT_OKT_ARPEGGIO_5: + channel->arpeggio_table = &arpeggio_okt_5; + break; + } + } + break; + + case IT_OKT_VOLUME_SLIDE_DOWN: + if ( entry->effectvalue <= 16 ) channel->volslide = -entry->effectvalue; + else + { + channel->volume -= entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 0; + } + break; + + case IT_OKT_VOLUME_SLIDE_UP: + if ( entry->effectvalue <= 16 ) channel->volslide = entry->effectvalue; + else + { + channel->volume += entry->effectvalue - 16; + if (channel->volume > 64) channel->volume = 64; + } + break; + } + } + + if (!(sigdata->flags & IT_WAS_AN_XM)) + post_process_it_volpan(sigrenderer, entry); + + return 0; +} + + + +static int process_it_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + // When tone portamento and instrument are specified: + // If Gxx is off: + // - same sample, do nothing but portamento + // - diff sample, retrigger all but keep current note+slide + do porta + // - if instrument is invalid, nothing; if sample is invalid, cut + // If Gxx is on: + // - same sample or new sample invalid, retrigger envelopes and initialise note value for portamento to 'seek' to + // - diff sample/inst, start using new envelopes + // When tone portamento is specified alone, sample won't change. + // TODO: consider what happens with instrument alone after all this... + + if (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) { + if (entry->mask & IT_ENTRY_INSTRUMENT) + channel->instrument = entry->instrument; + instrument_to_sample(sigdata, channel); + if (channel->note <= 120) { + if ((sigdata->flags & IT_USE_INSTRUMENTS) && channel->sample == 0) + it_retrigger_note(sigrenderer, channel); /* Stop the note */ /*return 1;*/ + if (entry->mask & IT_ENTRY_INSTRUMENT) + get_default_volpan(sigdata, channel); + } else + it_retrigger_note(sigrenderer, channel); /* Stop the note */ + } + + /** WARNING: This is not ideal, since channel->playing might not get allocated owing to lack of memory... */ + if (((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) || + ((entry->mask & IT_ENTRY_EFFECT) && (entry->effect == IT_TONE_PORTAMENTO || entry->effect == IT_VOLSLIDE_TONEPORTA))) + { + if (channel->playing && (entry->mask & IT_ENTRY_INSTRUMENT)) { + if (sigdata->flags & IT_COMPATIBLE_GXX) + it_compatible_gxx_retrigger(sigdata, channel); + else if ((!(sigdata->flags & IT_USE_INSTRUMENTS) || + (channel->instrument >= 1 && channel->instrument <= sigdata->n_instruments)) && + channel->sample != channel->playing->sampnum) + { + unsigned char note = channel->playing->note; + int slide = channel->playing->slide; + it_retrigger_note(sigrenderer, channel); + if (channel->playing) { + channel->playing->note = note; + channel->playing->slide = slide; + // Should we be preserving sample_vibrato_time? depth? + } + } + } + + if ((entry->mask & IT_ENTRY_VOLPAN) && entry->volpan >= 193 && entry->volpan <= 202) { + /* Tone Portamento in the volume column */ + static const unsigned char slidetable[] = {0, 1, 4, 8, 16, 32, 64, 96, 128, 255}; + unsigned char v = slidetable[entry->volpan - 193]; + if (sigdata->flags & IT_COMPATIBLE_GXX) { + if (v == 0) + v = channel->lastG; + channel->lastG = v; + } else { + if (v == 0) + v = channel->lastEF; + channel->lastEF = v; + } + if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { + if (channel->note <= 120) { + if (channel->sample) + channel->destnote = channel->truenote; + else + channel->destnote = channel->note; + } + } + channel->toneporta = v << 4; + } else { + /* Tone Portamento in the effect column */ + unsigned char v; + if (entry->effect == IT_TONE_PORTAMENTO) + v = entry->effectvalue; + else + v = 0; + if (sigdata->flags & IT_COMPATIBLE_GXX) { + if (v == 0) + v = channel->lastG; + channel->lastG = v; + } else { + if (v == 0 && !(sigdata->flags & IT_WAS_A_669)) + v = channel->lastEF; + channel->lastEF = v; + } + if ((entry->mask & IT_ENTRY_NOTE) || ((sigdata->flags & IT_COMPATIBLE_GXX) && (entry->mask & IT_ENTRY_INSTRUMENT))) { + if (channel->note <= 120) { + if (channel->sample) + channel->destnote = channel->truenote; + else + channel->destnote = channel->note; + } + } + channel->toneporta = v << 4; + } + if (channel->playing) goto skip_start_note; + } + + if ((entry->mask & IT_ENTRY_NOTE) || + ((entry->mask & IT_ENTRY_INSTRUMENT) && (!channel->playing || entry->instrument != channel->playing->instnum))) + { + if (channel->note <= 120) { + get_true_pan(sigdata, channel); + if ((entry->mask & IT_ENTRY_NOTE) || !(sigdata->flags & (IT_WAS_AN_S3M|IT_WAS_A_PTM))) + it_retrigger_note(sigrenderer, channel); + } + } + + skip_start_note: + + if (entry->mask & IT_ENTRY_VOLPAN) { + if (entry->volpan <= 64) { + /* Volume */ + channel->volume = entry->volpan; + } else if (entry->volpan <= 74) { + /* Fine volume slide up */ + unsigned char v = entry->volpan - 65; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect DxF where x == entry->volpan - 65 */ + channel->volume += v; + if (channel->volume > 64) channel->volume = 64; + } else if (entry->volpan <= 84) { + /* Fine volume slide down */ + unsigned char v = entry->volpan - 75; + if (v == 0) + v = channel->lastvolslide; + channel->lastvolslide = v; + /* = effect DFx where x == entry->volpan - 75 */ + channel->volume -= v; + if (channel->volume > 64) channel->volume = 0; + } else if (entry->volpan < 128) { + /* Volume slide up */ + /* Volume slide down */ + /* Portamento down */ + /* Portamento up */ + } else if (entry->volpan <= 192) { + /* Pan */ + channel->pan = entry->volpan - 128; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + } + /* else */ + /* Tone Portamento */ + /* Vibrato */ + } + return 0; +} + + + +static void retrigger_xm_envelopes(IT_PLAYING *playing) +{ + playing->volume_envelope.next_node = 0; + playing->volume_envelope.tick = -1; + playing->pan_envelope.next_node = 0; + playing->pan_envelope.tick = -1; + playing->fadeoutcount = 1024; +} + + + +static void process_xm_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + IT_PLAYING * playing = NULL; + + if (entry->mask & IT_ENTRY_INSTRUMENT) { + int oldsample = channel->sample; + channel->inv_loop_offset = 0; + channel->instrument = entry->instrument; + instrument_to_sample(sigdata, channel); + if (channel->playing && + !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) && + !((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0)) { + playing = dup_playing(channel->playing, channel, channel); + if (!playing) return; + if (!(sigdata->flags & IT_WAS_A_MOD)) { + /* Retrigger vol/pan envelopes if enabled, and cancel fadeout. + * Also reset vol/pan to that of _original_ instrument. + */ + channel->playing->flags &= ~(IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING); + it_playing_update_resamplers(channel->playing); + + channel->volume = channel->playing->sample->default_volume; + channel->truepan = 32 + channel->playing->sample->default_pan*64; + + retrigger_xm_envelopes(channel->playing); + } else { + /* Switch if sample changed */ + if (oldsample != channel->sample) { +#ifdef RAMP_DOWN + int i; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 2; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + + if (!channel->sample) { + if (channel->playing) + { + free_playing(channel->playing); + channel->playing = NULL; + } + } else { + if (channel->playing) { + free_playing(channel->playing); + } + channel->playing = playing; + playing = NULL; + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#else + if (!channel->sample) { + free_playing(channel->playing); + channel->playing = NULL; + } else { +#endif + channel->playing->sampnum = channel->sample; + channel->playing->sample = &sigdata->sample[channel->sample-1]; + it_playing_reset_resamplers(channel->playing, 0); + } + } + get_default_volpan(sigdata, channel); + } + } + } + + if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) && + (entry->mask & IT_ENTRY_NOTE)) + { + if (!(entry->mask & IT_ENTRY_INSTRUMENT)) + instrument_to_sample(sigdata, channel); + + if (channel->note >= 120) + xm_note_off(sigdata, channel); + else if (channel->sample == 0) { + /** If we get here, one of the following is the case: + ** 1. The instrument has never been specified on this channel. + ** 2. The specified instrument is invalid. + ** 3. The instrument has no sample mapped to the selected note. + ** What should happen? + ** + ** Experimentation shows that any existing note stops and cannot + ** be brought back. A subsequent instrument change fixes that. + **/ + if (channel->playing) { +#ifdef RAMP_DOWN + int i; + if (playing) { + free_playing(channel->playing); + channel->playing = playing; + playing = NULL; + } + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 2; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + if (channel->playing) { + free_playing(channel->playing); + channel->playing = NULL; + } +#else + free_playing(channel->playing); + channel->playing = NULL; +#endif + } + if (playing) free_playing(playing); + return; + } else if (channel->playing && (entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { + /* Don't retrigger note; portamento in the volume column. */ + } else if (channel->playing && + (entry->mask & IT_ENTRY_EFFECT) && + (entry->effect == IT_TONE_PORTAMENTO || + entry->effect == IT_VOLSLIDE_TONEPORTA)) { + /* Don't retrigger note; portamento in the effects column. */ + } else { + channel->destnote = IT_NOTE_OFF; + + if (!channel->playing) { + channel->playing = new_playing(); + if (!channel->playing) { + if (playing) free_playing(playing); + return; + } + // Adding the following seems to do the trick for the case where a piece starts with an instrument alone and then some notes alone. + retrigger_xm_envelopes(channel->playing); + } +#ifdef RAMP_DOWN + else if (playing) { + /* volume rampy stuff! move note to NNA */ + int i; + IT_PLAYING * ptemp; + if (playing->sample) ptemp = playing; + else ptemp = channel->playing; + if (!ptemp) { + if (playing) free_playing(playing); + return; + } + playing = NULL; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + ptemp->declick_stage = 2; + ptemp->flags |= IT_PLAYING_SUSTAINOFF | IT_PLAYING_FADING; + sigrenderer->playing[i] = ptemp; + ptemp = NULL; + break; + } + } + if (ptemp) free_playing(ptemp); + } +#endif + + channel->playing->flags = 0; + channel->playing->resampling_quality = sigrenderer->resampling_quality; + channel->playing->channel = channel; + channel->playing->sample = &sigdata->sample[channel->sample-1]; + if (sigdata->flags & IT_USE_INSTRUMENTS) + channel->playing->instrument = &sigdata->instrument[channel->instrument-1]; + else + channel->playing->instrument = NULL; + channel->playing->env_instrument = channel->playing->instrument; + channel->playing->sampnum = channel->sample; + channel->playing->instnum = channel->instrument; +#ifdef END_RAMPING + channel->playing->declick_stage = 0; + channel->playing->declick_volume = 1.f / 256.f; +#endif + channel->playing->channel_volume = channel->channelvolume; + channel->playing->ramp_volume[0] = 31337.0; /* special */ + channel->playing->note = channel->truenote; + channel->playing->enabled_envelopes = 0; + channel->playing->volume_offset = 0; + channel->playing->panning_offset = 0; + //channel->playing->output = channel->output; + if (sigdata->flags & IT_USE_INSTRUMENTS) { + IT_PLAYING * playing = channel->playing; + IT_INSTRUMENT * instrument = playing->instrument; + if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_VOLUME; + if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) playing->enabled_envelopes |= IT_ENV_PANNING; + //if (instrument->output) playing->output = instrument->output; + } + channel->playing->filter_cutoff = 127; + channel->playing->filter_resonance = 0; + channel->playing->true_filter_cutoff = 127 << 8; + channel->playing->true_filter_resonance = 0; + channel->playing->vibrato_speed = 0; + channel->playing->vibrato_depth = 0; + channel->playing->vibrato_n = 0; + channel->playing->vibrato_time = 0; + channel->playing->vibrato_waveform = 0; + channel->playing->tremolo_speed = 0; + channel->playing->tremolo_depth = 0; + channel->playing->tremolo_time = 0; + channel->playing->tremolo_waveform = 0; + channel->playing->panbrello_speed = 0; + channel->playing->panbrello_depth = 0; + channel->playing->panbrello_time = 0; + channel->playing->panbrello_waveform = 0; + channel->playing->panbrello_random = 0; + channel->playing->sample_vibrato_time = 0; + channel->playing->sample_vibrato_waveform = channel->playing->sample->vibrato_waveform; + channel->playing->sample_vibrato_depth = 0; + channel->playing->slide = 0; + channel->playing->finetune = channel->playing->sample->finetune; + it_reset_filter_state(&channel->playing->filter_state[0]); // Are these + it_reset_filter_state(&channel->playing->filter_state[1]); // necessary? + it_playing_reset_resamplers(channel->playing, 0); + + /** WARNING - is everything initialised? */ + } + } + + if (!((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_XM_KEY_OFF && entry->effectvalue == 0) && + !((entry->mask & IT_ENTRY_NOTE) && entry->note >= 120) && + (entry->mask & (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) == (IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT)) + { + if (channel->playing) retrigger_xm_envelopes(channel->playing); + get_default_volpan(sigdata, channel); + } + + if ((entry->mask & IT_ENTRY_VOLPAN) && ((entry->volpan>>4) == 0xF)) { + /* Tone Portamento */ + unsigned char v = (entry->volpan & 15) << 4; + if (v == 0) + v = channel->lastG; + channel->lastG = v; + if (entry->mask & IT_ENTRY_NOTE) + if (channel->sample && channel->note < 120) + channel->destnote = channel->truenote; + channel->toneporta = v << 4; + } else if ((entry->mask & IT_ENTRY_EFFECT) && + (entry->effect == IT_TONE_PORTAMENTO || + entry->effect == IT_VOLSLIDE_TONEPORTA)) { + unsigned char v; + if (entry->effect == IT_TONE_PORTAMENTO) + v = entry->effectvalue; + else + v = 0; + if (v == 0) + v = channel->lastG; + channel->lastG = v; + if (entry->mask & IT_ENTRY_NOTE) + if (channel->sample && channel->note < 120) + channel->destnote = channel->truenote; + channel->toneporta = v << 4; + } + + if (entry->mask & IT_ENTRY_VOLPAN) { + int effect = entry->volpan >> 4; + int value = entry->volpan & 15; + switch (effect) { + case 0x6: /* Volume slide down */ + channel->xm_volslide = -value; + break; + case 0x7: /* Volume slide up */ + channel->xm_volslide = value; + break; + case 0x8: /* Fine volume slide down */ + channel->volume -= value; + if (channel->volume > 64) channel->volume = 0; + break; + case 0x9: /* Fine volume slide up */ + channel->volume += value; + if (channel->volume > 64) channel->volume = 64; + break; + case 0xA: /* Set vibrato speed */ + if (value) + channel->lastHspeed = value; + if (channel->playing) + channel->playing->vibrato_speed = channel->lastHspeed; + break; + case 0xB: /* Vibrato */ + if (value) + channel->lastHdepth = value << 2; /** WARNING: correct ? */ + if (channel->playing) { + channel->playing->vibrato_depth = channel->lastHdepth; + channel->playing->vibrato_speed = channel->lastHspeed; + channel->playing->vibrato_n++; + } + break; + case 0xC: /* Set panning */ + channel->truepan = 32 + value*(17*64); + break; + case 0xD: /* Pan slide left */ + /* -128 is a special case for emulating a 'feature' in FT2. + * As soon as effects are processed, it goes hard left. + */ + channel->panslide = value ? -value : -128; + break; + case 0xE: /* Pan slide Right */ + channel->panslide = value; + break; + case 0xF: /* Tone porta */ + break; + default: /* Volume */ + channel->volume = entry->volpan - 0x10; + break; + } + } + + if (playing) free_playing(playing); +} + + + +/* This function assumes !IT_IS_END_ROW(entry). */ +static int process_note_data(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + + if (sigdata->flags & IT_WAS_AN_XM) + process_xm_note_data(sigrenderer, entry); + else + if (process_it_note_data(sigrenderer, entry)) return 0; + + return process_effects(sigrenderer, entry, ignore_cxx); +} + + + +static int process_entry(DUMB_IT_SIGRENDERER *sigrenderer, IT_ENTRY *entry, int ignore_cxx) +{ + IT_CHANNEL *channel = &sigrenderer->channel[(int)entry->channel]; + + if (entry->mask & IT_ENTRY_NOTE) + channel->note = entry->note; + + if ((entry->mask & (IT_ENTRY_NOTE|IT_ENTRY_EFFECT)) && (sigrenderer->sigdata->flags & IT_WAS_A_669)) { + reset_channel_effects(channel); + // XXX unknown + if (channel->playing) channel->playing->finetune = 0; + } + + if ((entry->mask & IT_ENTRY_EFFECT) && entry->effect == IT_S) { + /* channel->lastS was set in update_pattern_variables(). */ + unsigned char effectvalue = channel->lastS; + if (effectvalue >> 4 == IT_S_NOTE_DELAY) { + channel->note_delay_count = effectvalue & 15; + if (channel->note_delay_count == 0) + channel->note_delay_count = 1; + channel->note_delay_entry = entry; + return 0; + } + } + + return process_note_data(sigrenderer, entry, ignore_cxx); +} + + + +static void update_tick_counts(DUMB_IT_SIGRENDERER *sigrenderer) +{ + int i; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + + if (channel->key_off_count) { + channel->key_off_count--; + if (channel->key_off_count == 0) + xm_note_off(sigrenderer->sigdata, channel); + } else if (channel->note_cut_count) { + channel->note_cut_count--; + if (channel->note_cut_count == 0) { + if (sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_PTM)) + channel->volume = 0; + else if (channel->playing) { +#ifdef RAMP_DOWN + int i; + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (!sigrenderer->playing[i]) { + channel->playing->declick_stage = 2; + sigrenderer->playing[i] = channel->playing; + channel->playing = NULL; + break; + } + } + if (channel->playing) { + free_playing(channel->playing); + channel->playing = NULL; + } +#else + free_playing(channel->playing); + channel->playing = NULL; +#endif + } + } + } else if (channel->note_delay_count) { + channel->note_delay_count--; + if (channel->note_delay_count == 0) + process_note_data(sigrenderer, channel->note_delay_entry, 0); + /* Don't bother checking the return value; if the note + * was delayed, there can't have been a speed=0. + */ + } + } +} + + + +static int envelope_get_y(IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ +#if 1 + (void)envelope; //TODO: remove the parameter + return pe->value; +#else + int ys, ye; + int ts, te; + int t; + + if (pe->next_node <= 0) + return envelope->node_y[0] << IT_ENVELOPE_SHIFT; + + if (pe->next_node >= envelope->n_nodes) + return envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; + + ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; + ts = envelope->node_t[pe->next_node-1]; + te = envelope->node_t[pe->next_node]; + + if (ts == te) + return ys; + + ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; + + t = pe->tick; + + return ys + (ye - ys) * (t - ts) / (te - ts); +#endif +} + + + +#if 0 +static int it_envelope_end(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if (pe->next_node >= envelope->n_nodes) + return 1; + + if (pe->tick < envelope->node_t[pe->next_node]) return 0; + + if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && + envelope->loop_end >= pe->next_node && + envelope->node_t[envelope->loop_end] <= pe->tick) return 0; + + if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && + !(playing->flags & IT_PLAYING_SUSTAINOFF) && + envelope->sus_loop_end >= pe->next_node && + envelope->node_t[envelope->sus_loop_end] <= pe->tick) return 0; + + if (envelope->node_t[envelope->n_nodes-1] <= pe->tick) return 1; + + return 0; +} +#endif + + + +/* Returns 1 when fading should be initiated for a volume envelope. */ +static int update_it_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe, int flags) +{ + if (!(playing->enabled_envelopes & flags) || !envelope->n_nodes) + return 0; + + ASSERT(envelope->n_nodes > 0); + + if (pe->tick <= 0) + pe->value = envelope->node_y[0] << IT_ENVELOPE_SHIFT; + else if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) { + pe->value = envelope->node_y[envelope->n_nodes-1] << IT_ENVELOPE_SHIFT; + } else { + int ys = envelope->node_y[pe->next_node-1] << IT_ENVELOPE_SHIFT; + int ts = envelope->node_t[pe->next_node-1]; + int te = envelope->node_t[pe->next_node]; + + if (ts == te) + pe->value = ys; + else { + int ye = envelope->node_y[pe->next_node] << IT_ENVELOPE_SHIFT; + int t = pe->tick; + + pe->value = ys + (ye - ys) * (t - ts) / (te - ts); + } + } + + pe->tick++; + + recalculate_it_envelope_node(pe, envelope); + + if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) { + if (pe->tick > envelope->node_t[envelope->sus_loop_end]) { + pe->next_node = envelope->sus_loop_start + 1; + ASSERT(pe->next_node < envelope->n_nodes); + pe->tick = envelope->node_t[envelope->sus_loop_start]; + return 0; + } + } else if (envelope->flags & IT_ENVELOPE_LOOP_ON) { + if (pe->tick > envelope->node_t[envelope->loop_end]) { + pe->next_node = envelope->loop_start + 1; + ASSERT(pe->next_node < envelope->n_nodes); + pe->tick = envelope->node_t[envelope->loop_start]; + return 0; + } + } + else if (pe->tick > envelope->node_t[envelope->n_nodes - 1]) + return 1; + + return 0; +} + + + +static void update_it_envelopes(IT_PLAYING *playing) +{ + IT_ENVELOPE *envelope = &playing->env_instrument->volume_envelope; + IT_PLAYING_ENVELOPE *pe = &playing->volume_envelope; + + if (update_it_envelope(playing, envelope, pe, IT_ENV_VOLUME)) { + playing->flags |= IT_PLAYING_FADING; + if (pe->value == 0) + playing->flags |= IT_PLAYING_DEAD; + } + + update_it_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope, IT_ENV_PANNING); + update_it_envelope(playing, &playing->env_instrument->pitch_envelope, &playing->pitch_envelope, IT_ENV_PITCH); +} + + + +static int xm_envelope_is_sustaining(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if ((envelope->flags & IT_ENVELOPE_SUSTAIN_LOOP) && !(playing->flags & IT_PLAYING_SUSTAINOFF)) + if (envelope->sus_loop_start < envelope->n_nodes) + if (pe->tick == envelope->node_t[envelope->sus_loop_start]) + return 1; + return 0; +} + + + +static void update_xm_envelope(IT_PLAYING *playing, IT_ENVELOPE *envelope, IT_PLAYING_ENVELOPE *pe) +{ + if (!(envelope->flags & IT_ENVELOPE_ON)) + return; + + if (xm_envelope_is_sustaining(playing, envelope, pe)) + return; + + if (pe->tick >= envelope->node_t[envelope->n_nodes-1]) + return; + + pe->tick++; + + /* pe->next_node must be kept up to date for envelope_get_y(). */ + while (pe->tick > envelope->node_t[pe->next_node]) + pe->next_node++; + + if ((envelope->flags & IT_ENVELOPE_LOOP_ON) && envelope->loop_end < envelope->n_nodes) { + if (pe->tick == envelope->node_t[envelope->loop_end]) { + pe->next_node = MID(0, envelope->loop_start, envelope->n_nodes - 1); + pe->tick = envelope->node_t[pe->next_node]; + } + } + + xm_envelope_calculate_value(envelope, pe); +} + + + +static void update_xm_envelopes(IT_PLAYING *playing) +{ + update_xm_envelope(playing, &playing->env_instrument->volume_envelope, &playing->volume_envelope); + update_xm_envelope(playing, &playing->env_instrument->pan_envelope, &playing->pan_envelope); +} + + + +static void update_fadeout(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing) +{ + if (playing->flags & IT_PLAYING_FADING) { + playing->fadeoutcount -= playing->env_instrument->fadeout; + if (playing->fadeoutcount <= 0) { + playing->fadeoutcount = 0; + if (!(sigdata->flags & IT_WAS_AN_XM)) + playing->flags |= IT_PLAYING_DEAD; + } + } +} + +static int apply_pan_envelope(IT_PLAYING *playing); +static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume); + +static void playing_volume_setup(DUMB_IT_SIGRENDERER * sigrenderer, IT_PLAYING * playing, float invt2g) +{ + DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata; + int pan; + float vol, span; + + pan = apply_pan_envelope(playing); + + if ((sigrenderer->n_channels >= 2) && (sigdata->flags & IT_STEREO) && (sigrenderer->n_channels != 3 || !IT_IS_SURROUND_SHIFTED(pan))) { + span = (pan - (32<<8)) * sigdata->pan_separation * (1.0f / ((32<<8) * 128)); + vol = 0.5f; + if (!IT_IS_SURROUND_SHIFTED(pan)) vol *= 1.0f - span; + playing->float_volume[0] = vol; + vol = -vol; + if (!IT_IS_SURROUND_SHIFTED(pan)) vol += 1.0f; + playing->float_volume[1] = vol; + } else { + playing->float_volume[0] = 1.0f; + playing->float_volume[1] = 1.0f; + } + + vol = calculate_volume(sigrenderer, playing, 1.0f); + playing->float_volume[0] *= vol; + playing->float_volume[1] *= vol; + + if (!sigrenderer->ramp_style || playing->ramp_volume[0] == 31337.0) { + playing->ramp_volume[0] = playing->float_volume[0]; + playing->ramp_volume[1] = playing->float_volume[1]; + playing->ramp_delta[0] = 0; + playing->ramp_delta[1] = 0; + } else { + playing->ramp_delta[0] = invt2g * (playing->float_volume[0] - playing->ramp_volume[0]); + playing->ramp_delta[1] = invt2g * (playing->float_volume[1] - playing->ramp_volume[1]); + } +} + +static void process_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float invt2g) +{ + DUMB_IT_SIGDATA * sigdata = sigrenderer->sigdata; + + if (playing->instrument) { + if (sigdata->flags & IT_WAS_AN_XM) + update_xm_envelopes(playing); + else + update_it_envelopes(playing); + update_fadeout(sigdata, playing); + } + + playing_volume_setup(sigrenderer, playing, invt2g); + + if (sigdata->flags & IT_WAS_AN_XM) { + /* 'depth' is used to store the tick number for XM files. */ + if (playing->sample_vibrato_depth < playing->sample->vibrato_rate) + playing->sample_vibrato_depth++; + } else { + playing->sample_vibrato_depth += playing->sample->vibrato_rate; + if (playing->sample_vibrato_depth > playing->sample->vibrato_depth << 8) + playing->sample_vibrato_depth = playing->sample->vibrato_depth << 8; + } + + playing->sample_vibrato_time += playing->sample->vibrato_speed; +} + +#ifdef _MSC_VER +static float log2(float x) {return (float)log(x)/(float)log(2.0f);} +#endif + +static int delta_to_note(float delta, int base) +{ + float note; + note = log2(delta * 65536.f / (float)base)*12.0f+60.5f; + if (note > 119) note = 119; + else if (note < 0) note = 0; + return (int)note; +} + +// Period table for Protracker octaves 0-5: +static const unsigned short ProTrackerPeriodTable[6*12] = +{ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, + 856,808,762,720,678,640,604,570,538,508,480,453, + 428,404,381,360,339,320,302,285,269,254,240,226, + 214,202,190,180,170,160,151,143,135,127,120,113, + 107,101,95,90,85,80,75,71,67,63,60,56, + 53,50,47,45,42,40,37,35,33,31,30,28 +}; + + +static const unsigned short ProTrackerTunedPeriods[16*12] = +{ + 1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960,907, + 1700,1604,1514,1430,1348,1274,1202,1134,1070,1010,954,900, + 1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948,894, + 1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940,888, + 1664,1570,1482,1398,1320,1246,1176,1110,1048,990,934,882, + 1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926,874, + 1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920,868, + 1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914,862, + 1814,1712,1616,1524,1440,1356,1280,1208,1140,1076,1016,960, + 1800,1700,1604,1514,1430,1350,1272,1202,1134,1070,1010,954, + 1788,1688,1592,1504,1418,1340,1264,1194,1126,1064,1004,948, + 1774,1676,1582,1492,1408,1330,1256,1184,1118,1056,996,940, + 1762,1664,1570,1482,1398,1320,1246,1176,1110,1048,988,934, + 1750,1652,1558,1472,1388,1310,1238,1168,1102,1040,982,926, + 1736,1640,1548,1460,1378,1302,1228,1160,1094,1032,974,920, + 1724,1628,1536,1450,1368,1292,1220,1150,1086,1026,968,914 +}; + +static void process_all_playing(DUMB_IT_SIGRENDERER *sigrenderer) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + int i; + + float invt2g = 1.0f / ((float)TICK_TIME_DIVIDEND / (float)sigrenderer->tempo); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; + IT_PLAYING *playing = channel->playing; + + if (playing) { + int vibrato_shift; + switch (playing->vibrato_waveform) + { + default: + vibrato_shift = it_sine[playing->vibrato_time]; + break; + case 1: + vibrato_shift = it_sawtooth[playing->vibrato_time]; + break; + case 2: + vibrato_shift = it_squarewave[playing->vibrato_time]; + break; + case 3: + vibrato_shift = (rand() % 129) - 64; + break; + case 4: + vibrato_shift = it_xm_squarewave[playing->vibrato_time]; + break; + case 5: + vibrato_shift = it_xm_ramp[playing->vibrato_time]; + break; + case 6: + vibrato_shift = it_xm_ramp[255-playing->vibrato_time]; + break; + } + vibrato_shift *= playing->vibrato_n; + vibrato_shift *= playing->vibrato_depth; + vibrato_shift >>= 4; + + if (sigdata->flags & IT_OLD_EFFECTS) + vibrato_shift = -vibrato_shift; + + playing->volume = channel->volume; + playing->pan = channel->truepan; + + if (playing->volume_offset) { + playing->volume += (playing->volume_offset * playing->volume) >> 7; + if (playing->volume > 64) { + if (playing->volume_offset < 0) playing->volume = 0; + else playing->volume = 64; + } + } + + if (playing->panning_offset && !IT_IS_SURROUND_SHIFTED(playing->pan)) { + playing->pan += playing->panning_offset << IT_ENVELOPE_SHIFT; + if (playing->pan > 64 << IT_ENVELOPE_SHIFT) { + if (playing->panning_offset < 0) playing->pan = 0; + else playing->pan = 64 << IT_ENVELOPE_SHIFT; + } + } + + if (sigdata->flags & IT_LINEAR_SLIDES) { + int currpitch = ((playing->note - 60) << 8) + playing->slide + + vibrato_shift + + playing->finetune; + + /* We add a feature here, which is that of keeping the pitch + * within range. Otherwise it crashes. Trust me. It happened. + * The limit 32768 gives almost 11 octaves either way. + */ + if (currpitch < -32768) + currpitch = -32768; + else if (currpitch > 32767) + currpitch = 32767; + + playing->delta = (float)pow(DUMB_PITCH_BASE, currpitch); + playing->delta *= playing->sample->C5_speed * (1.f / 65536.0f); + } else { + int slide = playing->slide + vibrato_shift; + + playing->delta = (float)pow(DUMB_PITCH_BASE, ((60 - playing->note) << 8) - playing->finetune ); + /* playing->delta is 1.0 for C-5, 0.5 for C-6, etc. */ + + playing->delta *= 1.0f / playing->sample->C5_speed; + + playing->delta -= slide / AMIGA_DIVISOR; + + if (playing->delta < (1.0f / 65536.0f) / 32768.0f) { + // Should XM notes die if Amiga slides go out of range? + playing->flags |= IT_PLAYING_DEAD; + playing->delta = 1. / 32768.; + continue; + } + + playing->delta = (1.0f / 65536.0f) / playing->delta; + } + + if (playing->channel->glissando && playing->channel->toneporta && playing->channel->destnote < 120) { + playing->delta = (float)pow(DUMB_SEMITONE_BASE, delta_to_note(playing->delta, playing->sample->C5_speed) - 60) + * playing->sample->C5_speed * (1.f / 65536.f); + } + + /* + if ( channel->arpeggio ) { // another FT2 bug... + if ((sigdata->flags & (IT_LINEAR_SLIDES|IT_WAS_AN_XM|IT_WAS_A_MOD)) == (IT_WAS_AN_XM|IT_LINEAR_SLIDES) && + playing->flags & IT_PLAYING_SUSTAINOFF) + { + if ( channel->arpeggio > 0xFF ) + playing->delta = playing->sample->C5_speed * (1.f / 65536.f); + } + else*/ + { + int tick = sigrenderer->tick - 1; + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD))!=IT_WAS_AN_XM) + tick = sigrenderer->speed - tick - 1; + else if (tick == sigrenderer->speed - 1) + tick = 0; + else + ++tick; + playing->delta *= (float)pow(DUMB_SEMITONE_BASE, channel->arpeggio_offsets[channel->arpeggio_table[tick&31]]); + } + /* + }*/ + + playing->filter_cutoff = channel->filter_cutoff; + playing->filter_resonance = channel->filter_resonance; + } + } + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) { + process_playing(sigrenderer, sigrenderer->channel[i].playing, invt2g); + if (!(sigdata->flags & IT_WAS_AN_XM)) { + //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { + // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. + if (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + process_playing(sigrenderer, sigrenderer->playing[i], invt2g); + if (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD) { + free_playing(sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static int process_tick(DUMB_IT_SIGRENDERER *sigrenderer) +{ + DUMB_IT_SIGDATA *sigdata = sigrenderer->sigdata; + + // Set note vol/freq to vol/freq set for each channel + + if (sigrenderer->speed && --sigrenderer->tick == 0) { + reset_tick_counts(sigrenderer); + sigrenderer->tick = sigrenderer->speed; + sigrenderer->rowcount--; + if (sigrenderer->rowcount == 0) { + sigrenderer->rowcount = 1; + +#ifdef BIT_ARRAY_BULLSHIT + if (sigrenderer->n_rows) + { +#if 1 + /* + if (bit_array_test(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row)) + { + if (sigrenderer->callbacks->loop) { + if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) + return 1; + bit_array_reset(sigrenderer->played); + if (sigrenderer->speed == 0) + goto speed0; I love goto + } + } + */ +#endif + bit_array_set(sigrenderer->played, sigrenderer->order * 256 + sigrenderer->row); + { + int n; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) + { + IT_CHANNEL * channel = &sigrenderer->channel[n]; + if (channel->played_patjump) + { + if (channel->played_patjump_order == sigrenderer->order) + { + bit_array_set(channel->played_patjump, sigrenderer->row); + } + /* + else if ((channel->played_patjump_order & 0x7FFF) == sigrenderer->order) + { + channel->played_patjump_order |= 0x4000; + } + else if ((channel->played_patjump_order & 0x3FFF) == sigrenderer->order) + { + if ((sigdata->flags & (IT_WAS_AN_XM|IT_WAS_A_MOD)) == IT_WAS_AN_XM) + { + joy, was XM, pattern loop bug triggered break to row in same order + bit_array_mask(sigrenderer->played, channel->played_patjump, sigrenderer->order * 256); + } + bit_array_destroy(channel->played_patjump); + channel->played_patjump = 0; + channel->played_patjump_order = 0xFFFE; + } + */ + else + { + bit_array_destroy(channel->played_patjump); + channel->played_patjump = 0; + channel->played_patjump_order = 0xFFFE; + } + } + } + } + } +#endif + + sigrenderer->processrow++; + + if (sigrenderer->processrow >= sigrenderer->n_rows) { + IT_PATTERN *pattern; + int n; + int processorder = sigrenderer->processorder; + + if ((sigrenderer->processrow|0xC00) == 0xFFFE + 1) { /* It was incremented above! */ + sigrenderer->processrow = sigrenderer->breakrow; + sigrenderer->breakrow = 0; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) sigrenderer->channel[n].pat_loop_end_row = 0; + } else { + sigrenderer->processrow = sigrenderer->breakrow; + sigrenderer->breakrow = 0; // XXX lolwut + } + + if (sigrenderer->processorder == 0xFFFF) + sigrenderer->processorder = sigrenderer->order - 1; + + for (;;) { + sigrenderer->processorder++; + + if (sigrenderer->processorder >= sigdata->n_orders) { + sigrenderer->processorder = sigrenderer->restart_position; + if (sigrenderer->processorder >= sigdata->n_orders) { + /* Restarting beyond end. We'll loop for now. */ + sigrenderer->processorder = -1; + continue; + } + if (sigdata->flags & IT_WAS_AN_OKT) { + /* Reset some things */ + sigrenderer->speed = sigdata->speed; + sigrenderer->tempo = sigdata->tempo; + for (n = 0; n < DUMB_IT_N_CHANNELS; n++) { + xm_note_off(sigdata, &sigrenderer->channel[n]); + } + } + } + + n = sigdata->order[sigrenderer->processorder]; + + if (n < sigdata->n_patterns) + break; + +#ifdef INVALID_ORDERS_END_SONG + if (n != IT_ORDER_SKIP) +#else + if (n == IT_ORDER_END) +#endif + { + sigrenderer->processorder = sigrenderer->restart_position - 1; + } + +#ifdef BIT_ARRAY_BULLSHIT + /* Fix play tracking and timekeeping for orders containing skip commands */ + for (n = 0; n < 256; n++) { + bit_array_set(sigrenderer->played, sigrenderer->processorder * 256 + n); + timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n, sigrenderer->time_played); + timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->processorder * 256 + n); + } +#endif + } + + pattern = &sigdata->pattern[n]; + + n = sigrenderer->n_rows; + sigrenderer->n_rows = pattern->n_rows; + + if (sigrenderer->processrow >= sigrenderer->n_rows) + sigrenderer->processrow = 0; + +/** WARNING - everything pertaining to a new pattern initialised? */ + + sigrenderer->entry = sigrenderer->entry_start = pattern->entry; + sigrenderer->entry_end = sigrenderer->entry + pattern->n_entries; + + /* If n_rows was 0, we're only just starting. Don't do anything weird here. */ + /* added: process row check, for break to row spooniness */ + if (n && (processorder == 0xFFFF ? sigrenderer->order > sigrenderer->processorder : sigrenderer->order >= sigrenderer->processorder) +#ifdef BIT_ARRAY_BULLSHIT + && bit_array_test(sigrenderer->played, sigrenderer->processorder * 256 + sigrenderer->processrow) +#endif + ) { +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->looped = 1; +#endif + if (sigrenderer->callbacks->loop) { + if ((*sigrenderer->callbacks->loop)(sigrenderer->callbacks->loop_data)) + return 1; +#ifdef BIT_ARRAY_BULLSHIT + bit_array_reset(sigrenderer->played); +#endif + if (sigrenderer->speed == 0) + goto speed0; /* I love goto */ + } + } + sigrenderer->order = sigrenderer->processorder; + + n = sigrenderer->processrow; + while (n) { + while (sigrenderer->entry < sigrenderer->entry_end) { + if (IT_IS_END_ROW(sigrenderer->entry)) { + sigrenderer->entry++; + break; + } + sigrenderer->entry++; + } + n--; + } + sigrenderer->row = sigrenderer->processrow; + } else { + if (sigrenderer->entry) { + while (sigrenderer->entry < sigrenderer->entry_end) { + if (IT_IS_END_ROW(sigrenderer->entry)) { + sigrenderer->entry++; + break; + } + sigrenderer->entry++; + } + sigrenderer->row++; + } else { +#ifdef BIT_ARRAY_BULLSHIT + bit_array_clear(sigrenderer->played, sigrenderer->order * 256); +#endif + sigrenderer->entry = sigrenderer->entry_start; + sigrenderer->row = 0; + } + } + +#ifdef BIT_ARRAY_BULLSHIT + if (sigrenderer->looped == 0) { + timekeeping_array_push(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row, sigrenderer->time_played); + } + timekeeping_array_bump(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); +#endif + + if (!(sigdata->flags & IT_WAS_A_669)) + reset_effects(sigrenderer); + + { + IT_ENTRY *entry = sigrenderer->entry; + int ignore_cxx = 0; + + while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) + ignore_cxx |= update_pattern_variables(sigrenderer, entry++); + + entry = sigrenderer->entry; + + while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) + if (process_entry(sigrenderer, entry++, sigdata->flags & IT_WAS_AN_XM ? 0 : ignore_cxx)) + return 1; + } + + if (sigdata->flags & IT_WAS_AN_OKT) + update_effects(sigrenderer); + else if (!(sigdata->flags & IT_OLD_EFFECTS)) + update_smooth_effects(sigrenderer); + } else { + { + IT_ENTRY *entry = sigrenderer->entry; + + while (entry < sigrenderer->entry_end && !IT_IS_END_ROW(entry)) { + if (entry->mask & IT_ENTRY_EFFECT && entry->effect != IT_SET_SAMPLE_OFFSET) + process_effects(sigrenderer, entry, 0); + /* Don't bother checking the return value; if there + * was a pattern delay, there can't be a speed=0. + */ + entry++; + } + } + + update_effects(sigrenderer); + } + } else { + if ( !(sigdata->flags & IT_WAS_AN_STM) || !(sigrenderer->tick & 15)) { + speed0: + update_effects(sigrenderer); + update_tick_counts(sigrenderer); + } + } + + if (sigrenderer->globalvolume == 0) { + if (sigrenderer->callbacks->global_volume_zero) { + LONG_LONG t = sigrenderer->gvz_sub_time + ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; + sigrenderer->gvz_time += (int)(t >> 16); + sigrenderer->gvz_sub_time = (int)t & 65535; + if (sigrenderer->gvz_time >= 65536 * 12) { +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->looped = 1; +#endif + if ((*sigrenderer->callbacks->global_volume_zero)(sigrenderer->callbacks->global_volume_zero_data)) + return 1; + } + } + } else { + if (sigrenderer->callbacks->global_volume_zero) { + sigrenderer->gvz_time = 0; + sigrenderer->gvz_sub_time = 0; + } + } + + process_all_playing(sigrenderer); + + { + LONG_LONG t = ((LONG_LONG)TICK_TIME_DIVIDEND << 16) / sigrenderer->tempo; + if ( sigrenderer->sigdata->flags & IT_WAS_AN_STM ) { + t /= 16; + } + t += sigrenderer->sub_time_left; + sigrenderer->time_left += (int)(t >> 16); + sigrenderer->sub_time_left = (int)t & 65535; + } + + return 0; +} + + + +int dumb_it_max_to_mix = 64; + +static const int aiMODVol[] = +{ + 0, + 16, 24, 32, 48, 64, 80, 96, 112, + 128, 144, 160, 176, 192, 208, 224, 240, + 256, 272, 288, 304, 320, 336, 352, 368, + 384, 400, 416, 432, 448, 464, 480, 496, + 529, 545, 561, 577, 593, 609, 625, 641, + 657, 673, 689, 705, 721, 737, 753, 769, + 785, 801, 817, 833, 849, 865, 881, 897, + 913, 929, 945, 961, 977, 993, 1009, 1024 +}; + +static const int aiPTMVolScaled[] = +{ + 0, + 31, 54, 73, 96, 111, 130, 153, 172, + 191, 206, 222, 237, 252, 275, 298, 317, + 336, 351, 370, 386, 401, 416, 428, 443, + 454, 466, 477, 489, 512, 531, 553, 573, + 592, 611, 626, 645, 660, 679, 695, 710, + 725, 740, 756, 767, 782, 798, 809, 820, + 836, 847, 859, 870, 881, 897, 908, 916, + 927, 939, 950, 962, 969, 983, 1005, 1024 +}; + +static float calculate_volume(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume) +{ + if (volume != 0) { + int vol; + + if (playing->channel->flags & IT_CHANNEL_MUTED) + return 0; + + if ((playing->channel->tremor_time & 192) == 128) + return 0; + + switch (playing->tremolo_waveform) + { + default: + vol = it_sine[playing->tremolo_time]; + break; + case 1: + vol = it_sawtooth[playing->tremolo_time]; + break; + case 2: + vol = it_squarewave[playing->tremolo_time]; + break; + case 3: + vol = (rand() % 129) - 64; + break; + case 4: + vol = it_xm_squarewave[playing->tremolo_time]; + break; + case 5: + vol = it_xm_ramp[playing->tremolo_time]; + break; + case 6: + vol = it_xm_ramp[255-((sigrenderer->sigdata->flags & IT_WAS_A_MOD)?playing->vibrato_time:playing->tremolo_time)]; + break; + } + vol *= playing->tremolo_depth; + + vol = (playing->volume << 5) + vol; + + if (vol <= 0) + return 0; + + if (vol > 64 << 5) + vol = 64 << 5; + + if ( sigrenderer->sigdata->flags & IT_WAS_A_PTM ) + { + int v = aiPTMVolScaled[ vol >> 5 ]; + if ( vol < 64 << 5 ) + { + int f = vol & ( ( 1 << 5 ) - 1 ); + int f2 = ( 1 << 5 ) - f; + int v2 = aiPTMVolScaled[ ( vol >> 5 ) + 1 ]; + v = ( v * f2 + v2 * f ) >> 5; + } + vol = v << 1; + } + + volume *= vol; /* 64 << 5 */ + volume *= playing->sample->global_volume; /* 64 */ + volume *= playing->channel_volume; /* 64 */ + volume *= sigrenderer->globalvolume; /* 128 */ + volume *= sigrenderer->sigdata->mixing_volume; /* 128 */ + volume *= 1.0f / ((64 << 5) * 64.0f * 64.0f * 128.0f * 128.0f); + + if (volume && playing->instrument) { + if (playing->enabled_envelopes & IT_ENV_VOLUME && playing->env_instrument->volume_envelope.n_nodes) { + volume *= envelope_get_y(&playing->env_instrument->volume_envelope, &playing->volume_envelope); + volume *= 1.0f / (64 << IT_ENVELOPE_SHIFT); + } + volume *= playing->instrument->global_volume; /* 128 */ + volume *= playing->fadeoutcount; /* 1024 */ + volume *= 1.0f / (128.0f * 1024.0f); + } + } + + return volume; +} + + + +static int apply_pan_envelope(IT_PLAYING *playing) +{ + if (playing->pan <= 64 << IT_ENVELOPE_SHIFT) { + int pan; + if (playing->panbrello_depth) { + switch (playing->panbrello_waveform) { + default: + pan = it_sine[playing->panbrello_time]; + break; + case 1: + pan = it_sawtooth[playing->panbrello_time]; + break; + case 2: + pan = it_squarewave[playing->panbrello_time]; + break; + case 3: + pan = playing->panbrello_random; + break; + } + pan *= playing->panbrello_depth << 3; + + pan += playing->pan; + if (pan < 0) pan = 0; + else if (pan > 64 << IT_ENVELOPE_SHIFT) pan = 64 << IT_ENVELOPE_SHIFT; + } else { + pan = playing->pan; + } + + if (playing->env_instrument && (playing->enabled_envelopes & IT_ENV_PANNING)) { + int p = envelope_get_y(&playing->env_instrument->pan_envelope, &playing->pan_envelope); + if (pan > 32 << IT_ENVELOPE_SHIFT) + p *= (64 << IT_ENVELOPE_SHIFT) - pan; + else + p *= pan; + pan += p >> (5 + IT_ENVELOPE_SHIFT); + } + return pan; + } + return playing->pan; +} + + + +/* Note: if a click remover is provided, and store_end_sample is set, then + * the end point will be computed twice. This situation should not arise. + */ +#if 0 +static long render_playing(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int cr_record_which) +{ + int bits; + + long size_rendered; + + DUMB_VOLUME_RAMP_INFO lvol, rvol; + + if (playing->flags & IT_PLAYING_DEAD) + return 0; + + if (*left_to_mix <= 0) + volume = 0; + +#ifndef END_RAMPING + { + int quality = sigrenderer->resampling_quality; + if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality) + quality = playing->sample->max_resampling_quality; + playing->resampler.quality = quality; + } +#endif + + bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; + + if (volume == 0) { + if (playing->sample->flags & IT_SAMPLE_STEREO) + size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta); + else + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta); + } else { + lvol.volume = playing->ramp_volume [0]; + rvol.volume = playing->ramp_volume [1]; + lvol.delta = playing->ramp_delta [0] * main_delta; + rvol.delta = playing->ramp_delta [1] * main_delta; + lvol.target = playing->float_volume [0]; + rvol.target = playing->float_volume [1]; + rvol.mix = lvol.mix = volume; + if (sigrenderer->n_channels >= 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); + } + size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, &lvol, &rvol, delta); + if (store_end_sample) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click); + samples[0][(pos + size_rendered) * 2] = click[0]; + samples[0][(pos + size_rendered) * 2 + 1] = click[1]; + } + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); + } + } else { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); + } + size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, &lvol, &rvol, delta); + if (store_end_sample) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click); + samples[0][(pos + size_rendered) * 2] = click[0]; + samples[0][(pos + size_rendered) * 2 + 1] = click[1]; + } + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, &lvol, &rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); + } + } + } else { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos, click); + } + size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, &lvol, &rvol, delta); + if (store_end_sample) + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &samples[0][pos + size_rendered]); + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, &lvol, &rvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); + } + } else { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos, click); + } + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, &lvol, delta); + if (store_end_sample) + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &samples[0][pos + size_rendered]); + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, &lvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); + } + } + } + playing->ramp_volume [0] = lvol.volume; + playing->ramp_volume [1] = rvol.volume; + (*left_to_mix)--; + } + + if (playing->resampler.dir == 0) + playing->flags |= IT_PLAYING_DEAD; + + return size_rendered; +} +#endif + +#ifdef END_RAMPING +#if 1 +static long render_playing_part(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, DUMB_VOLUME_RAMP_INFO * lvol, DUMB_VOLUME_RAMP_INFO * rvol, int bits, float delta, long pos, long size, sample_t **samples, int store_end_sample, int cr_record_which) +{ + long size_rendered = 0; + + if (sigrenderer->n_channels == 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); + } + size_rendered = dumb_resample_n_2_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta); + if (store_end_sample) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click); + samples[0][(pos + size_rendered) * 2] = click[0]; + samples[0][(pos + size_rendered) * 2 + 1] = click[1]; + } + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_2_2(bits, &playing->resampler, lvol, rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); + } + } else { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos, click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos, click[1]); + } + size_rendered = dumb_resample_n_1_2(bits, &playing->resampler, samples[0] + pos*2, size, lvol, rvol, delta); + if (store_end_sample) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click); + samples[0][(pos + size_rendered) * 2] = click[0]; + samples[0][(pos + size_rendered) * 2 + 1] = click[1]; + } + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click[2]; + dumb_resample_get_current_sample_n_1_2(bits, &playing->resampler, lvol, rvol, click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click[0]); + dumb_record_click(sigrenderer->click_remover[1], pos + size_rendered, -click[1]); + } + } + } else { + if (playing->sample->flags & IT_SAMPLE_STEREO) { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos, click); + } + size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, samples[0] + pos, size, lvol, rvol, delta); + if (store_end_sample) + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &samples[0][pos + size_rendered]); + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_2_1(bits, &playing->resampler, lvol, rvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); + } + } else { + if ((cr_record_which & 1) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos, click); + } + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, samples[0] + pos, size, lvol, delta); + if (store_end_sample) + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &samples[0][pos + size_rendered]); + if ((cr_record_which & 2) && sigrenderer->click_remover) { + sample_t click; + dumb_resample_get_current_sample_n_1_1(bits, &playing->resampler, lvol, &click); + dumb_record_click(sigrenderer->click_remover[0], pos + size_rendered, -click); + } + } + } + return size_rendered; +} + +static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style) +{ + int bits; + + long size_rendered; + + DUMB_VOLUME_RAMP_INFO lvol, rvol; + + if (!size) return 0; + + if (playing->flags & IT_PLAYING_DEAD) + return 0; + + if (*left_to_mix <= 0) + volume = 0; + + { + int quality = sigrenderer->resampling_quality; + if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality) + quality = playing->sample->max_resampling_quality; + playing->resampler.quality = quality; + } + + bits = playing->sample->flags & IT_SAMPLE_16BIT ? 16 : 8; + size_rendered = size; + + if (volume == 0) { + if (playing->declick_stage < 2) { + if (playing->sample->flags & IT_SAMPLE_STEREO) + size_rendered = dumb_resample_n_2_1(bits, &playing->resampler, NULL, size, 0, 0, delta); + else + size_rendered = dumb_resample_n_1_1(bits, &playing->resampler, NULL, size, 0, delta); + } else { + playing->declick_stage = 3; + } + } else { + lvol.volume = playing->ramp_volume [0]; + rvol.volume = playing->ramp_volume [1]; + lvol.delta = playing->ramp_delta [0] * main_delta; + rvol.delta = playing->ramp_delta [1] * main_delta; + lvol.target = playing->float_volume [0]; + rvol.target = playing->float_volume [1]; + rvol.mix = lvol.mix = volume; + if (ramp_style) { + if (playing->declick_stage == 1) { + size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3); + } else if (playing->declick_stage == 0 || playing->declick_stage == 2) { + float declick_count = ((ramp_style == 2) ? 327.68 : 49.152) / main_delta; /* 5ms / 0.75ms */ + float declick_remain = playing->declick_volume * declick_count; + float declick_dir = -1; + float declick_target; + int remain; + DUMB_VOLUME_RAMP_INFO declick_lvol, declick_rvol; + if (playing->declick_stage == 0) { + declick_remain = declick_count - declick_remain; + declick_dir = 1; + } + if (size < declick_remain) declick_remain = size; + remain = declick_remain; + if (remain > size) + declick_remain = size; + declick_target = playing->declick_volume + declick_dir / declick_count * declick_remain; + declick_lvol.volume = lvol.volume * playing->declick_volume; + declick_rvol.volume = rvol.volume * playing->declick_volume; + declick_lvol.target = lvol.volume * declick_target; + declick_rvol.target = rvol.volume * declick_target; + declick_lvol.delta = (declick_lvol.target - declick_lvol.volume) / declick_remain; + declick_rvol.delta = (declick_rvol.target - declick_rvol.volume) / declick_remain; + declick_lvol.mix = declick_rvol.mix = volume; + if (remain < size) { + size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, 0, 1); + if (size_rendered == remain) { + if (playing->declick_stage == 0) { + size_rendered += render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos + remain, size - remain, samples, store_end_sample, 2); + } else { + size_rendered = size; + } + } + playing->declick_stage++; + playing->declick_volume = 1; + } else { + size_rendered = render_playing_part(sigrenderer, playing, &declick_lvol, &declick_rvol, bits, delta, pos, remain, samples, store_end_sample, 3); + playing->declick_volume = declick_target; + } + lvol.volume = declick_lvol.volume; + rvol.volume = declick_rvol.volume; + } else /*if (playing->declick_stage == 3)*/ { + (*left_to_mix)++; + } + } else if (playing->declick_stage < 2) { + size_rendered = render_playing_part(sigrenderer, playing, &lvol, &rvol, bits, delta, pos, size, samples, store_end_sample, 3); + } else { + playing->declick_stage = 3; + (*left_to_mix)++; + } + playing->ramp_volume [0] = lvol.volume; + playing->ramp_volume [1] = rvol.volume; + (*left_to_mix)--; + } + + if (playing->resampler.dir == 0) + playing->flags |= IT_PLAYING_DEAD; + + return size_rendered; +} +#else +static long render_playing_ramp(DUMB_IT_SIGRENDERER *sigrenderer, IT_PLAYING *playing, float volume, float main_delta, float delta, long pos, long size, sample_t **samples, int store_end_sample, int *left_to_mix, int ramp_style) +{ + long rv, trv; + int l_t_m_temp, cr_which; + if (!size) return 0; + + rv = 0; + cr_which = 3; + + { + int quality = sigrenderer->resampling_quality; + if (playing->sample->max_resampling_quality >= 0 && quality > playing->sample->max_resampling_quality) + quality = playing->sample->max_resampling_quality; + playing->resampler.quality = quality; + } + + if (ramp_style) + { + if (playing->declick_stage == 0) { + /* ramp up! */ + cr_which = 1; + while (playing->declick_stage == 0 && size) { + l_t_m_temp = *left_to_mix; + if (size == 1) cr_which |= 2; + trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which); + cr_which &= 2; + if (ramp_style == 2) playing->declick_volume += (1.f / 256.f) * main_delta; + else playing->declick_volume *= (float)pow(2.0f, main_delta); + if (playing->declick_volume >= 1.0f) { + playing->declick_volume = 1.0f; + playing->declick_stage = 1; + } + if (!trv) { + *left_to_mix = l_t_m_temp; + return rv; + } + rv += trv; + pos += trv; + size -= trv; + } + + if (!size) { + *left_to_mix = l_t_m_temp; + return rv; + } + + cr_which = 2; + + } +#ifdef RAMP_DOWN + else if (playing->declick_stage == 2) { + float halflife = pow(0.5, 1.0 / 64.0 * main_delta); + cr_which = 1; + while (playing->declick_stage == 2 && size) { + l_t_m_temp = *left_to_mix; + if (size == 1) cr_which |= 2; + trv = render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, &l_t_m_temp, cr_which); + cr_which &= 2; + /*if (ramp_style == 2) playing->declick_volume -= (1.f / 256.f) * main_delta; + else playing->declick_volume *= (float)pow(0.5f, main_delta);*/ + playing->declick_volume *= halflife; + if (playing->declick_volume < (1.f / 256.f)) { + playing->declick_stage = 3; + } + if (!trv) { + *left_to_mix = l_t_m_temp; + return rv; + } + rv += trv; + pos += trv; + size -= trv; + } + + if (size) rv += render_playing(sigrenderer, playing, volume * playing->declick_volume, main_delta, delta, pos, 1, samples, store_end_sample, left_to_mix, 2); + else *left_to_mix = l_t_m_temp; + + return rv; + + } else if (playing->declick_stage == 3) { + return size; + } +#endif + } else if (playing->declick_stage >= 2) { + playing->declick_stage = 3; + return size; + } + + return rv + render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, cr_which); +} +#endif +#else +#define render_playing_ramp(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, ramp_style) render_playing(sigrenderer, playing, volume, main_delta, delta, pos, size, samples, store_end_sample, left_to_mix, 3) +#endif + +typedef struct IT_TO_MIX +{ + IT_PLAYING *playing; + float volume; +} +IT_TO_MIX; + + + +static int it_to_mix_compare(const void *e1, const void *e2) +{ + if (((const IT_TO_MIX *)e1)->volume > ((const IT_TO_MIX *)e2)->volume) + return -1; + + if (((const IT_TO_MIX *)e1)->volume < ((const IT_TO_MIX *)e2)->volume) + return 1; + + return 0; +} + + + +static void apply_pitch_modifications(DUMB_IT_SIGDATA *sigdata, IT_PLAYING *playing, float *delta, int *cutoff) +{ + { + int sample_vibrato_shift; + switch (playing->sample_vibrato_waveform) + { + default: + sample_vibrato_shift = it_sine[playing->sample_vibrato_time]; + break; + case 1: + sample_vibrato_shift = it_sawtooth[playing->sample_vibrato_time]; + break; + case 2: + sample_vibrato_shift = it_squarewave[playing->sample_vibrato_time]; + break; + case 3: + sample_vibrato_shift = (rand() % 129) - 64; + break; + case 4: + sample_vibrato_shift = it_xm_squarewave[playing->sample_vibrato_time]; + break; + case 5: + sample_vibrato_shift = it_xm_ramp[playing->sample_vibrato_time]; + break; + case 6: + sample_vibrato_shift = it_xm_ramp[255-playing->sample_vibrato_time]; + break; + } + + if (sigdata->flags & IT_WAS_AN_XM) { + int depth = playing->sample->vibrato_depth; /* True depth */ + if (playing->sample->vibrato_rate) { + depth *= playing->sample_vibrato_depth; /* Tick number */ + depth /= playing->sample->vibrato_rate; /* XM sweep */ + } + sample_vibrato_shift *= depth; + } else + sample_vibrato_shift *= playing->sample_vibrato_depth >> 8; + + sample_vibrato_shift >>= 4; + + if (sample_vibrato_shift) { + if ((sigdata->flags & IT_LINEAR_SLIDES) || !(sigdata->flags & IT_WAS_AN_XM)) + *delta *= (float)pow(DUMB_PITCH_BASE, sample_vibrato_shift); + else { + /* complicated! */ + float scale = *delta / playing->delta; + + *delta = (1.0f / 65536.0f) / playing->delta; + + *delta -= sample_vibrato_shift / AMIGA_DIVISOR; + + if (*delta < (1.0f / 65536.0f) / 32767.0f) { + *delta = (1.0f / 65536.0f) / 32767.0f; + } + + *delta = (1.0f / 65536.0f) / *delta * scale; + } + } + } + + if (playing->env_instrument && + (playing->enabled_envelopes & IT_ENV_PITCH)) + { + int p = envelope_get_y(&playing->env_instrument->pitch_envelope, &playing->pitch_envelope); + if (playing->env_instrument->pitch_envelope.flags & IT_ENVELOPE_PITCH_IS_FILTER) + *cutoff = (*cutoff * (p+(32<> (6 + IT_ENVELOPE_SHIFT); + else + *delta *= (float)pow(DUMB_PITCH_BASE, p >> (IT_ENVELOPE_SHIFT - 7)); + } +} + + + +static void render_normal(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long size, sample_t **samples) +{ + int i; + + int n_to_mix = 0; + IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS]; + int left_to_mix = dumb_it_max_to_mix; + + sample_t **samples_to_filter = NULL; + + int ramp_style = sigrenderer->ramp_style; + + //int max_output = sigrenderer->max_output; + + if (ramp_style > 2) { + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == IT_WAS_AN_XM) ramp_style = 2; + else ramp_style -= 3; + } + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + to_mix[n_to_mix].playing = sigrenderer->channel[i].playing; + to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->channel[i].playing, volume); + n_to_mix++; + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */ + to_mix[n_to_mix].playing = sigrenderer->playing[i]; + to_mix[n_to_mix].volume = volume == 0 ? 0 : calculate_volume(sigrenderer, sigrenderer->playing[i], volume); + n_to_mix++; + } + } + + if (volume != 0) + qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare); + + for (i = 0; i < n_to_mix; i++) { + IT_PLAYING *playing = to_mix[i].playing; + float note_delta = delta * playing->delta; + int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + //int output = min( playing->output, max_output ); + + apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); + + if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { + playing->true_filter_cutoff = cutoff; + playing->true_filter_resonance = playing->filter_resonance; + } + + if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { + if (!samples_to_filter) { + samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); + if (!samples_to_filter) { + render_playing_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style); + continue; + } + } + { + long size_rendered; + DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; + dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1)); + sigrenderer->click_remover = NULL; + size_rendered = render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style); + sigrenderer->click_remover = cr; + if (sigrenderer->n_channels == 2) { + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[0 /*output*/]+1, pos, samples_to_filter[0]+1, size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + } else { + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered, + 1, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + } + // FIXME: filtering is not prevented by low left_to_mix! + // FIXME: change 'warning' to 'FIXME' everywhere + } + } else { + it_reset_filter_state(&playing->filter_state[0]); + it_reset_filter_state(&playing->filter_state[1]); + render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix, ramp_style); + } + } + + destroy_sample_buffer(samples_to_filter); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) { + //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { + // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. + if ( +#ifdef RAMP_DOWN + (sigrenderer->channel[i].playing->declick_stage == 3) || +#endif + (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + if ( +#ifdef RAMP_DOWN + (sigrenderer->playing[i]->declick_stage == 3) || +#endif + (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static void render_surround(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long size, sample_t **samples) +{ + int i; + + int n_to_mix = 0, n_to_mix_surround = 0; + IT_TO_MIX to_mix[DUMB_IT_TOTAL_CHANNELS]; + IT_TO_MIX to_mix_surround[DUMB_IT_TOTAL_CHANNELS]; + int left_to_mix = dumb_it_max_to_mix; + + int saved_channels = sigrenderer->n_channels; + + sample_t **samples_to_filter = NULL; + + int ramp_style = sigrenderer->ramp_style; + + DUMB_CLICK_REMOVER **saved_cr = sigrenderer->click_remover; + + //int max_output = sigrenderer->max_output; + + if (ramp_style > 2) { + if ((sigrenderer->sigdata->flags & (IT_WAS_AN_XM | IT_WAS_A_MOD)) == IT_WAS_AN_XM) ramp_style = 2; + else ramp_style -= 3; + } + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing && !(sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + IT_PLAYING *playing = sigrenderer->channel[i].playing; + IT_TO_MIX *_to_mix = IT_IS_SURROUND_SHIFTED(playing->pan) ? to_mix_surround + n_to_mix_surround++ : to_mix + n_to_mix++; + _to_mix->playing = playing; + _to_mix->volume = volume == 0 ? 0 : calculate_volume(sigrenderer, playing, volume); + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { /* Won't be dead; it would have been freed. */ + IT_PLAYING *playing = sigrenderer->playing[i]; + IT_TO_MIX *_to_mix = IT_IS_SURROUND_SHIFTED(playing->pan) ? to_mix_surround + n_to_mix_surround++ : to_mix + n_to_mix++; + _to_mix->playing = playing; + _to_mix->volume = volume == 0 ? 0 : calculate_volume(sigrenderer, playing, volume); + } + } + + if (volume != 0) { + qsort(to_mix, n_to_mix, sizeof(IT_TO_MIX), &it_to_mix_compare); + qsort(to_mix_surround, n_to_mix_surround, sizeof(IT_TO_MIX), &it_to_mix_compare); + } + + sigrenderer->n_channels = 2; + + for (i = 0; i < n_to_mix; i++) { + IT_PLAYING *playing = to_mix[i].playing; + float note_delta = delta * playing->delta; + int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + //int output = min( playing->output, max_output ); + + apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); + + if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { + playing->true_filter_cutoff = cutoff; + playing->true_filter_resonance = playing->filter_resonance; + } + + if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { + if (!samples_to_filter) { + samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); + if (!samples_to_filter) { + render_playing_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style); + continue; + } + } + { + long size_rendered; + DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; + dumb_silence(samples_to_filter[0], sigrenderer->n_channels * (size + 1)); + sigrenderer->click_remover = NULL; + size_rendered = render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style); + sigrenderer->click_remover = cr; + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[0 /*output*/], pos, samples_to_filter[0], size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + it_filter(cr ? cr[1] : NULL, &playing->filter_state[1], samples[0 /*output*/]+1, pos, samples_to_filter[0]+1, size_rendered, + 2, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + } + } else { + it_reset_filter_state(&playing->filter_state[0]); + it_reset_filter_state(&playing->filter_state[1]); + render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, samples /*&samples[output]*/, 0, &left_to_mix, ramp_style); + } + } + + sigrenderer->n_channels = 1; + sigrenderer->click_remover = saved_cr ? saved_cr + 2 : 0; + + for (i = 0; i < n_to_mix_surround; i++) { + IT_PLAYING *playing = to_mix_surround[i].playing; + float note_delta = delta * playing->delta; + int cutoff = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + //int output = min( playing->output, max_output ); + + apply_pitch_modifications(sigrenderer->sigdata, playing, ¬e_delta, &cutoff); + + if (cutoff != 127 << IT_ENVELOPE_SHIFT || playing->filter_resonance != 0) { + playing->true_filter_cutoff = cutoff; + playing->true_filter_resonance = playing->filter_resonance; + } + + if (volume && (playing->true_filter_cutoff != 127 << IT_ENVELOPE_SHIFT || playing->true_filter_resonance != 0)) { + if (!samples_to_filter) { + samples_to_filter = allocate_sample_buffer(sigrenderer->n_channels, size + 1); + if (!samples_to_filter) { + render_playing_ramp(sigrenderer, playing, 0, delta, note_delta, pos, size, NULL, 0, &left_to_mix, ramp_style); + continue; + } + } + { + long size_rendered; + DUMB_CLICK_REMOVER **cr = sigrenderer->click_remover; + dumb_silence(samples_to_filter[0], size + 1); + sigrenderer->click_remover = NULL; + size_rendered = render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, 0, size, samples_to_filter, 1, &left_to_mix, ramp_style); + sigrenderer->click_remover = cr; + it_filter(cr ? cr[0] : NULL, &playing->filter_state[0], samples[1 /*output*/], pos, samples_to_filter[0], size_rendered, + 1, (int)(65536.0f/delta), playing->true_filter_cutoff, playing->true_filter_resonance); + // FIXME: filtering is not prevented by low left_to_mix! + // FIXME: change 'warning' to 'FIXME' everywhere + } + } else { + it_reset_filter_state(&playing->filter_state[0]); + it_reset_filter_state(&playing->filter_state[1]); + render_playing_ramp(sigrenderer, playing, volume, delta, note_delta, pos, size, &samples[1], 0, &left_to_mix, ramp_style); + } + } + + sigrenderer->n_channels = saved_channels; + sigrenderer->click_remover = saved_cr; + + destroy_sample_buffer(samples_to_filter); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) { + //if ((sigrenderer->channel[i].playing->flags & (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) == (IT_PLAYING_BACKGROUND | IT_PLAYING_DEAD)) { + // This change was made so Gxx would work correctly when a note faded out or whatever. Let's hope nothing else was broken by it. + if ( +#ifdef RAMP_DOWN + (sigrenderer->channel[i].playing->declick_stage == 3) || +#endif + (sigrenderer->channel[i].playing->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->channel[i].playing); + sigrenderer->channel[i].playing = NULL; + } + } + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + if ( +#ifdef RAMP_DOWN + (sigrenderer->playing[i]->declick_stage == 3) || +#endif + (sigrenderer->playing[i]->flags & IT_PLAYING_DEAD)) { + free_playing(sigrenderer->playing[i]); + sigrenderer->playing[i] = NULL; + } + } + } +} + + + +static void render(DUMB_IT_SIGRENDERER *sigrenderer, float volume, float delta, long pos, long size, sample_t **samples) +{ + if (size == 0) return; + if (sigrenderer->n_channels == 1 || sigrenderer->n_channels == 2) + render_normal(sigrenderer, volume, delta, pos, size, samples); + else if (sigrenderer->n_channels == 3) + render_surround(sigrenderer, volume, delta, pos, size, samples); +} + + + +static DUMB_IT_SIGRENDERER *init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder, IT_CALLBACKS *callbacks, DUMB_CLICK_REMOVER **cr) +{ + DUMB_IT_SIGRENDERER *sigrenderer; + int i; + + if (startorder > sigdata->n_orders) { + free(callbacks); + dumb_destroy_click_remover_array(n_channels, cr); + return NULL; + } + + sigrenderer = malloc(sizeof(*sigrenderer)); + if (!sigrenderer) { + free(callbacks); + dumb_destroy_click_remover_array(n_channels, cr); + return NULL; + } + + sigrenderer->callbacks = callbacks; + sigrenderer->click_remover = cr; + + sigrenderer->sigdata = sigdata; + sigrenderer->n_channels = n_channels; + sigrenderer->resampling_quality = dumb_resampling_quality; + sigrenderer->ramp_style = 0; + sigrenderer->globalvolume = sigdata->global_volume; + sigrenderer->tempo = sigdata->tempo; + + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + IT_CHANNEL *channel = &sigrenderer->channel[i]; +#if IT_CHANNEL_MUTED != 1 +#error this is wrong +#endif + channel->flags = sigdata->channel_pan[i] >> 7; + channel->volume = (sigdata->flags & IT_WAS_AN_XM) ? 0 : 64; + channel->pan = sigdata->channel_pan[i] & 0x7F; + channel->truepan = channel->pan << IT_ENVELOPE_SHIFT; + channel->channelvolume = sigdata->channel_volume[i]; + channel->instrument = 0; + channel->sample = 0; + channel->note = IT_NOTE_OFF; + channel->SFmacro = 0; + channel->filter_cutoff = 127; + channel->filter_resonance = 0; + channel->new_note_action = 0xFF; + channel->xm_retrig = 0; + channel->retrig_tick = 0; + channel->tremor_time = 0; + channel->vibrato_waveform = 0; + channel->tremolo_waveform = 0; + channel->panbrello_waveform = 0; + channel->glissando = 0; + channel->toneslide = 0; + channel->ptm_toneslide = 0; + channel->ptm_last_toneslide = 0; + channel->okt_toneslide = 0; + channel->midi_state = 0; + channel->lastvolslide = 0; + channel->lastDKL = 0; + channel->lastEF = 0; + channel->lastG = 0; + channel->lastHspeed = 0; + channel->lastHdepth = 0; + channel->lastRspeed = 0; + channel->lastRdepth = 0; + channel->lastYspeed = 0; + channel->lastYdepth = 0; + channel->lastI = 0; + channel->lastJ = 0; + channel->lastN = 0; + channel->lastO = 0; + channel->high_offset = 0; + channel->lastP = 0; + channel->lastQ = 0; + channel->lastS = 0; + channel->pat_loop_row = 0; + channel->pat_loop_count = 0; + channel->pat_loop_end_row = 0; + channel->lastW = 0; + channel->xm_lastE1 = 0; + channel->xm_lastE2 = 0; + channel->xm_lastEA = 0; + channel->xm_lastEB = 0; + channel->xm_lastX1 = 0; + channel->xm_lastX2 = 0; + channel->inv_loop_delay = 0; + channel->inv_loop_speed = 0; + channel->inv_loop_offset = 0; + channel->playing = NULL; +#ifdef BIT_ARRAY_BULLSHIT + channel->played_patjump = NULL; + channel->played_patjump_order = 0xFFFE; +#endif + //channel->output = 0; + } + + if (sigdata->flags & IT_WAS_A_669) + reset_effects(sigrenderer); + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + sigrenderer->playing[i] = NULL; + + sigrenderer->speed = sigdata->speed; + + sigrenderer->processrow = 0xFFFE; + sigrenderer->n_rows = 0; + sigrenderer->breakrow = 0; + sigrenderer->rowcount = 1; + sigrenderer->order = startorder; + /* meh! + if (startorder > 0) { + int n; + for (n = startorder - 1; n >= 0; n--) { + if (sigdata->order[n] > sigdata->n_patterns) { + sigrenderer->restart_position = n + 1; + break; + } + } + } + */ + if (startorder > 0) { + sigrenderer->restart_position = startorder; + } else { + sigrenderer->restart_position = sigdata->restart_position; + } + + sigrenderer->row = 0; + sigrenderer->processorder = startorder - 1; + sigrenderer->tick = 1; + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->played = bit_array_create(sigdata->n_orders * 256); + + sigrenderer->looped = 0; + sigrenderer->time_played = 0; + sigrenderer->row_timekeeper = timekeeping_array_create(sigdata->n_orders * 256); +#endif + + { + int order; + for (order = 0; order < sigdata->n_orders; order++) { + int n = sigdata->order[order]; + if (n < sigdata->n_patterns) goto found_valid_order; +#ifdef INVALID_ORDERS_END_SONG + if (n != IT_ORDER_SKIP) +#else + if (n == IT_ORDER_END) +#endif + break; + +#ifdef BIT_ARRAY_BULLSHIT + /* Fix for played order detection for songs which have skips at the start of the orders list */ + for (n = 0; n < 256; n++) { + bit_array_set(sigrenderer->played, order * 256 + n); + timekeeping_array_push(sigrenderer->row_timekeeper, order * 256 + n, 0); + timekeeping_array_bump(sigrenderer->row_timekeeper, order * 256 + n); + } +#endif + } + /* If we get here, there were no valid orders in the song. */ + _dumb_it_end_sigrenderer(sigrenderer); + return NULL; + } + found_valid_order: + + sigrenderer->time_left = 0; + sigrenderer->sub_time_left = 0; + + sigrenderer->gvz_time = 0; + sigrenderer->gvz_sub_time = 0; + + //sigrenderer->max_output = 0; + + if ( !(sigdata->flags & IT_WAS_PROCESSED) ) { + dumb_it_add_lpc( sigdata ); + + sigdata->flags |= IT_WAS_PROCESSED; + } + + return sigrenderer; +} + + +void dumb_it_set_resampling_quality(DUMB_IT_SIGRENDERER * sigrenderer, int quality) +{ + if (sigrenderer && quality >= 0 && quality < DUMB_RQ_N_LEVELS) + { + int i; + sigrenderer->resampling_quality = quality; + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) + { + IT_PLAYING * playing = sigrenderer->channel[i].playing; + playing->resampling_quality = quality; + playing->resampler.quality = quality; + } + } + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) { + if (sigrenderer->playing[i]) { + IT_PLAYING * playing = sigrenderer->playing[i]; + playing->resampling_quality = quality; + playing->resampler.quality = quality; + } + } + } +} + + +void dumb_it_set_ramp_style(DUMB_IT_SIGRENDERER * sigrenderer, int ramp_style) { + if (sigrenderer && ramp_style >= 0 && ramp_style <= 4) { + sigrenderer->ramp_style = ramp_style; + } +} + + +void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->loop = callback; + sigrenderer->callbacks->loop_data = data; + } +} + + + +void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->xm_speed_zero = callback; + sigrenderer->callbacks->xm_speed_zero_data = data; + } +} + + + +void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->midi = callback; + sigrenderer->callbacks->midi_data = data; + } +} + + + +void dumb_it_set_global_volume_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data) +{ + if (sigrenderer) { + sigrenderer->callbacks->global_volume_zero = callback; + sigrenderer->callbacks->global_volume_zero_data = data; + } +} + + + +static IT_CALLBACKS *create_callbacks(void) +{ + IT_CALLBACKS *callbacks = malloc(sizeof(*callbacks)); + if (!callbacks) return NULL; + callbacks->loop = NULL; + callbacks->xm_speed_zero = NULL; + callbacks->midi = NULL; + callbacks->global_volume_zero = NULL; + return callbacks; +} + + + +static DUMB_IT_SIGRENDERER *dumb_it_init_sigrenderer(DUMB_IT_SIGDATA *sigdata, int n_channels, int startorder) +{ + IT_CALLBACKS *callbacks; + + if (!sigdata) return NULL; + + callbacks = create_callbacks(); + if (!callbacks) return NULL; + + return init_sigrenderer(sigdata, n_channels, startorder, callbacks, + dumb_create_click_remover_array(n_channels)); +} + + + +DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder) +{ + DUMB_IT_SIGDATA *itsd = duh_get_it_sigdata(duh); + DUMB_IT_SIGRENDERER *itsr = dumb_it_init_sigrenderer(itsd, n_channels, startorder); + /*duh->length = dumb_it_build_checkpoints(itsd, startorder);*/ + return duh_encapsulate_it_sigrenderer(itsr, n_channels, 0); +} + + + +static sigrenderer_t *it_start_sigrenderer(DUH *duh, sigdata_t *vsigdata, int n_channels, long pos) +{ + DUMB_IT_SIGDATA *sigdata = vsigdata; + DUMB_IT_SIGRENDERER *sigrenderer; + + (void)duh; + + { + IT_CALLBACKS *callbacks = create_callbacks(); + if (!callbacks) return NULL; + + if (sigdata->checkpoint) { + IT_CHECKPOINT *checkpoint = sigdata->checkpoint; + while (checkpoint->next && checkpoint->next->time < pos) + checkpoint = checkpoint->next; + sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, n_channels, callbacks); + if (!sigrenderer) return NULL; + sigrenderer->click_remover = dumb_create_click_remover_array(n_channels); + pos -= checkpoint->time; + } else { + sigrenderer = init_sigrenderer(sigdata, n_channels, 0, callbacks, + dumb_create_click_remover_array(n_channels)); + if (!sigrenderer) return NULL; + } + } + + while (pos > 0 && pos >= sigrenderer->time_left) { + render(sigrenderer, 0, 1.0f, 0, sigrenderer->time_left, NULL); + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)sigrenderer->time_left << 16; +#endif + + pos -= sigrenderer->time_left; + sigrenderer->time_left = 0; + + if (process_tick(sigrenderer)) { + _dumb_it_end_sigrenderer(sigrenderer); + return NULL; + } + } + + render(sigrenderer, 0, 1.0f, 0, pos, NULL); + sigrenderer->time_left -= pos; + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)pos << 16; +#endif + + return sigrenderer; +} + + + +static long it_sigrenderer_get_samples( + sigrenderer_t *vsigrenderer, + float volume, float delta, + long size, sample_t **samples +) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + long pos; + int dt; + long todo; + int ret; + LONG_LONG t; + + if (sigrenderer->order < 0) return 0; // problematic + + pos = 0; + dt = (int)(delta * 65536.0f + 0.5f); + + /* When samples is finally used in render_playing(), it won't be used if + * volume is 0. + */ + if (!samples) volume = 0; + + for (;;) { + todo = (long)((((LONG_LONG)sigrenderer->time_left << 16) | sigrenderer->sub_time_left) / dt); + + if (todo >= size) + break; + + render(sigrenderer, volume, delta, pos, todo, samples); + + pos += todo; + size -= todo; + + t = sigrenderer->sub_time_left - (LONG_LONG)todo * dt; + sigrenderer->sub_time_left = (long)t & 65535; + sigrenderer->time_left += (long)(t >> 16); + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)todo * dt; +#endif + + ret = process_tick(sigrenderer); + + if (ret) { + sigrenderer->order = -1; + sigrenderer->row = -1; + } + +#ifdef BIT_ARRAY_BULLSHIT + if (sigrenderer->looped == 1) { + sigrenderer->looped = -1; + size = 0; + timekeeping_array_reset(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); + sigrenderer->time_played = timekeeping_array_get_item(sigrenderer->row_timekeeper, sigrenderer->order * 256 + sigrenderer->row); + break; + } +#endif + + if (ret) { + return pos; + } + } + + render(sigrenderer, volume, delta, pos, size, samples); + + pos += size; + + t = sigrenderer->sub_time_left - (LONG_LONG)size * dt; + sigrenderer->sub_time_left = (long)t & 65535; + sigrenderer->time_left += (long)(t >> 16); + +#ifdef BIT_ARRAY_BULLSHIT + sigrenderer->time_played += (LONG_LONG)size * dt; +#endif + + if (samples) + dumb_remove_clicks_array(sigrenderer->n_channels, sigrenderer->click_remover, samples, pos, 512.0f / delta); + + return pos; +} + + + +static void it_sigrenderer_get_current_sample(sigrenderer_t *vsigrenderer, float volume, sample_t *samples) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + (void)volume; // for consideration: in any of these such functions, is 'volume' going to be required? + dumb_click_remover_get_offset_array(sigrenderer->n_channels, sigrenderer->click_remover, samples); +} + + + +void _dumb_it_end_sigrenderer(sigrenderer_t *vsigrenderer) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + + int i; + + if (sigrenderer) { + for (i = 0; i < DUMB_IT_N_CHANNELS; i++) { + if (sigrenderer->channel[i].playing) + free_playing(sigrenderer->channel[i].playing); +#ifdef BIT_ARRAY_BULLSHIT + bit_array_destroy(sigrenderer->channel[i].played_patjump); +#endif + } + + for (i = 0; i < DUMB_IT_N_NNA_CHANNELS; i++) + if (sigrenderer->playing[i]) + free_playing(sigrenderer->playing[i]); + + dumb_destroy_click_remover_array(sigrenderer->n_channels, sigrenderer->click_remover); + + if (sigrenderer->callbacks) + free(sigrenderer->callbacks); + +#ifdef BIT_ARRAY_BULLSHIT + bit_array_destroy(sigrenderer->played); + + timekeeping_array_destroy(sigrenderer->row_timekeeper); +#endif + + free(vsigrenderer); + } +} + + + +#ifdef BIT_ARRAY_BULLSHIT +static long it_sigrenderer_get_position(sigrenderer_t *vsigrenderer) +{ + DUMB_IT_SIGRENDERER *sigrenderer = vsigrenderer; + + return sigrenderer->time_played >> 16; +} +#endif + + + +DUH_SIGTYPE_DESC _dumb_sigtype_it = { + SIGTYPE_IT, + NULL, + &it_start_sigrenderer, + NULL, + &it_sigrenderer_get_samples, + &it_sigrenderer_get_current_sample, +#ifdef BIT_ARRAY_BULLSHIT + &it_sigrenderer_get_position, +#else + NULL, +#endif + &_dumb_it_end_sigrenderer, + &_dumb_it_unload_sigdata +}; + + + +DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos) +{ + return duh_encapsulate_raw_sigrenderer(it_sigrenderer, &_dumb_sigtype_it, n_channels, pos); +} + + + +DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer) +{ + return duh_get_raw_sigrenderer(sigrenderer, SIGTYPE_IT); +} + + + +/* Values of 64 or more will access NNA channels here. */ +void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state) +{ + IT_PLAYING *playing; + int t; /* temporary var for holding accurate pan and filter cutoff */ + float delta; + ASSERT(channel < DUMB_IT_TOTAL_CHANNELS); + if (!sr) { state->sample = 0; return; } + if (channel >= DUMB_IT_N_CHANNELS) { + playing = sr->playing[channel - DUMB_IT_N_CHANNELS]; + if (!playing) { state->sample = 0; return; } + } else { + playing = sr->channel[channel].playing; + if (!playing) { state->sample = 0; return; } + } + + if (playing->flags & IT_PLAYING_DEAD) { state->sample = 0; return; } + + state->channel = playing->channel - sr->channel; + state->sample = playing->sampnum; + state->volume = calculate_volume(sr, playing, 1.0f); + + t = apply_pan_envelope(playing); + state->pan = (unsigned char)((t + 128) >> IT_ENVELOPE_SHIFT); + state->subpan = (signed char)t; + + delta = playing->delta * 65536.0f; + t = playing->filter_cutoff << IT_ENVELOPE_SHIFT; + apply_pitch_modifications(sr->sigdata, playing, &delta, &t); + state->freq = (int)delta; + if (t == 127 << IT_ENVELOPE_SHIFT && playing->filter_resonance == 0) { + state->filter_resonance = playing->true_filter_resonance; + t = playing->true_filter_cutoff; + } else + state->filter_resonance = playing->filter_resonance; + state->filter_cutoff = (unsigned char)(t >> 8); + state->filter_subcutoff = (unsigned char)t; +} + + + +int dumb_it_callback_terminate(void *data) +{ + (void)data; + return 1; +} + + + +int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte) +{ + (void)data; + (void)channel; + (void)midi_byte; + return 1; +} + + + +#define IT_CHECKPOINT_INTERVAL (30 * 65536) /* Half a minute */ + +#define FUCKIT_THRESHOLD (120 * 60 * 65536) /* two hours? probably a pattern loop mess... */ + +/* Returns the length of the module, up until it first loops. */ +long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata, int startorder) +{ + IT_CHECKPOINT *checkpoint; + if (!sigdata) return 0; + checkpoint = sigdata->checkpoint; + while (checkpoint) { + IT_CHECKPOINT *next = checkpoint->next; + _dumb_it_end_sigrenderer(checkpoint->sigrenderer); + free(checkpoint); + checkpoint = next; + } + sigdata->checkpoint = NULL; + checkpoint = malloc(sizeof(*checkpoint)); + if (!checkpoint) return 0; + checkpoint->time = 0; + checkpoint->sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, startorder); + if (!checkpoint->sigrenderer) { + free(checkpoint); + return 0; + } + checkpoint->sigrenderer->callbacks->loop = &dumb_it_callback_terminate; + checkpoint->sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate; + checkpoint->sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate; + + if (sigdata->checkpoint) + { + IT_CHECKPOINT *checkpoint = sigdata->checkpoint; + while (checkpoint) { + IT_CHECKPOINT *next = checkpoint->next; + _dumb_it_end_sigrenderer(checkpoint->sigrenderer); + free(checkpoint); + checkpoint = next; + } + } + + sigdata->checkpoint = checkpoint; + + for (;;) { + long l; + DUMB_IT_SIGRENDERER *sigrenderer = dup_sigrenderer(checkpoint->sigrenderer, 0, checkpoint->sigrenderer->callbacks); + checkpoint->sigrenderer->callbacks = NULL; + if (!sigrenderer) { + checkpoint->next = NULL; + return checkpoint->time; + } + + l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL); + if (l < IT_CHECKPOINT_INTERVAL) { + _dumb_it_end_sigrenderer(sigrenderer); + checkpoint->next = NULL; + return checkpoint->time + l; + } + + checkpoint->next = malloc(sizeof(*checkpoint->next)); + if (!checkpoint->next) { + _dumb_it_end_sigrenderer(sigrenderer); + return checkpoint->time + IT_CHECKPOINT_INTERVAL; + } + + checkpoint->next->time = checkpoint->time + IT_CHECKPOINT_INTERVAL; + checkpoint = checkpoint->next; + checkpoint->sigrenderer = sigrenderer; + + if (checkpoint->time >= FUCKIT_THRESHOLD) { + checkpoint->next = NULL; + return 0; + } + } +} + + + +void dumb_it_do_initial_runthrough(DUH *duh) +{ + if (duh) { + DUMB_IT_SIGDATA *sigdata = duh_get_it_sigdata(duh); + + if (sigdata) + duh_set_length(duh, dumb_it_build_checkpoints(sigdata, 0)); + } +} + +static int is_pattern_silent(IT_PATTERN * pattern, int order) { + int ret = 1; + IT_ENTRY * entry, * end; + if (!pattern || !pattern->n_rows || !pattern->n_entries || !pattern->entry) return 2; + + if ( pattern->n_entries == pattern->n_rows ) { + int n; + entry = pattern->entry; + for ( n = 0; n < pattern->n_entries; ++n, ++entry ) { + if ( !IT_IS_END_ROW(entry) ) break; + } + if ( n == pattern->n_entries ) return 2; + // broken? + } + + entry = pattern->entry; + end = entry + pattern->n_entries; + + while (entry < end) { + if (!IT_IS_END_ROW(entry)) { + if (entry->mask & (IT_ENTRY_INSTRUMENT | IT_ENTRY_VOLPAN)) + return 0; + if (entry->mask & IT_ENTRY_NOTE && entry->note < 120) + return 0; + if (entry->mask & IT_ENTRY_EFFECT) { + switch (entry->effect) { + case IT_SET_GLOBAL_VOLUME: + if (entry->effectvalue) return 0; + break; + + case IT_SET_SPEED: + if (entry->effectvalue > 64) ret++; + break; + + case IT_SET_SONG_TEMPO: + case IT_XM_KEY_OFF: + break; + + case IT_JUMP_TO_ORDER: + if (entry->effectvalue != order) + return 0; + break; + + case IT_S: + switch (entry->effectvalue >> 4) { + case 0: // meh bastard + if ( entry->effectvalue != 0 ) return 0; + break; + + case IT_S_FINE_PATTERN_DELAY: + case IT_S_PATTERN_LOOP: + case IT_S_PATTERN_DELAY: + ret++; + break; + + case IT_S7: + if ((entry->effectvalue & 15) > 2) + return 0; + break; + + default: + return 0; + } + break; + + // clever idiot with his S L O W crap; do nothing + case IT_VOLSLIDE_TONEPORTA: + case IT_SET_SAMPLE_OFFSET: + case IT_GLOBAL_VOLUME_SLIDE: + if ( entry->effectvalue != 0 ) return 0; + break; + + // genius also uses this instead of jump to order by mistake, meh, and it's bloody BCD + case IT_BREAK_TO_ROW: + if ( ( ( entry->effectvalue >> 4 ) * 10 + ( entry->effectvalue & 15 ) ) != order ) return 0; + break; + + default: + return 0; + } + } + } + entry++; + } + + return ret; +} + +int dumb_it_trim_silent_patterns(DUH * duh) { + int n; + DUMB_IT_SIGDATA *sigdata; + + if (!duh) return -1; + + sigdata = duh_get_it_sigdata(duh); + + if (!sigdata || !sigdata->order || !sigdata->pattern) return -1; + + for (n = 0; n < sigdata->n_orders; n++) { + int p = sigdata->order[n]; + if (p < sigdata->n_patterns) { + IT_PATTERN * pattern = &sigdata->pattern[p]; + if (is_pattern_silent(pattern, n) > 1) { + pattern->n_rows = 1; + pattern->n_entries = 0; + if (pattern->entry) + { + free(pattern->entry); + pattern->entry = NULL; + } + } else + break; + } + } + + if (n == sigdata->n_orders) return -1; + + for (n = sigdata->n_orders - 1; n >= 0; n--) { + int p = sigdata->order[n]; + if (p < sigdata->n_patterns) { + IT_PATTERN * pattern = &sigdata->pattern[p]; + if (is_pattern_silent(pattern, n) > 1) { + pattern->n_rows = 1; + pattern->n_entries = 0; + if (pattern->entry) + { + free(pattern->entry); + pattern->entry = NULL; + } + } else + break; + } + } + + if (n < 0) return -1; + + /*duh->length = dumb_it_build_checkpoints(sigdata, 0);*/ + + return 0; +} + +int dumb_it_scan_for_playable_orders(DUMB_IT_SIGDATA *sigdata, dumb_scan_callback callback, void * callback_data) +{ + int n; + long length; + void * ba_played; + DUMB_IT_SIGRENDERER * sigrenderer; + + if (!sigdata->n_orders || !sigdata->order) return -1; + + ba_played = bit_array_create(sigdata->n_orders * 256); + if (!ba_played) return -1; + + /* Skip the first order, it should always be played */ + for (n = 1; n < sigdata->n_orders; n++) { + if ((sigdata->order[n] >= sigdata->n_patterns) || + (is_pattern_silent(&sigdata->pattern[sigdata->order[n]], n) > 1)) + bit_array_set(ba_played, n * 256); + } + + for (;;) { + for (n = 0; n < sigdata->n_orders; n++) { + if (!bit_array_test_range(ba_played, n * 256, 256)) break; + } + + if (n == sigdata->n_orders) break; + + sigrenderer = dumb_it_init_sigrenderer(sigdata, 0, n); + if (!sigrenderer) { + bit_array_destroy(ba_played); + return -1; + } + sigrenderer->callbacks->loop = &dumb_it_callback_terminate; + sigrenderer->callbacks->xm_speed_zero = &dumb_it_callback_terminate; + sigrenderer->callbacks->global_volume_zero = &dumb_it_callback_terminate; + + length = 0; + + for (;;) { + long l; + + l = it_sigrenderer_get_samples(sigrenderer, 0, 1.0f, IT_CHECKPOINT_INTERVAL, NULL); + length += l; + if (l < IT_CHECKPOINT_INTERVAL || length >= FUCKIT_THRESHOLD) { + /* SONG OVA! */ + break; + } + } + + if ((*callback)(callback_data, n, length) < 0) return -1; + + bit_array_merge(ba_played, sigrenderer->played, 0); + + _dumb_it_end_sigrenderer(sigrenderer); + } + + bit_array_destroy(ba_played); + + return 0; +} diff --git a/Frameworks/Dumb/dumb/src/it/itunload.c b/Frameworks/Dumb/dumb/src/it/itunload.c index efed192a6..136fd5c5a 100644 --- a/Frameworks/Dumb/dumb/src/it/itunload.c +++ b/Frameworks/Dumb/dumb/src/it/itunload.c @@ -1,72 +1,72 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * itunload.c - Code to free an Impulse Tracker / / \ \ - * module from memory. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include - -#include "dumb.h" -#include "internal/it.h" - - - -void _dumb_it_unload_sigdata(sigdata_t *vsigdata) -{ - if (vsigdata) { - DUMB_IT_SIGDATA *sigdata = vsigdata; - int n; - - if (sigdata->song_message) - free(sigdata->song_message); - - if (sigdata->order) - free(sigdata->order); - - if (sigdata->instrument) - free(sigdata->instrument); - - if (sigdata->sample) { - for (n = 0; n < sigdata->n_samples; n++) - if (sigdata->sample[n].data) - free(sigdata->sample[n].data); - - free(sigdata->sample); - } - - if (sigdata->pattern) { - for (n = 0; n < sigdata->n_patterns; n++) - if (sigdata->pattern[n].entry) - free(sigdata->pattern[n].entry); - free(sigdata->pattern); - } - - if (sigdata->midi) - free(sigdata->midi); - - { - IT_CHECKPOINT *checkpoint = sigdata->checkpoint; - while (checkpoint) { - IT_CHECKPOINT *next = checkpoint->next; - _dumb_it_end_sigrenderer(checkpoint->sigrenderer); - free(checkpoint); - checkpoint = next; - } - } - - free(vsigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * itunload.c - Code to free an Impulse Tracker / / \ \ + * module from memory. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include + +#include "dumb.h" +#include "internal/it.h" + + + +void _dumb_it_unload_sigdata(sigdata_t *vsigdata) +{ + if (vsigdata) { + DUMB_IT_SIGDATA *sigdata = vsigdata; + int n; + + if (sigdata->song_message) + free(sigdata->song_message); + + if (sigdata->order) + free(sigdata->order); + + if (sigdata->instrument) + free(sigdata->instrument); + + if (sigdata->sample) { + for (n = 0; n < sigdata->n_samples; n++) + if (sigdata->sample[n].data) + free(sigdata->sample[n].data); + + free(sigdata->sample); + } + + if (sigdata->pattern) { + for (n = 0; n < sigdata->n_patterns; n++) + if (sigdata->pattern[n].entry) + free(sigdata->pattern[n].entry); + free(sigdata->pattern); + } + + if (sigdata->midi) + free(sigdata->midi); + + { + IT_CHECKPOINT *checkpoint = sigdata->checkpoint; + while (checkpoint) { + IT_CHECKPOINT *next = checkpoint->next; + _dumb_it_end_sigrenderer(checkpoint->sigrenderer); + free(checkpoint); + checkpoint = next; + } + } + + free(vsigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/load669.c b/Frameworks/Dumb/dumb/src/it/load669.c new file mode 100644 index 000000000..e5a3fc3f4 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/load669.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod.c - Code to read a 669 Composer module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_669_quick(): loads a 669 file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_669_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_669_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/load6692.c b/Frameworks/Dumb/dumb/src/it/load6692.c new file mode 100644 index 000000000..2358838f4 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/load6692.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod2.c - Code to read a 669 Composer module / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_669(): loads a 669 file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_669(const char *filename) +{ + DUH *duh = dumb_load_669_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadamf.c b/Frameworks/Dumb/dumb/src/it/loadamf.c new file mode 100644 index 000000000..2695125f1 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadamf.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadamf.c - Code to read a DSMI AMF module file, / / \ \ + * opening and closing it for you. | < / \_ + * | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_amf_quick(): loads a AMF file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_amf_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_amf_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadamf2.c b/Frameworks/Dumb/dumb/src/it/loadamf2.c new file mode 100644 index 000000000..91f171de7 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadamf2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadamf2.c - Code to read a DSMI AMF module file, / / \ \ + * opening and closing it for you, and | < / \_ + * do an initial run-through. | \/ /\ / + * \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_amf(): loads a AMF file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_amf(const char *filename) +{ + DUH *duh = dumb_load_amf_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadany.c b/Frameworks/Dumb/dumb/src/it/loadany.c new file mode 100644 index 000000000..5d268eafa --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadany.c @@ -0,0 +1,38 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadany.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB, | < / \_ + * opening and closing the file for you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +DUH *dumb_load_any_quick(const char *filename, int restrict_, int subsong) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_any_quick(f, restrict_, subsong); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadany2.c b/Frameworks/Dumb/dumb/src/it/loadany2.c new file mode 100644 index 000000000..fb9439f66 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadany2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadany2.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB, | < / \_ + * opening and closing the file for | \/ /\ / + * you, and do an initial run-through. \_ / > / + * | \ / / + * by Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_any(const char *filename, int restrict_, int subsong) +{ + DUH *duh = dumb_load_any_quick(filename, restrict_, subsong); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadasy.c b/Frameworks/Dumb/dumb/src/it/loadasy.c new file mode 100644 index 000000000..86c6ad8ce --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadasy.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadasy.c - Code to read an ASYLUM Music Format / / \ \ + * module file, opening and closing it | < / \_ + * for you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_asy_quick(): loads a AMF file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_asy_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_asy_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadasy2.c b/Frameworks/Dumb/dumb/src/it/loadasy2.c new file mode 100644 index 000000000..7b253320e --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadasy2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadasy2.c - Code to read an ASYLUM Music Format / / \ \ + * module file, opening and closing it | < / \_ + * for you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_asy(): loads a AMF file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_asy(const char *filename) +{ + DUH *duh = dumb_load_asy_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadmod.c b/Frameworks/Dumb/dumb/src/it/loadmod.c index 121e2881d..4e3e03706 100644 --- a/Frameworks/Dumb/dumb/src/it/loadmod.c +++ b/Frameworks/Dumb/dumb/src/it/loadmod.c @@ -1,42 +1,42 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loadmod.c - Code to read a good old-fashioned / / \ \ - * Amiga module file, opening and | < / \_ - * closing it for you. | \/ /\ / - * \_ / > / - * By entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/it.h" - - - -/* dumb_load_mod_quick(): loads a MOD file into a DUH struct, returning a - * pointer to the DUH struct. When you have finished with it, you must - * pass the pointer to unload_duh() so that the memory can be freed. - */ -DUH *dumb_load_mod_quick(const char *filename) -{ - DUH *duh; - DUMBFILE *f = dumbfile_open(filename); - - if (!f) - return NULL; - - duh = dumb_read_mod_quick(f); - - dumbfile_close(f); - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod.c - Code to read a good old-fashioned / / \ \ + * Amiga module file, opening and | < / \_ + * closing it for you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_mod_quick(): loads a MOD file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_mod_quick(const char *filename, int restrict_) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_mod_quick(f, restrict_); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadmod2.c b/Frameworks/Dumb/dumb/src/it/loadmod2.c index ec705f81d..617897e78 100644 --- a/Frameworks/Dumb/dumb/src/it/loadmod2.c +++ b/Frameworks/Dumb/dumb/src/it/loadmod2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loadmod2.c - Function to read a good old- / / \ \ - * fashioned Amiga module file, | < / \_ - * opening and closing it for you, | \/ /\ / - * and do an initial run-through. \_ / > / - * | \ / / - * Split off from loadmod.c by entheh. | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_load_mod(const char *filename) -{ - DUH *duh = dumb_load_mod_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmod2.c - Function to read a good old- / / \ \ + * fashioned Amiga module file, | < / \_ + * opening and closing it for you, | \/ /\ / + * and do an initial run-through. \_ / > / + * | \ / / + * Split off from loadmod.c by entheh. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_mod(const char *filename, int restrict_) +{ + DUH *duh = dumb_load_mod_quick(filename, restrict_); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadmtm.c b/Frameworks/Dumb/dumb/src/it/loadmtm.c new file mode 100644 index 000000000..3c974880f --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadmtm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmtm.c - Code to read a MultiTracker Module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_mtm_quick(): loads a MTM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_mtm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_mtm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadmtm2.c b/Frameworks/Dumb/dumb/src/it/loadmtm2.c new file mode 100644 index 000000000..9fc238908 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadmtm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadmtm2.c - Code to read a MultiTracker Module / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_mtm(): loads a MTM file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_mtm(const char *filename) +{ + DUH *duh = dumb_load_mtm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadokt.c b/Frameworks/Dumb/dumb/src/it/loadokt.c new file mode 100644 index 000000000..2139d49e8 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadokt.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadokt.c - Code to read an Oktalyzer module / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_okt_quick(): loads an OKT file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_okt_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_okt_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadokt2.c b/Frameworks/Dumb/dumb/src/it/loadokt2.c new file mode 100644 index 000000000..aa752c582 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadokt2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadokt2.c - Function to read an Oktalyzer / / \ \ + * module file, opening and closing | < / \_ + * it for you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_okt(const char *filename) +{ + DUH *duh = dumb_load_okt_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadoldpsm.c b/Frameworks/Dumb/dumb/src/it/loadoldpsm.c new file mode 100644 index 000000000..547294b36 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadoldpsm.c @@ -0,0 +1,43 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadoldpsm.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_old_psm_quick(): loads an old PSM file into a DUH struct, + * returning a pointer to the DUH struct. When you have finished with it, + * you must pass the pointer to unload_duh() so that the memory can be + * freed. + */ +DUH *dumb_load_old_psm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_old_psm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadoldpsm2.c b/Frameworks/Dumb/dumb/src/it/loadoldpsm2.c new file mode 100644 index 000000000..ff2e427c4 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadoldpsm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadoldpsm2.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run- | \/ /\ / + * through. \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_old_psm(): loads an old PSM file into a DUH struct, returning + * a pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_old_psm(const char *filename) +{ + DUH *duh = dumb_load_old_psm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadpsm.c b/Frameworks/Dumb/dumb/src/it/loadpsm.c new file mode 100644 index 000000000..a6851d5b6 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadpsm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadpsm.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_psm_quick(): loads a PSM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_psm_quick(const char *filename, int subsong) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_psm_quick(f, subsong); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadpsm2.c b/Frameworks/Dumb/dumb/src/it/loadpsm2.c new file mode 100644 index 000000000..7a12257a1 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadpsm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadpsm2.c - Code to read a ProTracker Studio / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_psm(): loads a PSM file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_psm(const char *filename, int subsong) +{ + DUH *duh = dumb_load_psm_quick(filename, subsong); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadptm.c b/Frameworks/Dumb/dumb/src/it/loadptm.c new file mode 100644 index 000000000..ddcaaec95 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadptm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadptm.c - Code to read a Poly Tracker v2.03 / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_ptm_quick(): loads a PTM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_ptm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_ptm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadptm2.c b/Frameworks/Dumb/dumb/src/it/loadptm2.c new file mode 100644 index 000000000..ea8982f2b --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadptm2.c @@ -0,0 +1,34 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadptm2.c - Code to read a Poly Tracker v2.03 / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_ptm(): loads a PTM file into a DUH struct, returning a pointer + * to the DUH struct. When you have finished with it, you must pass the + * pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_ptm(const char *filename) +{ + DUH *duh = dumb_load_ptm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadriff.c b/Frameworks/Dumb/dumb/src/it/loadriff.c new file mode 100644 index 000000000..07994b0fc --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadriff.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadriff.c - Code to read a RIFF module file / / \ \ + * opening and closing it for you. | < / \_ + * | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_riff_quick(): loads a RIFF file into a DUH struct, returning + * a pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH * dumb_load_riff_quick( const char *filename ) +{ + DUH * duh; + DUMBFILE * f = dumbfile_open( filename ); + + if ( ! f ) + return NULL; + + duh = dumb_read_riff_quick( f ); + + dumbfile_close( f ); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadriff2.c b/Frameworks/Dumb/dumb/src/it/loadriff2.c new file mode 100644 index 000000000..11c5d4974 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadriff2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadriff2.c - Code to read a RIFF module file / / \ \ + * opening and closing it for you, | < / \_ + * and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_riff(const char *filename) +{ + DUH *duh = dumb_load_riff_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loads3m.c b/Frameworks/Dumb/dumb/src/it/loads3m.c index 777151c68..41af114ea 100644 --- a/Frameworks/Dumb/dumb/src/it/loads3m.c +++ b/Frameworks/Dumb/dumb/src/it/loads3m.c @@ -1,42 +1,42 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loads3m.c - Code to read a ScreamTracker 3 / / \ \ - * file, opening and closing it for | < / \_ - * you. | \/ /\ / - * \_ / > / - * By entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/it.h" - - - -/* dumb_load_s3m_quick(): loads an S3M file into a DUH struct, returning - * a pointer to the DUH struct. When you have finished with it, you must - * pass the pointer to unload_duh() so that the memory can be freed. - */ -DUH *dumb_load_s3m_quick(const char *filename) -{ - DUH *duh; - DUMBFILE *f = dumbfile_open(filename); - - if (!f) - return NULL; - - duh = dumb_read_s3m_quick(f); - - dumbfile_close(f); - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loads3m.c - Code to read a ScreamTracker 3 / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_s3m_quick(): loads an S3M file into a DUH struct, returning + * a pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_s3m_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_s3m_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loads3m2.c b/Frameworks/Dumb/dumb/src/it/loads3m2.c index 8b55e83dc..de81e09a2 100644 --- a/Frameworks/Dumb/dumb/src/it/loads3m2.c +++ b/Frameworks/Dumb/dumb/src/it/loads3m2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loads3m2.c - Function to read a ScreamTracker 3 / / \ \ - * file, opening and closing it for | < / \_ - * you, and do an initial run-through. | \/ /\ / - * \_ / > / - * Split off from loads3m.c by entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_load_s3m(const char *filename) -{ - DUH *duh = dumb_load_s3m_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loads3m2.c - Function to read a ScreamTracker 3 / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * Split off from loads3m.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_s3m(const char *filename) +{ + DUH *duh = dumb_load_s3m_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadstm.c b/Frameworks/Dumb/dumb/src/it/loadstm.c new file mode 100644 index 000000000..847852082 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadstm.c @@ -0,0 +1,42 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadstm.c - Code to read a ScreamTracker 2 / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_stm_quick(): loads an STM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_stm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_stm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadstm2.c b/Frameworks/Dumb/dumb/src/it/loadstm2.c new file mode 100644 index 000000000..4655b52d0 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/loadstm2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadstm2.c - Function to read a ScreamTracker 2 / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_stm(const char *filename) +{ + DUH *duh = dumb_load_stm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadxm.c b/Frameworks/Dumb/dumb/src/it/loadxm.c index 2c8067cbc..a0eba6162 100644 --- a/Frameworks/Dumb/dumb/src/it/loadxm.c +++ b/Frameworks/Dumb/dumb/src/it/loadxm.c @@ -1,42 +1,42 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loadxm.c - Code to read a Fast Tracker II / / \ \ - * file, opening and closing it for | < / \_ - * you. | \/ /\ / - * \_ / > / - * By entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" -#include "internal/it.h" - - - -/* dumb_load_xm_quick(): loads an XM file into a DUH struct, returning a - * pointer to the DUH struct. When you have finished with it, you must - * pass the pointer to unload_duh() so that the memory can be freed. - */ -DUH *dumb_load_xm_quick(const char *filename) -{ - DUH *duh; - DUMBFILE *f = dumbfile_open(filename); - - if (!f) - return NULL; - - duh = dumb_read_xm_quick(f); - - dumbfile_close(f); - - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadxm.c - Code to read a Fast Tracker II / / \ \ + * file, opening and closing it for | < / \_ + * you. | \/ /\ / + * \_ / > / + * By entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" + + + +/* dumb_load_xm_quick(): loads an XM file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must + * pass the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_load_xm_quick(const char *filename) +{ + DUH *duh; + DUMBFILE *f = dumbfile_open(filename); + + if (!f) + return NULL; + + duh = dumb_read_xm_quick(f); + + dumbfile_close(f); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/loadxm2.c b/Frameworks/Dumb/dumb/src/it/loadxm2.c index a596542a2..242a586d4 100644 --- a/Frameworks/Dumb/dumb/src/it/loadxm2.c +++ b/Frameworks/Dumb/dumb/src/it/loadxm2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * loadxm2.c - Function to read a Fast Tracker II / / \ \ - * file, opening and closing it for | < / \_ - * you, and do an initial run-through. | \/ /\ / - * \_ / > / - * Split off from loadxm.c by entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_load_xm(const char *filename) -{ - DUH *duh = dumb_load_xm_quick(filename); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * loadxm2.c - Function to read a Fast Tracker II / / \ \ + * file, opening and closing it for | < / \_ + * you, and do an initial run-through. | \/ /\ / + * \_ / > / + * Split off from loadxm.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_load_xm(const char *filename) +{ + DUH *duh = dumb_load_xm_quick(filename); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/ptmeffect.c b/Frameworks/Dumb/dumb/src/it/ptmeffect.c new file mode 100644 index 000000000..b05a5d744 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/ptmeffect.c @@ -0,0 +1,125 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * ptmeffect.c - Code for converting PTM / / \ \ + * effects to IT effects. | < / \_ + * | \/ /\ / + * By Chris Moeller. Based on xmeffect.c \_ / > / + * by Julien Cugniere. | \ / / + * | ' / + * \__/ + */ + + + +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +void _dumb_it_ptm_convert_effect(int effect, int value, IT_ENTRY *entry) +{ + if (effect >= PTM_N_EFFECTS) + return; + + /* Linearisation of the effect number... */ + if (effect == PTM_E) { + effect = PTM_EBASE + HIGH(value); + value = LOW(value); + } + + /* convert effect */ + entry->mask |= IT_ENTRY_EFFECT; + switch (effect) { + + case PTM_APPREGIO: effect = IT_ARPEGGIO; break; + case PTM_PORTAMENTO_UP: effect = IT_PORTAMENTO_UP; break; + case PTM_PORTAMENTO_DOWN: effect = IT_PORTAMENTO_DOWN; break; + case PTM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break; + case PTM_VIBRATO: effect = IT_VIBRATO; break; + case PTM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; + case PTM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; + case PTM_TREMOLO: effect = IT_TREMOLO; break; + case PTM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break; + case PTM_VOLUME_SLIDE: effect = IT_VOLUME_SLIDE; break; + case PTM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break; + case PTM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; + case PTM_PATTERN_BREAK: effect = IT_BREAK_TO_ROW; break; + case PTM_SET_GLOBAL_VOLUME: effect = IT_SET_GLOBAL_VOLUME; break; + case PTM_RETRIGGER: effect = IT_RETRIGGER_NOTE; break; + case PTM_FINE_VIBRATO: effect = IT_FINE_VIBRATO; break; + + /* TODO properly */ + case PTM_NOTE_SLIDE_UP: effect = IT_PTM_NOTE_SLIDE_UP; break; + case PTM_NOTE_SLIDE_DOWN: effect = IT_PTM_NOTE_SLIDE_DOWN; break; + case PTM_NOTE_SLIDE_UP_RETRIG: effect = IT_PTM_NOTE_SLIDE_UP_RETRIG; break; + case PTM_NOTE_SLIDE_DOWN_RETRIG: effect = IT_PTM_NOTE_SLIDE_DOWN_RETRIG; break; + + case PTM_SET_TEMPO_BPM: + effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); + break; + + case PTM_EBASE+PTM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; /** TODO */ + case PTM_EBASE+PTM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break; + case PTM_EBASE+PTM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break; + case PTM_EBASE+PTM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break; + case PTM_EBASE+PTM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break; + case PTM_EBASE+PTM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break; + + case PTM_EBASE+PTM_E_FINE_VOLSLIDE_UP: + effect = IT_VOLUME_SLIDE; + value = EFFECT_VALUE(value, 0xF); + break; + + case PTM_EBASE + PTM_E_FINE_VOLSLIDE_DOWN: + effect = IT_VOLUME_SLIDE; + value = EFFECT_VALUE(0xF, value); + break; + + case PTM_EBASE + PTM_E_FINE_PORTA_UP: + effect = IT_PORTAMENTO_UP; + value = EFFECT_VALUE(0xF, value); + break; + + case PTM_EBASE + PTM_E_FINE_PORTA_DOWN: + effect = IT_PORTAMENTO_DOWN; + value = EFFECT_VALUE(0xF, value); + break; + + case PTM_EBASE + PTM_E_RETRIG_NOTE: + effect = IT_XM_RETRIGGER_NOTE; + value = EFFECT_VALUE(0, value); + break; + + case PTM_EBASE + PTM_E_SET_VIBRATO_CONTROL: + effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM; + value &= ~4; /** TODO: value&4 -> don't retrig wave */ + break; + + case PTM_EBASE + PTM_E_SET_TREMOLO_CONTROL: + effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM; + value &= ~4; /** TODO: value&4 -> don't retrig wave */ + break; + + default: + /* user effect (often used in demos for synchronisation) */ + entry->mask &= ~IT_ENTRY_EFFECT; + } + + /* Inverse linearisation... */ + if (effect >= SBASE && effect < SBASE+16) { + value = EFFECT_VALUE(effect-SBASE, value); + effect = IT_S; + } + + entry->effect = effect; + entry->effectvalue = value; +} diff --git a/Frameworks/Dumb/dumb/src/it/read669.c b/Frameworks/Dumb/dumb/src/it/read669.c new file mode 100644 index 000000000..4e29afbce --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/read669.c @@ -0,0 +1,447 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * read669.c - Code to read a 669 Composer module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_669_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int tempo, int breakpoint, unsigned char *buffer, int * used_channels) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if (dumbfile_getnc((char *)buffer, 64 * 3 * 8, f) < 64 * 3 * 8) + return -1; + + /* compute number of entries */ + pattern->n_entries = 64 + 1; /* Account for the row end markers, speed command */ + if (breakpoint < 63) pattern->n_entries++; /* and break to row 0 */ + + pos = 0; + for (row = 0; row < 64; row++) { + for (channel = 0; channel < 8; channel++) { + if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF) + pattern->n_entries++; + pos += 3; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + if (breakpoint == 63) breakpoint++; + + entry = pattern->entry; + + entry->channel = 8; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_SPEED; + entry->effectvalue = tempo; + entry++; + + pos = 0; + for (row = 0; row < 64; row++) { + + if (row == breakpoint) { + entry->channel = 8; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_BREAK_TO_ROW; + entry->effectvalue = 0; + entry++; + } + + for (channel = 0; channel < 8; channel++) { + if (buffer[pos+0] != 0xFF || buffer[pos+2] != 0xFF) { + entry->channel = channel; + entry->mask = 0; + + if (buffer[pos+0] < 0xFE) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT; + entry->note = (buffer[pos+0] >> 2) + 36; + entry->instrument = (((buffer[pos+0] << 4) | (buffer[pos+1] >> 4)) & 0x3F) + 1; + } + if (buffer[pos+0] <= 0xFE) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = ((buffer[pos+1] & 15) << 6) / 15; + if (*used_channels < channel + 1) *used_channels = channel + 1; + } + if (buffer[pos+2] != 0xFF) { + entry->mask |= IT_ENTRY_EFFECT; + entry->effectvalue = buffer[pos+2] & 15; + switch (buffer[pos+2] >> 4) { + case 0: + entry->effect = IT_PORTAMENTO_UP; + break; + case 1: + entry->effect = IT_PORTAMENTO_DOWN; + break; + case 2: + entry->effect = IT_TONE_PORTAMENTO; + break; + case 3: + entry->effect = IT_S; + entry->effectvalue += IT_S_FINETUNE * 16 + 8; + break; + case 4: + entry->effect = IT_VIBRATO; + // XXX speed unknown + entry->effectvalue |= 0x10; + break; + case 5: + if (entry->effectvalue) { + entry->effect = IT_SET_SPEED; + } else { + entry->mask &= ~IT_ENTRY_EFFECT; + } + break; +#if 0 + /* dunno about this, really... */ + case 6: + if (entry->effectvalue == 0) { + entry->effect = IT_PANNING_SLIDE; + entry->effectvalue = 0xFE; + } else if (entry->effectvalue == 1) { + entry->effect = IT_PANNING_SLIDE; + entry->effectvalue = 0xEF; + } else { + entry->mask &= ~IT_ENTRY_EFFECT; + } + break; +#endif + default: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + } + if (*used_channels < channel + 1) *used_channels = channel + 1; + } + + entry++; + } + pos += 3; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static int it_669_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) +{ + dumbfile_getnc((char *)sample->name, 13, f); + sample->name[13] = 0; + + sample->filename[0] = 0; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + + if (dumbfile_error(f)) + return -1; + + if (sample->length <= 0) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->global_volume = 64; + sample->default_volume = 64; + + sample->default_pan = 0; + sample->C5_speed = 8363; + // the above line might be wrong + + if ((sample->loop_end > sample->length) && !(sample->loop_start)) + sample->loop_end = 0; + + if (sample->loop_end > sample->length) + sample->loop_end = sample->length; + + if (sample->loop_end - sample->loop_start > 2) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return 0; +} + + + +static int it_669_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f) +{ + long i; + long truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + sample->data = malloc(sample->length); + + if (!sample->data) + return -1; + + if (sample->length) + { + i = dumbfile_getnc(sample->data, sample->length, f); + + if (i < sample->length) { + //return -1; + // ficking truncated files + if (i <= 0) { + sample->flags = 0; + return 0; + } + sample->length = i; + if (sample->loop_end > i) sample->loop_end = i; + } else { + /* skip truncated data */ + dumbfile_skip(f, truncated_size); + // Should we be truncating it? + if (dumbfile_error(f)) + return -1; + } + + for (i = 0; i < sample->length; i++) + ((signed char *)sample->data)[i] ^= 0x80; + } + + return 0; +} + + +static DUMB_IT_SIGDATA *it_669_load_sigdata(DUMBFILE *f, int * ext) +{ + DUMB_IT_SIGDATA *sigdata; + int n_channels; + int i; + unsigned char tempolist[128]; + unsigned char breaklist[128]; + + i = dumbfile_igetw(f); + if (i != 0x6669 && i != 0x4E4A) return NULL; + + *ext = (i == 0x4E4A); + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + if (dumbfile_getnc((char *)sigdata->name, 36, f) < 36) { + free(sigdata); + return NULL; + } + sigdata->name[36] = 0; + + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + sigdata->sample = NULL; + + sigdata->n_instruments = 0; + + sigdata->song_message = malloc(72 + 2 + 1); + if (!sigdata->song_message) { + free(sigdata); + return NULL; + } + if (dumbfile_getnc((char *)sigdata->song_message, 36, f) < 36) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->song_message[36] = 13; + sigdata->song_message[36 + 1] = 10; + if (dumbfile_getnc((char *)sigdata->song_message + 38, 36, f) < 36) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->song_message[38 + 36] = 0; + + sigdata->n_samples = dumbfile_getc(f); + sigdata->n_patterns = dumbfile_getc(f); + sigdata->restart_position = dumbfile_getc(f); + + if ((sigdata->n_samples) > 64 || (sigdata->n_patterns > 128)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->order = malloc(128); /* We may need to scan the extra ones! */ + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if (dumbfile_getnc((char *)sigdata->order, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (i = 0; i < 128; i++) { + if (sigdata->order[i] == 255) break; + if (sigdata->order[i] >= sigdata->n_patterns) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + if (!i) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sigdata->n_orders = i; + + if (dumbfile_getnc((char *)tempolist, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (dumbfile_getnc((char *)breaklist, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (i = 0; i < sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + for (i = 0; i < sigdata->n_samples; i++) { + if (it_669_read_sample_header(&sigdata->sample[i], f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + /* May as well try to save a tiny bit of memory. */ + if (sigdata->n_orders < 128) { + unsigned char *order = realloc(sigdata->order, sigdata->n_orders); + if (order) sigdata->order = order; + } + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + n_channels = 0; + + /* Read in the patterns */ + { + unsigned char *buffer = malloc(64 * 3 * 8); /* 64 rows * 3 bytes * 8 channels */ + if (!buffer) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_669_read_pattern(&sigdata->pattern[i], f, tempolist[i], breaklist[i], buffer, &n_channels) != 0) { + free(buffer); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + sigdata->n_pchannels = n_channels; + + /* And finally, the sample data */ + for (i = 0; i < sigdata->n_samples; i++) { + if (it_669_read_sample_data(&sigdata->sample[i], f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_OLD_EFFECTS | IT_LINEAR_SLIDES | IT_STEREO | IT_WAS_A_669; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->speed = 4; + sigdata->tempo = 78; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i += 2) { + sigdata->channel_pan[i+0] = 48; + sigdata->channel_pan[i+1] = 16; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *dumb_read_669_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ext; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_669_load_sigdata(f, &ext); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = ext ? "669 Extended" : "669"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/read6692.c b/Frameworks/Dumb/dumb/src/it/read6692.c new file mode 100644 index 000000000..b5f4f2044 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/read6692.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * read6692.c - Code to read a 669 Composer module / / \ \ + * from an open file, and do an initial | < / \_ + * run-through. | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_669(DUMBFILE *f) +{ + DUH *duh = dumb_read_669_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readam.c b/Frameworks/Dumb/dumb/src/it/readam.c new file mode 100644 index 000000000..ac1cc3996 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readam.c @@ -0,0 +1,787 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readam.c - Code to read a RIFF AM module / / \ \ + * from a parsed RIFF structure. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/it.h" +#include "internal/riff.h" + +static int it_riff_am_process_sample( IT_SAMPLE * sample, DUMBFILE * f, int len, int ver ) +{ + int header_length; + int default_pan; + int default_volume; + int flags; + int length; + int length_bytes; + int loop_start; + int loop_end; + int sample_rate; + + long start = dumbfile_pos( f ); + + if ( ver == 0 ) + { + if ( len < 0x38 ) + return -1; + + header_length = 0x38; + + dumbfile_getnc( (char *) sample->name, 28, f ); + sample->name[ 28 ] = 0; + + default_pan = dumbfile_getc( f ); + default_volume = dumbfile_getc( f ); + flags = dumbfile_igetw( f ); + length = dumbfile_igetl( f ); + loop_start = dumbfile_igetl( f ); + loop_end = dumbfile_igetl( f ); + sample_rate = dumbfile_igetl( f ); + } + else + { + if (len < 4) return -1; + + header_length = dumbfile_igetl( f ); + if ( header_length < 0x40 ) + return -1; + if ( header_length + 4 > len ) + return -1; + + start += 4; + len -= 4; + + dumbfile_getnc( (char *) sample->name, 32, f ); + + default_pan = dumbfile_igetw( f ); + default_volume = dumbfile_igetw( f ); + flags = dumbfile_igetw( f ); + dumbfile_skip( f, 2 ); + length = dumbfile_igetl( f ); + loop_start = dumbfile_igetl( f ); + loop_end = dumbfile_igetl( f ); + sample_rate = dumbfile_igetl( f ); + + if ( default_pan > 0x7FFF || default_volume > 0x7FFF ) + return -1; + + default_pan = default_pan * 64 / 32767; + default_volume = default_volume * 64 / 32767; + } + + if ( ! length ) { + sample->flags &= ~IT_SAMPLE_EXISTS; + return 0; + } + + if ( flags & ~( 0x8000 | 0x80 | 0x20 | 0x10 | 0x08 | 0x04 ) ) + return -1; + + length_bytes = length << ( ( flags & 0x04 ) >> 2 ); + + if ( length_bytes + header_length > len ) + return -1; + + sample->flags = 0; + + if ( flags & 0x80 ) sample->flags |= IT_SAMPLE_EXISTS; + if ( flags & 0x04 ) sample->flags |= IT_SAMPLE_16BIT; + + sample->length = length; + sample->loop_start = loop_start; + sample->loop_end = loop_end; + sample->C5_speed = sample_rate; + sample->default_volume = default_volume; + sample->default_pan = default_pan | ( ( flags & 0x20 ) << 2 ); + sample->filename[0] = 0; + sample->global_volume = 64; + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + if ( flags & 0x08 ) + { + if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) && + ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) + { + sample->length = sample->loop_end; + sample->flags |= IT_SAMPLE_LOOP; + if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP; + } + } + + length_bytes = sample->length << ( ( flags & 0x04 ) >> 2 ); + + sample->data = malloc( length_bytes ); + if ( ! sample->data ) + return -1; + + if ( dumbfile_seek( f, start + header_length, DFS_SEEK_SET ) ) + return -1; + + dumbfile_getnc( sample->data, length_bytes, f ); + + return 0; +} + +static int it_riff_am_process_pattern( IT_PATTERN * pattern, DUMBFILE * f, int len, int ver ) +{ + int nrows, row; + long start, end; + unsigned flags; + int p, q, r; + IT_ENTRY * entry; + + nrows = dumbfile_getc( f ) + 1; + + pattern->n_rows = nrows; + + len -= 1; + + pattern->n_entries = 0; + + row = 0; + + start = dumbfile_pos( f ); + end = start + len; + + while ( (row < nrows) && !dumbfile_error( f ) && (dumbfile_pos( f ) < end) ) { + p = dumbfile_getc( f ); + if ( ! p ) { + ++ row; + continue; + } + + flags = p & 0xE0; + + if (flags) { + ++ pattern->n_entries; + if (flags & 0x80) dumbfile_skip( f, 2 ); + if (flags & 0x40) dumbfile_skip( f, 2 ); + if (flags & 0x20) dumbfile_skip( f, 1 ); + } + } + + if ( ! pattern->n_entries ) return 0; + + pattern->n_entries += nrows; + + pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) ); + if ( ! pattern->entry ) return -1; + + entry = pattern->entry; + + row = 0; + + dumbfile_seek( f, start, DFS_SEEK_SET ); + + while ( ( row < nrows ) && !dumbfile_error( f ) && ( dumbfile_pos( f ) < end ) ) + { + p = dumbfile_getc( f ); + + if ( ! p ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + continue; + } + + flags = p; + entry->channel = flags & 0x1F; + entry->mask = 0; + + if (flags & 0xE0) + { + if ( flags & 0x80 ) + { + q = dumbfile_getc( f ); + r = dumbfile_getc( f ); + _dumb_it_xm_convert_effect( r, q, entry, 0 ); + } + + if ( flags & 0x40 ) + { + q = dumbfile_getc( f ); + r = dumbfile_getc( f ); + if ( q ) + { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = q; + } + if ( r ) + { + entry->mask |= IT_ENTRY_NOTE; + entry->note = r - 1; + } + } + + if ( flags & 0x20 ) + { + q = dumbfile_getc( f ); + entry->mask |= IT_ENTRY_VOLPAN; + if ( ver == 0 ) entry->volpan = q; + else entry->volpan = q * 64 / 127; + } + + if (entry->mask) entry++; + } + } + + while ( row < nrows ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + } + + pattern->n_entries = entry - pattern->entry; + if ( ! pattern->n_entries ) return -1; + + return 0; +} + +static DUMB_IT_SIGDATA *it_riff_amff_load_sigdata( DUMBFILE * f, struct riff * stream ) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, p, found; + + if ( ! stream ) goto error; + + if ( stream->type != DUMB_ID( 'A', 'M', 'F', 'F' ) ) goto error; + + sigdata = malloc( sizeof( *sigdata ) ); + if ( ! sigdata ) goto error; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch( c->type ) + { + case DUMB_ID( 'M', 'A', 'I', 'N' ): + /* initialization data */ + if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd; + found |= 1; + break; + + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd; + found |= 2; + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_sd; + o = dumbfile_getc( f ); + if ( o >= sigdata->n_patterns ) sigdata->n_patterns = o + 1; + o = dumbfile_igetl( f ); + if ( (unsigned)o + 5 > c->size ) goto error_sd; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + if ( c->size < 0xE1 ) goto error_sd; + if ( dumbfile_seek( f, c->offset + 1, DFS_SEEK_SET ) ) goto error_sd; + o = dumbfile_getc( f ); + if ( o >= sigdata->n_samples ) sigdata->n_samples = o + 1; + if ( c->size >= 0x121 ) + { + if ( dumbfile_seek( f, c->offset + 0xE1, DFS_SEEK_SET ) ) goto error_sd; + if ( dumbfile_mgetl( f ) == DUMB_ID('S','A','M','P') ) + { + unsigned size = dumbfile_igetl( f ); + if ( size + 0xE1 + 8 > c->size ) goto error_sd; + } + } + } + break; + } + } + + if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd; + + if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + sigdata->restart_position = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'M', 'A', 'I', 'N' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + dumbfile_getnc( (char *) sigdata->name, 64, f ); + sigdata->name[ 64 ] = 0; + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; + o = dumbfile_getc( f ); + if ( ! ( o & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES; + if ( ( o & ~3 ) || ! ( o & 2 ) ) goto error_usd; // unknown flags + sigdata->n_pchannels = dumbfile_getc( f ); + sigdata->speed = dumbfile_getc( f ); + sigdata->tempo = dumbfile_getc( f ); + + dumbfile_skip( f, 4 ); + + sigdata->global_volume = dumbfile_getc( f ); + + if ( c->size < 0x48 + (unsigned)sigdata->n_pchannels ) goto error_usd; + + for ( o = 0; o < sigdata->n_pchannels; ++o ) + { + p = dumbfile_getc( f ); + sigdata->channel_pan[ o ] = p; + if ( p >= 128 ) + { + sigdata->channel_volume[ o ] = 0; + } + } + break; + } + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( ! sigdata->pattern ) goto error_usd; + for ( n = 0; n < sigdata->n_patterns; ++n ) + sigdata->pattern[ n ].entry = NULL; + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( ! sigdata->sample ) goto error_usd; + for ( n = 0; n < sigdata->n_samples; ++n ) + { + IT_SAMPLE * sample = sigdata->sample + n; + sample->data = NULL; + sample->flags = 0; + sample->name[ 0 ] = 0; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + sigdata->n_orders = dumbfile_getc( f ) + 1; + if ( (unsigned)sigdata->n_orders + 1 > c->size ) goto error_usd; + sigdata->order = malloc( sigdata->n_orders ); + if ( ! sigdata->order ) goto error_usd; + dumbfile_getnc( (char *) sigdata->order, sigdata->n_orders, f ); + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + o = dumbfile_getc( f ); + p = dumbfile_igetl( f ); + if ( it_riff_am_process_pattern( sigdata->pattern + o, f, p, 0 ) ) goto error_usd; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + IT_SAMPLE * sample; + if ( dumbfile_seek( f, c->offset + 1, DFS_SEEK_SET ) ) goto error_usd; + sample = sigdata->sample + dumbfile_getc( f ); + if ( c->size >= 0x121 ) + { + if ( dumbfile_seek( f, c->offset + 0xE1, DFS_SEEK_SET ) ) goto error_usd; + if ( dumbfile_mgetl( f ) == DUMB_ID('S','A','M','P') ) + { + unsigned size = dumbfile_igetl( f ); + if ( it_riff_am_process_sample( sample, f, size, 0 ) ) goto error_usd; + break; + } + } + dumbfile_seek( f, c->offset + 2, DFS_SEEK_SET ); + dumbfile_getnc( (char *) sample->name, 28, f ); + sample->name[ 28 ] = 0; + } + break; + } + } + + _dumb_it_fix_invalid_orders( sigdata ); + + return sigdata; + +error_usd: + _dumb_it_unload_sigdata( sigdata ); + goto error; +error_sd: + free( sigdata ); +error: + return NULL; +} + +static DUMB_IT_SIGDATA *it_riff_am_load_sigdata( DUMBFILE * f, struct riff * stream ) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, p, found; + + if ( ! f || ! stream ) goto error; + + if ( stream->type != DUMB_ID( 'A', 'M', ' ', ' ' ) ) goto error; + + sigdata = malloc(sizeof(*sigdata)); + if ( ! sigdata ) goto error; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch( c->type ) + { + case DUMB_ID( 'I' ,'N' ,'I' ,'T' ): + /* initialization data */ + if ( ( found & 1 ) || ( c->size < 0x48 ) ) goto error_sd; + found |= 1; + break; + + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( ( found & 2 ) || ( c->size < 1 ) ) goto error_sd; + found |= 2; + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_sd; + o = dumbfile_getc( f ); + if ( o >= sigdata->n_patterns ) sigdata->n_patterns = o + 1; + o = dumbfile_igetl( f ); + if ( (unsigned)o + 5 > c->size ) goto error_sd; + break; + + case DUMB_ID( 'R', 'I', 'F', 'F' ): + { + struct riff * str = c->nested; + switch ( str->type ) + { + case DUMB_ID( 'A', 'I', ' ', ' ' ): + for ( o = 0; (unsigned)o < str->chunk_count; ++o ) + { + struct riff_chunk * chk = str->chunks + o; + switch( chk->type ) + { + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + struct riff * temp; + unsigned size; + unsigned sample_found; + if ( dumbfile_seek( f, chk->offset, DFS_SEEK_SET ) ) goto error_sd; + size = dumbfile_igetl( f ); + if ( size < 0x142 ) goto error_sd; + sample_found = 0; + dumbfile_skip( f, 1 ); + p = dumbfile_getc( f ); + if ( p >= sigdata->n_samples ) sigdata->n_samples = p + 1; + temp = riff_parse( f, chk->offset + 4 + size, chk->size - size - 4, 1 ); + if ( temp ) + { + if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) ) + { + for ( p = 0; (unsigned)p < temp->chunk_count; ++p ) + { + if ( temp->chunks[ p ].type == DUMB_ID( 'S', 'A', 'M', 'P' ) ) + { + if ( sample_found ) + { + riff_free( temp ); + goto error_sd; + } + sample_found = 1; + } + } + } + riff_free( temp ); + } + } + } + } + } + } + break; + } + } + + if ( found != 3 || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd; + + if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + sigdata->restart_position = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'I', 'N', 'I', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + dumbfile_getnc( (char *) sigdata->name, 64, f ); + sigdata->name[ 64 ] = 0; + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; + o = dumbfile_getc( f ); + if ( ! ( o & 1 ) ) sigdata->flags |= IT_LINEAR_SLIDES; + if ( ( o & ~3 ) || ! ( o & 2 ) ) goto error_usd; // unknown flags + sigdata->n_pchannels = dumbfile_getc( f ); + sigdata->speed = dumbfile_getc( f ); + sigdata->tempo = dumbfile_getc( f ); + + dumbfile_skip( f, 4 ); + + sigdata->global_volume = dumbfile_getc( f ); + + if ( c->size < 0x48 + (unsigned)sigdata->n_pchannels ) goto error_usd; + + for ( o = 0; o < sigdata->n_pchannels; ++o ) + { + p = dumbfile_getc( f ); + if ( p <= 128 ) + { + sigdata->channel_pan[ o ] = p / 2; + } + else + { + sigdata->channel_volume[ o ] = 0; + } + } + break; + } + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( ! sigdata->pattern ) goto error_usd; + for ( n = 0; n < sigdata->n_patterns; ++n ) + sigdata->pattern[ n ].entry = NULL; + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( ! sigdata->sample ) goto error_usd; + for ( n = 0; n < sigdata->n_samples; ++n ) + { + IT_SAMPLE * sample = sigdata->sample + n; + sample->data = NULL; + sample->flags = 0; + sample->name[ 0 ] = 0; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'O', 'R', 'D', 'R' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + sigdata->n_orders = dumbfile_getc( f ) + 1; + if ( (unsigned)sigdata->n_orders + 1 > c->size ) goto error_usd; + sigdata->order = malloc( sigdata->n_orders ); + if ( ! sigdata->order ) goto error_usd; + dumbfile_getnc( (char *) sigdata->order, sigdata->n_orders, f ); + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + o = dumbfile_getc( f ); + p = dumbfile_igetl( f ); + if ( it_riff_am_process_pattern( sigdata->pattern + o, f, p, 1 ) ) goto error_usd; + break; + + case DUMB_ID( 'R', 'I', 'F', 'F' ): + { + struct riff * str = c->nested; + switch ( str->type ) + { + case DUMB_ID('A', 'I', ' ', ' '): + for ( o = 0; (unsigned)o < str->chunk_count; ++o ) + { + struct riff_chunk * chk = str->chunks + o; + switch( chk->type ) + { + case DUMB_ID( 'I', 'N', 'S', 'T' ): + { + struct riff * temp; + unsigned size; + unsigned sample_found; + IT_SAMPLE * sample; + if ( dumbfile_seek( f, chk->offset, DFS_SEEK_SET ) ) goto error_usd; + size = dumbfile_igetl( f ); + dumbfile_skip( f, 1 ); + p = dumbfile_getc( f ); + temp = riff_parse( f, chk->offset + 4 + size, chk->size - size - 4, 1 ); + sample_found = 0; + sample = sigdata->sample + p; + if ( temp ) + { + if ( temp->type == DUMB_ID( 'A', 'S', ' ', ' ' ) ) + { + for ( p = 0; (unsigned)p < temp->chunk_count; ++p ) + { + struct riff_chunk * c = temp->chunks + p; + if ( c->type == DUMB_ID( 'S', 'A', 'M', 'P' ) ) + { + if ( sample_found ) + { + riff_free( temp ); + goto error_usd; + } + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) + { + riff_free( temp ); + goto error_usd; + } + if ( it_riff_am_process_sample( sample, f, c->size, 1 ) ) + { + riff_free( temp ); + goto error_usd; + } + sample_found = 1; + } + } + } + riff_free( temp ); + } + if ( ! sample_found ) + { + dumbfile_seek( f, chk->offset + 6, DFS_SEEK_SET ); + dumbfile_getnc( (char *) sample->name, 32, f ); + sample->name[ 32 ] = 0; + } + } + } + } + } + } + break; + } + } + + _dumb_it_fix_invalid_orders( sigdata ); + + return sigdata; + +error_usd: + _dumb_it_unload_sigdata( sigdata ); + goto error; +error_sd: + free( sigdata ); +error: + return NULL; +} + +DUH *dumb_read_riff_amff( DUMBFILE * f, struct riff * stream ) +{ + sigdata_t *sigdata; + long length; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_riff_amff_load_sigdata( f, stream ); + + if (!sigdata) + return NULL; + + length = 0;/*_dumb_it_build_checkpoints(sigdata, 0);*/ + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "RIFF AMFF"; + return make_duh( length, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata ); + } +} + +DUH *dumb_read_riff_am( DUMBFILE * f, struct riff * stream ) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_riff_am_load_sigdata( f, stream ); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "RIFF AM"; + return make_duh( -1, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata ); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readamf.c b/Frameworks/Dumb/dumb/src/it/readamf.c new file mode 100644 index 000000000..400491776 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readamf.c @@ -0,0 +1,523 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readamf.c - Code to read a DSMI AMF module from / / \ \ + * an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static void it_amf_process_track( IT_ENTRY *entry_table, unsigned char *track, int rows, int channels ) +{ + int last_instrument = 0; + int tracksize = track[ 0 ] + ( track[ 1 ] << 8 ) + ( track[ 2 ] << 16 ); + track += 3; + while ( tracksize-- ) { + unsigned int row = track[ 0 ]; + unsigned int command = track[ 1 ]; + unsigned int argument = track[ 2 ]; + IT_ENTRY * entry = entry_table + row * channels; + if ( row >= ( unsigned int ) rows ) break; + if ( command < 0x7F ) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT | IT_ENTRY_VOLPAN; + entry->note = command; + if ( ! entry->instrument ) entry->instrument = last_instrument; + entry->volpan = argument; + } + else if ( command == 0x7F ) { + signed char row_delta = ( signed char ) argument; + int row_source = ( int ) row + ( int ) row_delta; + if ( row_source >= 0 && row_source < ( int ) rows ) { + *entry = entry_table[ row_source * channels ]; + } + } + else if ( command == 0x80 ) { + entry->mask |= IT_ENTRY_INSTRUMENT; + last_instrument = argument + 1; + entry->instrument = last_instrument; + } + else if ( command == 0x83 ) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = argument; + } + else { + unsigned int effect = command & 0x7F; + unsigned int effectvalue = argument; + switch (effect) { + case 0x01: effect = IT_SET_SPEED; break; + + case 0x02: effect = IT_VOLUME_SLIDE; + case 0x0A: if ( effect == 0x0A ) effect = IT_VOLSLIDE_TONEPORTA; + case 0x0B: if ( effect == 0x0B ) effect = IT_VOLSLIDE_VIBRATO; + if ( effectvalue & 0x80 ) effectvalue = ( -( signed char ) effectvalue ) & 0x0F; + else effectvalue = ( effectvalue & 0x0F ) << 4; + break; + + case 0x04: if ( effectvalue & 0x80 ) { effect = IT_PORTAMENTO_UP; effectvalue = ( -( signed char ) effectvalue ) & 0x7F; } + else { effect = IT_PORTAMENTO_DOWN; } + break; + + case 0x06: effect = IT_TONE_PORTAMENTO; break; + + case 0x07: effect = IT_TREMOR; break; + + case 0x08: effect = IT_ARPEGGIO; break; + + case 0x09: effect = IT_VIBRATO; break; + + case 0x0C: effect = IT_BREAK_TO_ROW; break; + + case 0x0D: effect = IT_JUMP_TO_ORDER; break; + + case 0x0F: effect = IT_RETRIGGER_NOTE; break; + + case 0x10: effect = IT_SET_SAMPLE_OFFSET; break; + + case 0x11: if ( effectvalue ) { effect = IT_VOLUME_SLIDE; + if ( effectvalue & 0x80 ) effectvalue = 0xF0 | ( ( -( signed char ) effectvalue ) & 0x0F ); + else effectvalue = 0x0F | ( ( effectvalue & 0x0F ) << 4 ); + } else effect = 0; + break; + + case 0x12: + case 0x16: if ( effectvalue ) { int mask = ( effect == 0x16 ) ? 0xE0 : 0xF0; + effect = ( effectvalue & 0x80 ) ? IT_PORTAMENTO_UP : IT_PORTAMENTO_DOWN; + if ( effectvalue & 0x80 ) effectvalue = mask | ( ( -( signed char ) effectvalue ) & 0x0F ); + else effectvalue = mask | ( effectvalue & 0x0F ); + } else effect = 0; + break; + + case 0x13: effect = IT_S; effectvalue = EFFECT_VALUE( IT_S_NOTE_DELAY, effectvalue & 0x0F ); break; + + case 0x14: effect = IT_S; effectvalue = EFFECT_VALUE( IT_S_DELAYED_NOTE_CUT, effectvalue & 0x0F ); break; + + case 0x15: effect = IT_SET_SONG_TEMPO; break; + + case 0x17: effectvalue = ( effectvalue + 64 ) & 0x7F; + if ( entry->mask & IT_ENTRY_EFFECT ) { if ( !( entry->mask & IT_ENTRY_VOLPAN ) ) { entry->volpan = ( effectvalue / 2 ) + 128; } effect = 0; } + else { effect = IT_SET_PANNING; } + break; + + default: effect = effectvalue = 0; + } + if ( effect ) { + entry->mask |= IT_ENTRY_EFFECT; + entry->effect = effect; + entry->effectvalue = effectvalue; + } + } + track += 3; + } +} + +static int it_amf_process_pattern( IT_PATTERN *pattern, IT_ENTRY *entry_table, int rows, int channels ) +{ + int i, j; + int n_entries = rows; + IT_ENTRY * entry; + + pattern->n_rows = rows; + + for ( i = 0, j = channels * rows; i < j; i++ ) { + if ( entry_table[ i ].mask ) { + n_entries++; + } + } + + pattern->n_entries = n_entries; + + pattern->entry = entry = malloc( n_entries * sizeof( IT_ENTRY ) ); + if ( !entry ) { + return -1; + } + + for ( i = 0; i < rows; i++ ) { + for ( j = 0; j < channels; j++ ) { + if ( entry_table[ i * channels + j ].mask ) { + *entry = entry_table[ i * channels + j ]; + entry->channel = j; + entry++; + } + } + IT_SET_END_ROW( entry ); + entry++; + } + + return 0; +} + +static int it_amf_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f, int * offset, int ver ) +{ + int exists; + + exists = dumbfile_getc( f ); + + dumbfile_getnc( (char *) sample->name, 32, f ); + sample->name[32] = 0; + + dumbfile_getnc( (char *) sample->filename, 13, f ); + sample->filename[13] = 0; + + *offset = dumbfile_igetl( f ); + sample->length = dumbfile_igetl( f ); + sample->C5_speed = dumbfile_igetw( f ); + sample->default_volume = dumbfile_getc( f ); + sample->global_volume = 64; + if ( sample->default_volume > 64 ) sample->default_volume = 64; + + if ( ver >= 11 ) { + sample->loop_start = dumbfile_igetl( f ); + sample->loop_end = dumbfile_igetl( f ); + } else { + sample->loop_start = dumbfile_igetw( f ); + sample->loop_end = sample->length; + } + + if ( sample->length <= 0 ) { + sample->flags = 0; + return 0; + } + + sample->flags = exists == 1 ? IT_SAMPLE_EXISTS : 0; + + sample->default_pan = 0; + sample->finetune = 0; + + if ( sample->loop_start != 0 ) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_amf_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f ) +{ + int i, read_length = 0; + + sample->data = malloc( sample->length ); + + if ( !sample->data ) + return -1; + + if ( sample->length ) + read_length = dumbfile_getnc( sample->data, sample->length, f ); + + for ( i = 0; i < read_length; i++ ) { + ( ( char * ) sample->data )[ i ] ^= 0x80; + } + + for ( i = read_length; i < sample->length; i++ ) { + ( ( char * ) sample->data )[ i ] = 0; + } + + return 0; /* Sometimes the last sample is truncated :( */ +} + +static DUMB_IT_SIGDATA *it_amf_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + int i, j, ver, ntracks, realntracks, nchannels; + + int maxsampleseekpos = 0; + int sampleseekpos[256]; + + unsigned short *orderstotracks; + unsigned short *trackmap; + unsigned int tracksize[256]; + + unsigned char **track; + + static const char sig[] = "AMF"; + + char signature [3]; + + if ( dumbfile_getnc( signature, 3, f ) != 3 || + memcmp( signature, sig, 3 ) ) { + return NULL; + } + + *version = ver = dumbfile_getc( f ); + if ( ver < 10 || ver > 14) { + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + dumbfile_getnc( (char *) sigdata->name, 32, f ); + sigdata->name[ 32 ] = 0; + sigdata->n_samples = dumbfile_getc( f ); + sigdata->n_orders = dumbfile_getc( f ); + ntracks = dumbfile_igetw( f ); + nchannels = dumbfile_getc( f ); + + if ( dumbfile_error( f ) || + sigdata->n_samples < 1 || sigdata->n_samples > 255 || + sigdata->n_orders < 1 || sigdata->n_orders > 255 || + ! ntracks || + nchannels < 1 || nchannels > 32 ) { + free( sigdata ); + return NULL; + } + + memset( sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS ); + + if ( ver >= 11 ) { + int nchannels = ( ver >= 13 ) ? 32 : 16; + for ( i = 0; i < nchannels; i++ ) { + signed char panpos = dumbfile_getc( f ); + int pan = ( panpos + 64 ) / 2; + if ( pan < 0 ) pan = 0; + else if ( pan > 64 ) pan = IT_SURROUND; + sigdata->channel_pan[ i ] = pan; + } + } + else { + for ( i = 0; i < 16; i++ ) { + sigdata->channel_pan[ i ] = ( dumbfile_getc( f ) & 1 ) ? 16 : 48; + } + } + + sigdata->tempo = 125; + sigdata->speed = 6; + if ( ver >= 13 ) { + i = dumbfile_getc( f ); + if ( i >= 32 ) sigdata->tempo = i; + i = dumbfile_getc( f ); + if ( i <= 32 ) sigdata->speed = i; + } + + sigdata->order = malloc( sigdata->n_orders ); + if ( !sigdata->order ) { + free( sigdata ); + return NULL; + } + + orderstotracks = malloc( sigdata->n_orders * nchannels * sizeof( unsigned short ) ); + if ( !orderstotracks ) { + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + for ( i = 0; i < sigdata->n_orders; i++ ) { + sigdata->order[ i ] = i; + tracksize[ i ] = 64; + if ( ver >= 14 ) { + tracksize[ i ] = dumbfile_igetw( f ); + } + for ( j = 0; j < nchannels; j++ ) { + orderstotracks[ i * nchannels + j ] = dumbfile_igetw( f ); + } + } + + if ( dumbfile_error( f ) ) { + free( orderstotracks ); + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( !sigdata->sample ) { + free( orderstotracks ); + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->restart_position = 0; + + sigdata->song_message = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for ( i = 0; i < sigdata->n_samples; ++i ) + sigdata->sample[i].data = NULL; + + for ( i = 0; i < sigdata->n_samples; ++i ) { + int offset; + if ( it_amf_read_sample_header( &sigdata->sample[i], f, &offset, ver ) ) { + goto error_ott; + } + sampleseekpos[ i ] = offset; + if ( offset > maxsampleseekpos ) maxsampleseekpos = offset; + } + + sigdata->n_patterns = sigdata->n_orders; + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( !sigdata->pattern ) { + goto error_ott; + } + for (i = 0; i < sigdata->n_patterns; ++i) + sigdata->pattern[i].entry = NULL; + + trackmap = malloc( ntracks * sizeof( unsigned short ) ); + if ( !trackmap ) { + goto error_ott; + } + + if ( dumbfile_getnc( ( char * ) trackmap, ntracks * sizeof( unsigned short ), f ) != (long)(ntracks * sizeof( unsigned short )) ) { + goto error_tm; + } + + realntracks = 0; + + for ( i = 0; i < ntracks; i++ ) { + if ( trackmap[ i ] > realntracks ) realntracks = trackmap[ i ]; + } + + track = calloc( realntracks, sizeof( unsigned char * ) ); + if ( !track ) { + goto error_tm; + } + + for ( i = 0; i < realntracks; i++ ) { + int tracksize = dumbfile_igetw( f ); + tracksize += dumbfile_getc( f ) << 16; + track[ i ] = malloc( tracksize * 3 + 3 ); + if ( !track[ i ] ) { + goto error_all; + } + track[ i ][ 0 ] = tracksize & 255; + track[ i ][ 1 ] = ( tracksize >> 8 ) & 255; + track[ i ][ 2 ] = ( tracksize >> 16 ) & 255; + if ( dumbfile_getnc( (char *) track[ i ] + 3, tracksize * 3, f ) != tracksize * 3 ) { + goto error_all; + } + } + + for ( i = 1; i <= maxsampleseekpos; i++ ) { + for ( j = 0; j < sigdata->n_samples; j++ ) { + if ( sampleseekpos[ j ] == i ) { + if ( it_amf_read_sample_data( &sigdata->sample[ j ], f ) ) { + goto error_all; + } + break; + } + } + } + + /* Process tracks into patterns */ + for ( i = 0; i < sigdata->n_patterns; i++ ) { + IT_ENTRY * entry_table = calloc( tracksize[ i ] * nchannels, sizeof( IT_ENTRY ) ); + if ( !entry_table ) { + goto error_all; + } + for ( j = 0; j < nchannels; j++ ) { + int ntrack = orderstotracks[ i * nchannels + j ]; + if ( ntrack && ntrack <= ntracks ) { + int realtrack = trackmap[ ntrack - 1 ]; + if ( realtrack ) { + realtrack--; + if ( realtrack < realntracks && track[ realtrack ] ) { + it_amf_process_track( entry_table + j, track[ realtrack ], tracksize[ i ], nchannels ); + } + } + } + } + if ( it_amf_process_pattern( &sigdata->pattern[ i ], entry_table, tracksize[ i ], nchannels ) ) { + free( entry_table ); + goto error_all; + } + free( entry_table ); + } + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + _dumb_it_fix_invalid_orders(sigdata); + + for ( i = 0; i < realntracks; i++ ) { + if ( track[ i ] ) { + free( track[ i ] ); + } + } + free( track ); + free( trackmap ); + free( orderstotracks ); + + return sigdata; + +error_all: + for ( i = 0; i < realntracks; i++ ) { + if ( track[ i ] ) { + free( track[ i ] ); + } + } + free( track ); +error_tm: + free( trackmap ); +error_ott: + free( orderstotracks ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; +} + + + +DUH *dumb_read_amf_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + int version; + + sigdata = it_amf_load_sigdata(f, &version); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + char ver_string[14]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + memcpy( ver_string, "DSMI AMF v", 10 ); + ver_string[10] = '0' + version / 10; + ver_string[11] = '.'; + ver_string[12] = '0' + version % 10; + ver_string[13] = 0; + tag[1][1] = ver_string; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readamf2.c b/Frameworks/Dumb/dumb/src/it/readamf2.c new file mode 100644 index 000000000..c2258fc89 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readamf2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readamf2.c - Function to read a DSMI AMF module / / \ \ + * from an open file and do an initial | < / \_ + * run-through. | \/ /\ / + * \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_amf(DUMBFILE *f) +{ + DUH *duh = dumb_read_amf_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readany.c b/Frameworks/Dumb/dumb/src/it/readany.c new file mode 100644 index 000000000..637ad16d3 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readany.c @@ -0,0 +1,132 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readany.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" + +#ifdef _MSC_VER + #define strnicmp _strnicmp +#else + #if defined(unix) || defined(__unix__) || defined(__unix) + #include + #endif + #define strnicmp strncasecmp +#endif + +enum { maximum_signature_size = 0x30 }; + +DUH *dumb_read_any_quick(DUMBFILE *f, int restrict_, int subsong) +{ + unsigned char signature[ maximum_signature_size ]; + unsigned long signature_size; + DUH * duh = NULL; + + signature_size = dumbfile_get_size(f); + + signature_size = dumbfile_getnc( (char *)signature, maximum_signature_size, f ); + dumbfile_seek( f, 0, DFS_SEEK_SET ); + + if (signature_size >= 4 && + signature[0] == 'I' && signature[1] == 'M' && + signature[2] == 'P' && signature[3] == 'M') + { + duh = dumb_read_it_quick( f ); + } + else if (signature_size >= 17 && !memcmp(signature, "Extended Module: ", 17)) + { + duh = dumb_read_xm_quick( f ); + } + else if (signature_size >= 0x30 && + signature[0x2C] == 'S' && signature[0x2D] == 'C' && + signature[0x2E] == 'R' && signature[0x2F] == 'M') + { + duh = dumb_read_s3m_quick( f ); + } + else if (signature_size >= 30 && + /*signature[28] == 0x1A &&*/ signature[29] == 2 && + ( ! strnicmp( ( const char * ) signature + 20, "!Scream!", 8 ) || + ! strnicmp( ( const char * ) signature + 20, "BMOD2STM", 8 ) || + ! strnicmp( ( const char * ) signature + 20, "WUZAMOD!", 8 ) ) ) + { + duh = dumb_read_stm_quick( f ); + } + else if (signature_size >= 2 && + ((signature[0] == 0x69 && signature[1] == 0x66) || + (signature[0] == 0x4A && signature[1] == 0x4E))) + { + duh = dumb_read_669_quick( f ); + } + else if (signature_size >= 0x30 && + signature[0x2C] == 'P' && signature[0x2D] == 'T' && + signature[0x2E] == 'M' && signature[0x2F] == 'F') + { + duh = dumb_read_ptm_quick( f ); + } + else if (signature_size >= 4 && + signature[0] == 'P' && signature[1] == 'S' && + signature[2] == 'M' && signature[3] == ' ') + { + duh = dumb_read_psm_quick( f, subsong ); + } + else if (signature_size >= 4 && + signature[0] == 'P' && signature[1] == 'S' && + signature[2] == 'M' && signature[3] == 254) + { + duh = dumb_read_old_psm_quick( f ); + } + else if (signature_size >= 3 && + signature[0] == 'M' && signature[1] == 'T' && + signature[2] == 'M') + { + duh = dumb_read_mtm_quick( f ); + } + else if ( signature_size >= 4 && + signature[0] == 'R' && signature[1] == 'I' && + signature[2] == 'F' && signature[3] == 'F') + { + duh = dumb_read_riff_quick( f ); + } + else if ( signature_size >= 24 && + !memcmp( signature, "ASYLUM Music Format", 19 ) && + !memcmp( signature + 19, " V1.0", 5 ) ) + { + duh = dumb_read_asy_quick( f ); + } + else if ( signature_size >= 3 && + signature[0] == 'A' && signature[1] == 'M' && + signature[2] == 'F') + { + duh = dumb_read_amf_quick( f ); + } + else if ( signature_size >= 8 && + !memcmp( signature, "OKTASONG", 8 ) ) + { + duh = dumb_read_okt_quick( f ); + } + + if ( !duh ) + { + dumbfile_seek( f, 0, DFS_SEEK_SET ); + duh = dumb_read_mod_quick( f, restrict_ ); + } + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readany2.c b/Frameworks/Dumb/dumb/src/it/readany2.c new file mode 100644 index 000000000..ac881a717 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readany2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readany2.c - Code to detect and read any of the / / \ \ + * module formats supported by DUMB | < / \_ + * from an open file and do an initial | \/ /\ / + * run-through. \_ / > / + * | \ / / + * by Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_any(DUMBFILE *f, int restrict_, int subsong) +{ + DUH *duh = dumb_read_any_quick(f, restrict_, subsong); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readasy.c b/Frameworks/Dumb/dumb/src/it/readasy.c new file mode 100644 index 000000000..0732facf4 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readasy.c @@ -0,0 +1,331 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readasy.c - Code to read an ASYLUM Music Format / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_asy_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer ) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if ( dumbfile_getnc( (char *) buffer, 64 * 8 * 4, f ) != 64 * 8 * 4 ) + return -1; + + /* compute number of entries */ + pattern->n_entries = 64; /* Account for the row end markers */ + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 8; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) + ++pattern->n_entries; + pos += 4; + } + } + + pattern->entry = malloc( pattern->n_entries * sizeof( *pattern->entry ) ); + if ( !pattern->entry ) + return -1; + + entry = pattern->entry; + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 8; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) { + entry->channel = channel; + entry->mask = 0; + + if ( buffer[ pos + 0 ] && buffer[ pos + 0 ] < 96 ) { + entry->note = buffer[ pos + 0 ]; + entry->mask |= IT_ENTRY_NOTE; + } + + if ( buffer[ pos + 1 ] && buffer[ pos + 1 ] <= 64 ) { + entry->instrument = buffer[ pos + 1 ]; + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + _dumb_it_xm_convert_effect( buffer[ pos + 2 ], buffer[ pos + 3 ], entry, 1 ); + + if ( entry->mask ) ++entry; + } + pos += 4; + } + IT_SET_END_ROW( entry ); + ++entry; + } + + pattern->n_entries = entry - pattern->entry; + + return 0; +} + + + +static int it_asy_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f ) +{ + int finetune, key_offset; + +/** + 21 22 Chars Sample 1 name. If the name is not a full + 22 chars in length, it will be null + terminated. + +If +the sample name begins with a '#' character (ASCII $23 (35)) then this is +assumed not to be an instrument name, and is probably a message. +*/ + dumbfile_getnc( (char *) sample->name, 22, f ); + sample->name[22] = 0; + + sample->filename[0] = 0; + +/** Each finetune step changes the note 1/8th of a semitone. */ + finetune = ( signed char ) ( dumbfile_getc( f ) << 4 ) >> 4; /* signed nibble */ + sample->default_volume = dumbfile_getc( f ); // Should we be setting global_volume to this instead? + sample->global_volume = 64; + if ( sample->default_volume > 64 ) sample->default_volume = 64; + key_offset = ( signed char ) dumbfile_getc( f ); /* base key offset */ + sample->length = dumbfile_igetl( f ); + sample->loop_start = dumbfile_igetl( f ); + sample->loop_end = sample->loop_start + dumbfile_igetl( f ); + + if ( sample->length <= 0 ) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 * pow( DUMB_SEMITONE_BASE, key_offset ) );//( long )( 16726.0 * pow( DUMB_PITCH_BASE, finetune * 32 ) ); + sample->finetune = finetune * 32; + // the above line might be wrong + + if ( ( sample->loop_end - sample->loop_start > 2 ) && ( sample->loop_end <= sample->length ) ) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_asy_read_sample_data( IT_SAMPLE *sample, DUMBFILE *f ) +{ + long truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ( ( sample->flags & IT_SAMPLE_LOOP ) && sample->loop_end < sample->length ) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + sample->data = malloc( sample->length ); + + if ( !sample->data ) + return -1; + + if ( sample->length ) + dumbfile_getnc( sample->data, sample->length, f ); + + dumbfile_skip( f, truncated_size ); + + return dumbfile_error( f ); +} + + + +static DUMB_IT_SIGDATA *it_asy_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + int i; + + static const char sig_part[] = "ASYLUM Music Format"; + static const char sig_rest[] = " V1.0"; /* whee, string space optimization with format type below */ + + char signature [32]; + + if ( dumbfile_getnc( signature, 32, f ) != 32 || + memcmp( signature, sig_part, 19 ) || + memcmp( signature + 19, sig_rest, 5 ) ) { + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + sigdata->speed = dumbfile_getc( f ); /* XXX seems to fit the files I have */ + sigdata->tempo = dumbfile_getc( f ); /* ditto */ + sigdata->n_samples = dumbfile_getc( f ); /* ditto */ + sigdata->n_patterns = dumbfile_getc( f ); + sigdata->n_orders = dumbfile_getc( f ); + sigdata->restart_position = dumbfile_getc( f ); + + if ( dumbfile_error( f ) || !sigdata->n_samples || sigdata->n_samples > 64 || !sigdata->n_patterns || + !sigdata->n_orders ) { + free( sigdata ); + return NULL; + } + + if ( sigdata->restart_position > sigdata->n_orders ) /* XXX */ + sigdata->restart_position = 0; + + sigdata->order = malloc( sigdata->n_orders ); + if ( !sigdata->order ) { + free( sigdata ); + return NULL; + } + + if ( dumbfile_getnc( (char *) sigdata->order, sigdata->n_orders, f ) != sigdata->n_orders || + dumbfile_skip( f, 256 - sigdata->n_orders ) ) { + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( !sigdata->sample ) { + free( sigdata->order ); + free( sigdata ); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for ( i = 0; i < sigdata->n_samples; ++i ) + sigdata->sample[i].data = NULL; + + for ( i = 0; i < sigdata->n_samples; ++i ) { + if ( it_asy_read_sample_header( &sigdata->sample[i], f ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + + if ( dumbfile_skip( f, 37 * ( 64 - sigdata->n_samples ) ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( !sigdata->pattern ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; ++i) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + { + unsigned char *buffer = malloc( 64 * 8 * 4 ); /* 64 rows * 8 channels * 4 bytes */ + if ( !buffer ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + for ( i = 0; i < sigdata->n_patterns; ++i ) { + if ( it_asy_read_pattern( &sigdata->pattern[i], f, buffer ) != 0 ) { + free( buffer ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + free( buffer ); + } + + /* And finally, the sample data */ + for ( i = 0; i < sigdata->n_samples; ++i ) { + if ( it_asy_read_sample_data( &sigdata->sample[i], f ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_pchannels = 8; + + sigdata->name[0] = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) { + sigdata->channel_pan[i+0] = 16; + sigdata->channel_pan[i+1] = 48; + sigdata->channel_pan[i+2] = 48; + sigdata->channel_pan[i+3] = 16; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *dumb_read_asy_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_asy_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "ASYLUM Music Format"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readdsmf.c b/Frameworks/Dumb/dumb/src/it/readdsmf.c new file mode 100644 index 000000000..0196f3df5 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readdsmf.c @@ -0,0 +1,382 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readam.c - Code to read a RIFF DSMF module / / \ \ + * from a parsed RIFF structure. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/it.h" +#include "internal/riff.h" + +static int it_riff_dsmf_process_sample( IT_SAMPLE * sample, DUMBFILE * f, int len ) +{ + int flags; + + dumbfile_getnc( (char *) sample->filename, 13, f ); + sample->filename[ 14 ] = 0; + + flags = dumbfile_igetw( f ); + sample->default_volume = dumbfile_getc( f ); + sample->length = dumbfile_igetl( f ); + sample->loop_start = dumbfile_igetl( f ); + sample->loop_end = dumbfile_igetl( f ); + dumbfile_skip( f, 32 - 28 ); + sample->C5_speed = dumbfile_igetw( f ) * 2; + dumbfile_skip( f, 36 - 34 ); + dumbfile_getnc( (char *) sample->name, 28, f ); + sample->name[ 28 ] = 0; + + /*if ( data[ 0x38 ] || data[ 0x39 ] || data[ 0x3A ] || data[ 0x3B ] ) + return -1;*/ + + if ( ! sample->length ) { + sample->flags &= ~IT_SAMPLE_EXISTS; + return 0; + } + + /*if ( flags & ~( 2 | 1 ) ) + return -1;*/ + + if ( sample->length + 64 > len ) + return -1; + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->global_volume = 64; + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + if ( flags & 1 ) + { + if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) && + ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) + { + sample->length = sample->loop_end; + sample->flags |= IT_SAMPLE_LOOP; + if ( flags & 0x10 ) sample->flags |= IT_SAMPLE_PINGPONG_LOOP; + } + } + + sample->data = malloc( sample->length ); + if ( ! sample->data ) + return -1; + + dumbfile_getnc( sample->data, sample->length, f ); + + if ( ! ( flags & 2 ) ) + { + for ( flags = 0; flags < sample->length; ++flags ) + ( ( signed char * ) sample->data ) [ flags ] ^= 0x80; + } + + return 0; +} + +static int it_riff_dsmf_process_pattern( IT_PATTERN * pattern, DUMBFILE * f, int len ) +{ + int length, row; + unsigned flags; + long start, end; + int p, q, r; + IT_ENTRY * entry; + + length = dumbfile_igetw( f ); + if ( length > len ) return -1; + + len = length - 2; + + pattern->n_rows = 64; + pattern->n_entries = 64; + + row = 0; + + start = dumbfile_pos( f ); + end = start + len; + + while ( (row < 64) && !dumbfile_error( f ) && (dumbfile_pos( f ) < end) ) { + p = dumbfile_getc( f ); + if ( ! p ) { + ++ row; + continue; + } + + flags = p & 0xF0; + + if (flags) { + ++ pattern->n_entries; + if (flags & 0x80) dumbfile_skip( f, 1 ); + if (flags & 0x40) dumbfile_skip( f, 1 ); + if (flags & 0x20) dumbfile_skip( f, 1 ); + if (flags & 0x10) dumbfile_skip( f, 2 ); + } + } + + if ( pattern->n_entries == 64 ) return 0; + + pattern->entry = malloc( pattern->n_entries * sizeof( * pattern->entry ) ); + if ( ! pattern->entry ) return -1; + + entry = pattern->entry; + + row = 0; + + if ( dumbfile_seek( f, start, DFS_SEEK_SET ) ) return -1; + + while ( ( row < 64 ) && !dumbfile_error( f ) && ( dumbfile_pos( f ) < end ) ) + { + p = dumbfile_getc( f ); + if ( ! p ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + continue; + } + + flags = p; + entry->channel = flags & 0x0F; + entry->mask = 0; + + if ( flags & 0xF0 ) + { + if ( flags & 0x80 ) + { + q = dumbfile_getc( f ); + if ( q ) + { + entry->mask |= IT_ENTRY_NOTE; + entry->note = q - 1; + } + } + + if ( flags & 0x40 ) + { + q = dumbfile_getc( f ); + if ( q ) + { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = q; + } + } + + if ( flags & 0x20 ) + { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = dumbfile_getc( f ); + } + + if ( flags & 0x10 ) + { + q = dumbfile_getc( f ); + r = dumbfile_getc( f ); + _dumb_it_xm_convert_effect( q, r, entry, 0 ); + } + + if (entry->mask) entry++; + } + } + + while ( row < 64 ) + { + IT_SET_END_ROW( entry ); + ++ entry; + ++ row; + } + + pattern->n_entries = entry - pattern->entry; + if ( ! pattern->n_entries ) return -1; + + return 0; +} + +static DUMB_IT_SIGDATA *it_riff_dsmf_load_sigdata( DUMBFILE * f, struct riff * stream ) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, found; + + if ( ! stream ) goto error; + + if ( stream->type != DUMB_ID( 'D', 'S', 'M', 'F' ) ) goto error; + + sigdata = malloc(sizeof(*sigdata)); + if ( ! sigdata ) goto error; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch( c->type ) + { + case DUMB_ID( 'S' ,'O' ,'N' ,'G' ): + /* initialization data */ + if ( ( found ) || ( c->size < 192 ) ) goto error_sd; + found = 1; + break; + + case DUMB_ID( 'P', 'A', 'T', 'T' ): + ++ sigdata->n_patterns; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + ++ sigdata->n_samples; + break; + } + } + + if ( !found || !sigdata->n_samples || !sigdata->n_patterns ) goto error_sd; + + if ( sigdata->n_samples > 255 || sigdata->n_patterns > 255 ) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + sigdata->restart_position = 0; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'S', 'O', 'N', 'G' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + dumbfile_getnc( (char *) sigdata->name, 28, f ); + sigdata->name[ 28 ] = 0; + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + dumbfile_skip( f, 36 - 28 ); + sigdata->n_orders = dumbfile_igetw( f ); + //sigdata->n_samples = ptr[ 38 ] | ( ptr[ 39 ] << 8 ); // whatever + //sigdata->n_patterns = ptr[ 40 ] | ( ptr[ 41 ] << 8 ); + dumbfile_skip( f, 42 - 38 ); + sigdata->n_pchannels = dumbfile_igetw( f ); + sigdata->global_volume = dumbfile_getc( f ); + sigdata->mixing_volume = dumbfile_getc( f ); + sigdata->speed = dumbfile_getc( f ); + sigdata->tempo = dumbfile_getc( f ); + + for ( o = 0; o < 16; ++o ) + { + sigdata->channel_pan[ o ] = dumbfile_getc( f ) / 2; + } + + sigdata->order = malloc( 128 ); + if ( ! sigdata->order ) goto error_usd; + dumbfile_getnc( (char *) sigdata->order, 128, f ); + + break; + } + } + + sigdata->pattern = malloc( sigdata->n_patterns * sizeof( *sigdata->pattern ) ); + if ( ! sigdata->pattern ) goto error_usd; + for ( n = 0; n < sigdata->n_patterns; ++n ) + sigdata->pattern[ n ].entry = NULL; + + sigdata->sample = malloc( sigdata->n_samples * sizeof( *sigdata->sample ) ); + if ( ! sigdata->sample ) goto error_usd; + for ( n = 0; n < sigdata->n_samples; ++n ) + { + IT_SAMPLE * sample = sigdata->sample + n; + sample->data = NULL; + } + + sigdata->n_samples = 0; + sigdata->n_patterns = 0; + + for ( n = 0; (unsigned)n < stream->chunk_count; ++n ) + { + struct riff_chunk * c = stream->chunks + n; + switch ( c->type ) + { + case DUMB_ID( 'P', 'A', 'T', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + if ( it_riff_dsmf_process_pattern( sigdata->pattern + sigdata->n_patterns, f, c->size ) ) goto error_usd; + ++ sigdata->n_patterns; + break; + + case DUMB_ID( 'I', 'N', 'S', 'T' ): + if ( dumbfile_seek( f, c->offset, DFS_SEEK_SET ) ) goto error_usd; + if ( it_riff_dsmf_process_sample( sigdata->sample + sigdata->n_samples, f, c->size ) ) goto error_usd; + ++ sigdata->n_samples; + break; + } + } + + _dumb_it_fix_invalid_orders( sigdata ); + + return sigdata; + +error_usd: + _dumb_it_unload_sigdata( sigdata ); + goto error; +error_sd: + free( sigdata ); +error: + return NULL; +} + +DUH *dumb_read_riff_dsmf( DUMBFILE * f, struct riff * stream ) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_riff_dsmf_load_sigdata( f, stream ); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "RIFF DSMF"; + return make_duh( -1, 2, ( const char * const (*) [ 2 ] ) tag, 1, & descptr, & sigdata ); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readmod.c b/Frameworks/Dumb/dumb/src/it/readmod.c index ebeb53a34..94fb7cc74 100644 --- a/Frameworks/Dumb/dumb/src/it/readmod.c +++ b/Frameworks/Dumb/dumb/src/it/readmod.c @@ -1,602 +1,631 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * readmod.c - Code to read a good old-fashioned / / \ \ - * Amiga module from an open file. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -#include -#include -#include - -#include "dumb.h" -#include "internal/it.h" - - - -static int it_mod_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer) -{ - int pos; - int channel; - int row; - IT_ENTRY *entry; - - pattern->n_rows = 64; - - if (n_channels == 0) { - /* Read the first four channels, leaving gaps for the rest. */ - for (pos = 0; pos < 64*8*4; pos += 8*4) - dumbfile_getnc(buffer + pos, 4*4, f); - /* Read the other channels into the gaps we left. */ - for (pos = 4*4; pos < 64*8*4; pos += 8*4) - dumbfile_getnc(buffer + pos, 4*4, f); - - n_channels = 8; - } else - dumbfile_getnc(buffer, 64 * n_channels * 4, f); - - if (dumbfile_error(f)) - return -1; - - /* compute number of entries */ - pattern->n_entries = 64; /* Account for the row end markers */ - pos = 0; - for (row = 0; row < 64; row++) { - for (channel = 0; channel < n_channels; channel++) { - if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) - pattern->n_entries++; - pos += 4; - } - } - - pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); - if (!pattern->entry) - return -1; - - entry = pattern->entry; - pos = 0; - for (row = 0; row < 64; row++) { - for (channel = 0; channel < n_channels; channel++) { - if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) { - unsigned char sample = (buffer[pos+0] & 0xF0) | (buffer[pos+2] >> 4); - int period = ((int)(buffer[pos+0] & 0x0F) << 8) | buffer[pos+1]; - - entry->channel = channel; - entry->mask = 0; - - if (period) { - int note; - entry->mask |= IT_ENTRY_NOTE; - - /* frequency = (AMIGA_DIVISOR / 8) / (period * 2) - * C-1: period = 214 -> frequency = 16726 - * so, set C5_speed to 16726 - * and period = 214 should translate to C5 aka 60 - * halve the period, go up an octive - * - * period = 214 / pow(DUMB_SEMITONE_BASE, note - 60) - * pow(DUMB_SEMITONE_BASE, note - 60) = 214 / period - * note - 60 = log(214/period) / log(DUMB_SEMITONE_BASE) - */ - note = (int)floor(log(214.0/period) / log(DUMB_SEMITONE_BASE) + 60.5); - entry->note = MID(0, note, 119); - // or should we preserve the period? - //entry->note = buffer[pos+0] & 0x0F; /* High nibble */ - //entry->volpan = buffer[pos+1]; /* Low byte */ - // and what about finetune? - } - - if (sample) { - entry->mask |= IT_ENTRY_INSTRUMENT; - entry->instrument = sample; - } - - _dumb_it_xm_convert_effect(buffer[pos+2] & 0x0F, buffer[pos+3], entry); - - entry++; - } - pos += 4; - } - IT_SET_END_ROW(entry); - entry++; - } - - return 0; -} - - - -static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) -{ - int finetune; - -/** - 21 22 Chars Sample 1 name. If the name is not a full - 22 chars in length, it will be null - terminated. - -If -the sample name begins with a '#' character (ASCII $23 (35)) then this is -assumed not to be an instrument name, and is probably a message. -*/ - dumbfile_getnc(sample->name, 22, f); - sample->name[22] = 0; - - sample->filename[0] = 0; - - sample->length = dumbfile_mgetw(f) << 1; - finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */ -/** Each finetune step changes the note 1/8th of a semitone. */ - sample->global_volume = 64; - sample->default_volume = dumbfile_getc(f); // Should we be setting global_volume to this instead? - sample->loop_start = dumbfile_mgetw(f) << 1; - sample->loop_end = sample->loop_start + (dumbfile_mgetw(f) << 1); -/** -Once this sample has been played completely from beginning -to end, if the repeat length (next field) is greater than two bytes it -will loop back to this position in the sample and continue playing. Once -it has played for the repeat length, it continues to loop back to the -repeat start offset. This means the sample continues playing until it is -told to stop. -*/ - - if (sample->length <= 0) { - sample->flags = 0; - return 0; - } - - sample->flags = IT_SAMPLE_EXISTS; - - sample->default_pan = 0; - sample->C5_speed = (long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); - // the above line might be wrong - - if (sample->loop_end > sample->length) - sample->loop_end = sample->length; - - if (sample->loop_end - sample->loop_start > 2) - sample->flags |= IT_SAMPLE_LOOP; - - sample->vibrato_speed = 0; - sample->vibrato_depth = 0; - sample->vibrato_rate = 0; - sample->vibrato_waveform = 0; // do we have to set _all_ these? - - return dumbfile_error(f); -} - - - -static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f) -{ - long i; - long truncated_size; - - /* let's get rid of the sample data coming after the end of the loop */ - if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { - truncated_size = sample->length - sample->loop_end; - sample->length = sample->loop_end; - } else { - truncated_size = 0; - } - - if (sample->length) { - sample->data = malloc(sample->length); - - if (!sample->data) - return -1; - - /* Sample data are stored in "8-bit two's compliment format" (sic). */ - for (i = 0; i < sample->length; i++) - ((signed char *)sample->data)[i] = dumbfile_getc(f); - } else - sample->flags &= ~IT_SAMPLE_EXISTS; - - /* skip truncated data */ - dumbfile_skip(f, truncated_size); - // Should we be truncating it? - - if (dumbfile_error(f)) - return -1; - - return 0; -} - - - -typedef struct BUFFERED_MOD BUFFERED_MOD; - -struct BUFFERED_MOD -{ - unsigned char *buffered; - long ptr, len; - DUMBFILE *remaining; -}; - - - -static int buffer_mod_skip(void *f, long n) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) { - bm->ptr += n; - if (bm->ptr >= bm->len) { - free(bm->buffered); - bm->buffered = NULL; - return dumbfile_skip(bm->remaining, bm->ptr - bm->len); - } - return 0; - } - return dumbfile_skip(bm->remaining, n); -} - - - -static int buffer_mod_getc(void *f) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) { - int rv = bm->buffered[bm->ptr++]; - if (bm->ptr >= bm->len) { - free(bm->buffered); - bm->buffered = NULL; - } - return rv; - } - return dumbfile_getc(bm->remaining); -} - - - -static long buffer_mod_getnc(char *ptr, long n, void *f) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) { - int left = bm->len - bm->ptr; - if (n >= left) { - int rv; - memcpy(ptr, bm->buffered + bm->ptr, left); - free(bm->buffered); - bm->buffered = NULL; - rv = dumbfile_getnc(ptr + left, n - left, bm->remaining); - return left + MAX(rv, 0); - } - memcpy(ptr, bm->buffered + bm->ptr, n); - bm->ptr += n; - return n; - } - return dumbfile_getnc(ptr, n, bm->remaining); -} - - - -static void buffer_mod_close(void *f) -{ - BUFFERED_MOD *bm = f; - if (bm->buffered) free(bm->buffered); - /* Do NOT close bm->remaining */ - free(f); -} - - - -DUMBFILE_SYSTEM buffer_mod_dfs = { - NULL, - &buffer_mod_skip, - &buffer_mod_getc, - &buffer_mod_getnc, - &buffer_mod_close -}; - - - -#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128) - -static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, unsigned long *fft) -{ - BUFFERED_MOD *bm = malloc(sizeof(*bm)); - if (!bm) return NULL; - - bm->buffered = malloc(MOD_FFT_OFFSET + 4); - if (!bm->buffered) { - free(bm); - return NULL; - } - - bm->len = dumbfile_getnc(bm->buffered, MOD_FFT_OFFSET + 4, f); - - if (bm->len > 0) { - if (bm->len >= MOD_FFT_OFFSET + 4) - *fft = (unsigned long)bm->buffered[MOD_FFT_OFFSET ] << 24 - | (unsigned long)bm->buffered[MOD_FFT_OFFSET+1] << 16 - | (unsigned long)bm->buffered[MOD_FFT_OFFSET+2] << 8 - | (unsigned long)bm->buffered[MOD_FFT_OFFSET+3]; - else - *fft = 0; - bm->ptr = 0; - } else { - free(bm->buffered); - bm->buffered = NULL; - } - - bm->remaining = f; - - return dumbfile_open_ex(bm, &buffer_mod_dfs); -} - - - -static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f) -{ - DUMB_IT_SIGDATA *sigdata; - int n_channels; - int i; - unsigned long fft; - - f = dumbfile_buffer_mod(f, &fft); - if (!f) - return NULL; - - sigdata = malloc(sizeof(*sigdata)); - if (!sigdata) { - dumbfile_close(f); - return NULL; - } - - /** - 1 20 Chars Title of the song. If the title is not a - full 20 chars in length, it will be null- - terminated. - */ - if (dumbfile_getnc(sigdata->name, 20, f) < 20) { - free(sigdata); - dumbfile_close(f); - return NULL; - } - sigdata->name[20] = 0; - - sigdata->n_samples = 31; - - switch (fft) { - case DUMB_ID('M','.','K','.'): - case DUMB_ID('M','!','K','!'): - case DUMB_ID('M','&','K','!'): - case DUMB_ID('N','.','T','.'): - case DUMB_ID('F','L','T','4'): - n_channels = 4; - break; - case DUMB_ID('F','L','T','8'): - n_channels = 0; - /* 0 indicates a special case; two four-channel patterns must be - * combined into one eight-channel pattern. Pattern indexes must - * be halved. Why oh why do they obfuscate so? - */ - for (i = 0; i < 128; i++) - sigdata->order[i] >>= 1; - break; - case DUMB_ID('C','D','8','1'): - case DUMB_ID('O','C','T','A'): - case DUMB_ID('O','K','T','A'): - n_channels = 8; - break; - case DUMB_ID('1','6','C','N'): - n_channels = 16; - break; - case DUMB_ID('3','2','C','N'): - n_channels = 32; - break; - default: - /* If we get an illegal tag, assume 4 channels 15 samples. */ - if ((fft & 0x0000FFFFL) == DUMB_ID(0,0,'C','H')) { - if (fft >= '1' << 24 && fft < '4' << 24) { - n_channels = ((fft & 0x00FF0000L) >> 16) - '0'; - if ((unsigned int)n_channels >= 10) { - /* Rightmost character wasn't a digit. */ - n_channels = 4; - sigdata->n_samples = 15; - } else { - n_channels += (((fft & 0xFF000000L) >> 24) - '0') * 10; - /* MODs should really only go up to 32 channels, but we're lenient. */ - if ((unsigned int)(n_channels - 1) >= DUMB_IT_N_CHANNELS - 1) { - /* No channels or too many? Can't be right... */ - n_channels = 4; - sigdata->n_samples = 15; - } - } - } else { - n_channels = 4; - sigdata->n_samples = 15; - } - } else if ((fft & 0x00FFFFFFL) == DUMB_ID(0,'C','H','N')) { - n_channels = (fft >> 24) - '0'; - if ((unsigned int)(n_channels - 1) >= 9) { - /* Character was '0' or it wasn't a digit */ - n_channels = 4; - sigdata->n_samples = 15; - } - } else if ((fft & 0xFFFFFF00L) == DUMB_ID('T','D','Z',0)) { - n_channels = (fft & 0x000000FFL) - '0'; - if ((unsigned int)(n_channels - 1) >= 9) { - /* We've been very lenient, given that it should have - * been 1, 2 or 3, but this MOD has been very naughty and - * must be punished. - */ - n_channels = 4; - sigdata->n_samples = 15; - } - } else { - n_channels = 4; - sigdata->n_samples = 15; - } - } - - sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); - if (!sigdata->sample) { - free(sigdata); - dumbfile_close(f); - return NULL; - } - - sigdata->song_message = NULL; - sigdata->order = NULL; - sigdata->instrument = NULL; - sigdata->pattern = NULL; - sigdata->midi = NULL; - sigdata->checkpoint = NULL; - - sigdata->n_instruments = 0; - - for (i = 0; i < sigdata->n_samples; i++) - sigdata->sample[i].data = NULL; - - for (i = 0; i < sigdata->n_samples; i++) { - if (it_mod_read_sample_header(&sigdata->sample[i], f)) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - } - - sigdata->n_orders = dumbfile_getc(f); - sigdata->restart_position = dumbfile_getc(f); - // what if this is >= 127? what about with Fast Tracker II? - - if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right? - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - - //if (sigdata->restart_position >= sigdata->n_orders) - //sigdata->restart_position = 0; - - sigdata->order = malloc(128); /* We may need to scan the extra ones! */ - if (!sigdata->order) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - if (dumbfile_getnc(sigdata->order, 128, f) < 128) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - - /* "The old NST format contains only 15 samples (instead of 31). Further - * it doesn't contain a file format tag (id). So Pattern data offset is - * at 20+15*30+1+1+128." - * - Then I shall assume the File Format Tag never exists if there are - * only 15 samples. I hope this isn't a faulty assumption... - */ - if (sigdata->n_samples == 31) - dumbfile_skip(f, 4); - - /* Work out how many patterns there are. */ - sigdata->n_patterns = -1; - for (i = 0; i < 128; i++) - if (sigdata->n_patterns < sigdata->order[i]) - sigdata->n_patterns = sigdata->order[i]; - sigdata->n_patterns++; - - /* May as well try to save a tiny bit of memory. */ - if (sigdata->n_orders < 128) { - unsigned char *order = realloc(sigdata->order, sigdata->n_orders); - if (order) sigdata->order = order; - } - - sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); - if (!sigdata->pattern) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - for (i = 0; i < sigdata->n_patterns; i++) - sigdata->pattern[i].entry = NULL; - - /* Read in the patterns */ - { - unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */ - if (!buffer) { - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - for (i = 0; i < sigdata->n_patterns; i++) { - if (it_mod_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) { - free(buffer); - _dumb_it_unload_sigdata(sigdata); - dumbfile_close(f); - return NULL; - } - } - free(buffer); - } - - /* And finally, the sample data */ - for (i = 0; i < sigdata->n_samples; i++) { - if (it_mod_read_sample_data(&sigdata->sample[i], f)) { - continue; - } - } - - dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */ - /* The DUMBFILE originally passed to our function is intact. */ - - /* Now let's initialise the remaining variables, and we're done! */ - sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; - - sigdata->global_volume = 128; - sigdata->mixing_volume = 48; - /* We want 50 ticks per second; 50/6 row advances per second; - * 50*10=500 row advances per minute; 500/4=125 beats per minute. - */ - sigdata->speed = 6; - sigdata->tempo = 125; - sigdata->pan_separation = 128; - - memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); - - for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) { - sigdata->channel_pan[i+0] = 16; - sigdata->channel_pan[i+1] = 48; - sigdata->channel_pan[i+2] = 48; - sigdata->channel_pan[i+3] = 16; - } - - _dumb_it_fix_invalid_orders(sigdata); - - return sigdata; -} - - - -DUH *dumb_read_mod_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_mod_load_sigdata(f); - - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readmod.c - Code to read a good old-fashioned / / \ \ + * Amiga module from an open file. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_mod_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if (n_channels == 0) { + /* Read the first four channels, leaving gaps for the rest. */ + for (pos = 0; pos < 64*8*4; pos += 8*4) + dumbfile_getnc((char *)buffer + pos, 4*4, f); + /* Read the other channels into the gaps we left. */ + for (pos = 4*4; pos < 64*8*4; pos += 8*4) + dumbfile_getnc((char *)buffer + pos, 4*4, f); + + n_channels = 8; + } else + dumbfile_getnc((char *)buffer, 64 * n_channels * 4, f); + + if (dumbfile_error(f)) + return -1; + + /* compute number of entries */ + pattern->n_entries = 64; /* Account for the row end markers */ + pos = 0; + for (row = 0; row < 64; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) + pattern->n_entries++; + pos += 4; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + entry = pattern->entry; + pos = 0; + for (row = 0; row < 64; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) { + unsigned char sample = (buffer[pos+0] & 0xF0) | (buffer[pos+2] >> 4); + int period = ((int)(buffer[pos+0] & 0x0F) << 8) | buffer[pos+1]; + + entry->channel = channel; + entry->mask = 0; + + if (period) { + int note; + entry->mask |= IT_ENTRY_NOTE; + + /* frequency = (AMIGA_DIVISOR / 8) / (period * 2) + * C-1: period = 214 -> frequency = 16726 + * so, set C5_speed to 16726 + * and period = 214 should translate to C5 aka 60 + * halve the period, go up an octive + * + * period = 214 / pow(DUMB_SEMITONE_BASE, note - 60) + * pow(DUMB_SEMITONE_BASE, note - 60) = 214 / period + * note - 60 = log(214/period) / log(DUMB_SEMITONE_BASE) + */ + note = (int)floor(log(214.0/period) / log(DUMB_SEMITONE_BASE) + 60.5); + entry->note = MID(0, note, 119); + // or should we preserve the period? + //entry->note = buffer[pos+0] & 0x0F; /* High nibble */ + //entry->volpan = buffer[pos+1]; /* Low byte */ + // and what about finetune? + } + + if (sample) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = sample; + } + + _dumb_it_xm_convert_effect(buffer[pos+2] & 0x0F, buffer[pos+3], entry, 1); + + entry++; + } + pos += 4; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f, int stk) +{ + int finetune, loop_start, loop_length; + +/** + 21 22 Chars Sample 1 name. If the name is not a full + 22 chars in length, it will be null + terminated. + +If +the sample name begins with a '#' character (ASCII $23 (35)) then this is +assumed not to be an instrument name, and is probably a message. +*/ + dumbfile_getnc((char *)sample->name, 22, f); + sample->name[22] = 0; + + sample->filename[0] = 0; + + sample->length = dumbfile_mgetw(f) << 1; + finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */ +/** Each finetune step changes the note 1/8th of a semitone. */ + sample->global_volume = 64; + sample->default_volume = dumbfile_getc(f); // Should we be setting global_volume to this instead? + loop_start = dumbfile_mgetw(f); + if ( !stk ) loop_start <<= 1; + loop_length = dumbfile_mgetw(f) << 1; + if ( loop_length > 2 && loop_start + loop_length > sample->length && loop_start / 2 + loop_length <= sample->length ) + loop_start /= 2; + sample->loop_start = loop_start; + sample->loop_end = loop_start + loop_length; +/** +Once this sample has been played completely from beginning +to end, if the repeat length (next field) is greater than two bytes it +will loop back to this position in the sample and continue playing. Once +it has played for the repeat length, it continues to loop back to the +repeat start offset. This means the sample continues playing until it is +told to stop. +*/ + + if (sample->length <= 0) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = finetune * 32; + // the above line might be wrong + + if (sample->loop_end > sample->length) + sample->loop_end = sample->length; + + if (sample->loop_end - sample->loop_start > 2) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f, unsigned long fft) +{ + long i; + long truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + if (sample->length) { + sample->data = malloc(sample->length); + + if (!sample->data) + return -1; + + /* Sample data are stored in "8-bit two's compliment format" (sic). */ + /* + for (i = 0; i < sample->length; i++) + ((signed char *)sample->left)[i] = dumbfile_getc(f); + */ + /* F U Olivier Lapicque */ + if (sample->length >= 5) + { + i = dumbfile_getnc(sample->data, 5, f); + if (i == 5) + { + if (!memcmp(sample->data, "ADPCM", 5)) + { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + + return 0; + } + else + { + i += dumbfile_getnc(((char *)sample->data) + 5, sample->length - 5, f); + } + } + } + else + { + i = dumbfile_getnc(sample->data, sample->length, f); + } + if (i < sample->length) + { + if (i <= 0) + { + sample->flags = 0; + return 0; + } + sample->length = i; + if (sample->loop_end > i) sample->loop_end = i; + // holy crap! + if (sample->loop_start > i) sample->flags &= ~IT_SAMPLE_LOOP; + } + else + { + /* skip truncated data */ + int feh = dumbfile_error(f); + + if (truncated_size) dumbfile_skip(f, truncated_size); + // Should we be truncating it? + + if (feh) + return -1; + } + + if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) { + int delta = 0; + for (i = 0; i < sample->length; i++) { + delta += ((signed char *)sample->data)[i]; + ((signed char *)sample->data)[i] = delta; + } + } + } + + return 0; +} + + + +#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128) + +static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f, int restrict_) +{ + DUMB_IT_SIGDATA *sigdata; + int n_channels; + int i; + unsigned long fft; + + if ( dumbfile_seek(f, MOD_FFT_OFFSET, DFS_SEEK_SET) ) + return NULL; + + fft = dumbfile_mgetl(f); + if (dumbfile_error(f)) + return NULL; + + if ( dumbfile_seek(f, 0, DFS_SEEK_SET) ) + return NULL; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) { + return NULL; + } + + /** + 1 20 Chars Title of the song. If the title is not a + full 20 chars in length, it will be null- + terminated. + */ + if (dumbfile_getnc((char *)sigdata->name, 20, f) < 20) { + free(sigdata); + return NULL; + } + sigdata->name[20] = 0; + + sigdata->n_samples = 31; + + switch (fft) { + case DUMB_ID('M','.','K','.'): + case DUMB_ID('M','!','K','!'): + case DUMB_ID('M','&','K','!'): + case DUMB_ID('N','.','T','.'): + case DUMB_ID('N','S','M','S'): + case DUMB_ID('F','L','T','4'): + case DUMB_ID('M',0,0,0): + case DUMB_ID('8',0,0,0): + n_channels = 4; + break; + case DUMB_ID('F','L','T','8'): + n_channels = 0; + /* 0 indicates a special case; two four-channel patterns must be + * combined into one eight-channel pattern. Pattern indexes must + * be halved. Why oh why do they obfuscate so? + */ + /*for (i = 0; i < 128; i++) + sigdata->order[i] >>= 1;*/ + break; + case DUMB_ID('C','D','8','1'): + case DUMB_ID('O','C','T','A'): + case DUMB_ID('O','K','T','A'): + n_channels = 8; + break; + case DUMB_ID('1','6','C','N'): + n_channels = 16; + break; + case DUMB_ID('3','2','C','N'): + n_channels = 32; + break; + default: + /* If we get an illegal tag, assume 4 channels 15 samples. */ + if ((fft & 0x0000FFFFL) == DUMB_ID(0,0,'C','H')) { + if (fft >= '1' << 24 && fft < '4' << 24) { + n_channels = ((fft & 0x00FF0000L) >> 16) - '0'; + if ((unsigned int)n_channels >= 10) { + /* Rightmost character wasn't a digit. */ + n_channels = 4; + sigdata->n_samples = 15; + } else { + n_channels += (((fft & 0xFF000000L) >> 24) - '0') * 10; + /* MODs should really only go up to 32 channels, but we're lenient. */ + if ((unsigned int)(n_channels - 1) >= DUMB_IT_N_CHANNELS - 1) { + /* No channels or too many? Can't be right... */ + n_channels = 4; + sigdata->n_samples = 15; + } + } + } else { + n_channels = 4; + sigdata->n_samples = 15; + } + } else if ((fft & 0x00FFFFFFL) == DUMB_ID(0,'C','H','N')) { + n_channels = (fft >> 24) - '0'; + if ((unsigned int)(n_channels - 1) >= 9) { + /* Character was '0' or it wasn't a digit */ + n_channels = 4; + sigdata->n_samples = 15; + } + } else if ((fft & 0xFFFFFF00L) == DUMB_ID('T','D','Z',0)) { + n_channels = (fft & 0x000000FFL) - '0'; + if ((unsigned int)(n_channels - 1) >= 9) { + /* We've been very lenient, given that it should have + * been 1, 2 or 3, but this MOD has been very naughty and + * must be punished. + */ + n_channels = 4; + sigdata->n_samples = 15; + } + } else { + n_channels = 4; + sigdata->n_samples = 15; + } + } + + // moo + if ( ( restrict_ & 1 ) && sigdata->n_samples == 15 ) + { + free(sigdata); + return NULL; + } + + sigdata->n_pchannels = n_channels ? n_channels : 8; /* special case for 0, see above */ + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + free(sigdata); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for (i = 0; i < sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + for (i = 0; i < sigdata->n_samples; i++) { + if (it_mod_read_sample_header(&sigdata->sample[i], f, sigdata->n_samples == 15)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + sigdata->n_orders = dumbfile_getc(f); + sigdata->restart_position = dumbfile_getc(f); + // what if this is >= 127? what about with Fast Tracker II? + +/* if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right? + _dumb_it_unload_sigdata(sigdata); + return NULL; + }*/ + + //if (sigdata->restart_position >= sigdata->n_orders) + //sigdata->restart_position = 0; + + sigdata->order = malloc(128); /* We may need to scan the extra ones! */ + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if (dumbfile_getnc((char *)sigdata->order, 128, f) < 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right? + sigdata->n_orders = 128; + //while (sigdata->n_orders > 1 && !sigdata->order[sigdata->n_orders - 1]) sigdata->n_orders--; + } + + if ( ! n_channels ) + for (i = 0; i < 128; i++) + sigdata->order[i] >>= 1; + + /* "The old NST format contains only 15 samples (instead of 31). Further + * it doesn't contain a file format tag (id). So Pattern data offset is + * at 20+15*30+1+1+128." + * - Then I shall assume the File Format Tag never exists if there are + * only 15 samples. I hope this isn't a faulty assumption... + */ + if (sigdata->n_samples == 31) + dumbfile_skip(f, 4); + + sigdata->n_patterns = -1; + + if ( ( restrict_ & 2 ) ) + { + unsigned char buffer[5]; + long sample_number; + long total_sample_size; + long offset = dumbfile_pos(f); + long remain = dumbfile_get_size(f) - offset; + if ( dumbfile_error( f ) || + dumbfile_seek( f, 0, SEEK_END ) ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + sample_number = sigdata->n_samples - 1; + total_sample_size = 0; + while (dumbfile_pos(f) > offset && sample_number >= 0) { + if (sigdata->sample[sample_number].flags & IT_SAMPLE_EXISTS) { + if ( dumbfile_seek(f, -((sigdata->sample[sample_number].length + 1) / 2 + 5 + 16), DFS_SEEK_CUR) || + dumbfile_getnc((char *)buffer, 5, f) < 5 ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if ( !memcmp( buffer, "ADPCM", 5 ) ) { /* BAH */ + total_sample_size += (sigdata->sample[sample_number].length + 1) / 2 + 5 + 16; + if ( dumbfile_seek(f, -5, DFS_SEEK_CUR) ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } else { + total_sample_size += sigdata->sample[sample_number].length; + if ( dumbfile_seek(f, -(sigdata->sample[sample_number].length - ((sigdata->sample[sample_number].length + 1) / 2 + 5 + 16) + 5), DFS_SEEK_CUR) ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + } + --sample_number; + } + + if (remain > total_sample_size) { + sigdata->n_patterns = ( remain - total_sample_size + 4 ) / ( 256 * sigdata->n_pchannels ); + if (fft == DUMB_ID('M',0,0,0) || fft == DUMB_ID('8',0,0,0)) { + remain -= sigdata->n_patterns * 256 * sigdata->n_pchannels; + if (dumbfile_skip(f, remain - total_sample_size)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + } + } + else + { + for (i = 0; i < 128; i++) + { + if (sigdata->order[i] > sigdata->n_patterns) + sigdata->n_patterns = sigdata->order[i]; + } + sigdata->n_patterns++; + } + + if ( sigdata->n_patterns <= 0 ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + /* May as well try to save a tiny bit of memory. */ + if (sigdata->n_orders < 128) { + unsigned char *order = realloc(sigdata->order, sigdata->n_orders); + if (order) sigdata->order = order; + } + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + { + unsigned char *buffer = malloc(256 * sigdata->n_pchannels); /* 64 rows * 4 bytes */ + if (!buffer) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_mod_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) { + free(buffer); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + /* And finally, the sample data */ + for (i = 0; i < sigdata->n_samples; i++) { + if (it_mod_read_sample_data(&sigdata->sample[i], f, fft)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + /* w00t! */ + /*if ( n_channels == 4 && + ( sigdata->n_samples == 15 || + ( ( fft & 240 ) != DUMB_ID( 0, 0, 'C', 0 ) && + ( fft & 240 ) != DUMB_ID( 0, 0, 'H', 0 ) && + ( fft & 240 ) != 0 ) ) ) { + for ( i = 0; i < sigdata->n_samples; ++i ) { + IT_SAMPLE * sample = &sigdata->sample [i]; + if ( sample && ( sample->flags & IT_SAMPLE_EXISTS ) ) { + int n, o; + o = sample->length; + if ( o > 4 ) o = 4; + for ( n = 0; n < o; ++n ) + ( ( char * ) sample->data ) [n] = 0; + } + } + }*/ + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + /* We want 50 ticks per second; 50/6 row advances per second; + * 50*10=500 row advances per minute; 500/4=125 beats per minute. + */ + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) { + sigdata->channel_pan[i+0] = 16; + sigdata->channel_pan[i+1] = 48; + sigdata->channel_pan[i+2] = 48; + sigdata->channel_pan[i+3] = 16; + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *dumb_read_mod_quick(DUMBFILE *f, int restrict_) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_mod_load_sigdata(f, restrict_); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "MOD"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readmod2.c b/Frameworks/Dumb/dumb/src/it/readmod2.c index a031b47e8..fa5eacc50 100644 --- a/Frameworks/Dumb/dumb/src/it/readmod2.c +++ b/Frameworks/Dumb/dumb/src/it/readmod2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * readmod2.c - Function to read a good old- / / \ \ - * fashioned Amiga module from an | < / \_ - * open file and do an initial | \/ /\ / - * run-through. \_ / > / - * | \ / / - * Split off from readmod.c by entheh. | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_read_mod(DUMBFILE *f) -{ - DUH *duh = dumb_read_mod_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readmod2.c - Function to read a good old- / / \ \ + * fashioned Amiga module from an | < / \_ + * open file and do an initial | \/ /\ / + * run-through. \_ / > / + * | \ / / + * Split off from readmod.c by entheh. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_mod(DUMBFILE *f, int restrict_) +{ + DUH *duh = dumb_read_mod_quick(f, restrict_); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readmtm.c b/Frameworks/Dumb/dumb/src/it/readmtm.c new file mode 100644 index 000000000..77f9d9e16 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readmtm.c @@ -0,0 +1,412 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readmtm.c - Code to read a MultiTracker Module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +size_t strlen_max(const char * ptr, size_t max) +{ + const char * end, * start; + if (ptr==0) return 0; + start = ptr; + end = ptr + max; + while(*ptr && ptr < end) ptr++; + return ptr - start; +} + +static int it_mtm_assemble_pattern(IT_PATTERN *pattern, const unsigned char * track, const unsigned short * sequence, int n_rows) +{ + int n, o, note, sample; + const unsigned char * t; + IT_ENTRY * entry; + + pattern->n_rows = n_rows; + pattern->n_entries = n_rows; + + for (n = 0; n < 32; n++) { + if (sequence[n]) { + t = &track[192 * (sequence[n] - 1)]; + for (o = 0; o < n_rows; o++) { + if (t[0] || t[1] || t[2]) pattern->n_entries++; + t += 3; + } + } + } + + entry = malloc(pattern->n_entries * sizeof(*entry)); + if (!entry) return -1; + pattern->entry = entry; + + for (n = 0; n < n_rows; n++) { + for (o = 0; o < 32; o++) { + if (sequence[o]) { + t = &track[192 * (sequence[o] - 1) + (n * 3)]; + if (t[0] || t[1] || t[2]) { + entry->channel = o; + entry->mask = 0; + note = t[0] >> 2; + sample = ((t[0] << 4) | (t[1] >> 4)) & 0x3F; + + if (note) { + entry->mask |= IT_ENTRY_NOTE; + entry->note = note + 24; + } + + if (sample) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = sample; + } + + _dumb_it_xm_convert_effect(t[1] & 0xF, t[2], entry, 1); + + if (entry->mask) entry++; + } + } + } + IT_SET_END_ROW(entry); + entry++; + } + + pattern->n_entries = entry - pattern->entry; + + return 0; +} + +static int it_mtm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) +{ + int finetune, flags; + + dumbfile_getnc((char *)sample->name, 22, f); + sample->name[22] = 0; + + sample->filename[0] = 0; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */ + sample->global_volume = 64; + sample->default_volume = dumbfile_getc(f); + + flags = dumbfile_getc(f); + + if (sample->length <= 0) { + sample->flags = 0; + return 0; + } + + sample->flags = IT_SAMPLE_EXISTS; + + if (flags & 1) { + sample->flags |= IT_SAMPLE_16BIT; + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 );//(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = finetune * 32; + // the above line might be wrong + + if (sample->loop_end > sample->length) + sample->loop_end = sample->length; + + if (sample->loop_end - sample->loop_start > 2) + sample->flags |= IT_SAMPLE_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + +static int it_mtm_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f) +{ + long i; + long truncated_size; + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + sample->data = malloc(sample->length); + + if (!sample->data) + return -1; + + dumbfile_getnc((char *)sample->data, sample->length, f); + dumbfile_skip(f, truncated_size); + + if (dumbfile_error(f)) + return -1; + + for (i = 0; i < sample->length; i++) + ((signed char *)sample->data)[i] ^= 0x80; + + return 0; +} + +static DUMB_IT_SIGDATA *it_mtm_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + + int n, o, n_tracks, l_comment, n_rows, n_channels; + + unsigned char * track; + + unsigned short * sequence; + + char * comment; + + if (dumbfile_getc(f) != 'M' || + dumbfile_getc(f) != 'T' || + dumbfile_getc(f) != 'M') goto error; + + *version = dumbfile_getc(f); + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) goto error; + + dumbfile_getnc((char *)sigdata->name, 20, f); + sigdata->name[20] = 0; + + n_tracks = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_getc(f) + 1; + sigdata->n_orders = dumbfile_getc(f) + 1; + l_comment = dumbfile_igetw(f); + sigdata->n_samples = dumbfile_getc(f); + //if (dumbfile_getc(f)) goto error_sd; + dumbfile_getc(f); + n_rows = dumbfile_getc(f); + n_channels = dumbfile_getc(f); + + if (dumbfile_error(f) || + (n_tracks <= 0) || + (sigdata->n_samples <= 0) || + (n_rows <= 0 || n_rows > 64) || + (n_channels <= 0 || n_channels > 32)) goto error_sd; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + if (dumbfile_getnc((char *)sigdata->channel_pan, 32, f) < 32) goto error_sd; + + for (n = 0; n < 32; n++) { + if (sigdata->channel_pan[n] <= 15) { + sigdata->channel_pan[n] -= (sigdata->channel_pan[n] & 8) >> 3; + sigdata->channel_pan[n] = (sigdata->channel_pan[n] * 32) / 7; + } else { + sigdata->channel_volume[n] = 0; + sigdata->channel_pan[n] = 7; + } + } + + for (n = 32; n < DUMB_IT_N_CHANNELS; n += 4) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) goto error_sd; + + sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + sigdata->restart_position = 0; + sigdata->n_pchannels = n_channels; + + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + + for (n = 0; n < sigdata->n_samples; n++) { + if (it_mtm_read_sample_header(&sigdata->sample[n], f)) goto error_usd; + } + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) goto error_usd; + + if (dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_usd; + if (sigdata->n_orders < 128) + if (dumbfile_skip(f, 128 - sigdata->n_orders)) goto error_usd; + + track = malloc(192 * n_tracks); + if (!track) goto error_usd; + + if (dumbfile_getnc((char *)track, 192 * n_tracks, f) < 192 * n_tracks) goto error_ft; + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) goto error_ft; + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + + sequence = malloc(sigdata->n_patterns * 32 * sizeof(*sequence)); + if (!sequence) goto error_ft; + + for (n = 0; n < sigdata->n_patterns; n++) { + for (o = 0; o < 32; o++) { + sequence[(n * 32) + o] = dumbfile_igetw(f); + if (sequence[(n * 32) + o] > n_tracks) + { + //goto error_fs; + // illegal track number, silence instead of rejecting the file + sequence[(n * 32) + o] = 0; + } + } + } + + for (n = 0; n < sigdata->n_patterns; n++) { + if (it_mtm_assemble_pattern(&sigdata->pattern[n], track, &sequence[n * 32], n_rows)) goto error_fs; + } + + if (l_comment) { + comment = malloc(l_comment); + if (!comment) goto error_fs; + if (dumbfile_getnc(comment, l_comment, f) < l_comment) goto error_fc; + + /* Time for annoying "logic", yes. We want each line which has text, + * and each blank line in between all the valid lines. + */ + + /* Find last actual line. */ + for (o = -1, n = 0; n < l_comment; n += 40) { + if (comment[n]) o = n; + } + + if (o >= 0) { + + int l, m; + + for (l = 0, n = 0; n <= o; n += 40) { + l += strlen_max(&comment[n], 40) + 2; + } + + l -= 1; + + sigdata->song_message = malloc(l); + if (!sigdata->song_message) goto error_fc; + + for (m = 0, n = 0; n <= o; n += 40) { + int p = strlen_max(&comment[n], 40); + if (p) { + memcpy(sigdata->song_message + m, &comment[n], p); + m += p; + } + if (l - m > 1) { + sigdata->song_message[m++] = 13; + sigdata->song_message[m++] = 10; + } + } + + sigdata->song_message[m] = 0; + } + + free(comment); + } + + for (n = 0; n < sigdata->n_samples; n++) { + if (it_mtm_read_sample_data(&sigdata->sample[n], f)) goto error_fs; + } + + _dumb_it_fix_invalid_orders(sigdata); + + free(sequence); + free(track); + + return sigdata; + +error_fc: + free(comment); +error_fs: + free(sequence); +error_ft: + free(track); +error_usd: + _dumb_it_unload_sigdata(sigdata); + return NULL; + +error_sd: + free(sigdata); +error: + return NULL; +} + +static char hexdigit(int in) +{ + if (in < 10) return in + '0'; + else return in + 'A' - 10; +} + +DUH *dumb_read_mtm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_mtm_load_sigdata(f, &ver); + + if (!sigdata) + return NULL; + + { + char version[16]; + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + version[0] = 'M'; + version[1] = 'T'; + version[2] = 'M'; + version[3] = ' '; + version[4] = 'v'; + version[5] = hexdigit(ver >> 4); + version[6] = '.'; + version[7] = hexdigit(ver & 15); + version[8] = 0; + tag[1][1] = (const char *) &version; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readokt.c b/Frameworks/Dumb/dumb/src/it/readokt.c new file mode 100644 index 000000000..75a38e1c9 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readokt.c @@ -0,0 +1,558 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt.c - Code to read an Oktalyzer module / / \ \ + * from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_okt_read_pattern(IT_PATTERN *pattern, const unsigned char *data, int length, int n_channels) +{ + int pos; + int channel; + int row; + int n_rows; + IT_ENTRY *entry; + + if (length < 2) return -1; + + n_rows = (data[0] << 8) | data[1]; + if (!n_rows) n_rows = 64; + + if (length < 2 + (n_rows * n_channels * 4)) return -1; + + pattern->n_rows = n_rows; + + /* compute number of entries */ + pattern->n_entries = n_rows; /* Account for the row end markers */ + pos = 2; + for (row = 0; row < pattern->n_rows; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (data[pos+0] | data[pos+2]) + pattern->n_entries++; + pos += 4; + } + } + + pattern->entry = (IT_ENTRY *) malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + entry = pattern->entry; + pos = 2; + for (row = 0; row < n_rows; row++) { + for (channel = 0; channel < n_channels; channel++) { + if (data[pos+0] | data[pos+2]) { + entry->channel = channel; + entry->mask = 0; + + if (data[pos+0] > 0 && data[pos+0] <= 36) { + entry->mask |= IT_ENTRY_NOTE | IT_ENTRY_INSTRUMENT; + + entry->note = data[pos+0] + 35; + entry->instrument = data[pos+1] + 1; + } + + entry->effect = 0; + entry->effectvalue = data[pos+3]; + + switch (data[pos+2]) { + case 2: if (data[pos+3]) entry->effect = IT_PORTAMENTO_DOWN; break; // XXX code calls this rs_portu, but it's adding to the period, which decreases the pitch + case 13: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN; break; + case 21: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_DOWN_ROW; break; + + case 1: if (data[pos+3]) entry->effect = IT_PORTAMENTO_UP; break; // XXX same deal here, increasing the pitch + case 17: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP; break; + case 30: if (data[pos+3]) entry->effect = IT_OKT_NOTE_SLIDE_UP_ROW; break; + + case 10: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_3; break; + case 11: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_4; break; + case 12: if (data[pos+3]) entry->effect = IT_OKT_ARPEGGIO_5; break; + + case 15: entry->effect = IT_S; entry->effectvalue = EFFECT_VALUE(IT_S_SET_FILTER, data[pos+3] & 0x0F); break; + + case 25: entry->effect = IT_JUMP_TO_ORDER; break; + + case 27: entry->note = IT_NOTE_OFF; entry->mask |= IT_ENTRY_NOTE; break; + + case 28: entry->effect = IT_SET_SPEED; break; + + case 31: + if ( data[pos+3] <= 0x40 ) entry->effect = IT_SET_CHANNEL_VOLUME; + else if ( data[pos+3] <= 0x50 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x40; } + else if ( data[pos+3] <= 0x60 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x50; } + else if ( data[pos+3] <= 0x70 ) { entry->effect = IT_OKT_VOLUME_SLIDE_DOWN; entry->effectvalue = data[pos+3] - 0x50; } + else if ( data[pos+3] <= 0x80 ) { entry->effect = IT_OKT_VOLUME_SLIDE_UP; entry->effectvalue = data[pos+3] - 0x60; } + break; + } + + if ( entry->effect ) entry->mask |= IT_ENTRY_EFFECT; + + entry++; + } + pos += 4; + } + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static void it_okt_read_sample_header(IT_SAMPLE *sample, const unsigned char * data) +{ + int loop_start, loop_length; + + memcpy(sample->name, data, 20); + sample->name[20] = 0; + + sample->filename[0] = 0; + + sample->length = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23]; + sample->global_volume = 64; + sample->default_volume = data[29]; + loop_start = ((data[24] << 8) | data[25]) << 1; + loop_length = ((data[26] << 8) | data[27]) << 1; + sample->sus_loop_start = loop_start; + sample->sus_loop_end = loop_start + loop_length; + + if (sample->length <= 0) { + sample->flags = 0; + return; + } + + sample->flags = IT_SAMPLE_EXISTS; + + sample->default_pan = 0; + sample->C5_speed = (int)( AMIGA_CLOCK / 214.0 ); //(long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32)); + sample->finetune = 0; + + if (sample->sus_loop_end > sample->length) + sample->sus_loop_end = sample->length; + + if (loop_length > 2) + sample->flags |= IT_SAMPLE_SUS_LOOP; + + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = 0; // do we have to set _all_ these? + sample->max_resampling_quality = -1; +} + + + +static int it_okt_read_sample_data(IT_SAMPLE *sample, const char * data, int length) +{ + if (length && sample->length) { + if (length < sample->length) { + sample->length = length; + if (length < sample->sus_loop_end) sample->sus_loop_end = length; + } + + sample->data = malloc(length); + + if (!sample->data) + return -1; + + memcpy(sample->data, data, length); + } + + return 0; +} + + + +typedef struct IFF_CHUNK IFF_CHUNK; +typedef struct IFF_CHUNKED IFF_CHUNKED; + +struct IFF_CHUNK +{ + unsigned type; + unsigned char * data; + unsigned size; +}; + +struct IFF_CHUNKED +{ + unsigned chunk_count; + IFF_CHUNK * chunks; +}; + + + +static IFF_CHUNKED *dumbfile_read_okt(DUMBFILE *f) +{ + IFF_CHUNKED *mod = (IFF_CHUNKED *) malloc(sizeof(*mod)); + if (!mod) return NULL; + + mod->chunk_count = 0; + mod->chunks = 0; + + for (;;) + { + long bytes_read; + IFF_CHUNK * chunk = ( IFF_CHUNK * ) realloc( mod->chunks, ( mod->chunk_count + 1 ) * sizeof( IFF_CHUNK ) ); + if ( !chunk ) + { + if ( mod->chunks ) free( mod->chunks ); + free( mod ); + return NULL; + } + mod->chunks = chunk; + chunk += mod->chunk_count; + + bytes_read = dumbfile_mgetl( f ); + if ( bytes_read < 0 ) break; + + chunk->type = bytes_read; + chunk->size = dumbfile_mgetl( f ); + + if ( dumbfile_error( f ) ) break; + + chunk->data = (unsigned char *) malloc( chunk->size ); + if ( !chunk->data ) + { + free( mod->chunks ); + free( mod ); + return NULL; + } + + bytes_read = dumbfile_getnc( ( char * ) chunk->data, chunk->size, f ); + if ( bytes_read < chunk->size ) + { + if ( bytes_read <= 0 ) { + free( chunk->data ); + break; + } else { + chunk->size = bytes_read; + mod->chunk_count++; + break; + } + } + + mod->chunk_count++; + } + + if ( !mod->chunk_count ) { + if ( mod->chunks ) free(mod->chunks); + free(mod); + mod = NULL; + } + + return mod; +} + +void free_okt(IFF_CHUNKED * mod) +{ + unsigned i; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].data) free(mod->chunks[i].data); + } + free(mod->chunks); + } + free(mod); + } +} + +const IFF_CHUNK * get_chunk_by_type(IFF_CHUNKED * mod, unsigned type, unsigned offset) +{ + unsigned i; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].type == type) + { + if (!offset) return &mod->chunks[i]; + else offset--; + } + } + } + } + return NULL; +} + +unsigned get_chunk_count(IFF_CHUNKED *mod, unsigned type) +{ + unsigned i, count = 0; + if (mod) + { + if (mod->chunks) + { + for (i = 0; i < mod->chunk_count; i++) + { + if (mod->chunks[i].type == type) count++; + } + } + } + return count; +} + + +static DUMB_IT_SIGDATA *it_okt_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + int n_channels; + int i, j, k, l; + IFF_CHUNKED *mod; + const IFF_CHUNK *chunk; + + char signature[8]; + + if (dumbfile_getnc(signature, 8, f) < 8 || + memcmp(signature, "OKTASONG", 8)) { + return NULL; + } + + mod = dumbfile_read_okt(f); + if (!mod) + return NULL; + + sigdata = (DUMB_IT_SIGDATA *) malloc(sizeof(*sigdata)); + if (!sigdata) { + free_okt(mod); + return NULL; + } + + sigdata->name[0] = 0; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','P','E','E'), 0); + if (!chunk || chunk->size < 2) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->speed = (chunk->data[0] << 8) | chunk->data[1]; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0); + if (!chunk || chunk->size < 32) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_samples = chunk->size / 32; + + chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0); + if (!chunk || chunk->size < 8) { + free(sigdata); + free_okt(mod); + return NULL; + } + + n_channels = 0; + + for (i = 0; i < 4; i++) { + j = (chunk->data[i * 2] << 8) | chunk->data[i * 2 + 1]; + if (!j) n_channels++; + else if (j == 1) n_channels += 2; + } + + if (!n_channels) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_pchannels = n_channels; + + sigdata->sample = (IT_SAMPLE *) malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + free(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + for (i = 0; i < sigdata->n_samples; i++) + sigdata->sample[i].data = NULL; + + chunk = get_chunk_by_type(mod, DUMB_ID('S','A','M','P'), 0); + + for (i = 0; i < sigdata->n_samples; i++) { + it_okt_read_sample_header(&sigdata->sample[i], chunk->data + 32 * i); + } + + sigdata->restart_position = 0; + + chunk = get_chunk_by_type(mod, DUMB_ID('P','L','E','N'), 0); + if (!chunk || chunk->size < 2) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_orders = (chunk->data[0] << 8) | chunk->data[1]; + // what if this is > 128? + + if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + chunk = get_chunk_by_type(mod, DUMB_ID('P','A','T','T'), 0); + if (!chunk || chunk->size < (unsigned)sigdata->n_orders) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->order = (unsigned char *) malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + memcpy(sigdata->order, chunk->data, sigdata->n_orders); + + /* Work out how many patterns there are. */ + chunk = get_chunk_by_type(mod, DUMB_ID('S','L','E','N'), 0); + if (!chunk || chunk->size < 2) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->n_patterns = (chunk->data[0] << 8) | chunk->data[1]; + + j = get_chunk_count(mod, DUMB_ID('P','B','O','D')); + if (sigdata->n_patterns > j) sigdata->n_patterns = j; + + if (!sigdata->n_patterns) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + + sigdata->pattern = (IT_PATTERN *) malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + /* Read in the patterns */ + for (i = 0; i < sigdata->n_patterns; i++) { + chunk = get_chunk_by_type(mod, DUMB_ID('P','B','O','D'), i); + if (it_okt_read_pattern(&sigdata->pattern[i], chunk->data, chunk->size, n_channels) != 0) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + } + + /* And finally, the sample data */ + k = get_chunk_count(mod, DUMB_ID('S','B','O','D')); + for (i = 0, j = 0; i < sigdata->n_samples && j < k; i++) { + if (sigdata->sample[i].flags & IT_SAMPLE_EXISTS) { + chunk = get_chunk_by_type(mod, DUMB_ID('S','B','O','D'), j); + if (it_okt_read_sample_data(&sigdata->sample[i], (const char *)chunk->data, chunk->size)) { + _dumb_it_unload_sigdata(sigdata); + free_okt(mod); + return NULL; + } + j++; + } + } + for (; i < sigdata->n_samples; i++) { + sigdata->sample[i].flags = 0; + } + + chunk = get_chunk_by_type(mod, DUMB_ID('C','M','O','D'), 0); + + for (i = 0, j = 0; i < n_channels && j < 4; j++) { + k = (chunk->data[j * 2] << 8) | chunk->data[j * 2 + 1]; + l = (j == 1 || j == 2) ? 48 : 16; + if (k == 0) { + sigdata->channel_pan[i++] = l; + } + else if (k == 1) { + sigdata->channel_pan[i++] = l; + sigdata->channel_pan[i++] = l; + } + } + + free_okt(mod); + + /* Now let's initialise the remaining variables, and we're done! */ + sigdata->flags = IT_WAS_AN_OKT | IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + /* We want 50 ticks per second; 50/6 row advances per second; + * 50*10=500 row advances per minute; 500/4=125 beats per minute. + */ + sigdata->tempo = 125; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + memset(sigdata->channel_pan + n_channels, 32, DUMB_IT_N_CHANNELS - n_channels); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +DUH *dumb_read_okt_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_okt_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[1][2]; + tag[0][0] = "FORMAT"; + tag[0][1] = "Oktalyzer"; + return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readokt2.c b/Frameworks/Dumb/dumb/src/it/readokt2.c new file mode 100644 index 000000000..c3fc5ed57 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readokt2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readokt2.c - Function to read an Oktalyzer / / \ \ + * module from an open file and do | < / \_ + * an initial run-through. | \/ /\ / + * \_ / > / + * | \ / / + * By Chris Moeller. | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_okt(DUMBFILE *f) +{ + DUH *duh = dumb_read_okt_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readoldpsm.c b/Frameworks/Dumb/dumb/src/it/readoldpsm.c new file mode 100644 index 000000000..d4faec147 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readoldpsm.c @@ -0,0 +1,688 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readpsm.c - Code to read an old Protracker / / \ \ + * Studio module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +static int psm_sample_compare(const void *e1, const void *e2) +{ + const unsigned char * pa = e1; + const unsigned char * pb = e2; + int a = pa[37] | (pa[38] << 8) | (pa[39] << 16) | (pa[40] << 24); + int b = pb[37] | (pb[38] << 8) | (pb[39] << 16) | (pb[40] << 24); + return a - b; +} + +static int it_old_psm_read_samples(IT_SAMPLE ** sample, DUMBFILE * f, int * num) +{ + int n, o, count = *num, true_num, snum, offset, flags, finetune, delta; + + unsigned char * buffer; + const unsigned char * sdata; + long sample_bytes; + + buffer = malloc(count * 64); + if (!buffer) goto error; + + if (dumbfile_getnc((char *)buffer, count * 64, f) < count * 64) goto error_fb; + + true_num = 0; + + for (n = 0; n < count; n++) { + snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8); + if ((snum < 1) || (snum > 255)) goto error_fb; + if (true_num < snum) true_num = snum; + } + + if (true_num > count) { + IT_SAMPLE * meh = realloc(*sample, true_num * sizeof(*meh)); + if (!meh) goto error_fb; + for (n = count; n < true_num; n++) { + meh[n].data = NULL; + } + *sample = meh; + *num = true_num; + } + + qsort(buffer, count, 64, &psm_sample_compare); + + for (n = 0; n < true_num; n++) { + (*sample)[n].flags = 0; + } + + for (n = 0; n < count; n++) { + IT_SAMPLE * s; + snum = buffer[(n * 64) + 45] | (buffer[(n * 64) + 46] << 8); + s = &((*sample)[snum - 1]); + memcpy(s->filename, buffer + (n * 64), 13); + s->filename[13] = 0; + memcpy(s->name, buffer + (n * 64) + 13, 24); + s->name[24] = 0; + offset = buffer[(n * 64) + 37] | (buffer[(n * 64) + 38] << 8) | + (buffer[(n * 64) + 39] << 16) | (buffer[(n * 64) + 40] << 24); + flags = buffer[(n * 64) + 47]; + s->length = buffer[(n * 64) + 48] | (buffer[(n * 64) + 49] << 8) | + (buffer[(n * 64) + 50] << 16) | (buffer[(n * 64) + 51] << 24); + s->loop_start = buffer[(n * 64) + 52] | (buffer[(n * 64) + 53] << 8) | + (buffer[(n * 64) + 54] << 16) | (buffer[(n * 64) + 55] << 24); + s->loop_end = buffer[(n * 64) + 56] | (buffer[(n * 64) + 57] << 8) | + (buffer[(n * 64) + 58] << 16) | (buffer[(n * 64) + 59] << 24); + + if (s->length <= 0) continue; + + finetune = buffer[(n * 64) + 60]; + s->default_volume = buffer[(n * 64) + 61]; + s->C5_speed = buffer[(n * 64) + 62] | (buffer[(n * 64) + 63] << 8); + if (finetune & 15) { + finetune &= 15; + if (finetune >= 8) finetune -= 16; + //s->C5_speed = (long)((double)s->C5_speed * pow(DUMB_PITCH_BASE, finetune*32)); + s->finetune = finetune * 32; + } + else s->finetune = 0; + + s->flags |= IT_SAMPLE_EXISTS; + if (flags & 0x41) { + s->flags &= ~IT_SAMPLE_EXISTS; + continue; + } + if (flags & 0x20) s->flags |= IT_SAMPLE_PINGPONG_LOOP; + if (flags & 4) s->flags |= IT_SAMPLE_16BIT; + + if (flags & 0x80) { + s->flags |= IT_SAMPLE_LOOP; + if ((unsigned int)s->loop_end > (unsigned int)s->length) + s->loop_end = s->length; + else if ((unsigned int)s->loop_start >= (unsigned int)s->loop_end) + s->flags &= ~IT_SAMPLE_LOOP; + else + s->length = s->loop_end; + } + + s->global_volume = 64; + + s->vibrato_speed = 0; + s->vibrato_depth = 0; + s->vibrato_rate = 0; + s->vibrato_waveform = IT_VIBRATO_SINE; + s->max_resampling_quality = -1; + + sample_bytes = s->length * ((flags & 4) ? 2 : 1); + s->data = malloc(sample_bytes); + if (!s->data) goto error_fb; + + if (dumbfile_seek(f, offset, DFS_SEEK_SET) || dumbfile_getnc(s->data, sample_bytes, f) < sample_bytes) goto error_fb; + sdata = ( const unsigned char * ) s->data; + + if (flags & 0x10) { + if (flags & 8) { + if (flags & 4) { + for (o = 0; o < s->length; o++) + ((short *)s->data)[o] = (sdata[o * 2] | (sdata[(o * 2) + 1] << 8)) ^ 0x8000; + } else { + for (o = 0; o < s->length; o++) + ((signed char *)s->data)[o] = sdata[o] ^ 0x80; + } + } else { + if (flags & 4) { + for (o = 0; o < s->length; o++) + ((short *)s->data)[o] = sdata[o * 2] | (sdata[(o * 2) + 1] << 8); + } else { + memcpy(s->data, sdata, s->length); + } + } + } else { + delta = 0; + if (flags & 8) { + /* unsigned delta? mehhh, does anything even use this? */ + if (flags & 4) { + for (o = 0; o < s->length; o++) { + delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8)); + ((short *)s->data)[o] = delta ^ 0x8000; + } + } else { + for (o = 0; o < s->length; o++) { + delta += (signed char)sdata[o]; + ((signed char *)s->data)[o] = delta ^ 0x80; + } + } + } else { + if (flags & 4) { + for (o = 0; o < s->length; o++) { + delta += (short)(sdata[o * 2] | (sdata[(o * 2) + 1] << 8)); + ((short *)s->data)[o] = delta; + } + } else { + for (o = 0; o < s->length; o++) { + delta += (signed char)sdata[o]; + ((signed char *)s->data)[o] = delta; + } + } + } + } + } + + free(buffer); + + return 0; + +error_fb: + free(buffer); +error: + return -1; +} + +static int it_old_psm_read_patterns(IT_PATTERN * pattern, DUMBFILE * f, int num, int size, int pchans) +{ + int n, offset, psize, rows, chans, row, flags, channel; + + unsigned char * buffer, * ptr, * end; + + IT_ENTRY * entry; + + buffer = malloc(size); + if (!buffer) goto error; + + if (dumbfile_getnc((char *)buffer, size, f) < size) goto error_fb; + + offset = 0; + + for (n = 0; n < num; n++) { + IT_PATTERN * p = &pattern[n]; + + if (offset >= size) goto error_fb; + + ptr = buffer + offset; + psize = ptr[0] | (ptr[1] << 8); + rows = ptr[2]; + chans = ptr[3]; + + if (!rows || !chans) { + p->n_rows = 1; + p->n_entries = 0; + continue; + } + + psize = (psize + 15) & ~15; + + end = ptr + psize; + ptr += 4; + + p->n_rows = rows; + p->n_entries = rows; + row = 0; + + while ((row < rows) && (ptr < end)) { + flags = *ptr++; + if (!flags) { + row++; + continue; + } + if (flags & 0xE0) { + p->n_entries++; + if (flags & 0x80) ptr += 2; + if (flags & 0x40) ptr++; + if (flags & 0x20) { + ptr++; + if (*ptr == 40) ptr += 3; + else ptr++; + } + } + } + + entry = malloc(p->n_entries * sizeof(*p->entry)); + if (!entry) goto error_fb; + + p->entry = entry; + + ptr = buffer + offset + 4; + row = 0; + + while ((row < rows) && (ptr < end)) { + flags = *ptr++; + if (!flags) { + IT_SET_END_ROW(entry); + entry++; + row++; + continue; + } + if (flags & 0xE0) { + entry->mask = 0; + entry->channel = channel = flags & 0x1F; + if (channel >= chans) + { + //channel = 0; + //goto error_fb; + } + if (flags & 0x80) { + if ((*ptr < 60) && (channel < pchans)) { + entry->mask |= IT_ENTRY_NOTE; + entry->note = *ptr + 35; + } + ptr++; + if (*ptr) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = *ptr; + } + ptr++; + } + if (flags & 0x40) { + if (*ptr <= 64) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = *ptr; + } + ptr++; + } + if (flags & 0x20) { + entry->mask |= IT_ENTRY_EFFECT; + + switch (*ptr) { + case 1: + entry->effect = IT_XM_FINE_VOLSLIDE_UP; + entry->effectvalue = ptr[1]; + break; + + case 2: + entry->effect = IT_VOLUME_SLIDE; + entry->effectvalue = (ptr[1] << 4) & 0xF0; + break; + + case 3: + entry->effect = IT_XM_FINE_VOLSLIDE_DOWN; + entry->effectvalue = ptr[1]; + break; + + case 4: + entry->effect = IT_VOLUME_SLIDE; + entry->effectvalue = ptr[1] & 0xF; + break; + + case 10: + entry->effect = IT_PORTAMENTO_UP; + entry->effectvalue = EFFECT_VALUE(0xF, ptr[1]); + break; + + case 11: + entry->effect = IT_PORTAMENTO_UP; + entry->effectvalue = ptr[1]; + break; + + case 12: + entry->effect = IT_PORTAMENTO_DOWN; + entry->effectvalue = EFFECT_VALUE(ptr[1], 0xF); + break; + + case 13: + entry->effect = IT_PORTAMENTO_DOWN; + entry->effectvalue = ptr[1]; + break; + + case 14: + entry->effect = IT_TONE_PORTAMENTO; + entry->effectvalue = ptr[1]; + break; + + case 15: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_GLISSANDO_CONTROL, ptr[1] & 15); + break; + + case 16: + entry->effect = IT_VOLSLIDE_TONEPORTA; + entry->effectvalue = ptr[1] << 4; + break; + + case 17: + entry->effect = IT_VOLSLIDE_TONEPORTA; + entry->effectvalue = ptr[1] & 0xF; + break; + + case 20: + entry->effect = IT_VIBRATO; + entry->effectvalue = ptr[1]; + break; + + case 21: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_VIBRATO_WAVEFORM, ptr[1] & 11); + break; + + case 22: + entry->effect = IT_VOLSLIDE_VIBRATO; + entry->effectvalue = ptr[1] << 4; + break; + + case 23: + entry->effect = IT_VOLSLIDE_VIBRATO; + entry->effectvalue = ptr[1] & 0xF; + break; + + case 30: + entry->effect = IT_TREMOLO; + entry->effectvalue = ptr[1]; + break; + + case 31: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_TREMOLO_WAVEFORM, ptr[1] & 11); + break; + + case 40: + entry->effect = IT_SET_SAMPLE_OFFSET; + entry->effectvalue = ptr[2]; + ptr += 2; + break; + + case 41: + entry->effect = IT_XM_RETRIGGER_NOTE; + entry->effectvalue = ptr[1]; + break; + + case 42: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_DELAYED_NOTE_CUT, ptr[1] & 0xF); + break; + + case 43: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_NOTE_DELAY, ptr[1] & 0xF); + break; + + case 50: + entry->effect = IT_JUMP_TO_ORDER; + entry->effectvalue = ptr[1]; + break; + + case 51: + entry->effect = IT_BREAK_TO_ROW; + entry->effectvalue = ptr[1]; + break; + + case 52: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_LOOP, ptr[1] & 0xF); + break; + + case 53: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_PATTERN_DELAY, ptr[1] & 0xF); + break; + + case 60: + entry->effect = IT_SET_SPEED; + entry->effectvalue = ptr[1]; + break; + + case 61: + entry->effect = IT_SET_SONG_TEMPO; + entry->effectvalue = ptr[1]; + break; + + case 70: + entry->effect = IT_ARPEGGIO; + entry->effectvalue = ptr[1]; + break; + + case 71: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_FINETUNE, ptr[1] & 0xF); + break; + + case 72: + /* "balance" ... panning? */ + entry->effect = IT_SET_PANNING; + entry->effectvalue = ((ptr[1] - ((ptr[1] & 8) >> 3)) << 5) / 7; + break; + + default: + entry->mask &= ~IT_ENTRY_EFFECT; + } + + ptr += 2; + } + if (entry->mask) entry++; + } + } + + p->n_entries = entry - p->entry; + offset += psize; + } + + free(buffer); + + return 0; + +error_fb: + free(buffer); +error: + return -1; +} + +#define PSM_COMPONENT_ORDERS 0 +#define PSM_COMPONENT_PANPOS 1 +#define PSM_COMPONENT_PATTERNS 2 +#define PSM_COMPONENT_SAMPLE_HEADERS 3 +#define PSM_COMPONENT_COMMENTS 4 + +typedef struct PSM_COMPONENT +{ + unsigned char type; + long offset; +} +PSM_COMPONENT; + +static int psm_component_compare(const void *e1, const void *e2) +{ + return ((const PSM_COMPONENT *)e1)->offset - + ((const PSM_COMPONENT *)e2)->offset; +} + +static DUMB_IT_SIGDATA *it_old_psm_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + + PSM_COMPONENT *component; + int n_components = 0; + + int n, flags, version, pver, n_orders, n_channels, total_pattern_size; + + if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',254)) goto error; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) goto error; + + if (dumbfile_getnc((char *)sigdata->name, 60, f) < 60 || + sigdata->name[59] != 0x1A) goto error_sd; + sigdata->name[59] = 0; + + flags = dumbfile_getc(f); + version = dumbfile_getc(f); + pver = dumbfile_getc(f); + sigdata->speed = dumbfile_getc(f); + sigdata->tempo = dumbfile_getc(f); + sigdata->mixing_volume = dumbfile_getc(f); + sigdata->n_orders = dumbfile_igetw(f); + n_orders = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_pchannels = dumbfile_igetw(f); + n_channels = dumbfile_igetw(f); + + if (dumbfile_error(f) || + (flags & 1) || + (version != 1 && version != 0x10) || + (pver) || + (sigdata->n_orders <= 0) || + (sigdata->n_orders > 255) || + (n_orders > 255) || + (n_orders < sigdata->n_orders) || + (sigdata->n_patterns > 255) || + (sigdata->n_samples > 255) || + (sigdata->n_pchannels > DUMB_IT_N_CHANNELS) || + (sigdata->n_pchannels > n_channels) || + (n_channels > DUMB_IT_N_CHANNELS)) + goto error_sd; + + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + + sigdata->global_volume = 128; + sigdata->pan_separation = 128; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + + sigdata->restart_position = 0; + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) goto error_usd; + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) goto error_usd; + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) goto error_usd; + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + component = malloc(5 * sizeof(*component)); + if (!component) goto error_usd; + + for (n = 0; n < 5; n++) { + component[n_components].offset = dumbfile_igetl(f); + if (component[n_components].offset) { + component[n_components].type = n; + n_components++; + } + } + + if (!n_components) goto error_fc; + + total_pattern_size = dumbfile_igetl(f); + if (!total_pattern_size) goto error_fc; + + qsort(component, n_components, sizeof(PSM_COMPONENT), &psm_component_compare); + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + for (n = 0; n < n_components; n++) + { + int o; + + if ( dumbfile_seek(f, component[n].offset, DFS_SEEK_SET) ) goto error_fc; + + switch (component[n].type) { + + case PSM_COMPONENT_ORDERS: + if (dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f) < sigdata->n_orders) goto error_fc; + if (n_orders > sigdata->n_orders) + if (dumbfile_skip(f, n_orders - sigdata->n_orders)) + goto error_fc; + if (dumbfile_igetw(f)) goto error_fc; + break; + + case PSM_COMPONENT_PANPOS: + if (dumbfile_getnc((char *)sigdata->channel_pan, sigdata->n_pchannels, f) < sigdata->n_pchannels) goto error_fc; + for (o = 0; o < sigdata->n_pchannels; o++) { + sigdata->channel_pan[o] -= (sigdata->channel_pan[o] & 8) >> 3; + sigdata->channel_pan[o] = ((int)sigdata->channel_pan[o] << 5) / 7; + } + break; + + case PSM_COMPONENT_PATTERNS: + if (it_old_psm_read_patterns(sigdata->pattern, f, sigdata->n_patterns, total_pattern_size, sigdata->n_pchannels)) goto error_fc; + break; + + case PSM_COMPONENT_SAMPLE_HEADERS: + if (it_old_psm_read_samples(&sigdata->sample, f, &sigdata->n_samples)) goto error_fc; + break; + + case PSM_COMPONENT_COMMENTS: + if (dumbfile_mgetl(f) == DUMB_ID('T','E','X','T')) { + o = dumbfile_igetw(f); + if (o > 0) { + sigdata->song_message = malloc(o + 1); + if (dumbfile_getnc((char *)sigdata->song_message, o, f) < o) goto error_fc; + sigdata->song_message[o] = 0; + } + } + break; + } + } + + _dumb_it_fix_invalid_orders(sigdata); + + free(component); + + return sigdata; + +error_fc: + free(component); +error_usd: + _dumb_it_unload_sigdata(sigdata); + return NULL; +error_sd: + free(sigdata); +error: + return NULL; +} + +DUH *dumb_read_old_psm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_old_psm_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "PSM (old)"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readpsm.c b/Frameworks/Dumb/dumb/src/it/readpsm.c new file mode 100644 index 000000000..bdece48bb --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readpsm.c @@ -0,0 +1,1286 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readpsm.c - Code to read a Protracker Studio / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +#ifndef min +#define min(a, b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifdef _MSC_VER +#define snprintf sprintf_s +#endif + +#define PSMV_OLD 940730 +#define PSMV_NEW 940902 + +typedef struct _PSMCHUNK +{ + int id; + int len; + unsigned char * data; +} PSMCHUNK; + +typedef struct _PSMEVENT +{ + int type; + unsigned char data[8]; +} PSMEVENT; + +#define PSM_EVENT_END 0 +#define PSM_EVENT_PLAY_PATTERN 1 +#define PSM_EVENT_JUMP_TO_LINE 4 +#define PSM_EVENT_SET_SPEED 7 +#define PSM_EVENT_SET_BPM 8 +#define PSM_EVENT_SAMPLE_MAP_TABLE 12 +#define PSM_EVENT_CHANGE_PAN 13 +#define PSM_EVENT_CHANGE_VOL 14 + +static int it_psm_process_sample(IT_SAMPLE * sample, const unsigned char * data, int len, int id, int version) { + int flags; + int insno; + int length; + int loopstart; + int loopend; + int panpos; + int defvol; + int samplerate; + + if (len < 0x60) return -1; + + flags = data[0]; + + if (version == PSMV_OLD) { + memcpy(sample->name, data + 0x0D, 34); + sample->name[34] = 0; + + insno = data[0x34] | (data[0x35] << 8); + length = data[0x36] | (data[0x37] << 8) | (data[0x38] << 16) | (data[0x39] << 24); + loopstart = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24); + loopend = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24); + panpos = data[0x43]; + defvol = data[0x44]; + samplerate = data[0x49] | (data[0x4A] << 8) | (data[0x4B] << 16) | (data[0x4C] << 24); + } else /*if (version == PSMV_NEW)*/ { + memcpy(sample->name, data + 0x11, 34); + sample->name[34] = 0; + + insno = data[0x38] | (data[0x39] << 8); + length = data[0x3A] | (data[0x3B] << 8) | (data[0x3C] << 16) | (data[0x3D] << 24); + loopstart = data[0x3E] | (data[0x3F] << 8) | (data[0x40] << 16) | (data[0x41] << 24); + loopend = data[0x42] | (data[0x43] << 8) | (data[0x44] << 16) | (data[0x45] << 24); + panpos = data[0x48]; + defvol = data[0x49]; + samplerate = data[0x4E] | (data[0x4F] << 8) | (data[0x50] << 16) | (data[0x51] << 24); + } + + if (insno != id) return -1; + + if (!length) { + sample->flags &= ~IT_SAMPLE_EXISTS; + return 0; + } + + if ((length > len - 0x60) || ((flags & 0x7F) != 0)) return -1; + + sample->flags = IT_SAMPLE_EXISTS; + sample->length = length; + sample->loop_start = loopstart; + sample->loop_end = loopend; + sample->C5_speed = samplerate; + sample->default_volume = defvol >> 1; + sample->default_pan = 0; + sample->filename[0] = 0; + sample->global_volume = 64; + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + if (flags & 0x80) { + if (((unsigned int)sample->loop_end <= (unsigned int)sample->length) && + ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end)) { + sample->length = sample->loop_end; + sample->flags |= IT_SAMPLE_LOOP; + } + } + + sample->data = malloc(sample->length); + if (!sample->data) + return -1; + + flags = 0; + data += 0x60; + + for (insno = 0; insno < sample->length; insno++) { + flags += (signed char)(*data++); + ((signed char *)sample->data)[insno] = flags; + } + + return 0; +} + +static int it_psm_process_pattern(IT_PATTERN * pattern, const unsigned char * data, int len, int speed, int bpm, const unsigned char * pan, const int * vol, int version) { + int length, nrows, row, rowlen, pos; + unsigned flags, chan; + IT_ENTRY * entry; + + length = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + if (len > length) len = length; + + if (version == PSMV_OLD) { + if (len < 10) return -1; + data += 8; + len -= 8; + } else /*if (version == PSMV_NEW)*/ { + if (len < 14) return -1; + data += 12; + len -= 12; + } + + nrows = data[0] | (data[1] << 8); + + if (!nrows) return 0; + + pattern->n_rows = nrows; + + data += 2; + len -= 2; + + pattern->n_entries = 0; + + row = 0; + pos = 2; + rowlen = data[0] | (data[1] << 8); + + while ((row < nrows) && (pos < len)) { + if (pos >= rowlen) { + row++; + rowlen += data[pos] | (data[pos+1] << 8); + pos += 2; + continue; + } + + flags = data[pos++]; + chan = data[pos++]; + + if (chan > 63) return -1; + + if (flags & 0xF0) { + pattern->n_entries++; + if (flags & 0x80) pos++; + if (flags & 0x40) pos++; + if (flags & 0x20) pos++; + if (flags & 0x10) { + switch (data[pos]) { + case 0x29: + pos++; + case 0x33: + pos++; + default: + pos += 2; + } + } + } + } + + if (!pattern->n_entries) return 0; + + pattern->n_entries += nrows; + if (speed) pattern->n_entries++; + if (bpm >= 0x20) pattern->n_entries++; + + for (pos = 0; pos < 32; pos++) { + if (!(pan[pos*2+1] & 0xF9)) pattern->n_entries++; + if (vol[pos] != -1) pattern->n_entries++; + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) return -1; + + entry = pattern->entry; + + if (speed) { + entry->channel = 0; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_SPEED; + entry->effectvalue = speed; + entry++; + } + + if (bpm >= 0x20) { + entry->channel = 0; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_SONG_TEMPO; + entry->effectvalue = bpm; + entry++; + } + + for (pos = 0; pos < 32; pos++) { + if (!(pan[pos*2+1] & 0xF9)) { + entry->channel = pos; + entry->mask = IT_ENTRY_EFFECT; + switch (pan[pos*2+1]) { + case 0: + entry->effect = IT_SET_PANNING; + entry->effectvalue = pan[pos*2] ^ 128; + break; + case 2: + entry->effect = IT_S; + entry->effectvalue = EFFECT_VALUE(IT_S_SET_SURROUND_SOUND,1); + break; + case 4: + entry->effect = IT_SET_PANNING; + entry->effectvalue = 128; + break; + } + entry++; + } + if (vol[pos] != -1) { + entry->channel = pos; + entry->mask = IT_ENTRY_EFFECT; + entry->effect = IT_SET_CHANNEL_VOLUME; + entry->effectvalue = (vol[pos] + 2) >> 2; + entry++; + } + } + + row = 0; + pos = 2; + rowlen = data[0] | (data[1] << 8); + + while ((row < nrows) && (pos < len)) { + if (pos >= rowlen) { + IT_SET_END_ROW(entry); + entry++; + row++; + rowlen += data[pos] | (data[pos+1] << 8); + pos += 2; + continue; + } + + flags = data[pos++]; + entry->channel = data[pos++]; + entry->mask = 0; + + if (flags & 0xF0) { + if (flags & 0x80) { + entry->mask |= IT_ENTRY_NOTE; + if (version == PSMV_OLD) { + if ((data[pos] < 0x80)) entry->note = (data[pos]>>4)*12+(data[pos]&0x0f)+12; + else entry->mask &= ~IT_ENTRY_NOTE; + } else /*if (version == PSMV_NEW)*/ { + if ((data[pos]) && (data[pos] < 84)) entry->note = data[pos] + 35; + else entry->mask &= ~IT_ENTRY_NOTE; + } + pos++; + } + + if (flags & 0x40) { + entry->mask |= IT_ENTRY_INSTRUMENT; + entry->instrument = data[pos++] + 1; + } + + if (flags & 0x20) { + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = (data[pos++] + 1) >> 1; + } + + if (flags & 0x10) { + entry->mask |= IT_ENTRY_EFFECT; + length = data[pos+1]; + switch (data[pos]) { + case 1: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = ((length&0x1e)<<3) | 0xF; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = (length<<4) | 0xF; + break; + + case 2: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = (length << 3) & 0xF0; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = (length << 4) & 0xF0; + break; + + case 3: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = (length >> 1) | 0xF0; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = length | 0xF0; + break; + + case 4: + entry->effect = IT_VOLUME_SLIDE; + if (version == PSMV_OLD) entry->effectvalue = (length >> 1) & 0xF; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = length & 0xF; + break; + + case 12: + entry->effect = IT_PORTAMENTO_UP; + if (version == PSMV_OLD) { + if (length < 4) entry->effectvalue = length | 0xF0; + else entry->effectvalue = length >> 2; + } else /*if (version == PSMV_NEW)*/ { + entry->effectvalue = length; + } + break; + + case 14: + entry->effect = IT_PORTAMENTO_DOWN; + if (version == PSMV_OLD) { + if (length < 4) entry->effectvalue = length | 0xF0; + else entry->effectvalue = length >> 2; + } else /*if (version == PSMV_NEW)*/ { + entry->effectvalue = length; + } + break; + + case 15: + entry->effect = IT_TONE_PORTAMENTO; + if (version == PSMV_OLD) entry->effectvalue = length >> 2; + else /*if (version == PSMV_NEW)*/ entry->effectvalue = length; + break; + + case 0x15: + entry->effect = IT_VIBRATO; + entry->effectvalue = length; + break; + + case 0x18: + entry->effect = IT_VOLSLIDE_VIBRATO; + entry->effectvalue = length; + break; + + case 0x29: + entry->effect = IT_SET_SAMPLE_OFFSET; + entry->effectvalue = data[pos+2]; + pos += 2; + break; + + case 0x2A: + entry->effect = IT_RETRIGGER_NOTE; + entry->effectvalue = length; + break; + + case 0x33: +#if 0 + entry->effect = IT_POSITION_JUMP; + entry->effectvalue = data[pos+2]; +#else + entry->mask &= ~IT_ENTRY_EFFECT; +#endif + pos++; + break; + + case 0x34: + entry->effect = IT_BREAK_TO_ROW; + entry->effectvalue = length; + break; + + case 0x3D: + entry->effect = IT_SET_SPEED; + entry->effectvalue = length; + break; + + case 0x3E: + if (length >= 0x20) { + entry->effect = IT_SET_SONG_TEMPO; + entry->effectvalue = length; + } else { + entry->mask &= ~IT_ENTRY_EFFECT; + } + break; + + case 0x47: + entry->effect = IT_ARPEGGIO; + entry->effectvalue = length; + break; + + default: + return -1; + } + pos += 2; + } + if (entry->mask) entry++; + } + } + + while (row < nrows) { + IT_SET_END_ROW(entry); + entry++; + row++; + } + + pattern->n_entries = entry - pattern->entry; + if (!pattern->n_entries) return -1; + + return 0; +} + + +static void free_chunks(PSMCHUNK * chunk, int count) { + int n; + + for (n = 0; n < count; n++) { + if (chunk[n].data) + free(chunk[n].data); + } + + free(chunk); +} + +static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata); + +static int pattcmp( const unsigned char *, const unsigned char *, size_t ); + +static DUMB_IT_SIGDATA *it_psm_load_sigdata(DUMBFILE *f, int * ver, int subsong) +{ + DUMB_IT_SIGDATA *sigdata; + + PSMCHUNK *chunk; + int n_chunks = 0; + + PSMCHUNK *songchunk; + int n_song_chunks = 0; + + PSMEVENT *event = 0; + int n_events = 0; + + unsigned char * ptr; + + int n, length, o; + + int found; + + int n_patterns = 0; + + int first_pattern_line = -1; + int first_pattern; + + int speed, bpm; + unsigned char pan[64]; + int vol[32]; + + if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) goto error; + + length = dumbfile_igetl(f); + + if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) goto error; + + chunk = calloc(768, sizeof(*chunk)); + + while (length >= 8) { + chunk[n_chunks].id = dumbfile_mgetl(f); + n = dumbfile_igetl(f); + length -= 8; + if (n < 0 || n > length) + goto error_fc; + chunk[n_chunks].len = n; + if (n) { + ptr = malloc(n); + if (!ptr) goto error_fc; + if (dumbfile_getnc((char *)ptr, n, f) < n) + { + free(ptr); + goto error_fc; + } + chunk[n_chunks].data = ptr; + } + n_chunks++; + length -= n; + } + + if (!n_chunks) goto error_fc; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) goto error_fc; + + sigdata->n_patterns = 0; + sigdata->n_samples = 0; + sigdata->name[0] = 0; + + found = 0; + + for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + switch(c->id) { + case DUMB_ID('S','D','F','T'): + /* song data format? */ + if ((found & 1) || (c->len != 8) || memcmp(c->data, "MAINSONG", 8)) goto error_sd; + found |= 1; + break; + + case DUMB_ID('S','O','N','G'): + if (/*(found & 2) ||*/ (c->len < 11) /*|| memcmp(c->data, "MAINSONG", 8)*/) goto error_sd; + found |= 2; + break; + + case DUMB_ID('D','S','M','P'): + sigdata->n_samples++; + break; + + case DUMB_ID('T','I','T','L'): + length = min(sizeof(sigdata->name) - 1, (unsigned)c->len); + memcpy(sigdata->name, c->data, length); + sigdata->name[length] = 0; + } + } + + if (found != 3 || !sigdata->n_samples) goto error_sd; + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + sigdata->n_orders = 0; + + for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + if (c->id == DUMB_ID('S','O','N','G')) { + if (subsong == 0) break; + subsong--; + } + } + + if (n == n_chunks) return NULL; + subsong = n; + + /*for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + if (c->id == DUMB_ID('S','O','N','G')) {*/ + { + PSMCHUNK * c = &chunk[subsong]; + { + ptr = c->data; + if (ptr[10] > 32) goto error_usd; + sigdata->n_pchannels = ptr[10]; + length = c->len - 11; + ptr += 11; + songchunk = 0; + if (length >= 8) { + songchunk = malloc(128 * sizeof(*songchunk)); + if (!songchunk) goto error_usd; + while (length >= 8) { + songchunk[n_song_chunks].id = DUMB_ID(ptr[0], ptr[1], ptr[2], ptr[3]); + n = ptr[4] | (ptr[5] << 8) | (ptr[6] << 16) | (ptr[7] << 24); + length -= 8; + if (n > length) goto error_sc; + songchunk[n_song_chunks].len = n; + songchunk[n_song_chunks].data = ptr + 8; + n_song_chunks++; + length -= n; + ptr += 8 + n; + } + } + /*break;*/ + } + } + + if (!n_song_chunks) goto error_sc; + + found = 0; + + for (n = 0; n < n_song_chunks; n++) { + PSMCHUNK * c = &songchunk[n]; + + if (c->id == DUMB_ID('D','A','T','E')) { + /* date of the library build / format spec */ + if (c->len == 6) { + length = c->len; + ptr = c->data; + while (length > 0) { + if (*ptr >= '0' && *ptr <= '9') { + found = (found * 10) + (*ptr - '0'); + } else { + found = 0; + break; + } + ptr++; + length--; + } + } + break; + } + } + + /* + if (found != 940506 && + found != 940509 && + found != 940510 && + found != 940530 && + found != 940629 && + found != PSMV_OLD && + found != 941011 && + found != PSMV_NEW && + found != 940906 && + found != 940903 && + found != 940914 && + found != 941213 && + found != 800211) WTF? + goto error_sc; + */ + + *ver = found; + + if (found == 800211 || + found == PSMV_NEW || + found == 940903 || + found == 940906 || + found == 940914 || + found == 941213) found = PSMV_NEW; + else found = PSMV_OLD; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + + for (n = 0; n < DUMB_IT_N_CHANNELS; n += 4) { + sigdata->channel_pan[n ] = 16; + sigdata->channel_pan[n+1] = 48; + sigdata->channel_pan[n+2] = 48; + sigdata->channel_pan[n+3] = 16; + } + + for (n = 0; n < n_song_chunks; n++) { + PSMCHUNK * c = &songchunk[n]; + + switch (c->id) { + case DUMB_ID('O','P','L','H'): + if (c->len < 2) goto error_sc; + ptr = c->data; + o = ptr[0] | (ptr[1] << 8); + if (!o) goto error_sc; + event = malloc(o * sizeof(*event)); + if (!event) goto error_sc; + length = c->len - 2; + ptr += 2; + while ((length > 0) && (n_events < o)) { + event[n_events].type = *ptr; + switch (*ptr) { + case PSM_EVENT_END: + ptr++; + length--; + break; + + case PSM_EVENT_PLAY_PATTERN: + if (found == PSMV_OLD) { + if (length < 5) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 4); + ptr += 5; + length -= 5; + } else /*if (found == PSMV_NEW)*/ { + if (length < 9) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 8); + ptr += 9; + length -= 9; + } + break; + + case PSM_EVENT_SET_SPEED: + case PSM_EVENT_SET_BPM: + if (length < 2) goto error_ev; + event[n_events].data[0] = ptr[1]; + ptr += 2; + length -= 2; + break; + + case PSM_EVENT_JUMP_TO_LINE: + case PSM_EVENT_CHANGE_VOL: + if (length < 3) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 2); + ptr += 3; + length -= 3; + break; + + case PSM_EVENT_SAMPLE_MAP_TABLE: + if (length < 7) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 6); + ptr += 7; + length -= 7; + break; + + case PSM_EVENT_CHANGE_PAN: + if (length < 4) goto error_ev; + memcpy(event[n_events].data, ptr + 1, 3); + ptr += 4; + length -= 4; + break; + + default: + goto error_ev; + } + n_events++; + } + break; + + case DUMB_ID('P','P','A','N'): + length = c->len; + if (length & 1) goto error_ev; + ptr = c->data; + o = 0; + while (length > 0) { + switch (ptr[0]) { + case 0: + sigdata->channel_pan[o] = ((((int)(signed char)ptr[1]) * 32) / 127) + 32; + break; + case 2: + sigdata->channel_pan[o] = IT_SURROUND; + break; + case 4: + sigdata->channel_pan[o] = 32; + break; + } + ptr += 2; + length -= 2; + if (++o >= DUMB_IT_N_CHANNELS) break; + } + break; + + /* + case DUMB_ID('P','A','T','T'): + case DUMB_ID('D','S','A','M'): + */ + } + } + + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; + + sigdata->global_volume = 128; + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + speed = 0; + bpm = 0; + memset(pan, 255, sizeof(pan)); + memset(vol, 255, sizeof(vol)); + + sigdata->n_patterns = n_events; + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) goto error_ev; + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + + for (n = 0; n < n_events; n++) { + PSMEVENT * e = &event[n]; + switch (e->type) { + case PSM_EVENT_END: + n = n_events; + break; + + case PSM_EVENT_PLAY_PATTERN: + for (o = 0; o < n_chunks; o++) { + PSMCHUNK * c = &chunk[o]; + if (c->id == DUMB_ID('P','B','O','D')) { + ptr = c->data; + length = c->len; + if (found == PSMV_OLD) { + if (length < 8) goto error_ev; + if (!pattcmp(ptr + 4, e->data, 4)) { + if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev; + if (first_pattern_line < 0) { + first_pattern_line = n; + first_pattern = o; + } + e->data[0] = n_patterns; + e->data[1] = n_patterns >> 8; + n_patterns++; + break; + } + } else /*if (found == PSMV_NEW)*/ { + if (length < 12) goto error_ev; + if (!pattcmp(ptr + 4, e->data, 8)) { + if (it_psm_process_pattern(&sigdata->pattern[n_patterns], ptr, length, speed, bpm, pan, vol, found)) goto error_ev; + if (first_pattern_line < 0) { + first_pattern_line = n; + first_pattern = o; + } + e->data[0] = n_patterns; + e->data[1] = n_patterns >> 8; + n_patterns++; + break; + } + } + } + } + if (o == n_chunks) goto error_ev; + + speed = 0; + bpm = 0; + memset(pan, 255, sizeof(pan)); + memset(vol, 255, sizeof(vol)); + + e->type = PSM_EVENT_END; + break; + + case PSM_EVENT_JUMP_TO_LINE: + o = e->data[0] | (e->data[1] << 8); + if (o >= n_events) goto error_ev; + if (o == 0) { + /* whew! easy case! */ + sigdata->restart_position = 0; + n = n_events; + } else if (o == n) { + /* freeze */ + n = n_events; + } else if (o > n) { + /* jump ahead, setting played event numbers to zero will prevent endless looping */ + n = o - 1; + } else if (o >= first_pattern_line) { + /* another semi-easy case */ + sigdata->restart_position = event[o].data[0] | (event[o].data[1] << 8); + n = n_events; + } else { + /* crud, try to simulate rerunning all of the commands from the indicated + * line up to the first pattern, then dupe the first pattern again. + */ + /* + PSMCHUNK * c = &chunk[first_pattern]; + + for (; o < first_pattern_line; o++) { + PSMEVENT * ev = &event[o]; + switch (ev->type) { + case PSM_EVENT_SET_SPEED: + speed = ev->data[0]; + break; + case PSM_EVENT_SET_BPM: + bpm = ev->data[0]; + break; + case PSM_EVENT_CHANGE_PAN: + if (ev->data[0] > 31) goto error_ev; + pan[ev->data[0] * 2] = ev->data[1]; + pan[ev->data[0] * 2 + 1] = ev->data[2]; + break; + case PSM_EVENT_CHANGE_VOL: + if (ev->data[0] > 31) goto error_ev; + vol[ev->data[0]] = ev->data[1]; + break; + } + } + + if (it_psm_process_pattern(&sigdata->pattern[n_patterns], c->data, c->len, speed, bpm, pan, vol, found)) goto error_ev; + n_patterns++; + sigdata->restart_position = 1; + n = n_events; + + Eh, what the hell? PSM has no panning commands anyway. + */ + sigdata->restart_position = 0; + n = n_events; + } + e->type = PSM_EVENT_END; + break; + + case PSM_EVENT_SET_SPEED: + speed = e->data[0]; + break; + + case PSM_EVENT_SET_BPM: + bpm = e->data[0]; + break; + + case PSM_EVENT_CHANGE_PAN: + o = e->data[0]; + if (o > 31) goto error_ev; + pan[o * 2] = e->data[1]; + pan[o * 2 + 1] = e->data[2]; + break; + + case PSM_EVENT_CHANGE_VOL: + o = e->data[0]; + if (o > 31) goto error_ev; + vol[o] = e->data[1]; + break; + + case PSM_EVENT_SAMPLE_MAP_TABLE: + if (e->data[0] != 0 || e->data[1] != 0xFF || + e->data[2] != 0 || e->data[3] != 0 || + e->data[4] != 1 || e->data[5] != 0) + goto error_ev; + break; + } + } + + if (n_patterns > 256) goto error_ev; + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) goto error_ev; + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + + o = 0; + for (n = 0; n < n_chunks; n++) { + PSMCHUNK * c = &chunk[n]; + if (c->id == DUMB_ID('D','S','M','P')) { + if (it_psm_process_sample(&sigdata->sample[o], c->data, c->len, o, found)) goto error_ev; + o++; + } + } + + sigdata->n_orders = n_patterns; + sigdata->n_patterns = n_patterns; + + sigdata->order = malloc(n_patterns); + + for (n = 0; n < n_patterns; n++) { + sigdata->order[n] = n; + } + + free(event); + free(songchunk); + free_chunks(chunk, n_chunks); + + _dumb_it_fix_invalid_orders(sigdata); + + dumb_it_optimize_orders(sigdata); + + return sigdata; + +error_ev: + free(event); +error_sc: + if (songchunk) free(songchunk); +error_usd: + _dumb_it_unload_sigdata(sigdata); + goto error_fc; +error_sd: + free(sigdata); +error_fc: + free_chunks(chunk, n_chunks); +error: + return NULL; +} + +static int it_order_compare(const void *e1, const void *e2) { + if (*((const char *)e1) < *((const char *)e2)) + return -1; + + if (*((const char *)e1) > *((const char *)e2)) + return 1; + + return 0; +} + +/* +static int it_optimize_compare(const void *e1, const void *e2) { + if (((const IT_ENTRY *)e1)->channel < ((const IT_ENTRY *)e2)->channel) + return -1; + + if (((const IT_ENTRY *)e1)->channel > ((const IT_ENTRY *)e2)->channel) + return 1; + + return 0; +} +*/ + +static int it_entry_compare(const IT_ENTRY * e1, const IT_ENTRY * e2) { + if (IT_IS_END_ROW(e1) && IT_IS_END_ROW(e2)) return 1; + if (e1->channel != e2->channel) return 0; + if (e1->mask != e2->mask) return 0; + if ((e1->mask & IT_ENTRY_NOTE) && (e1->note != e2->note)) return 0; + if ((e1->mask & IT_ENTRY_INSTRUMENT) && (e1->instrument != e2->instrument)) return 0; + if ((e1->mask & IT_ENTRY_VOLPAN) && (e1->volpan != e2->volpan)) return 0; + if ((e1->mask & IT_ENTRY_EFFECT) && ((e1->effect != e2->effect) || (e1->effectvalue != e2->effectvalue))) return 0; + return 1; +} + +/* +static void dumb_it_optimize_pattern(IT_PATTERN * pattern) { + IT_ENTRY * entry, * end; + IT_ENTRY * rowstart, * rowend; + IT_ENTRY * current; + + if (!pattern->n_entries || !pattern->entry) return; + + current = entry = pattern->entry; + end = entry + pattern->n_entries; + + while (entry < end) { + rowstart = entry; + while (!IT_IS_END_ROW(entry)) entry++; + rowend = entry; + if (rowend > rowstart + 1) + qsort(rowstart, rowend - rowstart, sizeof(IT_ENTRY), &it_optimize_compare); + entry = rowstart; + while (entry < rowend) { + if (!(entry->mask)) {} + else if (it_entry_compare(entry, current)) {} + else if (!(current->mask) || + ((entry->channel == current->channel) && + ((entry->mask | current->mask) == (entry->mask ^ current->mask)))) { + current->mask |= entry->mask; + if (entry->mask & IT_ENTRY_NOTE) current->note = entry->note; + if (entry->mask & IT_ENTRY_INSTRUMENT) current->instrument = entry->instrument; + if (entry->mask & IT_ENTRY_VOLPAN) current->volpan = entry->volpan; + if (entry->mask & IT_ENTRY_EFFECT) { + current->effect = entry->effect; + current->effectvalue = entry->effectvalue; + } + } else { + if (++current < entry) *current = *entry; + } + entry++; + } + if (++current < entry) *current = *entry; + entry++; + } + + current++; + + if (current < end) { + IT_ENTRY * opt; + pattern->n_entries = current - pattern->entry; + opt = realloc(pattern->entry, pattern->n_entries * sizeof(*pattern->entry)); + if (opt) pattern->entry = opt; + } +} +*/ + +static int it_pattern_compare(const IT_PATTERN * p1, const IT_PATTERN * p2) { + IT_ENTRY * e1, * end; + IT_ENTRY * e2; + + if (p1 == p2) return 1; + if (p1->n_entries != p2->n_entries) return 0; + + e1 = p1->entry; end = e1 + p1->n_entries; + e2 = p2->entry; + + while (e1 < end) { + if (!it_entry_compare(e1, e2)) return 0; + e1++; e2++; + } + + return 1; +} + +static void dumb_it_optimize_orders(DUMB_IT_SIGDATA * sigdata) { + int n, o, p; + + /*int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;*/ + + unsigned char * order_list; + int n_patterns; + + IT_PATTERN * pattern; + + if (!sigdata->n_orders || !sigdata->n_patterns) return; + + n_patterns = 0; + order_list = malloc(sigdata->n_orders); + + if (!order_list) return; + + for (n = 0; n < sigdata->n_orders; n++) { + if (sigdata->order[n] < sigdata->n_patterns) { + for (o = 0; o < n_patterns; o++) { + if (sigdata->order[n] == order_list[o]) break; + } + if (o == n_patterns) { + order_list[n_patterns++] = sigdata->order[n]; + } + } + } + + if (!n_patterns) { + free(order_list); + return; + } + + /*for (n = 0; n < n_patterns; n++) { + dumb_it_optimize_pattern(&sigdata->pattern[order_list[n]]); + }*/ + + for (n = 0; n < n_patterns; n++) { + for (o = n + 1; o < n_patterns; o++) { + if ((order_list[n] != order_list[o]) && + it_pattern_compare(&sigdata->pattern[order_list[n]], &sigdata->pattern[order_list[o]])) { + for (p = 0; p < sigdata->n_orders; p++) { + if (sigdata->order[p] == order_list[o]) { + sigdata->order[p] = order_list[n]; + } + } + for (p = o + 1; p < n_patterns; p++) { + if (order_list[p] == order_list[o]) { + order_list[p] = order_list[n]; + } + } + order_list[o] = order_list[n]; + } + } + } + + qsort(order_list, n_patterns, sizeof(*order_list), &it_order_compare); + + for (n = 0, o = 0; n < n_patterns; n++) { + if (order_list[n] != order_list[o]) { + if (++o < n) order_list[o] = order_list[n]; + } + } + + n_patterns = o + 1; + + pattern = malloc(n_patterns * sizeof(*pattern)); + if (!pattern) { + free(order_list); + return; + } + + for (n = 0; n < n_patterns; n++) { + pattern[n] = sigdata->pattern[order_list[n]]; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + for (o = 0; o < n_patterns; o++) { + if (order_list[o] == n) break; + } + if (o == n_patterns) { + if (sigdata->pattern[n].entry) + free(sigdata->pattern[n].entry); + } + } + + free(sigdata->pattern); + sigdata->pattern = pattern; + sigdata->n_patterns = n_patterns; + + for (n = 0; n < sigdata->n_orders; n++) { + for (o = 0; o < n_patterns; o++) { + if (sigdata->order[n] == order_list[o]) { + sigdata->order[n] = o; + break; + } + } + } + + free(order_list); +} + +int dumb_get_psm_subsong_count(DUMBFILE *f) { + int length, subsongs; + long l; + + if (dumbfile_mgetl(f) != DUMB_ID('P','S','M',' ')) return 0; + + length = dumbfile_igetl(f); + + if (dumbfile_mgetl(f) != DUMB_ID('F','I','L','E')) return 0; + + subsongs = 0; + + while (length >= 8 && !dumbfile_error(f)) { + if (dumbfile_mgetl(f) == DUMB_ID('S','O','N','G')) subsongs++; + l = dumbfile_igetl(f); + dumbfile_skip(f, l); + length -= l + 8; + } + + if (dumbfile_error(f)) return 0; + + return subsongs; +} + + + +/* Eww */ +int pattcmp( const unsigned char * a, const unsigned char * b, size_t l ) +{ + size_t i, j, na, nb; + char * p; + + na = nb = 0; + + i = memcmp( a, b, l ); + if ( !i ) return i; + + /* damnit */ + + for ( i = 0; i < l; ++i ) + { + if ( a [i] >= '0' && a [i] <= '9' ) break; + } + + if ( i < l ) + { + na = strtoul( (const char *)a + i, &p, 10 ); + if ( (const unsigned char *)p == a + i ) return 1; + } + + for ( j = 0; j < l; ++j ) + { + if ( b [j] >= '0' && b [j] <= '9' ) break; + } + + if ( j < l ) + { + nb = strtoul( (const char *)b + j, &p, 10 ); + if ( (const unsigned char *)p == b + j ) return -1; + } + + if ( i < j ) return -1; + else if ( j > i ) return 1; + + i = memcmp( a, b, j ); + if ( i ) return i; + + return na - nb; +} + + + +DUH *dumb_read_psm_quick(DUMBFILE *f, int subsong) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_psm_load_sigdata(f, &ver, subsong); + + if (!sigdata) + return NULL; + + { + int n_tags = 2; + char version[16]; + const char *tag[3][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "PSM"; + if ( ver ) + { + tag[2][0] = "FORMATVERSION"; + snprintf( version, 15, "%u", ver ); + version[15] = 0; + tag[2][1] = (const char *) &version; + ++n_tags; + } + return make_duh(-1, n_tags, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readptm.c b/Frameworks/Dumb/dumb/src/it/readptm.c new file mode 100644 index 000000000..59f2c0d14 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readptm.c @@ -0,0 +1,551 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readptm.c - Code to read a Poly Tracker v2.03 / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. Based on reads3m.c \_ / > / + * by entheh. | \ / / + * | ' / + * \__/ + */ + +// IT_STEREO... :o +#include +#include + +#include "dumb.h" +#include "internal/it.h" + + + +static int it_ptm_read_sample_header(IT_SAMPLE *sample, long *offset, DUMBFILE *f) +{ + int flags; + + flags = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->filename, 12, f); + sample->filename[12] = 0; + + sample->default_volume = dumbfile_getc(f); + + sample->C5_speed = dumbfile_igetw(f) << 1; + + dumbfile_skip(f, 2); /* segment */ + + *offset = dumbfile_igetl(f); + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + + /* GUSBegin, GUSLStart, GUSLEnd, GUSLoop, reserverd */ + dumbfile_skip(f, 4+4+4+1+1); + + dumbfile_getnc((char *)sample->name, 28, f); + sample->name[28] = 0; + + /* + if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','S')) + return -1; + */ + + /* BLAH! Shit likes to have broken or missing sample IDs */ + dumbfile_skip(f, 4); + + if ((flags & 3) == 0) { + /* Looks like no sample */ + sample->flags &= ~IT_SAMPLE_EXISTS; + return dumbfile_error(f); + } + + sample->global_volume = 64; + + sample->flags = IT_SAMPLE_EXISTS; + if (flags & 4) sample->flags |= IT_SAMPLE_LOOP; + if (flags & 8) sample->flags |= IT_SAMPLE_PINGPONG_LOOP; + + if (flags & 16) { + sample->flags |= IT_SAMPLE_16BIT; + + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + + if (sample->loop_end) sample->loop_end--; + + sample->default_pan = 0; // 0 = don't use, or 160 = centre? + + if (sample->length <= 0) + sample->flags &= ~IT_SAMPLE_EXISTS; + else if (sample->flags & IT_SAMPLE_LOOP) { + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_LOOP; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + else + sample->length = sample->loop_end; + } + + + //Do we need to set all these? + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + +static int it_ptm_read_byte(DUMBFILE *f) +{ + int meh = dumbfile_getc(f); + if (meh < 0) return 0; + return meh; +} + +static int it_ptm_read_sample_data(IT_SAMPLE *sample, int last, DUMBFILE *f) +{ + long n; + int s; + + sample->data = malloc(sample->length * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + s = 0; + + if (sample->flags & IT_SAMPLE_16BIT) { + unsigned char a, b; + for (n = 0; n < sample->length; n++) { + a = s += (signed char) it_ptm_read_byte(f); + b = s += (signed char) it_ptm_read_byte(f); + ((short *)sample->data)[n] = a | (b << 8); + } + } else { + for (n = 0; n < sample->length; n++) { + s += (signed char) it_ptm_read_byte(f); + ((signed char *)sample->data)[n] = s; + } + } + + if (dumbfile_error(f) && !last) + return -1; + + return 0; +} + + + +static int it_ptm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer, int length) +{ + int buflen = 0; + int bufpos = 0; + int effect, effectvalue; + + IT_ENTRY *entry; + + unsigned char channel; + + if (!length) + return -1; + + pattern->n_rows = 0; + pattern->n_entries = 0; + + /* Read in the pattern data, little by little, and work out how many + * entries we need room for. Sorry, but this is just so funny... + */ + for (;;) { + unsigned char b = buffer[buflen++] = dumbfile_getc(f); + +#if 1 + static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5}; + channel = b & 31; + b >>= 5; + pattern->n_entries++; + if (b) { + if (buflen + used[b] >= 65536) return -1; + dumbfile_getnc((char *)buffer + buflen, used[b], f); + buflen += used[b]; + } else { + /* End of row */ + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } +#else + if (b == 0) { + /* End of row */ + pattern->n_entries++; + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } else { + static const unsigned char used[8] = {0, 2, 2, 4, 1, 3, 3, 5}; + channel = b & 31; + b >>= 5; + if (b) { + pattern->n_entries++; + if (buflen + used[b] >= 65536) return -1; + dumbfile_getnc(buffer + buflen, used[b], f); + buflen += used[b]; + } + } +#endif + + /* We have ensured that buflen < 65536 at this point, so it is safe + * to iterate and read at least one more byte without checking. + * However, now would be a good time to check for errors reading from + * the file. + */ + + if (dumbfile_error(f)) + return -1; + + /* Great. We ran out of data, but there should be data for more rows. + * Fill the rest with null data... + */ + if (buflen >= length && pattern->n_rows < 64) + { + while (pattern->n_rows < 64) + { + if (buflen >= 65536) return -1; + buffer[buflen++] = 0; + pattern->n_entries++; + pattern->n_rows++; + } + break; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + + if (!pattern->entry) + return -1; + + entry = pattern->entry; + + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + + if (b == 0) + { + /* End of row */ + IT_SET_END_ROW(entry); + entry++; + continue; + } + + channel = b & 31; + + if (b & 224) { + entry->mask = 0; + entry->channel = channel; + + if (b & 32) { + unsigned char n = buffer[bufpos++]; + if (n == 254 || (n >= 1 && n <= 120)) { + if (n == 254) + entry->note = IT_NOTE_CUT; + else + entry->note = n - 1; + entry->mask |= IT_ENTRY_NOTE; + } + + entry->instrument = buffer[bufpos++]; + if (entry->instrument) + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + if (b & 64) { + effect = buffer[bufpos++]; + effectvalue = buffer[bufpos++]; + _dumb_it_ptm_convert_effect(effect, effectvalue, entry); + } + + if (b & 128) { + entry->volpan = buffer[bufpos++]; + if (entry->volpan <= 64) + entry->mask |= IT_ENTRY_VOLPAN; + } + + entry++; + } + } + + ASSERT(entry == pattern->entry + pattern->n_entries); + + return 0; +} + + + +/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */ +/* Currently we assume the sample data are stored after the sample headers in + * module files. This assumption may be unjustified; let me know if you have + * trouble. + */ + +#define PTM_COMPONENT_INSTRUMENT 1 +#define PTM_COMPONENT_PATTERN 2 +#define PTM_COMPONENT_SAMPLE 3 + +typedef struct PTM_COMPONENT +{ + unsigned char type; + unsigned char n; + long offset; +} +PTM_COMPONENT; + + + +static int ptm_component_compare(const void *e1, const void *e2) +{ + return ((const PTM_COMPONENT *)e1)->offset - + ((const PTM_COMPONENT *)e2)->offset; +} + + + +static DUMB_IT_SIGDATA *it_ptm_load_sigdata(DUMBFILE *f) +{ + DUMB_IT_SIGDATA *sigdata; + + PTM_COMPONENT *component; + int n_components = 0; + + int n; + + unsigned char *buffer; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) return NULL; + + /* Skip song name. */ + dumbfile_getnc((char *)sigdata->name, 28, f); + sigdata->name[28] = 0; + + if (dumbfile_getc(f) != 0x1A || dumbfile_igetw(f) != 0x203) { + free(sigdata); + return NULL; + } + + dumbfile_skip(f, 1); + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_orders = dumbfile_igetw(f); + sigdata->n_instruments = 0; + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 255 || sigdata->n_patterns > 128) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->n_pchannels = dumbfile_igetw(f); + + if (dumbfile_igetw(f) != 0) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + dumbfile_skip(f, 2); + + if (dumbfile_mgetl(f) != DUMB_ID('P','T','M','F')) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + dumbfile_skip(f, 16); + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + /** WARNING: which ones? */ + sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_A_PTM; + + sigdata->global_volume = 128; + sigdata->speed = 6; + sigdata->tempo = 125; + sigdata->mixing_volume = 48; + + /* Panning positions for 32 channels */ + { + int i; + for (i = 0; i < 32; i++) { + int c = dumbfile_getc(f); + if (c <= 15) { + sigdata->channel_volume[i] = 64; + sigdata->channel_pan[i] = c; + } else { + /** WARNING: this could be improved if we support channel muting... */ + sigdata->channel_volume[i] = 0; + sigdata->channel_pan[i] = 7; + } + } + } + + /* Orders, byte each, length = sigdata->n_orders (should be even) */ + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + sigdata->restart_position = 0; + + component = malloc(768*sizeof(*component)); + if (!component) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (dumbfile_seek(f, 352, DFS_SEEK_SET)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + component[n_components].type = PTM_COMPONENT_PATTERN; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetw(f) << 4; + n_components++; + } + + if (dumbfile_seek(f, 608, DFS_SEEK_SET)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < sigdata->n_samples; n++) { + if (it_ptm_read_sample_header(&sigdata->sample[n], &component[n_components].offset, f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + if (!(sigdata->sample[n].flags & IT_SAMPLE_EXISTS)) continue; + component[n_components].type = PTM_COMPONENT_SAMPLE; + component[n_components].n = n; + n_components++; + } + + qsort(component, n_components, sizeof(PTM_COMPONENT), &ptm_component_compare); + + { + int i; + for (i = 0; i < 32; i++) { + sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3; + sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7; + if (sigdata->channel_pan[i] > 64) sigdata->channel_pan[i] = 64; + } + } + + sigdata->pan_separation = 128; + + if (dumbfile_error(f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + buffer = malloc(65536); + if (!buffer) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < n_components; n++) { + if (dumbfile_seek(f, component[n].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + switch (component[n].type) { + + case PTM_COMPONENT_PATTERN: + if (it_ptm_read_pattern(&sigdata->pattern[component[n].n], f, buffer, (n + 1 < n_components) ? (component[n+1].offset - component[n].offset) : 0)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case PTM_COMPONENT_SAMPLE: + if (it_ptm_read_sample_data(&sigdata->sample[component[n].n], (n + 1 == n_components), f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + } + + free(buffer); + free(component); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + +DUH *dumb_read_ptm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_ptm_load_sigdata(f); + + if (!sigdata) + return NULL; + + { + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "PTM"; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readriff.c b/Frameworks/Dumb/dumb/src/it/readriff.c new file mode 100644 index 000000000..1dd852d76 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readriff.c @@ -0,0 +1,57 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readriff.c - Code to read a RIFF module file / / \ \ + * from memory. | < / \_ + * | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" +#include "internal/it.h" +#include "internal/riff.h" + + +DUH *dumb_read_riff_amff( DUMBFILE * f, struct riff * stream ); +DUH *dumb_read_riff_am( DUMBFILE * f, struct riff * stream ); +DUH *dumb_read_riff_dsmf( DUMBFILE * f, struct riff * stream ); + +/* dumb_read_riff_quick(): reads a RIFF file into a DUH struct, returning a + * pointer to the DUH struct. When you have finished with it, you must pass + * the pointer to unload_duh() so that the memory can be freed. + */ +DUH *dumb_read_riff_quick( DUMBFILE * f ) +{ + DUH * duh; + struct riff * stream; + long size; + + size = dumbfile_get_size(f); + + stream = riff_parse( f, 0, size, 1 ); + if ( ! stream ) stream = riff_parse( f, 0, size, 0 ); + + if ( ! stream ) return 0; + + if ( stream->type == DUMB_ID( 'A', 'M', ' ', ' ' ) ) + duh = dumb_read_riff_am( f, stream ); + else if ( stream->type == DUMB_ID( 'A', 'M', 'F', 'F' ) ) + duh = dumb_read_riff_amff( f, stream ); + else if ( stream->type == DUMB_ID( 'D', 'S', 'M', 'F' ) ) + duh = dumb_read_riff_dsmf( f, stream ); + else duh = 0; + + riff_free( stream ); + + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/reads3m.c b/Frameworks/Dumb/dumb/src/it/reads3m.c index 97d09d7d3..2ee647a4a 100644 --- a/Frameworks/Dumb/dumb/src/it/reads3m.c +++ b/Frameworks/Dumb/dumb/src/it/reads3m.c @@ -1,670 +1,765 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * reads3m.c - Code to read a ScreamTracker 3 / / \ \ - * module from an open file. | < / \_ - * | \/ /\ / - * By entheh. \_ / > / - * | \ / / - * | ' / - * \__/ - */ - -// IT_STEREO... :o -#include -#include - -#include "dumb.h" -#include "internal/it.h" - - - -/** WARNING: this is duplicated in itread.c */ -static int it_seek(DUMBFILE *f, long offset) -{ - long pos = dumbfile_pos(f); - - if (pos > offset) - return -1; - - if (pos < offset) - if (dumbfile_skip(f, offset - pos)) - return -1; - - return 0; -} - - - -static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, DUMBFILE *f) -{ - unsigned char type; - int flags; - - type = dumbfile_getc(f); - - if (type > 1) { - /** WARNING: no adlib support */ - } - - dumbfile_getnc(sample->filename, 13, f); - sample->filename[13] = 0; - - *offset = dumbfile_igetw(f) << 4; - - sample->length = dumbfile_igetl(f); - sample->loop_start = dumbfile_igetl(f); - sample->loop_end = dumbfile_igetl(f); - - sample->default_volume = dumbfile_getc(f); - - dumbfile_skip(f, 1); - - if (dumbfile_getc(f) != 0) - /* Sample is packed apparently (or error reading from file). We don't - * know how to read packed samples. - */ - return -1; - - flags = dumbfile_getc(f); - - sample->C5_speed = dumbfile_igetl(f) << 1; - - /* Skip four unused bytes and three internal variables. */ - dumbfile_skip(f, 4+2+2+4); - - dumbfile_getnc(sample->name, 28, f); - sample->name[28] = 0; - - if (type == 0) { - /* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */ - sample->flags &= ~IT_SAMPLE_EXISTS; - return dumbfile_error(f); - } - - if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S')) - return -1; - - sample->global_volume = 64; - - sample->flags = IT_SAMPLE_EXISTS; - if (flags & 1) sample->flags |= IT_SAMPLE_LOOP; - if (flags & 2) sample->flags |= IT_SAMPLE_STEREO; - if (flags & 4) sample->flags |= IT_SAMPLE_16BIT; - - sample->default_pan = 0; // 0 = don't use, or 160 = centre? - - if (sample->length <= 0) - sample->flags &= ~IT_SAMPLE_EXISTS; - else if (sample->flags & IT_SAMPLE_LOOP) { - if ((unsigned int)sample->loop_end > (unsigned int)sample->length) - sample->flags &= ~IT_SAMPLE_LOOP; - else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) - sample->flags &= ~IT_SAMPLE_LOOP; - else - /* ScreamTracker seems not to save what comes after the loop end - * point, but rather to assume it is a duplicate of what comes at - * the loop start point. I am not completely sure of this though. - * It is easy to evade; simply truncate the sample. - */ - sample->length = sample->loop_end; - } - - - //Do we need to set all these? - sample->vibrato_speed = 0; - sample->vibrato_depth = 0; - sample->vibrato_rate = 0; - sample->vibrato_waveform = IT_VIBRATO_SINE; - - return dumbfile_error(f); -} - - - -static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, DUMBFILE *f) -{ - long n; - - long datasize = sample->length; - if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1; - - sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); - if (!sample->data) - return -1; - - if (sample->flags & IT_SAMPLE_STEREO) { - if (sample->flags & IT_SAMPLE_16BIT) { - for (n = 0; n < datasize; n += 2) - ((short *)sample->data)[n] = dumbfile_igetw(f); - for (n = 1; n < datasize; n += 2) - ((short *)sample->data)[n] = dumbfile_igetw(f); - } else { - for (n = 0; n < datasize; n += 2) - ((signed char *)sample->data)[n] = dumbfile_getc(f); - for (n = 1; n < datasize; n += 2) - ((signed char *)sample->data)[n] = dumbfile_getc(f); - } - } else if (sample->flags & IT_SAMPLE_16BIT) - for (n = 0; n < sample->length; n++) - ((short *)sample->data)[n] = dumbfile_igetw(f); - else - for (n = 0; n < sample->length; n++) - ((signed char *)sample->data)[n] = dumbfile_getc(f); - - if (dumbfile_error(f)) - return -1; - - if (ffi != 1) { - /* Convert to signed. */ - if (sample->flags & IT_SAMPLE_16BIT) - for (n = 0; n < datasize; n++) - ((short *)sample->data)[n] ^= 0x8000; - else - for (n = 0; n < datasize; n++) - ((signed char *)sample->data)[n] ^= 0x80; - } - - return 0; -} - - - -static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer) -{ - int buflen = 0; - int bufpos = 0; - - IT_ENTRY *entry; - - unsigned char channel; - - /* Haha, this is hilarious! - * - * Well, after some experimentation, it seems that different S3M writers - * define the format in different ways. The S3M docs say that the first - * two bytes hold the "length of [the] packed pattern", and the packed - * pattern data follow. Judging by the contents of ARMANI.S3M, packaged - * with ScreamTracker itself, the measure of length _includes_ the two - * bytes used to store the length; in other words, we should read - * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug - * Tracker, excludes these two bytes, so (length) more bytes must be - * read. - * - * Call me crazy, but I just find it insanely funny that the format was - * misunderstood in this way :D - * - * Now we can't just risk reading two extra bytes, because then we - * overshoot, and DUMBFILEs don't support backward seeking (for a good - * reason). Luckily, there is a way. We can read the data little by - * little, and stop when we have 64 rows in memory. Provided we protect - * against buffer overflow, this method should work with all sensibly - * written S3M files. If you find one for which it does not work, please - * let me know at entheh@users.sf.net so I can look at it. - */ - - /* Discard the length. */ - dumbfile_skip(f, 2); - - if (dumbfile_error(f)) - return -1; - - pattern->n_rows = 0; - pattern->n_entries = 0; - - /* Read in the pattern data, little by little, and work out how many - * entries we need room for. Sorry, but this is just so funny... - */ - for (;;) { - unsigned char b = buffer[buflen++] = dumbfile_getc(f); - -#if 1 - static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5}; - channel = b & 31; - b >>= 5; - pattern->n_entries++; - if (b) { - if (buflen + used[b] >= 65536) return -1; - dumbfile_getnc(buffer + buflen, used[b], f); - buflen += used[b]; - } else { - /* End of row */ - if (++pattern->n_rows == 64) break; - if (buflen >= 65536) return -1; - } -#else - if (b == 0) { - /* End of row */ - pattern->n_entries++; - if (++pattern->n_rows == 64) break; - if (buflen >= 65536) return -1; - } else { - static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5}; - channel = b & 31; - b >>= 5; - if (b) { - pattern->n_entries++; - if (buflen + used[b] >= 65536) return -1; - dumbfile_getnc(buffer + buflen, used[b], f); - buflen += used[b]; - } - } -#endif - - /* We have ensured that buflen < 65536 at this point, so it is safe - * to iterate and read at least one more byte without checking. - * However, now would be a good time to check for errors reading from - * the file. - */ - - if (dumbfile_error(f)) - return -1; - } - - pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); - - if (!pattern->entry) - return -1; - - entry = pattern->entry; - - while (bufpos < buflen) { - unsigned char b = buffer[bufpos++]; - -#if 1 - if (!(b & ~31)) -#else - if (b == 0) -#endif - { - /* End of row */ - IT_SET_END_ROW(entry); - entry++; - continue; - } - - channel = b & 31; - - if (b & 224) { - entry->mask = 0; - entry->channel = channel; - - if (b & 32) { - unsigned char n = buffer[bufpos++]; - if (n != 255) { - if (n == 254) - entry->note = IT_NOTE_CUT; - else - entry->note = (n >> 4) * 12 + (n & 15); - entry->mask |= IT_ENTRY_NOTE; - } - - entry->instrument = buffer[bufpos++]; - if (entry->instrument) - entry->mask |= IT_ENTRY_INSTRUMENT; - } - - if (b & 64) { - entry->volpan = buffer[bufpos++]; - if (entry->volpan != 255) - entry->mask |= IT_ENTRY_VOLPAN; - } - - if (b & 128) { - entry->effect = buffer[bufpos++]; - entry->effectvalue = buffer[bufpos++]; - if (entry->effect != 255) { - entry->mask |= IT_ENTRY_EFFECT; - if (entry->effect == IT_BREAK_TO_ROW) - entry->effectvalue -= (entry->effectvalue >> 4) * 6; - } - /** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */ - } - - entry++; - } - } - - ASSERT(entry == pattern->entry + pattern->n_entries); - - return 0; -} - - - -/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */ -/* Currently we assume the sample data are stored after the sample headers in - * module files. This assumption may be unjustified; let me know if you have - * trouble. - */ - -#define IT_COMPONENT_INSTRUMENT 1 -#define IT_COMPONENT_PATTERN 2 -#define IT_COMPONENT_SAMPLE 3 - -typedef struct IT_COMPONENT -{ - unsigned char type; - unsigned char n; - long offset; - short sampfirst; /* component[sampfirst] = first sample data after this */ - short sampnext; /* sampnext is used to create linked lists of sample data */ -} -IT_COMPONENT; - - - -static int it_component_compare(const void *e1, const void *e2) -{ - return ((const IT_COMPONENT *)e1)->offset - - ((const IT_COMPONENT *)e2)->offset; -} - - - -static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f) -{ - DUMB_IT_SIGDATA *sigdata; - - int flags, cwtv, ffi; - int default_pan_present; - - IT_COMPONENT *component; - int n_components = 0; - - int n; - - unsigned char *buffer; - - sigdata = malloc(sizeof(*sigdata)); - if (!sigdata) return NULL; - - dumbfile_getnc(sigdata->name, 28, f); - sigdata->name[28] = 0; - - if (dumbfile_getc(f) != 0x1A || dumbfile_getc(f) != 16) { - free(sigdata); - return NULL; - } - - dumbfile_skip(f, 2); - - sigdata->song_message = NULL; - sigdata->order = NULL; - sigdata->instrument = NULL; - sigdata->sample = NULL; - sigdata->pattern = NULL; - sigdata->midi = NULL; - sigdata->checkpoint = NULL; - - sigdata->n_orders = dumbfile_igetw(f); - sigdata->n_instruments = 0; - sigdata->n_samples = dumbfile_igetw(f); - sigdata->n_patterns = dumbfile_igetw(f); - - if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - sigdata->order = malloc(sigdata->n_orders); - if (!sigdata->order) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (sigdata->n_samples) { - sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); - if (!sigdata->sample) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (n = 0; n < sigdata->n_samples; n++) - sigdata->sample[n].data = NULL; - } - - if (sigdata->n_patterns) { - sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); - if (!sigdata->pattern) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (n = 0; n < sigdata->n_patterns; n++) - sigdata->pattern[n].entry = NULL; - } - - flags = dumbfile_igetw(f); - - cwtv = dumbfile_igetw(f); - - if (cwtv == 0x1300) { - /** WARNING: volume slides on every frame */ - } - - ffi = dumbfile_igetw(f); - - /** WARNING: which ones? */ - sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX; - - if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - sigdata->global_volume = dumbfile_getc(f) << 1; - sigdata->speed = dumbfile_getc(f); - if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? - sigdata->tempo = dumbfile_getc(f); - /*master_volume = */dumbfile_getc(f); // 7 bits; +128 for stereo - //what do we do with master_volume? it's not the same as mixing volume... - sigdata->mixing_volume = 48; - - /* Skip GUS Ultra Click Removal byte. */ - dumbfile_getc(f); - - default_pan_present = dumbfile_getc(f); - - dumbfile_skip(f, 8); - - /* Skip Special Custom Data Pointer. */ - /** WARNING: investigate this? */ - dumbfile_igetw(f); - - /* Channel settings for 32 channels, 255=unused, +128=disabled */ - { - int i; - for (i = 0; i < 32; i++) { - int c = dumbfile_getc(f); - if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */ - sigdata->channel_volume[i] = 64; - sigdata->channel_pan[i] = c & 8 ? 12 : 3; - /** WARNING: ah, but it should be 7 for mono... */ - } else { - /** WARNING: this could be improved if we support channel muting... */ - sigdata->channel_volume[i] = 0; - sigdata->channel_pan[i] = 7; - } - } - } - - /* Orders, byte each, length = sigdata->n_orders (should be even) */ - dumbfile_getnc(sigdata->order, sigdata->n_orders, f); - sigdata->restart_position = 0; - - component = malloc(768*sizeof(*component)); - if (!component) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - for (n = 0; n < sigdata->n_samples; n++) { - component[n_components].type = IT_COMPONENT_SAMPLE; - component[n_components].n = n; - component[n_components].offset = dumbfile_igetw(f) << 4; - component[n_components].sampfirst = -1; - n_components++; - } - - for (n = 0; n < sigdata->n_patterns; n++) { - long offset = dumbfile_igetw(f) << 4; - if (offset) { - component[n_components].type = IT_COMPONENT_PATTERN; - component[n_components].n = n; - component[n_components].offset = offset; - component[n_components].sampfirst = -1; - n_components++; - } else { - /** WARNING: Empty 64-row pattern ... ? (this does happen!) */ - sigdata->pattern[n].n_rows = 64; - sigdata->pattern[n].n_entries = 0; - } - } - - qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare); - - /* I found a really dumb S3M file that claimed to contain default pan - * data but didn't contain any. Programs would load it by reading part of - * the first instrument header, assuming the data to be default pan - * positions, and then rereading the instrument module. We cannot do this - * without obfuscating the file input model, so we insert an extra check - * here that we won't overrun the start of the first component. - */ - if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) { - /* Channel default pan positions */ - int i; - for (i = 0; i < 32; i++) { - int c = dumbfile_getc(f); - if (c & 32) - sigdata->channel_pan[i] = c & 15; - } - } - - { - int i; - for (i = 0; i < 32; i++) { - sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3; - sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7; - } - } - - sigdata->pan_separation = 128; - - if (dumbfile_error(f)) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - buffer = malloc(65536); - if (!buffer) { - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - for (n = 0; n < n_components; n++) { - long offset; - int m; - - if (it_seek(f, component[n].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - switch (component[n].type) { - - case IT_COMPONENT_PATTERN: - if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - break; - - case IT_COMPONENT_SAMPLE: - if (it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, f)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) { - short *sample; - - for (m = n + 1; m < n_components; m++) - if (component[m].offset > offset) - break; - m--; - - sample = &component[m].sampfirst; - - while (*sample >= 0 && component[*sample].offset <= offset) - sample = &component[*sample].sampnext; - - component[n].sampnext = *sample; - *sample = n; - - component[n].offset = offset; - } - } - - m = component[n].sampfirst; - - while (m >= 0) { - if (it_seek(f, component[m].offset)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, f)) { - free(buffer); - free(component); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - m = component[m].sampnext; - } - } - - free(buffer); - free(component); - - _dumb_it_fix_invalid_orders(sigdata); - - return sigdata; -} - - - -DUH *dumb_read_s3m_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_s3m_load_sigdata(f); - - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * reads3m.c - Code to read a ScreamTracker 3 / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By entheh. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +// IT_STEREO... :o +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, unsigned char *pack, int cwtv, DUMBFILE *f) +{ + unsigned char type; + int flags; + + type = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->filename, 12, f); + sample->filename[12] = 0; + + if (type > 1) { + /** WARNING: no adlib support */ + dumbfile_skip(f, 3 + 12 + 1 + 1 + 2 + 2 + 2 + 12); + dumbfile_getnc((char *)sample->name, 28, f); + sample->name[28] = 0; + dumbfile_skip(f, 4); + sample->flags &= ~IT_SAMPLE_EXISTS; + return dumbfile_error(f); + } + + *offset = dumbfile_getc(f) << 20; + *offset += dumbfile_igetw(f) << 4; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = dumbfile_igetl(f); + + sample->default_volume = dumbfile_getc(f); + + dumbfile_skip(f, 1); + + flags = dumbfile_getc(f); + + if (flags < 0 || (flags != 0 && flags != 4)) + /* Sample is packed apparently (or error reading from file). We don't + * know how to read packed samples. + */ + return -1; + + *pack = flags; + + flags = dumbfile_getc(f); + + sample->C5_speed = dumbfile_igetl(f) << 1; + + /* Skip four unused bytes and three internal variables. */ + dumbfile_skip(f, 4+2+2+4); + + dumbfile_getnc((char *)sample->name, 28, f); + sample->name[28] = 0; + + if (type == 0 || sample->length <= 0) { + /* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */ + sample->flags &= ~IT_SAMPLE_EXISTS; + return dumbfile_error(f); + } + + if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S')) + return -1; + + sample->global_volume = 64; + + sample->flags = IT_SAMPLE_EXISTS; + if (flags & 1) sample->flags |= IT_SAMPLE_LOOP; + + /* The ST3 TECH.DOC is unclear on this, but IMAGO Orpheus is not. Piece of crap. */ + + if (flags & 2) { + sample->flags |= IT_SAMPLE_STEREO; + + if ((cwtv & 0xF000) == 0x2000) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + } + + if (flags & 4) { + sample->flags |= IT_SAMPLE_16BIT; + + if ((cwtv & 0xF000) == 0x2000) { + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } + } + + sample->default_pan = 0; // 0 = don't use, or 160 = centre? + + if (sample->flags & IT_SAMPLE_LOOP) { + if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + /*sample->flags &= ~IT_SAMPLE_LOOP;*/ + sample->loop_end = sample->length; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + else + /* ScreamTracker seems not to save what comes after the loop end + * point, but rather to assume it is a duplicate of what comes at + * the loop start point. I am not completely sure of this though. + * It is easy to evade; simply truncate the sample. + */ + sample->length = sample->loop_end; + } + + + //Do we need to set all these? + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + + + +static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, unsigned char pack, DUMBFILE *f) +{ + long n; + + long datasize = sample->length; + if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1; + + sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + if (pack == 4) { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + } + else if (sample->flags & IT_SAMPLE_STEREO) { + if (sample->flags & IT_SAMPLE_16BIT) { + for (n = 0; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + for (n = 1; n < datasize; n += 2) + ((short *)sample->data)[n] = dumbfile_igetw(f); + } else { + for (n = 0; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + for (n = 1; n < datasize; n += 2) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + } + } else if (sample->flags & IT_SAMPLE_16BIT) + for (n = 0; n < sample->length; n++) + ((short *)sample->data)[n] = dumbfile_igetw(f); + else + for (n = 0; n < sample->length; n++) + ((signed char *)sample->data)[n] = dumbfile_getc(f); + + if (dumbfile_error(f)) + return -1; + + if (ffi != 1) { + /* Convert to signed. */ + if (sample->flags & IT_SAMPLE_16BIT) + for (n = 0; n < datasize; n++) + ((short *)sample->data)[n] ^= 0x8000; + else + for (n = 0; n < datasize; n++) + ((signed char *)sample->data)[n] ^= 0x80; + } + + return 0; +} + + + +static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer) +{ + int length; + int buflen = 0; + int bufpos = 0; + + IT_ENTRY *entry; + + unsigned char channel; + + /* Haha, this is hilarious! + * + * Well, after some experimentation, it seems that different S3M writers + * define the format in different ways. The S3M docs say that the first + * two bytes hold the "length of [the] packed pattern", and the packed + * pattern data follow. Judging by the contents of ARMANI.S3M, packaged + * with ScreamTracker itself, the measure of length _includes_ the two + * bytes used to store the length; in other words, we should read + * (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug + * Tracker, excludes these two bytes, so (length) more bytes must be + * read. + * + * Call me crazy, but I just find it insanely funny that the format was + * misunderstood in this way :D + * + * Now we can't just risk reading two extra bytes, because then we + * overshoot, and DUMBFILEs don't support backward seeking (for a good + * reason). Luckily, there is a way. We can read the data little by + * little, and stop when we have 64 rows in memory. Provided we protect + * against buffer overflow, this method should work with all sensibly + * written S3M files. If you find one for which it does not work, please + * let me know at entheh@users.sf.net so I can look at it. + * + * "for a good reason" ? What's this nonsense? -kode54 + * + */ + + length = dumbfile_igetw(f); + + if (dumbfile_error(f) || !length) + return -1; + + pattern->n_rows = 0; + pattern->n_entries = 0; + + /* Read in the pattern data, little by little, and work out how many + * entries we need room for. Sorry, but this is just so funny... + */ + for (;;) { + unsigned char b = buffer[buflen++] = dumbfile_getc(f); + +#if 1 + static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5}; + channel = b & 31; + b >>= 5; + pattern->n_entries++; + if (b) { + if (buflen + used[b] >= 65536) return -1; + if (buflen + used[b] <= length) + dumbfile_getnc((char *)buffer + buflen, used[b], f); + else + memset(buffer + buflen, 0, used[b]); + buflen += used[b]; + } else { + /* End of row */ + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } +#else + if (b == 0) { + /* End of row */ + pattern->n_entries++; + if (++pattern->n_rows == 64) break; + if (buflen >= 65536) return -1; + } else { + static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5}; + channel = b & 31; + b >>= 5; + if (b) { + pattern->n_entries++; + if (buflen + used[b] >= 65536) return -1; + dumbfile_getnc(buffer + buflen, used[b], f); + buflen += used[b]; + } + } +#endif + + /* We have ensured that buflen < 65536 at this point, so it is safe + * to iterate and read at least one more byte without checking. + * However, now would be a good time to check for errors reading from + * the file. + */ + + if (dumbfile_error(f)) + return -1; + + /* Great. We ran out of data, but there should be data for more rows. + * Fill the rest with null data... + */ + if (buflen >= length && pattern->n_rows < 64) + { + while (pattern->n_rows < 64) + { + if (buflen >= 65536) return -1; + buffer[buflen++] = 0; + pattern->n_entries++; + pattern->n_rows++; + } + break; + } + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + + if (!pattern->entry) + return -1; + + entry = pattern->entry; + + while (bufpos < buflen) { + unsigned char b = buffer[bufpos++]; + +#if 1 + if (!(b & ~31)) +#else + if (b == 0) +#endif + { + /* End of row */ + IT_SET_END_ROW(entry); + entry++; + continue; + } + + channel = b & 31; + + if (b & 224) { + entry->mask = 0; + entry->channel = channel; + + if (b & 32) { + unsigned char n = buffer[bufpos++]; + if (n != 255) { + if (n == 254) + entry->note = IT_NOTE_CUT; + else + entry->note = (n >> 4) * 12 + (n & 15); + entry->mask |= IT_ENTRY_NOTE; + } + + entry->instrument = buffer[bufpos++]; + if (entry->instrument) + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + if (b & 64) { + entry->volpan = buffer[bufpos++]; + if (entry->volpan != 255) + entry->mask |= IT_ENTRY_VOLPAN; + } + + if (b & 128) { + entry->effect = buffer[bufpos++]; + entry->effectvalue = buffer[bufpos++]; + // XXX woot + if (entry->effect && entry->effect < IT_MIDI_MACRO /*!= 255*/) { + entry->mask |= IT_ENTRY_EFFECT; + switch (entry->effect) { + case IT_BREAK_TO_ROW: + entry->effectvalue -= (entry->effectvalue >> 4) * 6; + break; + + case IT_SET_CHANNEL_VOLUME: + case IT_CHANNEL_VOLUME_SLIDE: + case IT_PANNING_SLIDE: + case IT_GLOBAL_VOLUME_SLIDE: + case IT_PANBRELLO: + case IT_MIDI_MACRO: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + + case IT_S: + switch (entry->effectvalue >> 4) { + case IT_S_SET_PANBRELLO_WAVEFORM: + case IT_S_FINE_PATTERN_DELAY: + case IT_S7: + case IT_S_SET_SURROUND_SOUND: + case IT_S_SET_MIDI_MACRO: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + } + break; + } + } + /** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */ + } + + entry++; + } + } + + ASSERT(entry == pattern->entry + pattern->n_entries); + + return 0; +} + + + +/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */ +/* Currently we assume the sample data are stored after the sample headers in + * module files. This assumption may be unjustified; let me know if you have + * trouble. + */ + +#define S3M_COMPONENT_INSTRUMENT 1 +#define S3M_COMPONENT_PATTERN 2 +#define S3M_COMPONENT_SAMPLE 3 + +typedef struct S3M_COMPONENT +{ + unsigned char type; + unsigned char n; + long offset; + short sampfirst; /* component[sampfirst] = first sample data after this */ + short sampnext; /* sampnext is used to create linked lists of sample data */ +} +S3M_COMPONENT; + + + +static int s3m_component_compare(const void *e1, const void *e2) +{ + return ((const S3M_COMPONENT *)e1)->offset - + ((const S3M_COMPONENT *)e2)->offset; +} + + + +static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f, int * cwtv) +{ + DUMB_IT_SIGDATA *sigdata; + + int flags, ffi; + int default_pan_present; + + int master_volume; + + unsigned char sample_pack[256]; + + S3M_COMPONENT *component; + int n_components = 0; + + int n; + + unsigned char *buffer; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) return NULL; + + dumbfile_getnc((char *)sigdata->name, 28, f); + sigdata->name[28] = 0; + + n = dumbfile_getc(f); + + if (n != 0x1A && n != 0) { + free(sigdata); + return NULL; + } + + if (dumbfile_getc(f) != 16) { + free(sigdata); + return NULL; + } + + dumbfile_skip(f, 2); + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_orders = dumbfile_igetw(f); + sigdata->n_instruments = 0; + sigdata->n_samples = dumbfile_igetw(f); + sigdata->n_patterns = dumbfile_igetw(f); + + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->order = malloc(sigdata->n_orders); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->n_samples) { + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + } + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + flags = dumbfile_igetw(f); + + *cwtv = dumbfile_igetw(f); + + if (*cwtv == 0x1300) { + /** WARNING: volume slides on every frame */ + } + + ffi = dumbfile_igetw(f); + + /** WARNING: which ones? */ + sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M; + + if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->global_volume = dumbfile_getc(f); + if ( !sigdata->global_volume || sigdata->global_volume > 64 ) sigdata->global_volume = 64; + sigdata->speed = dumbfile_getc(f); + if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? + sigdata->tempo = dumbfile_getc(f); + master_volume = dumbfile_getc(f); // 7 bits; +128 for stereo + sigdata->mixing_volume = master_volume & 127; + + if (master_volume & 128) sigdata->flags |= IT_STEREO; + + /* Skip GUS Ultra Click Removal byte. */ + dumbfile_getc(f); + + default_pan_present = dumbfile_getc(f); + + dumbfile_skip(f, 8); + + /* Skip Special Custom Data Pointer. */ + /** WARNING: investigate this? */ + dumbfile_igetw(f); + + sigdata->n_pchannels = 0; + /* Channel settings for 32 channels, 255=unused, +128=disabled */ + { + int i; + for (i = 0; i < 32; i++) { + int c = dumbfile_getc(f); + if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */ + if (sigdata->n_pchannels < i + 1) sigdata->n_pchannels = i + 1; + sigdata->channel_volume[i] = 64; + sigdata->channel_pan[i] = c & 8 ? 12 : 3; + /** WARNING: ah, but it should be 7 for mono... */ + } else { + /** WARNING: this could be improved if we support channel muting... */ + sigdata->channel_volume[i] = 0; + sigdata->channel_pan[i] = 7; + } + } + } + + /* Orders, byte each, length = sigdata->n_orders (should be even) */ + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + sigdata->restart_position = 0; + + component = malloc(768*sizeof(*component)); + if (!component) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < sigdata->n_samples; n++) { + component[n_components].type = S3M_COMPONENT_SAMPLE; + component[n_components].n = n; + component[n_components].offset = dumbfile_igetw(f) << 4; + component[n_components].sampfirst = -1; + n_components++; + } + + for (n = 0; n < sigdata->n_patterns; n++) { + long offset = dumbfile_igetw(f) << 4; + if (offset) { + component[n_components].type = S3M_COMPONENT_PATTERN; + component[n_components].n = n; + component[n_components].offset = offset; + component[n_components].sampfirst = -1; + n_components++; + } else { + /** WARNING: Empty 64-row pattern ... ? (this does happen!) */ + sigdata->pattern[n].n_rows = 64; + sigdata->pattern[n].n_entries = 0; + } + } + + qsort(component, n_components, sizeof(S3M_COMPONENT), &s3m_component_compare); + + /* I found a really dumb S3M file that claimed to contain default pan + * data but didn't contain any. Programs would load it by reading part of + * the first instrument header, assuming the data to be default pan + * positions, and then rereading the instrument module. We cannot do this + * without obfuscating the file input model, so we insert an extra check + * here that we won't overrun the start of the first component. + */ + if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) { + /* Channel default pan positions */ + int i; + for (i = 0; i < 32; i++) { + int c = dumbfile_getc(f); + if (c & 32) + sigdata->channel_pan[i] = c & 15; + } + } + + { + int i; + for (i = 0; i < 32; i++) { + sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3; + sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7; + } + } + + sigdata->pan_separation = 128; + + if (dumbfile_error(f)) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + buffer = malloc(65536); + if (!buffer) { + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + for (n = 0; n < n_components; n++) { + long offset; + int m; + + offset = 0; + if (dumbfile_seek(f, component[n].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + switch (component[n].type) { + + case S3M_COMPONENT_PATTERN: + if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + break; + + case S3M_COMPONENT_SAMPLE: + if (it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, &sample_pack[component[n].n], *cwtv, f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) { + short *sample; + + for (m = n + 1; m < n_components; m++) + if (component[m].offset > offset) + break; + m--; + + sample = &component[m].sampfirst; + + while (*sample >= 0 && component[*sample].offset <= offset) + sample = &component[*sample].sampnext; + + component[n].sampnext = *sample; + *sample = n; + + component[n].offset = offset; + } + } + + m = component[n].sampfirst; + + while (m >= 0) { + // XXX + if (dumbfile_seek(f, component[m].offset, DFS_SEEK_SET)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, sample_pack[component[m].n], f)) { + free(buffer); + free(component); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + m = component[m].sampnext; + } + } + + free(buffer); + free(component); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + +static char hexdigit(int in) +{ + if (in < 10) return in + '0'; + else return in + 'A' - 10; +} + +DUH *dumb_read_s3m_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int cwtv; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_s3m_load_sigdata(f, &cwtv); + + if (!sigdata) + return NULL; + + { + char version[8]; + const char *tag[3][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + tag[1][1] = "S3M"; + tag[2][0] = "TRACKERVERSION"; + version[0] = hexdigit((cwtv >> 8) & 15); + version[1] = '.'; + version[2] = hexdigit((cwtv >> 4) & 15); + version[3] = hexdigit(cwtv & 15); + version[4] = 0; + tag[2][1] = (const char *) &version; + return make_duh(-1, 3, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/reads3m2.c b/Frameworks/Dumb/dumb/src/it/reads3m2.c index c9447c260..0499eecde 100644 --- a/Frameworks/Dumb/dumb/src/it/reads3m2.c +++ b/Frameworks/Dumb/dumb/src/it/reads3m2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * reads3m2.c - Function to read a ScreamTracker 3 / / \ \ - * module from an open file and do an | < / \_ - * initial run-through. | \/ /\ / - * \_ / > / - * Split off from reads3m.c by entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_read_s3m(DUMBFILE *f) -{ - DUH *duh = dumb_read_s3m_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * reads3m2.c - Function to read a ScreamTracker 3 / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * Split off from reads3m.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_s3m(DUMBFILE *f) +{ + DUH *duh = dumb_read_s3m_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readstm.c b/Frameworks/Dumb/dumb/src/it/readstm.c new file mode 100644 index 000000000..2cbe715d6 --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readstm.c @@ -0,0 +1,395 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readstm.c - Code to read a ScreamTracker 2 / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Chris Moeller. \_ / > / + * | \ / / + * | ' / + * \__/ + */ + +// IT_STEREO... :o +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +#ifdef _MSC_VER + #define strnicmp _strnicmp +#else + #if defined(unix) || defined(__unix__) || defined(__unix) + #include + #endif + #define strnicmp strncasecmp +#endif + +static int it_stm_read_sample_header( IT_SAMPLE *sample, DUMBFILE *f, unsigned short *offset ) +{ + dumbfile_getnc( (char *) sample->filename, 12, f ); + sample->filename[12] = 0; + + memcpy( sample->name, sample->filename, 13 ); + + dumbfile_skip( f, 2 ); + + *offset = dumbfile_igetw( f ); + + sample->length = dumbfile_igetw( f ); + sample->loop_start = dumbfile_igetw( f ); + sample->loop_end = dumbfile_igetw( f ); + + sample->default_volume = dumbfile_getc( f ); + + dumbfile_skip( f, 1 ); + + sample->C5_speed = dumbfile_igetw( f ) << 3; + + dumbfile_skip( f, 6 ); + + if ( sample->length < 4 || !sample->default_volume ) { + /* Looks like no-existy. */ + sample->flags &= ~IT_SAMPLE_EXISTS; + sample->length = 0; + return dumbfile_error( f ); + } + + sample->flags = IT_SAMPLE_EXISTS; + sample->global_volume = 64; + sample->default_pan = 0; // 0 = don't use, or 160 = centre? + + if ( ( sample->loop_start < sample->length ) && + ( sample->loop_end > sample->loop_start ) && + ( sample->loop_end != 0xFFFF ) ) { + sample->flags |= IT_SAMPLE_LOOP; + if ( sample->loop_end > sample->length ) sample->loop_end = sample->length; + } + + //Do we need to set all these? + sample->vibrato_speed = 0; + sample->vibrato_depth = 0; + sample->vibrato_rate = 0; + sample->vibrato_waveform = IT_VIBRATO_SINE; + sample->finetune = 0; + sample->max_resampling_quality = -1; + + return dumbfile_error(f); +} + +static int it_stm_read_sample_data( IT_SAMPLE *sample, DUMBFILE * f ) +{ + if ( ! sample->length ) return 0; + + sample->data = malloc( sample->length ); + if (!sample->data) + return -1; + + dumbfile_getnc( sample->data, sample->length, f ); + + return dumbfile_error( f ); +} + +static int it_stm_read_pattern( IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer ) +{ + int pos; + int channel; + int row; + IT_ENTRY *entry; + + pattern->n_rows = 64; + + if ( dumbfile_getnc( (char *) buffer, 64 * 4 * 4, f ) != 64 * 4 * 4 ) + return -1; + + pattern->n_entries = 64; + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 4; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) + ++pattern->n_entries; + pos += 4; + } + } + + pattern->entry = malloc( pattern->n_entries * sizeof( *pattern->entry ) ); + if ( !pattern->entry ) + return -1; + + entry = pattern->entry; + pos = 0; + for ( row = 0; row < 64; ++row ) { + for ( channel = 0; channel < 4; ++channel ) { + if ( buffer[ pos + 0 ] | buffer[ pos + 1 ] | buffer[ pos + 2 ] | buffer[ pos + 3 ] ) { + unsigned note; + note = buffer[ pos + 0 ]; + entry->channel = channel; + entry->mask = 0; + entry->instrument = buffer[ pos + 1 ] >> 3; + entry->volpan = ( buffer[ pos + 1 ] & 0x07 ) + ( buffer[ pos + 2 ] >> 1 ); + entry->effect = buffer[ pos + 2 ] & 0x0F; + entry->effectvalue = buffer[ pos + 3 ]; + if ( entry->instrument && entry->instrument < 32 ) + entry->mask |= IT_ENTRY_INSTRUMENT; + if ( note < 251 ) { + entry->mask |= IT_ENTRY_NOTE; + entry->note = ( note >> 4 ) * 12 + ( note & 0x0F ); + } + if ( entry->volpan <= 64 ) + entry->mask |= IT_ENTRY_VOLPAN; + entry->mask |= IT_ENTRY_EFFECT; + switch ( entry->effect ) { + case IT_SET_SPEED: + /* taken care of in the renderer */ + break; + + case IT_BREAK_TO_ROW: + entry->effectvalue -= (entry->effectvalue >> 4) * 6; + break; + + case IT_JUMP_TO_ORDER: + case IT_VOLUME_SLIDE: + case IT_PORTAMENTO_DOWN: + case IT_PORTAMENTO_UP: + case IT_TONE_PORTAMENTO: + case IT_VIBRATO: + case IT_TREMOR: + case IT_ARPEGGIO: + case IT_VOLSLIDE_VIBRATO: + case IT_VOLSLIDE_TONEPORTA: + break; + + default: + entry->mask &= ~IT_ENTRY_EFFECT; + break; + } + if ( entry->mask ) ++entry; + } + pos += 4; + } + IT_SET_END_ROW(entry); + ++entry; + } + + pattern->n_entries = entry - pattern->entry; + + return 0; +} + + + +static DUMB_IT_SIGDATA *it_stm_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + + char tracker_name[ 8 ]; + + unsigned short sample_offset[ 31 ]; + + int n; + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) return NULL; + + /* Skip song name. */ + dumbfile_getnc((char *)sigdata->name, 20, f); + sigdata->name[20] = 0; + + dumbfile_getnc(tracker_name, 8, f); + n = dumbfile_getc(f); + if ( n != 0x02 && n != 0x1A && n != 0x1B ) + { + free( sigdata ); + return NULL; + } + if ( dumbfile_getc(f) != 2 ) /* only support modules */ + { + free( sigdata ); + return NULL; + } + if ( strnicmp( tracker_name, "!Scream!", 8 ) && + strnicmp( tracker_name, "BMOD2STM", 8 ) && + strnicmp( tracker_name, "WUZAMOD!", 8 ) ) + { + free( sigdata ); + return NULL; + } + + *version = dumbfile_mgetw(f); + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_instruments = 0; + sigdata->n_samples = 31; + sigdata->n_pchannels = 4; + + sigdata->tempo = 125; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + /** WARNING: which ones? */ + sigdata->flags = IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_WAS_AN_S3M | IT_WAS_AN_STM | IT_STEREO; + + n = dumbfile_getc(f); + if ( n < 32 ) n = 32; + sigdata->speed = n; + sigdata->n_patterns = dumbfile_getc(f); + sigdata->global_volume = dumbfile_getc(f) << 1; + if ( sigdata->global_volume > 128 ) sigdata->global_volume = 128; + + dumbfile_skip(f, 13); + + if ( dumbfile_error(f) || sigdata->n_patterns < 1 || sigdata->n_patterns > 99 ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample)); + if (!sigdata->sample) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_samples; n++) + sigdata->sample[n].data = NULL; + + if (sigdata->n_patterns) { + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (n = 0; n < sigdata->n_patterns; n++) + sigdata->pattern[n].entry = NULL; + } + + memset( sigdata->channel_volume, 64, 4 ); + sigdata->channel_pan[ 0 ] = 48; + sigdata->channel_pan[ 1 ] = 16; + sigdata->channel_pan[ 2 ] = 48; + sigdata->channel_pan[ 3 ] = 16; + + for ( n = 0; n < sigdata->n_samples; ++n ) { + if ( it_stm_read_sample_header( &sigdata->sample[ n ], f, &sample_offset[ n ] ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + + sigdata->order = malloc( 128 ); + if ( !sigdata->order ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + /* Orders, byte each, length = sigdata->n_orders (should be even) */ + dumbfile_getnc( (char *) sigdata->order, *version >= 0x200 ? 128 : 64, f ); + if (*version < 0x200) memset( sigdata->order + 64, 0xFF, 64 ); + sigdata->restart_position = 0; + + for ( n = 127; n >= 0; --n ) { + if ( sigdata->order[ n ] < sigdata->n_patterns ) break; + } + if ( n < 0 ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + sigdata->n_orders = n + 1; + + for ( n = 0; n < 128; ++n ) { + if ( sigdata->order[ n ] >= 99 ) sigdata->order[ n ] = 0xFF; + } + + if ( sigdata->n_patterns ) { + unsigned char * buffer = malloc( 64 * 4 * 4 ); + if ( ! buffer ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + for ( n = 0; n < sigdata->n_patterns; ++n ) { + if ( it_stm_read_pattern( &sigdata->pattern[ n ], f, buffer ) ) { + free( buffer ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + free( buffer ); + } + + for ( n = 0; n < sigdata->n_samples; ++n ) { + if ( sample_offset[ n ] ) + { + if ( dumbfile_seek( f, sample_offset[ n ] * 16, DFS_SEEK_SET ) || + it_stm_read_sample_data( &sigdata->sample[ n ], f ) ) { + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + } + else + { + sigdata->sample[ n ].flags = 0; + sigdata->sample[ n ].length = 0; + } + } + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + +DUH *dumb_read_stm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_stm_load_sigdata(f , &ver); + + if (!sigdata) + return NULL; + + { + char version[16]; + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + version[0] = 'S'; + version[1] = 'T'; + version[2] = 'M'; + version[3] = ' '; + version[4] = 'v'; + version[5] = '0' + ((ver >> 8) & 15); + version[6] = '.'; + if ((ver & 255) > 99) + { + version[7] = '0' + ((ver & 255) / 100 ); + version[8] = '0' + (((ver & 255) / 10) % 10); + version[9] = '0' + ((ver & 255) % 10); + version[10] = 0; + } + else + { + version[7] = '0' + ((ver & 255) / 10); + version[8] = '0' + ((ver & 255) % 10); + version[9] = 0; + } + tag[1][1] = (const char *) &version; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readstm2.c b/Frameworks/Dumb/dumb/src/it/readstm2.c new file mode 100644 index 000000000..c336b2a3d --- /dev/null +++ b/Frameworks/Dumb/dumb/src/it/readstm2.c @@ -0,0 +1,29 @@ +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readstm2.c - Function to read a ScreamTracker 2 / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * By Chris Moeller. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_stm(DUMBFILE *f) +{ + DUH *duh = dumb_read_stm_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/readxm.c b/Frameworks/Dumb/dumb/src/it/readxm.c index 53f80d50e..03a73e6b0 100644 --- a/Frameworks/Dumb/dumb/src/it/readxm.c +++ b/Frameworks/Dumb/dumb/src/it/readxm.c @@ -1,1007 +1,1417 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * readxm.c - Code to read a Fast Tracker II / / \ \ - * module from an open file. | < / \_ - * | \/ /\ / - * By Julien Cugniere. Some bits of code taken \_ / > / - * from reads3m.c. | \ / / - * | ' / - * \__/ - */ - -#include -#include -#include - -#include "dumb.h" -#include "internal/it.h" - - - -/** TODO: - - * XM_TREMOLO doesn't sound quite right... - * XM_E_SET_FINETUNE done but not tested yet. - * XM_SET_ENVELOPE_POSITION todo. - - * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check - that effect memory is correct when using XM_VOLSLIDE_VIBRATO. - - sample vibrato (instrument vibrato) is now handled correctly. - entheh - - * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when - a new instrument is played. In retrigger_note()?. Is it worth implementing? - - * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all. - - * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader. - - * A lot of things need to be reset when the end of the song is reached. - - * It seems that IT and XM don't behave the same way when dealing with - mixed loops. When IT encounters multiple SBx (x>0) commands on the same - row, it decrements the loop count for all, but only execute the loop of - the last one (highest channel). FT2 only decrements the loop count of the - last one. Not that I know of any modules using so convoluted combinations! - - * Maybe we could remove patterns that don't appear in the order table ? Or - provide a function to "optimize" a DUMB_IT_SIGDATA ? - -*/ - - - -#define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */ - -#define XM_ENTRY_PACKED 128 -#define XM_ENTRY_NOTE 1 -#define XM_ENTRY_INSTRUMENT 2 -#define XM_ENTRY_VOLUME 4 -#define XM_ENTRY_EFFECT 8 -#define XM_ENTRY_EFFECTVALUE 16 - -#define XM_NOTE_OFF 97 - -#define XM_ENVELOPE_ON 1 -#define XM_ENVELOPE_SUSTAIN 2 -#define XM_ENVELOPE_LOOP 4 - -#define XM_SAMPLE_NO_LOOP 0 -#define XM_SAMPLE_FORWARD_LOOP 1 -#define XM_SAMPLE_PINGPONG_LOOP 2 -#define XM_SAMPLE_16BIT 16 -#define XM_SAMPLE_STEREO 32 - -#define XM_VIBRATO_SINE 0 -#define XM_VIBRATO_SQUARE 1 -#define XM_VIBRATO_RAMP_DOWN 2 -#define XM_VIBRATO_RAMP_UP 3 - - - -/* Probably useless :) */ -static const char xm_convert_vibrato[] = { - IT_VIBRATO_SINE, - IT_VIBRATO_SQUARE, - IT_VIBRATO_SAWTOOTH, - IT_VIBRATO_SAWTOOTH -}; - - - -#define XM_MAX_SAMPLES_PER_INSTRUMENT 16 - - - -/* Extra data that doesn't fit inside IT_INSTRUMENT */ -typedef struct XM_INSTRUMENT_EXTRA -{ - int n_samples; - int vibrato_type; - int vibrato_sweep; /* 0-0xFF */ - int vibrato_depth; /* 0-0x0F */ - int vibrato_speed; /* 0-0x3F */ -} -XM_INSTRUMENT_EXTRA; - - - -/* Frees the original block if it can't resize it or if size is 0, and acts - * as malloc if ptr is NULL. - */ -static void *safe_realloc(void *ptr, size_t size) -{ - if (ptr == NULL) - return malloc(size); - - if (size == 0) { - free(ptr); - return NULL; - } else { - void *new_block = realloc(ptr, size); - if (!new_block) - free(ptr); - return new_block; - } -} - - - -/* The interpretation of the XM volume column is left to the player. Here, we - * just filter bad values. - */ -// This function is so tiny now, should we inline it? -static void it_xm_convert_volume(int volume, IT_ENTRY *entry) -{ - entry->mask |= IT_ENTRY_VOLPAN; - entry->volpan = volume; - - switch (volume >> 4) { - case 0xA: /* set vibrato speed */ - case 0xB: /* vibrato */ - case 0xF: /* tone porta */ - case 0x6: /* vol slide up */ - case 0x7: /* vol slide down */ - case 0x8: /* fine vol slide up */ - case 0x9: /* fine vol slide down */ - case 0xC: /* set panning */ - case 0xD: /* pan slide left */ - case 0xE: /* pan slide right */ - case 0x1: /* set volume */ - case 0x2: /* set volume */ - case 0x3: /* set volume */ - case 0x4: /* set volume */ - break; - - case 0x5: - if (volume == 0x50) - break; /* set volume */ - /* else fall through */ - - default: - entry->mask &= ~IT_ENTRY_VOLPAN; - break; - } -} - - - -static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer) -{ - int size; - int pos; - int channel; - int row; - int effect, effectvalue; - IT_ENTRY *entry; - - /* pattern header size */ - if (dumbfile_igetl(f) != 0x09) { - TRACE("XM error: unexpected pattern header size\n"); - return -1; - } - - /* pattern data packing type */ - if (dumbfile_getc(f) != 0) { - TRACE("XM error: unexpected pattern packing type\n"); - return -1; - } - - pattern->n_rows = dumbfile_igetw(f); /* 1..256 */ - size = dumbfile_igetw(f); - pattern->n_entries = 0; - - if (dumbfile_error(f)) - return -1; - - if (size == 0) - return 0; - - if (size > 1280 * n_channels) { - TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels); - return -1; - } - - if (dumbfile_getnc(buffer, size, f) < size) - return -1; - - /* compute number of entries */ - pattern->n_entries = 0; - pos = channel = row = 0; - while (pos < size) { - if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31)) - pattern->n_entries++; - - channel++; - if (channel >= n_channels) { - channel = 0; - row++; - pattern->n_entries++; - } - - if (buffer[pos] & XM_ENTRY_PACKED) { - static const char offset[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, - 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 }; - pos += 1 + offset[buffer[pos] & 31]; - } else { - pos += 5; - } - } - - if (row != pattern->n_rows) { - TRACE("XM error: wrong number of rows in pattern data\n"); - return -1; - } - - pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); - if (!pattern->entry) - return -1; - - /* read the entries */ - entry = pattern->entry; - pos = channel = row = 0; - while (pos < size) { - unsigned char mask; - - if (buffer[pos] & XM_ENTRY_PACKED) - mask = buffer[pos++] & 31; - else - mask = 31; - - if (mask) { - ASSERT(entry < pattern->entry + pattern->n_entries); - - entry->channel = channel; - entry->mask = 0; - - if (mask & XM_ENTRY_NOTE) { - int note = buffer[pos++]; /* 1-96 <=> C0-B7 */ - entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note-1); - entry->mask |= IT_ENTRY_NOTE; - } - - if (mask & XM_ENTRY_INSTRUMENT) { - entry->instrument = buffer[pos++]; /* 1-128 */ - entry->mask |= IT_ENTRY_INSTRUMENT; - } - - if (mask & XM_ENTRY_VOLUME) - it_xm_convert_volume(buffer[pos++], entry); - - effect = effectvalue = 0; - if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++]; - if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++]; - _dumb_it_xm_convert_effect(effect, effectvalue, entry); - - entry++; - } - - channel++; - if (channel >= n_channels) { - channel = 0; - row++; - IT_SET_END_ROW(entry); - entry++; - } - } - - return 0; -} - - - -static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset) -{ - int i, pos; - - if (envelope->n_nodes > 12) { - TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); - envelope->n_nodes = 0; - return -1; - } - - pos = 0; - for (i = 0; i < envelope->n_nodes; i++) { - envelope->node_t[i] = data[pos++]; - if (data[pos] > 64) { - TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]); - envelope->n_nodes = 0; - return -1; - } - envelope->node_y[i] = (signed char)(data[pos++] + y_offset); - } - - return 0; -} - - - -static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f) -{ - unsigned long size, bytes_read; - unsigned short vol_points[24]; - unsigned short pan_points[24]; - int i, type; - - /* Header size. Tends to be more than the actual size of the structure. - * So unread bytes must be skipped before reading the first sample - * header. - */ - size = dumbfile_igetl(f); - - dumbfile_getnc(instrument->name, 22, f); - instrument->name[22] = 0; - instrument->filename[0] = 0; - dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */ - extra->n_samples = dumbfile_igetw(f); - - if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT) - return -1; - - bytes_read = 4 + 22 + 1 + 2; - - if (extra->n_samples) { - /* sample header size */ - if (dumbfile_igetl(f) != 0x28) { - TRACE("XM error: unexpected sample header size\n"); - return -1; - } - - /* sample map */ - for (i = 0; i < 96; i++) { - instrument->map_sample[i] = dumbfile_getc(f) + 1; - instrument->map_note[i] = i; - } - - if (dumbfile_error(f)) - return 1; - - /* volume/panning envelopes */ - for (i = 0; i < 24; i++) - vol_points[i] = dumbfile_igetw(f); - for (i = 0; i < 24; i++) - pan_points[i] = dumbfile_igetw(f); - - instrument->volume_envelope.n_nodes = dumbfile_getc(f); - instrument->pan_envelope.n_nodes = dumbfile_getc(f); - - if (dumbfile_error(f)) - return -1; - - instrument->volume_envelope.sus_loop_start = dumbfile_getc(f); - instrument->volume_envelope.loop_start = dumbfile_getc(f); - instrument->volume_envelope.loop_end = dumbfile_getc(f); - - instrument->pan_envelope.sus_loop_start = dumbfile_getc(f); - instrument->pan_envelope.loop_start = dumbfile_getc(f); - instrument->pan_envelope.loop_end = dumbfile_getc(f); - - /* The envelope handler for XM files won't use sus_loop_end. */ - - type = dumbfile_getc(f); - instrument->volume_envelope.flags = 0; - if ((type & XM_ENVELOPE_ON) && instrument->volume_envelope.n_nodes) - instrument->volume_envelope.flags |= IT_ENVELOPE_ON; - if (type & XM_ENVELOPE_LOOP) instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON; -#if 1 - if (type & XM_ENVELOPE_SUSTAIN) instrument->volume_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP; -#else // This is now handled in itrender.c - /* let's avoid fading out when reaching the last envelope node */ - if (!(type & XM_ENVELOPE_LOOP)) { - instrument->volume_envelope.loop_start = instrument->volume_envelope.n_nodes-1; - instrument->volume_envelope.loop_end = instrument->volume_envelope.n_nodes-1; - } - instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON; -#endif - - type = dumbfile_getc(f); - instrument->pan_envelope.flags = 0; - if ((type & XM_ENVELOPE_ON) && instrument->pan_envelope.n_nodes) - instrument->pan_envelope.flags |= IT_ENVELOPE_ON; - if (type & XM_ENVELOPE_LOOP) instrument->pan_envelope.flags |= IT_ENVELOPE_LOOP_ON; // should this be here? - if (type & XM_ENVELOPE_SUSTAIN) instrument->pan_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP; - - if (it_xm_make_envelope(&instrument->volume_envelope, vol_points, 0) != 0) { - TRACE("XM error: volume envelope\n"); - if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) return -1; - } - - if (it_xm_make_envelope(&instrument->pan_envelope, pan_points, -32) != 0) { - TRACE("XM error: pan envelope\n"); - if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) return -1; - } - - instrument->pitch_envelope.flags = 0; - - extra->vibrato_type = dumbfile_getc(f); - extra->vibrato_sweep = dumbfile_getc(f); - extra->vibrato_depth = dumbfile_getc(f); - extra->vibrato_speed = dumbfile_getc(f); - - if (dumbfile_error(f) || extra->vibrato_type >= 4) - return -1; - - /** WARNING: lossy approximation */ - instrument->fadeout = (dumbfile_igetw(f)*128 + 64)/0xFFF; - - dumbfile_skip(f, 2); /* reserved */ - - bytes_read += 4 + 96 + 48 + 48 + 14*1 + 2 + 2; - } else - for (i = 0; i < 96; i++) - instrument->map_sample[i] = 0; - - if (dumbfile_skip(f, size - bytes_read)) - return -1; - - instrument->new_note_action = NNA_NOTE_CUT; - instrument->dup_check_type = DCT_OFF; - instrument->dup_check_action = DCA_NOTE_CUT; - instrument->pp_separation = 0; - instrument->pp_centre = 60; /* C-5 */ - instrument->global_volume = 128; - instrument->default_pan = 32; - instrument->random_volume = 0; - instrument->random_pan = 0; - instrument->filter_cutoff = 0; - instrument->filter_resonance = 0; - - return 0; -} - - - -/* I (entheh) have two XM files saved by a very naughty program. After a - * 16-bit sample, it saved a rogue byte. The length of the sample was indeed - * an odd number, incremented to include the rogue byte. - * - * In this function we are converting sample lengths and loop points so they - * are measured in samples. This means we forget about the extra bytes, and - * they don't get skipped. So we fail trying to read the next instrument. - * - * To get around this, this function returns the number of rogue bytes that - * won't be accounted for by reading sample->length samples. It returns a - * negative number on failure. - */ -static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) -{ - int type; - int relative_note_number; /* relative to C4 */ - int finetune; - int roguebytes; - int roguebytesmask; - - sample->length = dumbfile_igetl(f); - sample->loop_start = dumbfile_igetl(f); - sample->loop_end = sample->loop_start + dumbfile_igetl(f); - sample->global_volume = 64; - sample->default_volume = dumbfile_getc(f); - finetune = (signed char)dumbfile_getc(f); /* -128..127 <=> -1 semitone .. +127/128 of a semitone */ - type = dumbfile_getc(f); - sample->default_pan = dumbfile_getc(f); /* 0-255 */ - relative_note_number = (signed char)dumbfile_getc(f); - - dumbfile_skip(f, 1); /* reserved */ - - dumbfile_getnc(sample->name, 22, f); - sample->name[22] = 0; - - sample->filename[0] = 0; - - if (dumbfile_error(f)) - return -1; - - sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number)*pow(DUMB_PITCH_BASE, finetune*2)); - - sample->flags = IT_SAMPLE_EXISTS; - - roguebytes = (int)sample->length; - roguebytesmask = 3; - - if (type & XM_SAMPLE_16BIT) { - sample->flags |= IT_SAMPLE_16BIT; - sample->length >>= 1; - sample->loop_start >>= 1; - sample->loop_end >>= 1; - } else - roguebytesmask >>= 1; - - if (type & XM_SAMPLE_STEREO) { - sample->flags |= IT_SAMPLE_STEREO; - sample->length >>= 1; - sample->loop_start >>= 1; - sample->loop_end >>= 1; - } else - roguebytesmask >>= 1; - - roguebytes &= roguebytesmask; - - if ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end) { - if (type & XM_SAMPLE_FORWARD_LOOP) sample->flags |= IT_SAMPLE_LOOP; - if (type & XM_SAMPLE_PINGPONG_LOOP) sample->flags |= IT_SAMPLE_LOOP | IT_SAMPLE_PINGPONG_LOOP; - } - - if (sample->length <= 0) - sample->flags &= ~IT_SAMPLE_EXISTS; - else if ((unsigned int)sample->loop_end > (unsigned int)sample->length) - sample->flags &= ~IT_SAMPLE_LOOP; - else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) - sample->flags &= ~IT_SAMPLE_LOOP; - - return roguebytes; -} - - - -static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f) -{ - int old; - long i; - long truncated_size; - int n_channels; - long datasize; - - if (!(sample->flags & IT_SAMPLE_EXISTS)) - return dumbfile_skip(f, roguebytes); - - /* let's get rid of the sample data coming after the end of the loop */ - if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) { - truncated_size = sample->length - sample->loop_end; - sample->length = sample->loop_end; - } else { - truncated_size = 0; - } - - n_channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; - datasize = sample->length * n_channels; - - sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); - if (!sample->data) - return -1; - - /* sample data is stored as signed delta values */ - old = 0; - if (sample->flags & IT_SAMPLE_16BIT) - for (i = 0; i < sample->length; i++) - ((short *)sample->data)[i*n_channels] = old += dumbfile_igetw(f); - else - for (i = 0; i < sample->length; i++) - ((signed char *)sample->data)[i*n_channels] = old += dumbfile_getc(f); - - /* skip truncated data */ - dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); - - if (sample->flags & IT_SAMPLE_STEREO) { - old = 0; - if (sample->flags & IT_SAMPLE_16BIT) - for (i = 1; i < datasize; i += 2) - ((short *)sample->data)[i] = old += dumbfile_igetw(f); - else - for (i = 1; i < datasize; i += 2) - ((signed char *)sample->data)[i] = old += dumbfile_getc(f); - - /* skip truncated data */ - dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); - } - - dumbfile_skip(f, roguebytes); - - if (dumbfile_error(f)) - return -1; - - return 0; -} - - - -/* "Real programmers don't document. If it was hard to write, - * it should be hard to understand." - * - * (Never trust the documentation provided with a tracker. - * Real files are the only truth...) - */ -static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f) -{ - DUMB_IT_SIGDATA *sigdata; - char id_text[18]; - - int flags; - int n_channels; - int total_samples; - int i, j; - - /* check ID text */ - if (dumbfile_getnc(id_text, 17, f) < 17) - return NULL; - id_text[17] = 0; - if (strcmp(id_text, "Extended Module: ") != 0) { - TRACE("XM error: Not an Extended Module\n"); - return NULL; - } - - sigdata = malloc(sizeof(*sigdata)); - if (!sigdata) - return NULL; - - /* song name */ - if (dumbfile_getnc(sigdata->name, 20, f) < 20) { - free(sigdata); - return NULL; - } - sigdata->name[20] = 0; - - if (dumbfile_getc(f) != 0x1A) { - TRACE("XM error: 0x1A not found\n"); - free(sigdata); - return NULL; - } - - /* tracker name */ - if (dumbfile_skip(f, 20)) { - free(sigdata); - return NULL; - } - - /* version number */ - if (dumbfile_igetw(f) != 0x0104) { - TRACE("XM error: wrong format version\n"); - free(sigdata); - return NULL; - } - - /* - ------------------ - --- Header --- - ------------------ - */ - - /* header size */ - if (dumbfile_igetl(f) != 0x0114) { - TRACE("XM error: unexpected header size\n"); - free(sigdata); - return NULL; - } - - sigdata->song_message = NULL; - sigdata->order = NULL; - sigdata->instrument = NULL; - sigdata->sample = NULL; - sigdata->pattern = NULL; - sigdata->midi = NULL; - sigdata->checkpoint = NULL; - - sigdata->n_samples = 0; - sigdata->n_orders = dumbfile_igetw(f); - sigdata->restart_position = dumbfile_igetw(f); - n_channels = dumbfile_igetw(f); /* max 32 but we'll be lenient */ - sigdata->n_patterns = dumbfile_igetw(f); - sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */ - flags = dumbfile_igetw(f); - sigdata->speed = dumbfile_igetw(f); - if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? - sigdata->tempo = dumbfile_igetw(f); - - /* sanity checks */ - if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > 256 || sigdata->n_patterns > 256 || sigdata->n_instruments > 128 || n_channels > DUMB_IT_N_CHANNELS) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - //if (sigdata->restart_position >= sigdata->n_orders) - //sigdata->restart_position = 0; - - /* order table */ - sigdata->order = malloc(sigdata->n_orders*sizeof(*sigdata->order)); - if (!sigdata->order) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - dumbfile_getnc(sigdata->order, sigdata->n_orders, f); - dumbfile_skip(f, 256 - sigdata->n_orders); - - if (dumbfile_error(f)) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - /* - -------------------- - --- Patterns --- - -------------------- - */ - - sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); - if (!sigdata->pattern) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (i = 0; i < sigdata->n_patterns; i++) - sigdata->pattern[i].entry = NULL; - - { - unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */ - if (!buffer) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (i = 0; i < sigdata->n_patterns; i++) { - if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) { - free(buffer); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - } - free(buffer); - } - - /* - ----------------------------------- - --- Instruments and Samples --- - ----------------------------------- - */ - - sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); - if (!sigdata->instrument) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - /* With XM, samples are not global, they're part of an instrument. In a - * file, each instrument is stored with its samples. Because of this, I - * don't know how to find how many samples are present in the file. Thus - * I have to do n_instruments reallocation on sigdata->sample. - * Looking at FT2, it doesn't seem possible to have more than 16 samples - * per instrument (even though n_samples is stored as 2 bytes). So maybe - * we could allocate a 128*16 array of samples, and shrink it back to the - * correct size when we know it? - * Alternatively, I could allocate samples by blocks of N (still O(n)), - * or double the number of allocated samples when I need more (O(log n)). - */ - total_samples = 0; - sigdata->sample = NULL; - - for (i = 0; i < sigdata->n_instruments; i++) { - XM_INSTRUMENT_EXTRA extra; - - if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) { - TRACE("XM error: instrument %d\n", i+1); - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - - if (extra.n_samples) { - unsigned char roguebytes[XM_MAX_SAMPLES_PER_INSTRUMENT]; - - /* adjust instrument sample map (make indices absolute) */ - for (j = 0; j < 96; j++) - sigdata->instrument[i].map_sample[j] += total_samples; - - sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); - if (!sigdata->sample) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - for (j = total_samples; j < total_samples+extra.n_samples; j++) - sigdata->sample[j].data = NULL; - - /* read instrument's samples */ - for (j = 0; j < extra.n_samples; j++) { - IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; - int b = it_xm_read_sample_header(sample, f); - if (b < 0) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - roguebytes[j] = b; - // Any reason why these can't be set inside it_xm_read_sample_header()? - sample->vibrato_speed = extra.vibrato_speed; - sample->vibrato_depth = extra.vibrato_depth; - sample->vibrato_rate = extra.vibrato_sweep; - /* Rate and sweep don't match, but the difference is - * accounted for in itrender.c. - */ - sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type]; - } - for (j = 0; j < extra.n_samples; j++) { - if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) { - _dumb_it_unload_sigdata(sigdata); - return NULL; - } - } - total_samples += extra.n_samples; - } - } - - sigdata->n_samples = total_samples; - - sigdata->flags = IT_WAS_AN_XM | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_USE_INSTRUMENTS; - // Are we OK with IT_COMPATIBLE_GXX off? - // - // When specifying note + instr + tone portamento, and an old note is still playing (even after note off): - // - If Compatible Gxx is on, the new note will be triggered only if the instrument _changes_. - // - If Compatible Gxx is off, the new note will always be triggered, provided the instrument is specified. - // - FT2 seems to do the latter (unconfirmed). - - // Err, wait. XM playback has its own code. The change made to the IT - // playbackc code didn't affect XM playback. Forget this then. There's - // still a bug in XM playback though, and it'll need some investigation... - // tomorrow... - - // UPDATE: IT_COMPATIBLE_GXX is required to be on, so that tone porta has - // separate memory from portamento. - - if (flags & XM_LINEAR_FREQUENCY) - sigdata->flags |= IT_LINEAR_SLIDES; - - sigdata->global_volume = 128; - sigdata->mixing_volume = 48; - sigdata->pan_separation = 128; - - memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); - memset(sigdata->channel_pan, 32, DUMB_IT_N_CHANNELS); - - _dumb_it_fix_invalid_orders(sigdata); - - return sigdata; -} - - - -#if 0 // no fucking way, dude! - -/* The length returned is the time required to play from the beginning of the - * file to the last row of the last order (which is when the player will - * loop). Depending on the song, the sound might stop sooner. - * Due to fixed point roundoffs, I think this is only reliable to the second. - * Full precision could be achieved by using a double during the computation, - * or maybe a LONG_LONG. - */ -long it_compute_length(const DUMB_IT_SIGDATA *sigdata) -{ - IT_PATTERN *pattern; - int tempo, speed; - int loop_start[IT_N_CHANNELS]; - char loop_count[IT_N_CHANNELS]; - int order, entry; - int row_first_entry = 0; - int jump, jump_dest; - int delay, fine_delay; - int i; - long t; - - if (!sigdata) - return 0; - - tempo = sigdata->tempo; - speed = sigdata->speed; - order = entry = 0; - jump = jump_dest = 0; - t = 0; - - /* for each PATTERN */ - for (order = 0; order < sigdata->n_orders; order++) { - - if (sigdata->order[order] == IT_ORDER_END) break; - if (sigdata->order[order] == IT_ORDER_SKIP) continue; - - for (i = 0; i < IT_N_CHANNELS; i++) - loop_count[i] = -1; - - pattern = &sigdata->pattern[ sigdata->order[order] ]; - entry = 0; - if (jump == IT_BREAK_TO_ROW) { - int row = 0; - while (row < jump_dest) - if (pattern->entry[entry++].channel >= IT_N_CHANNELS) - row++; - } - - /* for each ROW */ - while (entry < pattern->n_entries) { - row_first_entry = entry; - delay = fine_delay = 0; - jump = 0; - - /* for each note NOTE */ - while (entry < pattern->n_entries && pattern->entry[entry].channel < IT_N_CHANNELS) { - int value = pattern->entry[entry].effectvalue; - int channel = pattern->entry[entry].channel; - - switch (pattern->entry[entry].effect) { - - case IT_SET_SPEED: speed = value; break; - - case IT_JUMP_TO_ORDER: - if (value <= order) /* infinite loop */ - return 0; - jump = IT_JUMP_TO_ORDER; - jump_dest = value; - break; - - case IT_BREAK_TO_ROW: - jump = IT_BREAK_TO_ROW; - jump_dest = value; - break; - - case IT_S: - switch (HIGH(value)) { - case IT_S_PATTERN_DELAY: delay = LOW(value); break; - case IT_S_FINE_PATTERN_DELAY: fine_delay = LOW(value); break; - case IT_S_PATTERN_LOOP: - if (LOW(value) == 0) { - loop_start[channel] = row_first_entry; - } else { - if (loop_count[channel] == -1) - loop_count[channel] = LOW(value); - - if (loop_count[channel]) { - jump = IT_S_PATTERN_LOOP; - jump_dest = loop_start[channel]; - } - loop_count[channel]--; - } - break; - } - break; - - case IT_SET_SONG_TEMPO: - switch (HIGH(value)) { /* slides happen every non-row frames */ - case 0: tempo = tempo - LOW(value)*(speed-1); break; - case 1: tempo = tempo + LOW(value)*(speed-1); break; - default: tempo = value; - } - tempo = MID(32, tempo, 255); - break; - } - - entry++; - } - - /* end of ROW */ - entry++; - t += TICK_TIME_DIVIDEND * (speed*(1+delay) + fine_delay) / tempo; - - if (jump == IT_JUMP_TO_ORDER) { - order = jump_dest - 1; - break; - } else if (jump == IT_BREAK_TO_ROW) - break; - else if (jump == IT_S_PATTERN_LOOP) - entry = jump_dest - 1; - } - - /* end of PATTERN */ - } - - return t; -} - -#endif /* 0 */ - - - -DUH *dumb_read_xm_quick(DUMBFILE *f) -{ - sigdata_t *sigdata; - - DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; - - sigdata = it_xm_load_sigdata(f); - - if (!sigdata) - return NULL; - - { - const char *tag[1][2]; - tag[0][0] = "TITLE"; - tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name; - return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata); - } -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readxm.c - Code to read a Fast Tracker II / / \ \ + * module from an open file. | < / \_ + * | \/ /\ / + * By Julien Cugniere. Some bits of code taken \_ / > / + * from reads3m.c. | \ / / + * | ' / + * \__/ + */ + +#include +#include +#include + +#include "dumb.h" +#include "internal/it.h" +#include "internal/dumbfile.h" + + + +/** TODO: + + * XM_TREMOLO doesn't sound quite right... + * XM_SET_ENVELOPE_POSITION todo. + + * VIBRATO conversion needs to be checked (sample/effect/volume). Plus check + that effect memory is correct when using XM_VOLSLIDE_VIBRATO. + - sample vibrato (instrument vibrato) is now handled correctly. - entheh + + * XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when + a new instrument is played. In retrigger_note()?. Is it worth implementing? + + * Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all. + + * Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader. + + * A lot of things need to be reset when the end of the song is reached. + + * It seems that IT and XM don't behave the same way when dealing with + mixed loops. When IT encounters multiple SBx (x>0) commands on the same + row, it decrements the loop count for all, but only execute the loop of + the last one (highest channel). FT2 only decrements the loop count of the + last one. Not that I know of any modules using so convoluted combinations! + + * Maybe we could remove patterns that don't appear in the order table ? Or + provide a function to "optimize" a DUMB_IT_SIGDATA ? + +*/ + + + +#define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */ + +#define XM_ENTRY_PACKED 128 +#define XM_ENTRY_NOTE 1 +#define XM_ENTRY_INSTRUMENT 2 +#define XM_ENTRY_VOLUME 4 +#define XM_ENTRY_EFFECT 8 +#define XM_ENTRY_EFFECTVALUE 16 + +#define XM_NOTE_OFF 97 + +#define XM_ENVELOPE_ON 1 +#define XM_ENVELOPE_SUSTAIN 2 +#define XM_ENVELOPE_LOOP 4 + +#define XM_SAMPLE_NO_LOOP 0 +#define XM_SAMPLE_FORWARD_LOOP 1 +#define XM_SAMPLE_PINGPONG_LOOP 2 +#define XM_SAMPLE_16BIT 16 +#define XM_SAMPLE_STEREO 32 + +#define XM_VIBRATO_SINE 0 +#define XM_VIBRATO_SQUARE 1 +#define XM_VIBRATO_RAMP_DOWN 2 +#define XM_VIBRATO_RAMP_UP 3 + + + +/* Probably useless :) */ +const char xm_convert_vibrato[] = { + IT_VIBRATO_SINE, + IT_VIBRATO_XM_SQUARE, + IT_VIBRATO_RAMP_DOWN, + IT_VIBRATO_RAMP_UP, + IT_VIBRATO_RANDOM +}; + + + +#define XM_MAX_SAMPLES_PER_INSTRUMENT 16 + + + +/* Extra data that doesn't fit inside IT_INSTRUMENT */ +typedef struct XM_INSTRUMENT_EXTRA +{ + int n_samples; + int vibrato_type; + int vibrato_sweep; /* 0-0xFF */ + int vibrato_depth; /* 0-0x0F */ + int vibrato_speed; /* 0-0x3F */ + int sample_header_size; +} +XM_INSTRUMENT_EXTRA; + + + +/* Trims off trailing white space, usually added by the tracker on file creation + */ +static void trim_whitespace(char *ptr, size_t size) +{ + char *p = ptr + size - 1; + while (p >= ptr && *p <= 0x20) *p-- = '\0'; +} + +/* Frees the original block if it can't resize it or if size is 0, and acts + * as malloc if ptr is NULL. + */ +static void *safe_realloc(void *ptr, size_t size) +{ + if (ptr == NULL) + return malloc(size); + + if (size == 0) { + free(ptr); + return NULL; + } else { + void *new_block = realloc(ptr, size); + if (!new_block) + free(ptr); + return new_block; + } +} + + + +/* The interpretation of the XM volume column is left to the player. Here, we + * just filter bad values. + */ +// This function is so tiny now, should we inline it? +static void it_xm_convert_volume(int volume, IT_ENTRY *entry) +{ + entry->mask |= IT_ENTRY_VOLPAN; + entry->volpan = volume; + + switch (volume >> 4) { + case 0xA: /* set vibrato speed */ + case 0xB: /* vibrato */ + case 0xF: /* tone porta */ + case 0x6: /* vol slide up */ + case 0x7: /* vol slide down */ + case 0x8: /* fine vol slide up */ + case 0x9: /* fine vol slide down */ + case 0xC: /* set panning */ + case 0xD: /* pan slide left */ + case 0xE: /* pan slide right */ + case 0x1: /* set volume */ + case 0x2: /* set volume */ + case 0x3: /* set volume */ + case 0x4: /* set volume */ + break; + + case 0x5: + if (volume == 0x50) + break; /* set volume */ + /* else fall through */ + + default: + entry->mask &= ~IT_ENTRY_VOLPAN; + break; + } +} + + + +static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer, int version) +{ + int size; + int pos; + int channel; + int row; + int effect, effectvalue; + IT_ENTRY *entry; + + /* pattern header size */ + if (dumbfile_igetl(f) != ( version == 0x0102 ? 0x08 : 0x09 ) ) { + TRACE("XM error: unexpected pattern header size\n"); + return -1; + } + + /* pattern data packing type */ + if (dumbfile_getc(f) != 0) { + TRACE("XM error: unexpected pattern packing type\n"); + return -1; + } + + if ( version == 0x0102 ) + pattern->n_rows = dumbfile_getc(f) + 1; + else + pattern->n_rows = dumbfile_igetw(f); /* 1..256 */ + size = dumbfile_igetw(f); + pattern->n_entries = 0; + + if (dumbfile_error(f)) + return -1; + + if (size == 0) + return 0; + + if (size > 1280 * n_channels) { + TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels); + return -1; + } + + if (dumbfile_getnc((char *)buffer, size, f) < size) + return -1; + + /* compute number of entries */ + pattern->n_entries = 0; + pos = channel = row = 0; + while (pos < size) { + if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31)) + pattern->n_entries++; + + channel++; + if (channel >= n_channels) { + channel = 0; + row++; + pattern->n_entries++; + } + + if (buffer[pos] & XM_ENTRY_PACKED) { + static const char offset[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 }; + pos += 1 + offset[buffer[pos] & 31]; + } else { + pos += 5; + } + } + + if (row > pattern->n_rows) { + TRACE("XM error: wrong number of rows in pattern data\n"); + return -1; + } + + /* Whoops, looks like some modules may be short, a few channels, maybe even rows... */ + + while (row < pattern->n_rows) + { + pattern->n_entries++; + row++; + } + + pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry)); + if (!pattern->entry) + return -1; + + /* read the entries */ + entry = pattern->entry; + pos = channel = row = 0; + while (pos < size) { + unsigned char mask; + + if (buffer[pos] & XM_ENTRY_PACKED) + mask = buffer[pos++] & 31; + else + mask = 31; + + if (mask) { + ASSERT(entry < pattern->entry + pattern->n_entries); + + entry->channel = channel; + entry->mask = 0; + + if (mask & XM_ENTRY_NOTE) { + int note = buffer[pos++]; /* 1-96 <=> C0-B7 */ + entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note-1); + entry->mask |= IT_ENTRY_NOTE; + } + + if (mask & XM_ENTRY_INSTRUMENT) { + entry->instrument = buffer[pos++]; /* 1-128 */ + entry->mask |= IT_ENTRY_INSTRUMENT; + } + + if (mask & XM_ENTRY_VOLUME) + it_xm_convert_volume(buffer[pos++], entry); + + effect = effectvalue = 0; + if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++]; + if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++]; + _dumb_it_xm_convert_effect(effect, effectvalue, entry, 0); + + entry++; + } + + channel++; + if (channel >= n_channels) { + channel = 0; + row++; + IT_SET_END_ROW(entry); + entry++; + } + } + + while (row < pattern->n_rows) + { + row++; + IT_SET_END_ROW(entry); + entry++; + } + + return 0; +} + + + +static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset) +{ + int i, pos, val; + + if (envelope->n_nodes > 12) { + /* XXX + TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes); + envelope->n_nodes = 0; + return -1; */ + envelope->n_nodes = 12; + } + + if (envelope->sus_loop_start >= 12) envelope->flags &= ~IT_ENVELOPE_SUSTAIN_LOOP; + if (envelope->loop_end >= 12) envelope->loop_end = 0; + if (envelope->loop_start >= envelope->loop_end) envelope->flags &= ~IT_ENVELOPE_LOOP_ON; + + pos = 0; + for (i = 0; i < envelope->n_nodes; i++) { + envelope->node_t[i] = data[pos++]; + val = data[pos++]; + if (val > 64) { + TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, val); + /* FT2 seems to simply clip the value */ + val = 64; + } + envelope->node_y[i] = (signed char)(val + y_offset); + } + + return 0; +} + + + +typedef struct LIMITED_XM LIMITED_XM; + +struct LIMITED_XM +{ + unsigned char *buffered; + long ptr, limit, allocated; + DUMBFILE *remaining; +}; + +static int limit_xm_resize(void *f, long n) +{ + DUMBFILE *df = f; + LIMITED_XM *lx = df->file; + if (lx->buffered || n) { + if (n > lx->allocated) { + unsigned char *buffered = realloc( lx->buffered, n ); + if ( !buffered ) return -1; + lx->buffered = buffered; + memset( buffered + lx->allocated, 0, n - lx->allocated ); + lx->allocated = n; + } + if ( dumbfile_getnc( (char *)lx->buffered, n, lx->remaining ) < n ) return -1; + } else if (!n) { + if ( lx->buffered ) free( lx->buffered ); + lx->buffered = NULL; + lx->allocated = 0; + } + lx->limit = n; + lx->ptr = 0; + return 0; +} + +static int limit_xm_skip_end(void *f, long n) +{ + DUMBFILE *df = f; + LIMITED_XM *lx = df->file; + return dumbfile_skip( lx->remaining, n ); +} + +static int limit_xm_skip(void *f, long n) +{ + LIMITED_XM *lx = f; + lx->ptr += n; + return 0; +} + + + +static int limit_xm_getc(void *f) +{ + LIMITED_XM *lx = f; + if (lx->ptr >= lx->allocated) { + return 0; + } + return lx->buffered[lx->ptr++]; +} + + + +static long limit_xm_getnc(char *ptr, long n, void *f) +{ + LIMITED_XM *lx = f; + int left; + left = lx->allocated - lx->ptr; + if (n > left) { + if (left > 0) { + memcpy( ptr, lx->buffered + lx->ptr, left ); + memset( ptr + left, 0, n - left ); + } else { + memset( ptr, 0, n ); + } + } else { + memcpy( ptr, lx->buffered + lx->ptr, n ); + } + lx->ptr += n; + return n; +} + + + +static void limit_xm_close(void *f) +{ + LIMITED_XM *lx = f; + if (lx->buffered) free(lx->buffered); + /* Do NOT close lx->remaining */ + free(f); +} + + +/* These two can be stubs since this implementation doesn't use seeking */ +static int limit_xm_seek(void *f, long n) +{ + (void)f; + (void)n; + return 1; +} + + + +static long limit_xm_get_size(void *f) +{ + (void)f; + return 0; +} + + + +DUMBFILE_SYSTEM limit_xm_dfs = { + NULL, + &limit_xm_skip, + &limit_xm_getc, + &limit_xm_getnc, + &limit_xm_close, + &limit_xm_seek, + &limit_xm_get_size +}; + +static DUMBFILE *dumbfile_limit_xm(DUMBFILE *f) +{ + LIMITED_XM * lx = malloc(sizeof(*lx)); + lx->remaining = f; + lx->buffered = NULL; + lx->ptr = 0; + lx->limit = 0; + lx->allocated = 0; + return dumbfile_open_ex( lx, &limit_xm_dfs ); +} + +static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f) +{ + unsigned long size, bytes_read; + unsigned short vol_points[24]; + unsigned short pan_points[24]; + int i, type; + const unsigned long max_size = 4 + 22 + 1 + 2 + 4 + 96 + 48 + 48 + 1 * 14 + 2 + 2; + unsigned long skip_end = 0; + + /* Header size. Tends to be more than the actual size of the structure. + * So unread bytes must be skipped before reading the first sample + * header. + */ + + if ( limit_xm_resize( f, 4 ) < 0 ) return -1; + + size = dumbfile_igetl(f); + + if ( size == 0 ) size = max_size; + else if ( size > max_size ) + { + skip_end = size - max_size; + size = max_size; + } + + if ( limit_xm_resize( f, size - 4 ) < 0 ) return -1; + + dumbfile_getnc((char *)instrument->name, 22, f); + instrument->name[22] = 0; + trim_whitespace((char *)instrument->name, 22); + instrument->filename[0] = 0; + dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */ + extra->n_samples = dumbfile_igetw(f); + + if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT) + return -1; + + bytes_read = 4 + 22 + 1 + 2; + + if (extra->n_samples) { + /* sample header size */ + /*i = dumbfile_igetl(f); + if (!i || i > 0x28) i = 0x28;*/ + dumbfile_skip(f, 4); + i = 0x28; + extra->sample_header_size = i; + + /* sample map */ + for (i = 0; i < 96; i++) { + instrument->map_sample[i] = dumbfile_getc(f) + 1; + instrument->map_note[i] = i; + } + + if (dumbfile_error(f)) + return 1; + + /* volume/panning envelopes */ + for (i = 0; i < 24; i++) + vol_points[i] = dumbfile_igetw(f); + for (i = 0; i < 24; i++) + pan_points[i] = dumbfile_igetw(f); + + instrument->volume_envelope.n_nodes = dumbfile_getc(f); + instrument->pan_envelope.n_nodes = dumbfile_getc(f); + + if (dumbfile_error(f)) + return -1; + + instrument->volume_envelope.sus_loop_start = dumbfile_getc(f); + instrument->volume_envelope.loop_start = dumbfile_getc(f); + instrument->volume_envelope.loop_end = dumbfile_getc(f); + + instrument->pan_envelope.sus_loop_start = dumbfile_getc(f); + instrument->pan_envelope.loop_start = dumbfile_getc(f); + instrument->pan_envelope.loop_end = dumbfile_getc(f); + + /* The envelope handler for XM files won't use sus_loop_end. */ + + type = dumbfile_getc(f); + instrument->volume_envelope.flags = 0; + if ((type & XM_ENVELOPE_ON) && instrument->volume_envelope.n_nodes) + instrument->volume_envelope.flags |= IT_ENVELOPE_ON; + if (type & XM_ENVELOPE_LOOP) instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON; +#if 1 + if (type & XM_ENVELOPE_SUSTAIN) instrument->volume_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP; +#else // This is now handled in itrender.c + /* let's avoid fading out when reaching the last envelope node */ + if (!(type & XM_ENVELOPE_LOOP)) { + instrument->volume_envelope.loop_start = instrument->volume_envelope.n_nodes-1; + instrument->volume_envelope.loop_end = instrument->volume_envelope.n_nodes-1; + } + instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON; +#endif + + type = dumbfile_getc(f); + instrument->pan_envelope.flags = 0; + if ((type & XM_ENVELOPE_ON) && instrument->pan_envelope.n_nodes) + instrument->pan_envelope.flags |= IT_ENVELOPE_ON; + if (type & XM_ENVELOPE_LOOP) instrument->pan_envelope.flags |= IT_ENVELOPE_LOOP_ON; // should this be here? + if (type & XM_ENVELOPE_SUSTAIN) instrument->pan_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP; + + if (it_xm_make_envelope(&instrument->volume_envelope, vol_points, 0) != 0) { + TRACE("XM error: volume envelope\n"); + if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) return -1; + } + + if (it_xm_make_envelope(&instrument->pan_envelope, pan_points, -32) != 0) { + TRACE("XM error: pan envelope\n"); + if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) return -1; + } + + instrument->pitch_envelope.flags = 0; + + extra->vibrato_type = dumbfile_getc(f); + extra->vibrato_sweep = dumbfile_getc(f); + extra->vibrato_depth = dumbfile_getc(f); + extra->vibrato_speed = dumbfile_getc(f); + + if (dumbfile_error(f) || extra->vibrato_type > 4) // XXX + return -1; + + /** WARNING: lossy approximation */ + instrument->fadeout = (dumbfile_igetw(f)*128 + 64)/0xFFF; + + dumbfile_skip(f, 2); /* reserved */ + + bytes_read += 4 + 96 + 48 + 48 + 14*1 + 2 + 2; + } else + for (i = 0; i < 96; i++) + instrument->map_sample[i] = 0; + + if (size > bytes_read && dumbfile_skip(f, size - bytes_read)) + return -1; + + if (skip_end && limit_xm_skip_end(f, skip_end)) + return -1; + + instrument->new_note_action = NNA_NOTE_CUT; + instrument->dup_check_type = DCT_OFF; + instrument->dup_check_action = DCA_NOTE_CUT; + instrument->pp_separation = 0; + instrument->pp_centre = 60; /* C-5 */ + instrument->global_volume = 128; + instrument->default_pan = 32; + instrument->random_volume = 0; + instrument->random_pan = 0; + instrument->filter_cutoff = 0; + instrument->filter_resonance = 0; + + return 0; +} + + + +/* I (entheh) have two XM files saved by a very naughty program. After a + * 16-bit sample, it saved a rogue byte. The length of the sample was indeed + * an odd number, incremented to include the rogue byte. + * + * In this function we are converting sample lengths and loop points so they + * are measured in samples. This means we forget about the extra bytes, and + * they don't get skipped. So we fail trying to read the next instrument. + * + * To get around this, this function returns the number of rogue bytes that + * won't be accounted for by reading sample->length samples. It returns a + * negative number on failure. + */ +static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f) +{ + int type; + int relative_note_number; /* relative to C4 */ + int finetune; + int roguebytes; + int roguebytesmask; + int reserved; + + sample->length = dumbfile_igetl(f); + sample->loop_start = dumbfile_igetl(f); + sample->loop_end = sample->loop_start + dumbfile_igetl(f); + sample->global_volume = 64; + sample->default_volume = dumbfile_getc(f); + finetune = (signed char)dumbfile_getc(f); /* -128..127 <=> -1 semitone .. +127/128 of a semitone */ + type = dumbfile_getc(f); + sample->default_pan = dumbfile_getc(f); /* 0-255 */ + relative_note_number = (signed char)dumbfile_getc(f); + + reserved = dumbfile_getc(f); + + dumbfile_getnc((char *)sample->name, 22, f); + sample->name[22] = 0; + trim_whitespace((char *)sample->name, 22); + + sample->filename[0] = 0; + + if (dumbfile_error(f)) + return -1; + + sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number) /**pow(DUMB_PITCH_BASE, )*/ ); + sample->finetune = finetune*2; + + sample->flags = IT_SAMPLE_EXISTS; + + if (reserved == 0xAD && + (!(type & (XM_SAMPLE_16BIT | XM_SAMPLE_STEREO)))) + { + /* F U Olivier Lapicque */ + roguebytes = 4; + roguebytesmask = 4 << 2; + } + else + { + roguebytes = (int)sample->length; + roguebytesmask = 3; + } + + if (type & XM_SAMPLE_16BIT) { + sample->flags |= IT_SAMPLE_16BIT; + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } else + roguebytesmask >>= 1; + + if (type & XM_SAMPLE_STEREO) { + sample->flags |= IT_SAMPLE_STEREO; + sample->length >>= 1; + sample->loop_start >>= 1; + sample->loop_end >>= 1; + } else + roguebytesmask >>= 1; + + roguebytes &= roguebytesmask; + + if ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end) { + if (type & XM_SAMPLE_FORWARD_LOOP) sample->flags |= IT_SAMPLE_LOOP; + if (type & XM_SAMPLE_PINGPONG_LOOP) sample->flags |= IT_SAMPLE_LOOP | IT_SAMPLE_PINGPONG_LOOP; + } + + if (sample->length <= 0) + sample->flags &= ~IT_SAMPLE_EXISTS; + else if ((unsigned int)sample->loop_end > (unsigned int)sample->length) + sample->flags &= ~IT_SAMPLE_LOOP; + else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end) + sample->flags &= ~IT_SAMPLE_LOOP; + + return roguebytes; +} + + + +static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f) +{ + int old; + long i; + long truncated_size; + int n_channels; + long datasize; + + if (!(sample->flags & IT_SAMPLE_EXISTS)) + return dumbfile_skip(f, roguebytes); + + /* let's get rid of the sample data coming after the end of the loop */ + if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length && roguebytes != 4) { + truncated_size = sample->length - sample->loop_end; + sample->length = sample->loop_end; + } else { + truncated_size = 0; + } + + n_channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + datasize = sample->length * n_channels; + + sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1)); + if (!sample->data) + return -1; + + if (roguebytes == 4) + { + if (_dumb_it_read_sample_data_adpcm4(sample, f) < 0) + return -1; + roguebytes = 0; + } + else + { + /* sample data is stored as signed delta values */ + old = 0; + if (sample->flags & IT_SAMPLE_16BIT) + for (i = 0; i < sample->length; i++) + ((short *)sample->data)[i*n_channels] = old += dumbfile_igetw(f); + else + for (i = 0; i < sample->length; i++) + ((signed char *)sample->data)[i*n_channels] = old += dumbfile_getc(f); + } + + /* skip truncated data */ + dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); + + if (sample->flags & IT_SAMPLE_STEREO) { + old = 0; + if (sample->flags & IT_SAMPLE_16BIT) + for (i = 1; i < datasize; i += 2) + ((short *)sample->data)[i] = old += dumbfile_igetw(f); + else + for (i = 1; i < datasize; i += 2) + ((signed char *)sample->data)[i] = old += dumbfile_getc(f); + + /* skip truncated data */ + dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size)); + } + + dumbfile_skip(f, roguebytes); + + if (dumbfile_error(f)) + return -1; + + return 0; +} + + + +/* "Real programmers don't document. If it was hard to write, + * it should be hard to understand." + * + * (Never trust the documentation provided with a tracker. + * Real files are the only truth...) + */ +static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f, int * version) +{ + DUMB_IT_SIGDATA *sigdata; + char id_text[18]; + + int header_size; + int flags; + int n_channels; + int total_samples; + int i, j; + + /* check ID text */ + if (dumbfile_getnc(id_text, 17, f) < 17) + return NULL; + id_text[17] = 0; + if (strcmp(id_text, "Extended Module: ") != 0) { + TRACE("XM error: Not an Extended Module\n"); + return NULL; + } + + sigdata = malloc(sizeof(*sigdata)); + if (!sigdata) + return NULL; + + /* song name */ + if (dumbfile_getnc((char *)sigdata->name, 20, f) < 20) { + free(sigdata); + return NULL; + } + sigdata->name[20] = 0; + trim_whitespace((char *)sigdata->name, 20); + + if (dumbfile_getc(f) != 0x1A) { + TRACE("XM error: 0x1A not found\n"); + free(sigdata); + return NULL; + } + + /* tracker name */ + if (dumbfile_skip(f, 20)) { + free(sigdata); + return NULL; + } + + /* version number */ + * version = dumbfile_igetw(f); + if (* version > 0x0104 || * version < 0x0102) { + TRACE("XM error: wrong format version\n"); + free(sigdata); + return NULL; + } + + /* + ------------------ + --- Header --- + ------------------ + */ + + /* header size */ + header_size = dumbfile_igetl(f); + if (header_size < (4 + 2*8 + 1) || header_size > 0x114) { + TRACE("XM error: unexpected header size\n"); + free(sigdata); + return NULL; + } + + sigdata->song_message = NULL; + sigdata->order = NULL; + sigdata->instrument = NULL; + sigdata->sample = NULL; + sigdata->pattern = NULL; + sigdata->midi = NULL; + sigdata->checkpoint = NULL; + + sigdata->n_samples = 0; + sigdata->n_orders = dumbfile_igetw(f); + sigdata->restart_position = dumbfile_igetw(f); + n_channels = dumbfile_igetw(f); /* max 32 but we'll be lenient */ + sigdata->n_pchannels = n_channels; + sigdata->n_patterns = dumbfile_igetw(f); + sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */ /* XXX upped to 256 */ + flags = dumbfile_igetw(f); + sigdata->speed = dumbfile_igetw(f); + if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo? + sigdata->tempo = dumbfile_igetw(f); + + /* sanity checks */ + // XXX + i = header_size - 4 - 2 * 8; /* Maximum number of orders expected */ + if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > i || sigdata->n_patterns > 256 || sigdata->n_instruments > 256 || n_channels > DUMB_IT_N_CHANNELS) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + //if (sigdata->restart_position >= sigdata->n_orders) + //sigdata->restart_position = 0; + + /* order table */ + sigdata->order = malloc(sigdata->n_orders*sizeof(*sigdata->order)); + if (!sigdata->order) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + dumbfile_getnc((char *)sigdata->order, sigdata->n_orders, f); + dumbfile_skip(f, i - sigdata->n_orders); + + if (dumbfile_error(f)) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if ( * version > 0x103 ) { + /* + -------------------- + --- Patterns --- + -------------------- + */ + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + { + unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */ + if (!buffer) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer, * version) != 0) { + free(buffer); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + /* + ----------------------------------- + --- Instruments and Samples --- + ----------------------------------- + */ + + sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); + if (!sigdata->instrument) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + /* With XM, samples are not global, they're part of an instrument. In a + * file, each instrument is stored with its samples. Because of this, I + * don't know how to find how many samples are present in the file. Thus + * I have to do n_instruments reallocation on sigdata->sample. + * Looking at FT2, it doesn't seem possible to have more than 16 samples + * per instrument (even though n_samples is stored as 2 bytes). So maybe + * we could allocate a 128*16 array of samples, and shrink it back to the + * correct size when we know it? + * Alternatively, I could allocate samples by blocks of N (still O(n)), + * or double the number of allocated samples when I need more (O(log n)). + */ + total_samples = 0; + sigdata->sample = NULL; + + for (i = 0; i < sigdata->n_instruments; i++) { + XM_INSTRUMENT_EXTRA extra; + + DUMBFILE * lf = dumbfile_limit_xm( f ); + if ( !lf ) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) { + // XXX + if ( ! i ) + { + TRACE("XM error: instrument %d\n", i+1); + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + else + { + dumbfile_close( lf ); + sigdata->n_instruments = i; + break; + } + } + + if (extra.n_samples) { + unsigned char roguebytes[XM_MAX_SAMPLES_PER_INSTRUMENT]; + + /* adjust instrument sample map (make indices absolute) */ + for (j = 0; j < 96; j++) + sigdata->instrument[i].map_sample[j] += total_samples; + + sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); + if (!sigdata->sample) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (j = total_samples; j < total_samples+extra.n_samples; j++) + sigdata->sample[j].data = NULL; + + if ( limit_xm_resize( lf, 0 ) < 0 ) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + /* read instrument's samples */ + for (j = 0; j < extra.n_samples; j++) { + IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; + int b; + if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + b = it_xm_read_sample_header(sample, lf); + if (b < 0) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + roguebytes[j] = b; + // Any reason why these can't be set inside it_xm_read_sample_header()? + sample->vibrato_speed = extra.vibrato_speed; + sample->vibrato_depth = extra.vibrato_depth; + sample->vibrato_rate = extra.vibrato_sweep; + /* Rate and sweep don't match, but the difference is + * accounted for in itrender.c. + */ + sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type]; + sample->max_resampling_quality = -1; + } + for (j = 0; j < extra.n_samples; j++) { + if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) { + dumbfile_close( lf ); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + total_samples += extra.n_samples; + } + + dumbfile_close( lf ); + } + + sigdata->n_samples = total_samples; + } else { + // ahboy! old layout! + // first instruments and sample headers, then patterns, then sample data! + + /* + ----------------------------------- + --- Instruments and Samples --- + ----------------------------------- + */ + + unsigned char * roguebytes = malloc( sigdata->n_instruments * XM_MAX_SAMPLES_PER_INSTRUMENT ); + if (!roguebytes) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument)); + if (!sigdata->instrument) { + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + total_samples = 0; + sigdata->sample = NULL; + + for (i = 0; i < sigdata->n_instruments; i++) { + XM_INSTRUMENT_EXTRA extra; + + DUMBFILE * lf = dumbfile_limit_xm( f ); + if ( !lf ) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (it_xm_read_instrument(&sigdata->instrument[i], &extra, lf) < 0) { + TRACE("XM error: instrument %d\n", i+1); + dumbfile_close(lf); + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + + if (extra.n_samples) { + /* adjust instrument sample map (make indices absolute) */ + for (j = 0; j < 96; j++) + sigdata->instrument[i].map_sample[j] += total_samples; + + sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples)); + if (!sigdata->sample) { + dumbfile_close( lf ); + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (j = total_samples; j < total_samples+extra.n_samples; j++) + sigdata->sample[j].data = NULL; + + if ( limit_xm_resize( lf, 0 ) < 0 ) { + dumbfile_close( lf ); + free( roguebytes ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + + /* read instrument's samples */ + for (j = 0; j < extra.n_samples; j++) { + IT_SAMPLE *sample = &sigdata->sample[total_samples+j]; + int b; + if ( limit_xm_resize( lf, extra.sample_header_size ) < 0 ) { + dumbfile_close( lf ); + free( roguebytes ); + _dumb_it_unload_sigdata( sigdata ); + return NULL; + } + b = it_xm_read_sample_header(sample, lf); + if (b < 0) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + roguebytes[total_samples+j] = b; + // Any reason why these can't be set inside it_xm_read_sample_header()? + sample->vibrato_speed = extra.vibrato_speed; + sample->vibrato_depth = extra.vibrato_depth; + sample->vibrato_rate = extra.vibrato_sweep; + /* Rate and sweep don't match, but the difference is + * accounted for in itrender.c. + */ + sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type]; + sample->max_resampling_quality = -1; + } + total_samples += extra.n_samples; + } + + dumbfile_close( lf ); + } + + sigdata->n_samples = total_samples; + + /* + -------------------- + --- Patterns --- + -------------------- + */ + + sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern)); + if (!sigdata->pattern) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) + sigdata->pattern[i].entry = NULL; + + { + unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */ + if (!buffer) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + for (i = 0; i < sigdata->n_patterns; i++) { + if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer, * version) != 0) { + free(buffer); + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + free(buffer); + } + + // and now we load the sample data + for (j = 0; j < total_samples; j++) { + if (it_xm_read_sample_data(&sigdata->sample[j], roguebytes[j], f) != 0) { + free(roguebytes); + _dumb_it_unload_sigdata(sigdata); + return NULL; + } + } + + free(roguebytes); + } + + + sigdata->flags = IT_WAS_AN_XM | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_USE_INSTRUMENTS; + // Are we OK with IT_COMPATIBLE_GXX off? + // + // When specifying note + instr + tone portamento, and an old note is still playing (even after note off): + // - If Compatible Gxx is on, the new note will be triggered only if the instrument _changes_. + // - If Compatible Gxx is off, the new note will always be triggered, provided the instrument is specified. + // - FT2 seems to do the latter (unconfirmed). + + // Err, wait. XM playback has its own code. The change made to the IT + // playbackc code didn't affect XM playback. Forget this then. There's + // still a bug in XM playback though, and it'll need some investigation... + // tomorrow... + + // UPDATE: IT_COMPATIBLE_GXX is required to be on, so that tone porta has + // separate memory from portamento. + + if (flags & XM_LINEAR_FREQUENCY) + sigdata->flags |= IT_LINEAR_SLIDES; + + sigdata->global_volume = 128; + sigdata->mixing_volume = 48; + sigdata->pan_separation = 128; + + memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS); + memset(sigdata->channel_pan, 32, DUMB_IT_N_CHANNELS); + + _dumb_it_fix_invalid_orders(sigdata); + + return sigdata; +} + + + +#if 0 // no fucking way, dude! + +/* The length returned is the time required to play from the beginning of the + * file to the last row of the last order (which is when the player will + * loop). Depending on the song, the sound might stop sooner. + * Due to fixed point roundoffs, I think this is only reliable to the second. + * Full precision could be achieved by using a double during the computation, + * or maybe a LONG_LONG. + */ +long it_compute_length(const DUMB_IT_SIGDATA *sigdata) +{ + IT_PATTERN *pattern; + int tempo, speed; + int loop_start[IT_N_CHANNELS]; + char loop_count[IT_N_CHANNELS]; + int order, entry; + int row_first_entry = 0; + int jump, jump_dest; + int delay, fine_delay; + int i; + long t; + + if (!sigdata) + return 0; + + tempo = sigdata->tempo; + speed = sigdata->speed; + order = entry = 0; + jump = jump_dest = 0; + t = 0; + + /* for each PATTERN */ + for (order = 0; order < sigdata->n_orders; order++) { + + if (sigdata->order[order] == IT_ORDER_END) break; + if (sigdata->order[order] == IT_ORDER_SKIP) continue; + + for (i = 0; i < IT_N_CHANNELS; i++) + loop_count[i] = -1; + + pattern = &sigdata->pattern[ sigdata->order[order] ]; + entry = 0; + if (jump == IT_BREAK_TO_ROW) { + int row = 0; + while (row < jump_dest) + if (pattern->entry[entry++].channel >= IT_N_CHANNELS) + row++; + } + + /* for each ROW */ + while (entry < pattern->n_entries) { + row_first_entry = entry; + delay = fine_delay = 0; + jump = 0; + + /* for each note NOTE */ + while (entry < pattern->n_entries && pattern->entry[entry].channel < IT_N_CHANNELS) { + int value = pattern->entry[entry].effectvalue; + int channel = pattern->entry[entry].channel; + + switch (pattern->entry[entry].effect) { + + case IT_SET_SPEED: speed = value; break; + + case IT_JUMP_TO_ORDER: + if (value <= order) /* infinite loop */ + return 0; + jump = IT_JUMP_TO_ORDER; + jump_dest = value; + break; + + case IT_BREAK_TO_ROW: + jump = IT_BREAK_TO_ROW; + jump_dest = value; + break; + + case IT_S: + switch (HIGH(value)) { + case IT_S_PATTERN_DELAY: delay = LOW(value); break; + case IT_S_FINE_PATTERN_DELAY: fine_delay = LOW(value); break; + case IT_S_PATTERN_LOOP: + if (LOW(value) == 0) { + loop_start[channel] = row_first_entry; + } else { + if (loop_count[channel] == -1) + loop_count[channel] = LOW(value); + + if (loop_count[channel]) { + jump = IT_S_PATTERN_LOOP; + jump_dest = loop_start[channel]; + } + loop_count[channel]--; + } + break; + } + break; + + case IT_SET_SONG_TEMPO: + switch (HIGH(value)) { /* slides happen every non-row frames */ + case 0: tempo = tempo - LOW(value)*(speed-1); break; + case 1: tempo = tempo + LOW(value)*(speed-1); break; + default: tempo = value; + } + tempo = MID(32, tempo, 255); + break; + } + + entry++; + } + + /* end of ROW */ + entry++; + t += TICK_TIME_DIVIDEND * (speed*(1+delay) + fine_delay) / tempo; + + if (jump == IT_JUMP_TO_ORDER) { + order = jump_dest - 1; + break; + } else if (jump == IT_BREAK_TO_ROW) + break; + else if (jump == IT_S_PATTERN_LOOP) + entry = jump_dest - 1; + } + + /* end of PATTERN */ + } + + return t; +} + +#endif /* 0 */ + + +static char hexdigit(int in) +{ + if (in < 10) return in + '0'; + else return in + 'A' - 10; +} + +DUH *dumb_read_xm_quick(DUMBFILE *f) +{ + sigdata_t *sigdata; + int ver; + + DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it; + + sigdata = it_xm_load_sigdata(f, &ver); + + if (!sigdata) + return NULL; + + { + char version[16]; + const char *tag[2][2]; + tag[0][0] = "TITLE"; + tag[0][1] = (const char *)(((DUMB_IT_SIGDATA *)sigdata)->name); + tag[1][0] = "FORMAT"; + version[0] = 'X'; + version[1] = 'M'; + version[2] = ' '; + version[3] = 'v'; + version[4] = hexdigit( ( ver >> 8 ) & 15 ); + version[5] = '.'; + version[6] = hexdigit( ( ver >> 4 ) & 15 ); + version[7] = hexdigit( ver & 15 ); + version[8] = 0; + tag[1][1] = ( const char * ) & version; + return make_duh(-1, 2, (const char *const (*)[2])tag, 1, &descptr, &sigdata); + } +} diff --git a/Frameworks/Dumb/dumb/src/it/readxm2.c b/Frameworks/Dumb/dumb/src/it/readxm2.c index b678bd2d1..c79f753fd 100644 --- a/Frameworks/Dumb/dumb/src/it/readxm2.c +++ b/Frameworks/Dumb/dumb/src/it/readxm2.c @@ -1,29 +1,29 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * readxm2.c - Function to read a Fast Tracker II / / \ \ - * module from an open file and do an | < / \_ - * initial run-through. | \/ /\ / - * \_ / > / - * Split off from readxm.c by entheh. | \ / / - * | ' / - * \__/ - */ - -#include "dumb.h" - - - -DUH *dumb_read_xm(DUMBFILE *f) -{ - DUH *duh = dumb_read_xm_quick(f); - dumb_it_do_initial_runthrough(duh); - return duh; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * readxm2.c - Function to read a Fast Tracker II / / \ \ + * module from an open file and do an | < / \_ + * initial run-through. | \/ /\ / + * \_ / > / + * Split off from readxm.c by entheh. | \ / / + * | ' / + * \__/ + */ + +#include "dumb.h" + + + +DUH *dumb_read_xm(DUMBFILE *f) +{ + DUH *duh = dumb_read_xm_quick(f); + dumb_it_do_initial_runthrough(duh); + return duh; +} diff --git a/Frameworks/Dumb/dumb/src/it/xmeffect.c b/Frameworks/Dumb/dumb/src/it/xmeffect.c index cd39749a8..1aa489222 100644 --- a/Frameworks/Dumb/dumb/src/it/xmeffect.c +++ b/Frameworks/Dumb/dumb/src/it/xmeffect.c @@ -1,242 +1,245 @@ -/* _______ ____ __ ___ ___ - * \ _ \ \ / \ / \ \ / / ' ' ' - * | | \ \ | | || | \/ | . . - * | | | | | | || ||\ /| | - * | | | | | | || || \/ | | ' ' ' - * | | | | | | || || | | . . - * | |_/ / \ \__// || | | - * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque - * / \ - * / . \ - * xmeffect.c - Code for converting MOD/XM / / \ \ - * effects to IT effects. | < / \_ - * | \/ /\ / - * By Julien Cugniere. Ripped out of readxm.c \_ / > / - * by entheh. | \ / / - * | ' / - * \__/ - */ - - - -#include -#include - -#include "dumb.h" -#include "internal/it.h" - - - -#if 0 -unsigned char **_dumb_malloc2(int w, int h) -{ - unsigned char **line = malloc(h * sizeof(*line)); - int i; - if (!line) return NULL; - - line[0] = malloc(w * h * sizeof(*line[0])); - if (!line[0]) { - free(line); - return NULL; - } - - for (i = 1; i < h; i++) - line[i] = line[i-1] + w; - - memset(line[0], 0, w*h); - - return line; -} - - - -void _dumb_free2(unsigned char **line) -{ - if (line) { - if (line[0]) - free(line[0]); - free(line); - } -} - - - -/* Effects having a memory. 2 means that the two parts of the effectvalue - * should be handled separately. - */ -static const char xm_has_memory[] = { -/* 0 1 2 3 4 5 6 7 8 9 A B C D (E) F G H K L P R T (X) */ - 0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, - -/* E0 E1 E2 E3 E4 E5 E6 E7 E9 EA EB EC ED EE X1 X2 */ - 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; -#endif - - - -/* Effects marked with 'special' are handled specifically in itrender.c */ -void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry) -{ -const int log = 0; - - if ((!effect && !value) || (effect >= XM_N_EFFECTS)) - return; - -if (log) printf("%c%02X", (effect<10)?('0'+effect):('A'+effect-10), value); - - /* Linearisation of the effect number... */ - if (effect == XM_E) { - effect = EBASE + HIGH(value); - value = LOW(value); - } else if (effect == XM_X) { - effect = XBASE + HIGH(value); - value = LOW(value); - } - -if (log) printf(" - %2d %02X", effect, value); - -#if 0 // This should be handled in itrender.c! - /* update effect memory */ - switch (xm_has_memory[effect]) { - case 1: - if (!value) - value = memory[entry->channel][effect]; - else - memory[entry->channel][effect] = value; - break; - - case 2: - if (!HIGH(value)) - SET_HIGH(value, HIGH(memory[entry->channel][effect])); - else - SET_HIGH(memory[entry->channel][effect], HIGH(value)); - - if (!LOW(value)) - SET_LOW(value, LOW(memory[entry->channel][effect])); - else - SET_LOW(memory[entry->channel][effect], LOW(value)); - break; - } -#endif - - /* convert effect */ - entry->mask |= IT_ENTRY_EFFECT; - switch (effect) { - - case XM_APPREGIO: effect = IT_ARPEGGIO; break; - case XM_VIBRATO: effect = IT_VIBRATO; break; - case XM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break; /** TODO: glissando control */ - case XM_TREMOLO: effect = IT_TREMOLO; break; - case XM_SET_PANNING: effect = IT_SET_PANNING; break; - case XM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break; - case XM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break; - case XM_MULTI_RETRIG: effect = IT_RETRIGGER_NOTE; break; - case XM_TREMOR: effect = IT_TREMOR; break; - case XM_PORTAMENTO_UP: effect = IT_XM_PORTAMENTO_UP; break; - case XM_PORTAMENTO_DOWN: effect = IT_XM_PORTAMENTO_DOWN; break; - case XM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; /* special */ - case XM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; /* special */ - case XM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; /* special */ - - case XM_PATTERN_BREAK: - effect = IT_BREAK_TO_ROW; - value = BCD_TO_NORMAL(value); - break; - - case XM_VOLUME_SLIDE: /* special */ - effect = IT_VOLUME_SLIDE; - value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); - break; - - case XM_PANNING_SLIDE: - effect = IT_PANNING_SLIDE; - value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); - //value = HIGH(value) ? EFFECT_VALUE(0, HIGH(value)) : EFFECT_VALUE(LOW(value), 0); - break; - - case XM_GLOBAL_VOLUME_SLIDE: /* special */ - effect = IT_GLOBAL_VOLUME_SLIDE; - value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); - break; - - case XM_SET_TEMPO_BPM: - effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); - break; - - case XM_SET_GLOBAL_VOLUME: - effect = IT_SET_GLOBAL_VOLUME; - value *= 2; - break; - - case XM_KEY_OFF: - effect = IT_XM_KEY_OFF; - break; - - case XM_SET_ENVELOPE_POSITION: - effect = IT_XM_SET_ENVELOPE_POSITION; - break; - - case EBASE+XM_E_SET_FILTER: effect = SBASE+IT_S_SET_FILTER; break; - case EBASE+XM_E_SET_GLISSANDO_CONTROL: effect = SBASE+IT_S_SET_GLISSANDO_CONTROL; break; /** TODO */ - case EBASE+XM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; /** TODO */ - case EBASE+XM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break; - case EBASE+XM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break; - case EBASE+XM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break; - case EBASE+XM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break; - case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break; - case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break; - - case EBASE + XM_E_FINE_PORTA_UP: - effect = IT_PORTAMENTO_UP; - value = EFFECT_VALUE(0xF, value); - break; - - case EBASE + XM_E_FINE_PORTA_DOWN: - effect = IT_PORTAMENTO_DOWN; - value = EFFECT_VALUE(0xF, value); - break; - - case EBASE + XM_E_RETRIG_NOTE: - effect = IT_XM_RETRIGGER_NOTE; - value = EFFECT_VALUE(0, value); - break; - - case EBASE + XM_E_SET_VIBRATO_CONTROL: - effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM; - value &= ~4; /** TODO: value&4 -> don't retrig wave */ - break; - - case EBASE + XM_E_SET_TREMOLO_CONTROL: - effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM; - value &= ~4; /** TODO: value&4 -> don't retrig wave */ - break; - - case XBASE + XM_X_EXTRAFINE_PORTA_UP: - effect = IT_PORTAMENTO_UP; - value = EFFECT_VALUE(0xE, value); - break; - - case XBASE + XM_X_EXTRAFINE_PORTA_DOWN: - effect = IT_PORTAMENTO_DOWN; - value = EFFECT_VALUE(0xE, value); - break; - - default: - /* user effect (often used in demos for synchronisation) */ - entry->mask &= ~IT_ENTRY_EFFECT; - } - -if (log) printf(" - %2d %02X", effect, value); - - /* Inverse linearisation... */ - if (effect >= SBASE && effect < SBASE+16) { - value = EFFECT_VALUE(effect-SBASE, value); - effect = IT_S; - } - -if (log) printf(" - %c%02X\n", 'A'+effect-1, value); - - entry->effect = effect; - entry->effectvalue = value; -} +/* _______ ____ __ ___ ___ + * \ _ \ \ / \ / \ \ / / ' ' ' + * | | \ \ | | || | \/ | . . + * | | | | | | || ||\ /| | + * | | | | | | || || \/ | | ' ' ' + * | | | | | | || || | | . . + * | |_/ / \ \__// || | | + * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque + * / \ + * / . \ + * xmeffect.c - Code for converting MOD/XM / / \ \ + * effects to IT effects. | < / \_ + * | \/ /\ / + * By Julien Cugniere. Ripped out of readxm.c \_ / > / + * by entheh. | \ / / + * | ' / + * \__/ + */ + + + +#include +#include + +#include "dumb.h" +#include "internal/it.h" + +#if 0 +unsigned char **_dumb_malloc2(int w, int h) +{ + unsigned char **line = malloc(h * sizeof(*line)); + int i; + if (!line) return NULL; + + line[0] = malloc(w * h * sizeof(*line[0])); + if (!line[0]) { + free(line); + return NULL; + } + + for (i = 1; i < h; i++) + line[i] = line[i-1] + w; + + memset(line[0], 0, w*h); + + return line; +} + + + +void _dumb_free2(unsigned char **line) +{ + if (line) { + if (line[0]) + free(line[0]); + free(line); + } +} + + + +/* Effects having a memory. 2 means that the two parts of the effectvalue + * should be handled separately. + */ +static const char xm_has_memory[] = { +/* 0 1 2 3 4 5 6 7 8 9 A B C D (E) F G H K L P R T (X) */ + 0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + +/* E0 E1 E2 E3 E4 E5 E6 E7 E9 EA EB EC ED EE X1 X2 */ + 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; +#endif + + + +/* Effects marked with 'special' are handled specifically in itrender.c */ +void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry, int mod) +{ +const int log = 0; + + if ((!effect && !value) || (effect >= XM_N_EFFECTS)) + return; + +if (log) printf("%c%02X", (effect<10)?('0'+effect):('A'+effect-10), value); + + /* Linearisation of the effect number... */ + if (effect == XM_E) { + effect = EBASE + HIGH(value); + value = LOW(value); + } else if (effect == XM_X) { + effect = XBASE + HIGH(value); + value = LOW(value); + } + +if (log) printf(" - %2d %02X", effect, value); + +#if 0 // This should be handled in itrender.c! + /* update effect memory */ + switch (xm_has_memory[effect]) { + case 1: + if (!value) + value = memory[entry->channel][effect]; + else + memory[entry->channel][effect] = value; + break; + + case 2: + if (!HIGH(value)) + SET_HIGH(value, HIGH(memory[entry->channel][effect])); + else + SET_HIGH(memory[entry->channel][effect], HIGH(value)); + + if (!LOW(value)) + SET_LOW(value, LOW(memory[entry->channel][effect])); + else + SET_LOW(memory[entry->channel][effect], LOW(value)); + break; + } +#endif + + /* convert effect */ + entry->mask |= IT_ENTRY_EFFECT; + switch (effect) { + + case XM_APPREGIO: effect = IT_ARPEGGIO; break; + case XM_VIBRATO: effect = IT_VIBRATO; break; + case XM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break; + case XM_TREMOLO: effect = IT_TREMOLO; break; + case XM_SET_PANNING: effect = IT_SET_PANNING; break; + case XM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break; + case XM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break; + case XM_MULTI_RETRIG: effect = IT_RETRIGGER_NOTE; break; + case XM_TREMOR: effect = IT_TREMOR; break; + case XM_PORTAMENTO_UP: effect = IT_XM_PORTAMENTO_UP; break; + case XM_PORTAMENTO_DOWN: effect = IT_XM_PORTAMENTO_DOWN; break; + case XM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; /* special */ + case XM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; /* special */ + case XM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; /* special */ + + case XM_PATTERN_BREAK: + effect = IT_BREAK_TO_ROW; + value = BCD_TO_NORMAL(value); + if (value > 63) value = 0; /* FT2, maybe ProTracker? */ + break; + + case XM_VOLUME_SLIDE: /* special */ + effect = IT_VOLUME_SLIDE; + value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); + break; + + case XM_PANNING_SLIDE: + effect = IT_PANNING_SLIDE; + //value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); + value = HIGH(value) ? EFFECT_VALUE(0, HIGH(value)) : EFFECT_VALUE(LOW(value), 0); + break; + + case XM_GLOBAL_VOLUME_SLIDE: /* special */ + effect = IT_GLOBAL_VOLUME_SLIDE; + value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value)); + break; + + case XM_SET_TEMPO_BPM: + if (mod) effect = (value <= 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); + else effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO); + break; + + case XM_SET_GLOBAL_VOLUME: + effect = IT_SET_GLOBAL_VOLUME; + value *= 2; + if (value > 128) value = 128; + break; + + case XM_KEY_OFF: + effect = IT_XM_KEY_OFF; + break; + + case XM_SET_ENVELOPE_POSITION: + effect = IT_XM_SET_ENVELOPE_POSITION; + break; + + case EBASE+XM_E_SET_FILTER: effect = SBASE+IT_S_SET_FILTER; break; + case EBASE+XM_E_SET_GLISSANDO_CONTROL: effect = SBASE+IT_S_SET_GLISSANDO_CONTROL; break; /** TODO */ + case EBASE+XM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; + case EBASE+XM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break; + case EBASE+XM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break; + case EBASE+XM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break; + case EBASE+XM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break; + case EBASE+XM_E_SET_PANNING: effect = SBASE+IT_S_SET_PAN; break; + case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break; + case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break; + case EBASE+XM_E_SET_MIDI_MACRO: effect = SBASE+IT_S_SET_MIDI_MACRO; break; + + case EBASE + XM_E_FINE_PORTA_UP: + effect = IT_PORTAMENTO_UP; + value = EFFECT_VALUE(0xF, value); + break; + + case EBASE + XM_E_FINE_PORTA_DOWN: + effect = IT_PORTAMENTO_DOWN; + value = EFFECT_VALUE(0xF, value); + break; + + case EBASE + XM_E_RETRIG_NOTE: + effect = IT_XM_RETRIGGER_NOTE; + value = EFFECT_VALUE(0, value); + break; + + case EBASE + XM_E_SET_VIBRATO_CONTROL: + effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM; + value &= ~4; + break; + + case EBASE + XM_E_SET_TREMOLO_CONTROL: + effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM; + value &= ~4; + break; + + case XBASE + XM_X_EXTRAFINE_PORTA_UP: + effect = IT_PORTAMENTO_UP; + value = EFFECT_VALUE(0xE, value); + break; + + case XBASE + XM_X_EXTRAFINE_PORTA_DOWN: + effect = IT_PORTAMENTO_DOWN; + value = EFFECT_VALUE(0xE, value); + break; + + default: + /* user effect (often used in demos for synchronisation) */ + entry->mask &= ~IT_ENTRY_EFFECT; + } + +if (log) printf(" - %2d %02X", effect, value); + + /* Inverse linearisation... */ + if (effect >= SBASE && effect < SBASE+16) { + value = EFFECT_VALUE(effect-SBASE, value); + effect = IT_S; + } + +if (log) printf(" - %c%02X\n", 'A'+effect-1, value); + + entry->effect = effect; + entry->effectvalue = value; +} diff --git a/Frameworks/Dumb/dumb/vc6/dumb/.gitignore b/Frameworks/Dumb/dumb/vc6/dumb/.gitignore new file mode 100644 index 000000000..32cff875e --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/.gitignore @@ -0,0 +1,3 @@ +*.user +Debug +Release \ No newline at end of file diff --git a/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj new file mode 100644 index 000000000..525bc56fc --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcproj @@ -0,0 +1,2007 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj new file mode 100644 index 000000000..a6441671a --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj @@ -0,0 +1,221 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {612D360C-A51B-4B34-8F49-33F42A2957F5} + dumb + + + + + + + + + + + + StaticLibrary + true + v110_xp + + + StaticLibrary + v110_xp + + + + + + + + + + + + + <_ProjectFileVersion>10.0.21006.1 + AllRules.ruleset + + + AllRules.ruleset + + + + + + Disabled + ../../include;%(AdditionalIncludeDirectories) + _USE_SSE;_DEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED;DEBUGMODE=1;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + Level3 + true + EditAndContinue + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + + + MaxSpeed + AnySuitable + ../../include;%(AdditionalIncludeDirectories) + _USE_SSE;NDEBUG;WIN32;_LIB;DUMB_DECLARE_DEPRECATED;%(PreprocessorDefinitions) + true + MultiThreaded + true + Level3 + true + ProgramDatabase + Default + Fast + NoExtensions + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Document + true + true + + + Document + true + true + + + Document + true + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters new file mode 100644 index 000000000..c8d1360f3 --- /dev/null +++ b/Frameworks/Dumb/dumb/vc6/dumb/dumb.vcxproj.filters @@ -0,0 +1,341 @@ + + + + + {419c5e1f-2bf4-473a-b2e5-2e531285aa62} + + + {44b333b3-1607-4820-82bc-e4c21a40e31a} + + + {0b122556-3781-4ef3-87fe-ffa5fb50b493} + + + {e961cd19-26f6-4df0-b895-e099d3e81db9} + + + {82e35139-08ff-4e99-a3ce-2254d7427ec4} + + + {5f7fc0f6-4008-4166-83ad-e5d914718bd0} + + + {0fd0715e-5824-4419-aa5b-2d4272d222ce} + + + {b9e26fe7-6056-4580-b2c6-10e6116d4129} + + + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\core + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\helpers + + + src\it\loaders + + + src\it\loaders + + + src\it + + + src\it + + + src\it\readers + + + src\it\readers + + + src\it + + + src\it + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it\readers + + + src\it + + + src\helpers + + + src\it\readers + + + src\it\readers + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\loaders + + + src\it\readers + + + src\it\readers + + + src\helpers + + + src\helpers + + + + + include + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + include\internal + + + + + src\helpers + + + src\helpers + + + src\helpers + + + \ No newline at end of file diff --git a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj index f9170e2ea..86a46f3bc 100644 --- a/Frameworks/FLAC/flac.xcodeproj/project.pbxproj +++ b/Frameworks/FLAC/flac.xcodeproj/project.pbxproj @@ -420,9 +420,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "flac" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* flac */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -503,6 +509,7 @@ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders"; PRODUCT_NAME = FLAC; PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers"; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = ""; USE_SEPERATE_HEADERMAPS = YES; WRAPPER_EXTENSION = framework; @@ -543,6 +550,7 @@ PRIVATE_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/PrivateHeaders"; PRODUCT_NAME = FLAC; PUBLIC_HEADERS_FOLDER_PATH = "$(CONTENTS_FOLDER_PATH)/Headers"; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = ""; USE_SEPERATE_HEADERMAPS = YES; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index a43cbe1b1..7fabe9042 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 17C8F1F40CBED286008D969D /* Ay_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */; }; 17C8F1F50CBED286008D969D /* Ay_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F18C0CBED286008D969D /* Ay_Apu.h */; }; 17C8F1F60CBED286008D969D /* Ay_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F18D0CBED286008D969D /* Ay_Cpu.cpp */; }; - 17C8F1F70CBED286008D969D /* Ay_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F18E0CBED286008D969D /* Ay_Cpu.h */; }; 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F18F0CBED286008D969D /* Ay_Emu.cpp */; }; 17C8F1F90CBED286008D969D /* Ay_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1900CBED286008D969D /* Ay_Emu.h */; }; 17C8F1FA0CBED286008D969D /* blargg_common.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1910CBED286008D969D /* blargg_common.h */; }; @@ -31,7 +30,6 @@ 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A00CBED286008D969D /* Fir_Resampler.h */; }; 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1A10CBED286008D969D /* Gb_Apu.cpp */; }; 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A20CBED286008D969D /* Gb_Apu.h */; }; - 17C8F20C0CBED286008D969D /* gb_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A30CBED286008D969D /* gb_cpu_io.h */; }; 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1A40CBED286008D969D /* Gb_Cpu.cpp */; }; 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1A50CBED286008D969D /* Gb_Cpu.h */; }; 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1A60CBED286008D969D /* Gb_Oscs.cpp */; }; @@ -46,13 +44,11 @@ 17C8F2190CBED286008D969D /* Gym_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B00CBED286008D969D /* Gym_Emu.h */; }; 17C8F21A0CBED286008D969D /* Hes_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B10CBED286008D969D /* Hes_Apu.cpp */; }; 17C8F21B0CBED286008D969D /* Hes_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B20CBED286008D969D /* Hes_Apu.h */; }; - 17C8F21C0CBED286008D969D /* hes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B30CBED286008D969D /* hes_cpu_io.h */; }; 17C8F21D0CBED286008D969D /* Hes_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B40CBED286008D969D /* Hes_Cpu.cpp */; }; 17C8F21E0CBED286008D969D /* Hes_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B50CBED286008D969D /* Hes_Cpu.h */; }; 17C8F21F0CBED286008D969D /* Hes_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B60CBED286008D969D /* Hes_Emu.cpp */; }; 17C8F2200CBED286008D969D /* Hes_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B70CBED286008D969D /* Hes_Emu.h */; }; 17C8F2210CBED286008D969D /* Kss_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1B80CBED286008D969D /* Kss_Cpu.cpp */; }; - 17C8F2220CBED286008D969D /* Kss_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1B90CBED286008D969D /* Kss_Cpu.h */; }; 17C8F2230CBED286008D969D /* Kss_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1BA0CBED286008D969D /* Kss_Emu.cpp */; }; 17C8F2240CBED286008D969D /* Kss_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1BB0CBED286008D969D /* Kss_Emu.h */; }; 17C8F2250CBED286008D969D /* Kss_Scc_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1BC0CBED286008D969D /* Kss_Scc_Apu.cpp */; }; @@ -65,7 +61,6 @@ 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C40CBED286008D969D /* Music_Emu.h */; }; 17C8F22E0CBED286008D969D /* Nes_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1C50CBED286008D969D /* Nes_Apu.cpp */; }; 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C60CBED286008D969D /* Nes_Apu.h */; }; - 17C8F2300CBED286008D969D /* nes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C70CBED286008D969D /* nes_cpu_io.h */; }; 17C8F2310CBED286008D969D /* Nes_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1C80CBED286008D969D /* Nes_Cpu.cpp */; }; 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1C90CBED286008D969D /* Nes_Cpu.h */; }; 17C8F2330CBED286008D969D /* Nes_Fme7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1CA0CBED286008D969D /* Nes_Fme7_Apu.cpp */; }; @@ -82,14 +77,11 @@ 17C8F23E0CBED286008D969D /* Nsfe_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1D50CBED286008D969D /* Nsfe_Emu.h */; }; 17C8F23F0CBED286008D969D /* Sap_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1D60CBED286008D969D /* Sap_Apu.cpp */; }; 17C8F2400CBED286008D969D /* Sap_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1D70CBED286008D969D /* Sap_Apu.h */; }; - 17C8F2410CBED286008D969D /* sap_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1D80CBED286008D969D /* sap_cpu_io.h */; }; 17C8F2420CBED286008D969D /* Sap_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1D90CBED286008D969D /* Sap_Cpu.cpp */; }; - 17C8F2430CBED286008D969D /* Sap_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DA0CBED286008D969D /* Sap_Cpu.h */; }; 17C8F2440CBED286008D969D /* Sap_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1DB0CBED286008D969D /* Sap_Emu.cpp */; }; 17C8F2450CBED286008D969D /* Sap_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DC0CBED286008D969D /* Sap_Emu.h */; }; 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */; }; 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DE0CBED286008D969D /* Sms_Apu.h */; }; - 17C8F2480CBED286008D969D /* Sms_Oscs.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DF0CBED286008D969D /* Sms_Oscs.h */; }; 17C8F2490CBED286008D969D /* Snes_Spc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */; }; 17C8F24A0CBED286008D969D /* Snes_Spc.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E10CBED286008D969D /* Snes_Spc.h */; }; 17C8F24B0CBED286008D969D /* Spc_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */; }; @@ -98,14 +90,171 @@ 17C8F24E0CBED286008D969D /* Spc_Dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E50CBED286008D969D /* Spc_Dsp.h */; }; 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */; }; 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E70CBED286008D969D /* Spc_Emu.h */; }; - 17C8F2510CBED286008D969D /* Vgm_Emu_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E80CBED286008D969D /* Vgm_Emu_Impl.cpp */; }; - 17C8F2520CBED286008D969D /* Vgm_Emu_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E90CBED286008D969D /* Vgm_Emu_Impl.h */; }; 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */; }; 17C8F2540CBED286008D969D /* Vgm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1EB0CBED286008D969D /* Vgm_Emu.h */; }; 17C8F2550CBED286008D969D /* Ym2413_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EC0CBED286008D969D /* Ym2413_Emu.cpp */; }; 17C8F2560CBED286008D969D /* Ym2413_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1ED0CBED286008D969D /* Ym2413_Emu.h */; }; 17C8F2570CBED286008D969D /* Ym2612_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EE0CBED286008D969D /* Ym2612_Emu.cpp */; }; 17C8F2580CBED286008D969D /* Ym2612_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1EF0CBED286008D969D /* Ym2612_Emu.h */; }; + 8370B72F17F615FE001A4D7A /* adlib.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B68C17F615FD001A4D7A /* adlib.h */; }; + 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */; }; + 8370B73117F615FE001A4D7A /* Ay_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B68E17F615FD001A4D7A /* Ay_Core.h */; }; + 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B68F17F615FD001A4D7A /* blargg_common.cpp */; }; + 8370B73317F615FE001A4D7A /* blargg_errors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69017F615FD001A4D7A /* blargg_errors.cpp */; }; + 8370B73417F615FE001A4D7A /* blargg_errors.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69117F615FD001A4D7A /* blargg_errors.h */; }; + 8370B73517F615FE001A4D7A /* Blip_Buffer_impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69217F615FD001A4D7A /* Blip_Buffer_impl.h */; }; + 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */; }; + 8370B73717F615FE001A4D7A /* Bml_Parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */; }; + 8370B73817F615FE001A4D7A /* Bml_Parser.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69517F615FD001A4D7A /* Bml_Parser.h */; }; + 8370B73917F615FE001A4D7A /* C140_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69617F615FD001A4D7A /* C140_Emu.cpp */; }; + 8370B73A17F615FE001A4D7A /* C140_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69717F615FD001A4D7A /* C140_Emu.h */; }; + 8370B73B17F615FE001A4D7A /* c140.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69817F615FD001A4D7A /* c140.c */; }; + 8370B73C17F615FE001A4D7A /* c140.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69917F615FD001A4D7A /* c140.h */; }; + 8370B73D17F615FE001A4D7A /* Chip_Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */; }; + 8370B73E17F615FE001A4D7A /* dac_control.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69B17F615FD001A4D7A /* dac_control.c */; }; + 8370B73F17F615FE001A4D7A /* dac_control.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69C17F615FD001A4D7A /* dac_control.h */; }; + 8370B74017F615FE001A4D7A /* dbopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B69D17F615FD001A4D7A /* dbopl.cpp */; }; + 8370B74117F615FE001A4D7A /* dbopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69E17F615FD001A4D7A /* dbopl.h */; }; + 8370B74217F615FE001A4D7A /* divfix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B69F17F615FD001A4D7A /* divfix.h */; }; + 8370B74317F615FE001A4D7A /* Downsampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A017F615FD001A4D7A /* Downsampler.cpp */; }; + 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A117F615FD001A4D7A /* Downsampler.h */; }; + 8370B74517F615FE001A4D7A /* emuconfig.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A217F615FD001A4D7A /* emuconfig.h */; }; + 8370B74617F615FE001A4D7A /* fm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A317F615FD001A4D7A /* fm.c */; }; + 8370B74717F615FE001A4D7A /* fm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A417F615FD001A4D7A /* fm.h */; }; + 8370B74817F615FE001A4D7A /* fm2612.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A517F615FD001A4D7A /* fm2612.c */; }; + 8370B74917F615FE001A4D7A /* fmopl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A617F615FD001A4D7A /* fmopl.cpp */; }; + 8370B74A17F615FE001A4D7A /* fmopl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A717F615FD001A4D7A /* fmopl.h */; }; + 8370B74B17F615FE001A4D7A /* Gb_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */; }; + 8370B74C17F615FE001A4D7A /* Gbs_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */; }; + 8370B74D17F615FE001A4D7A /* Gbs_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */; }; + 8370B74E17F615FE001A4D7A /* Gbs_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6AB17F615FD001A4D7A /* Gbs_Cpu.cpp */; }; + 8370B75117F615FE001A4D7A /* Gme_Loader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6AE17F615FD001A4D7A /* Gme_Loader.cpp */; }; + 8370B75217F615FE001A4D7A /* Gme_Loader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6AF17F615FD001A4D7A /* Gme_Loader.h */; }; + 8370B75317F615FE001A4D7A /* Hes_Apu_Adpcm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B017F615FD001A4D7A /* Hes_Apu_Adpcm.cpp */; }; + 8370B75417F615FE001A4D7A /* Hes_Apu_Adpcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B117F615FD001A4D7A /* Hes_Apu_Adpcm.h */; }; + 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */; }; + 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B317F615FD001A4D7A /* Hes_Core.h */; }; + 8370B75717F615FE001A4D7A /* Hes_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */; }; + 8370B75817F615FE001A4D7A /* i_fmpac.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B517F615FD001A4D7A /* i_fmpac.h */; }; + 8370B75917F615FE001A4D7A /* i_fmunit.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B617F615FD001A4D7A /* i_fmunit.h */; }; + 8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B717F615FD001A4D7A /* i_vrc7.h */; }; + 8370B75B17F615FE001A4D7A /* K051649_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */; }; + 8370B75C17F615FE001A4D7A /* K051649_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6B917F615FD001A4D7A /* K051649_Emu.h */; }; + 8370B75D17F615FE001A4D7A /* k051649.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BA17F615FD001A4D7A /* k051649.c */; }; + 8370B75E17F615FE001A4D7A /* k051649.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BB17F615FD001A4D7A /* k051649.h */; }; + 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */; }; + 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */; }; + 8370B76117F615FE001A4D7A /* k053260.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6BE17F615FD001A4D7A /* k053260.c */; }; + 8370B76217F615FE001A4D7A /* k053260.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6BF17F615FD001A4D7A /* k053260.h */; }; + 8370B76317F615FE001A4D7A /* K054539_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */; }; + 8370B76417F615FE001A4D7A /* K054539_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C117F615FD001A4D7A /* K054539_Emu.h */; }; + 8370B76517F615FE001A4D7A /* k054539.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C217F615FD001A4D7A /* k054539.c */; }; + 8370B76617F615FE001A4D7A /* k054539.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C317F615FD001A4D7A /* k054539.h */; }; + 8370B76717F615FE001A4D7A /* kmsnddev.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C417F615FD001A4D7A /* kmsnddev.h */; }; + 8370B76817F615FE001A4D7A /* Kss_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */; }; + 8370B76917F615FE001A4D7A /* Kss_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C617F615FD001A4D7A /* Kss_Core.h */; }; + 8370B76A17F615FE001A4D7A /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C717F615FD001A4D7A /* mamedef.h */; }; + 8370B76B17F615FE001A4D7A /* mathdefs.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C817F615FD001A4D7A /* mathdefs.h */; }; + 8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */; }; + 8370B76D17F615FE001A4D7A /* Nes_Fds_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */; }; + 8370B76E17F615FE001A4D7A /* Nes_Fds_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */; }; + 8370B76F17F615FE001A4D7A /* Nes_Mmc5_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */; }; + 8370B77017F615FE001A4D7A /* Nes_Vrc7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */; }; + 8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */; }; + 8370B77217F615FE001A4D7A /* nestypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6CF17F615FD001A4D7A /* nestypes.h */; }; + 8370B77317F615FE001A4D7A /* Nsf_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */; }; + 8370B77417F615FE001A4D7A /* Nsf_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D117F615FD001A4D7A /* Nsf_Core.h */; }; + 8370B77517F615FE001A4D7A /* Nsf_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */; }; + 8370B77617F615FE001A4D7A /* Nsf_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */; }; + 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */; }; + 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */; }; + 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */; }; + 8370B77A17F615FE001A4D7A /* okim6258.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D717F615FD001A4D7A /* okim6258.c */; }; + 8370B77B17F615FE001A4D7A /* okim6258.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6D817F615FD001A4D7A /* okim6258.h */; }; + 8370B77C17F615FE001A4D7A /* Okim6295_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */; }; + 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */; }; + 8370B77E17F615FE001A4D7A /* okim6295.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DB17F615FD001A4D7A /* okim6295.c */; }; + 8370B77F17F615FE001A4D7A /* okim6295.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DC17F615FD001A4D7A /* okim6295.h */; }; + 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */; }; + 8370B78117F615FE001A4D7A /* Opl_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */; }; + 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */; }; + 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */; }; + 8370B78417F615FE001A4D7A /* pwm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E117F615FE001A4D7A /* pwm.c */; }; + 8370B78517F615FE001A4D7A /* pwm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E217F615FE001A4D7A /* pwm.h */; }; + 8370B78617F615FE001A4D7A /* qmix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E317F615FE001A4D7A /* qmix.c */; }; + 8370B78717F615FE001A4D7A /* qmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E417F615FE001A4D7A /* qmix.h */; }; + 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */; }; + 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */; }; + 8370B78A17F615FE001A4D7A /* Resampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E717F615FE001A4D7A /* Resampler.cpp */; }; + 8370B78B17F615FE001A4D7A /* Resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6E817F615FE001A4D7A /* Resampler.h */; }; + 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */; }; + 8370B78D17F615FE001A4D7A /* Rf5C68_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */; }; + 8370B78E17F615FE001A4D7A /* rf5c68.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6EB17F615FE001A4D7A /* rf5c68.c */; }; + 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EC17F615FE001A4D7A /* rf5c68.h */; }; + 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */; }; + 8370B79117F615FE001A4D7A /* Rf5C164_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */; }; + 8370B79217F615FE001A4D7A /* Rom_Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */; }; + 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F017F615FE001A4D7A /* Rom_Data.h */; }; + 8370B79417F615FE001A4D7A /* s_deltat.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F117F615FE001A4D7A /* s_deltat.c */; }; + 8370B79517F615FE001A4D7A /* s_deltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F217F615FE001A4D7A /* s_deltat.h */; }; + 8370B79617F615FE001A4D7A /* s_logtbl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F317F615FE001A4D7A /* s_logtbl.c */; }; + 8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F417F615FE001A4D7A /* s_logtbl.h */; }; + 8370B79817F615FE001A4D7A /* s_opl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F517F615FE001A4D7A /* s_opl.c */; }; + 8370B79917F615FE001A4D7A /* s_opl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F617F615FE001A4D7A /* s_opl.h */; }; + 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F717F615FE001A4D7A /* s_opltbl.c */; }; + 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6F817F615FE001A4D7A /* s_opltbl.h */; }; + 8370B79C17F615FE001A4D7A /* Sap_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */; }; + 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FA17F615FE001A4D7A /* Sap_Core.h */; }; + 8370B79E17F615FE001A4D7A /* scd_pcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FB17F615FE001A4D7A /* scd_pcm.c */; }; + 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FC17F615FE001A4D7A /* scd_pcm.h */; }; + 8370B7A017F615FE001A4D7A /* SegaPcm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */; }; + 8370B7A117F615FE001A4D7A /* SegaPcm_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */; }; + 8370B7A217F615FE001A4D7A /* segapcm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B6FF17F615FE001A4D7A /* segapcm.c */; }; + 8370B7A317F615FE001A4D7A /* segapcm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70017F615FE001A4D7A /* segapcm.h */; }; + 8370B7A417F615FE001A4D7A /* Sgc_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */; }; + 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70217F615FE001A4D7A /* Sgc_Core.h */; }; + 8370B7A617F615FE001A4D7A /* Sgc_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */; }; + 8370B7A717F615FE001A4D7A /* Sgc_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70417F615FE001A4D7A /* Sgc_Emu.cpp */; }; + 8370B7A817F615FE001A4D7A /* Sgc_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70517F615FE001A4D7A /* Sgc_Emu.h */; }; + 8370B7A917F615FE001A4D7A /* Sgc_Impl.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70617F615FE001A4D7A /* Sgc_Impl.cpp */; }; + 8370B7AA17F615FE001A4D7A /* Sgc_Impl.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70717F615FE001A4D7A /* Sgc_Impl.h */; }; + 8370B7AB17F615FE001A4D7A /* Sms_Fm_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70817F615FE001A4D7A /* Sms_Fm_Apu.cpp */; }; + 8370B7AC17F615FE001A4D7A /* Sms_Fm_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70917F615FE001A4D7A /* Sms_Fm_Apu.h */; }; + 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */; }; + 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70B17F615FE001A4D7A /* Spc_Filter.h */; }; + 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */; }; + 8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */; }; + 8370B7B117F615FE001A4D7A /* Track_Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70E17F615FE001A4D7A /* Track_Filter.cpp */; }; + 8370B7B217F615FE001A4D7A /* Track_Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70F17F615FE001A4D7A /* Track_Filter.h */; }; + 8370B7B317F615FE001A4D7A /* Upsampler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71017F615FE001A4D7A /* Upsampler.cpp */; }; + 8370B7B417F615FE001A4D7A /* Upsampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71117F615FE001A4D7A /* Upsampler.h */; }; + 8370B7B517F615FE001A4D7A /* Vgm_Core.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */; }; + 8370B7B617F615FE001A4D7A /* Vgm_Core.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71317F615FE001A4D7A /* Vgm_Core.h */; }; + 8370B7B717F615FE001A4D7A /* Ym2151_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */; }; + 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */; }; + 8370B7B917F615FE001A4D7A /* ym2151.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71617F615FE001A4D7A /* ym2151.c */; }; + 8370B7BA17F615FE001A4D7A /* ym2151.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71717F615FE001A4D7A /* ym2151.h */; }; + 8370B7BB17F615FE001A4D7A /* Ym2203_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */; }; + 8370B7BC17F615FE001A4D7A /* Ym2203_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */; }; + 8370B7BD17F615FE001A4D7A /* ym2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71A17F615FE001A4D7A /* ym2413.c */; }; + 8370B7BE17F615FE001A4D7A /* ym2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71B17F615FE001A4D7A /* ym2413.h */; }; + 8370B7BF17F615FE001A4D7A /* Ym2608_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */; }; + 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */; }; + 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */; }; + 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */; }; + 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */; }; + 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */; }; + 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72417F615FE001A4D7A /* ymdeltat.cpp */; }; + 8370B7C817F615FE001A4D7A /* ymdeltat.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72517F615FE001A4D7A /* ymdeltat.h */; }; + 8370B7C917F615FE001A4D7A /* Ymf262_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */; }; + 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */; }; + 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */; }; + 8370B7CC17F615FE001A4D7A /* Ymz280b_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */; }; + 8370B7CD17F615FE001A4D7A /* ymz280b.c in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72A17F615FE001A4D7A /* ymz280b.c */; }; + 8370B7CE17F615FE001A4D7A /* ymz280b.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72B17F615FE001A4D7A /* ymz280b.h */; }; + 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */; }; + 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */; }; + 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -118,7 +267,6 @@ 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Apu.cpp; path = gme/Ay_Apu.cpp; sourceTree = ""; }; 17C8F18C0CBED286008D969D /* Ay_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ay_Apu.h; path = gme/Ay_Apu.h; sourceTree = ""; }; 17C8F18D0CBED286008D969D /* Ay_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Cpu.cpp; path = gme/Ay_Cpu.cpp; sourceTree = ""; }; - 17C8F18E0CBED286008D969D /* Ay_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ay_Cpu.h; path = gme/Ay_Cpu.h; sourceTree = ""; }; 17C8F18F0CBED286008D969D /* Ay_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Emu.cpp; path = gme/Ay_Emu.cpp; sourceTree = ""; }; 17C8F1900CBED286008D969D /* Ay_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ay_Emu.h; path = gme/Ay_Emu.h; sourceTree = ""; }; 17C8F1910CBED286008D969D /* blargg_common.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = blargg_common.h; path = gme/blargg_common.h; sourceTree = ""; }; @@ -139,7 +287,6 @@ 17C8F1A00CBED286008D969D /* Fir_Resampler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Fir_Resampler.h; path = gme/Fir_Resampler.h; sourceTree = ""; }; 17C8F1A10CBED286008D969D /* Gb_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Gb_Apu.cpp; path = gme/Gb_Apu.cpp; sourceTree = ""; }; 17C8F1A20CBED286008D969D /* Gb_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Gb_Apu.h; path = gme/Gb_Apu.h; sourceTree = ""; }; - 17C8F1A30CBED286008D969D /* gb_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = gb_cpu_io.h; path = gme/gb_cpu_io.h; sourceTree = ""; }; 17C8F1A40CBED286008D969D /* Gb_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Gb_Cpu.cpp; path = gme/Gb_Cpu.cpp; sourceTree = ""; }; 17C8F1A50CBED286008D969D /* Gb_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Gb_Cpu.h; path = gme/Gb_Cpu.h; sourceTree = ""; }; 17C8F1A60CBED286008D969D /* Gb_Oscs.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Gb_Oscs.cpp; path = gme/Gb_Oscs.cpp; sourceTree = ""; }; @@ -154,13 +301,11 @@ 17C8F1B00CBED286008D969D /* Gym_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Gym_Emu.h; path = gme/Gym_Emu.h; sourceTree = ""; }; 17C8F1B10CBED286008D969D /* Hes_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Apu.cpp; path = gme/Hes_Apu.cpp; sourceTree = ""; }; 17C8F1B20CBED286008D969D /* Hes_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Hes_Apu.h; path = gme/Hes_Apu.h; sourceTree = ""; }; - 17C8F1B30CBED286008D969D /* hes_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = hes_cpu_io.h; path = gme/hes_cpu_io.h; sourceTree = ""; }; 17C8F1B40CBED286008D969D /* Hes_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Cpu.cpp; path = gme/Hes_Cpu.cpp; sourceTree = ""; }; 17C8F1B50CBED286008D969D /* Hes_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Hes_Cpu.h; path = gme/Hes_Cpu.h; sourceTree = ""; }; 17C8F1B60CBED286008D969D /* Hes_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Emu.cpp; path = gme/Hes_Emu.cpp; sourceTree = ""; }; 17C8F1B70CBED286008D969D /* Hes_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Hes_Emu.h; path = gme/Hes_Emu.h; sourceTree = ""; }; 17C8F1B80CBED286008D969D /* Kss_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Cpu.cpp; path = gme/Kss_Cpu.cpp; sourceTree = ""; }; - 17C8F1B90CBED286008D969D /* Kss_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Kss_Cpu.h; path = gme/Kss_Cpu.h; sourceTree = ""; }; 17C8F1BA0CBED286008D969D /* Kss_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Emu.cpp; path = gme/Kss_Emu.cpp; sourceTree = ""; }; 17C8F1BB0CBED286008D969D /* Kss_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Kss_Emu.h; path = gme/Kss_Emu.h; sourceTree = ""; }; 17C8F1BC0CBED286008D969D /* Kss_Scc_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Scc_Apu.cpp; path = gme/Kss_Scc_Apu.cpp; sourceTree = ""; }; @@ -173,7 +318,6 @@ 17C8F1C40CBED286008D969D /* Music_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Music_Emu.h; path = gme/Music_Emu.h; sourceTree = ""; }; 17C8F1C50CBED286008D969D /* Nes_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Apu.cpp; path = gme/Nes_Apu.cpp; sourceTree = ""; }; 17C8F1C60CBED286008D969D /* Nes_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Nes_Apu.h; path = gme/Nes_Apu.h; sourceTree = ""; }; - 17C8F1C70CBED286008D969D /* nes_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = nes_cpu_io.h; path = gme/nes_cpu_io.h; sourceTree = ""; }; 17C8F1C80CBED286008D969D /* Nes_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Cpu.cpp; path = gme/Nes_Cpu.cpp; sourceTree = ""; }; 17C8F1C90CBED286008D969D /* Nes_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Nes_Cpu.h; path = gme/Nes_Cpu.h; sourceTree = ""; }; 17C8F1CA0CBED286008D969D /* Nes_Fme7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Fme7_Apu.cpp; path = gme/Nes_Fme7_Apu.cpp; sourceTree = ""; }; @@ -190,14 +334,11 @@ 17C8F1D50CBED286008D969D /* Nsfe_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Nsfe_Emu.h; path = gme/Nsfe_Emu.h; sourceTree = ""; }; 17C8F1D60CBED286008D969D /* Sap_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Apu.cpp; path = gme/Sap_Apu.cpp; sourceTree = ""; }; 17C8F1D70CBED286008D969D /* Sap_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Apu.h; path = gme/Sap_Apu.h; sourceTree = ""; }; - 17C8F1D80CBED286008D969D /* sap_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = sap_cpu_io.h; path = gme/sap_cpu_io.h; sourceTree = ""; }; 17C8F1D90CBED286008D969D /* Sap_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Cpu.cpp; path = gme/Sap_Cpu.cpp; sourceTree = ""; }; - 17C8F1DA0CBED286008D969D /* Sap_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Cpu.h; path = gme/Sap_Cpu.h; sourceTree = ""; }; 17C8F1DB0CBED286008D969D /* Sap_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Emu.cpp; path = gme/Sap_Emu.cpp; sourceTree = ""; }; 17C8F1DC0CBED286008D969D /* Sap_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Emu.h; path = gme/Sap_Emu.h; sourceTree = ""; }; 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sms_Apu.cpp; path = gme/Sms_Apu.cpp; sourceTree = ""; }; 17C8F1DE0CBED286008D969D /* Sms_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Apu.h; path = gme/Sms_Apu.h; sourceTree = ""; }; - 17C8F1DF0CBED286008D969D /* Sms_Oscs.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Oscs.h; path = gme/Sms_Oscs.h; sourceTree = ""; }; 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Snes_Spc.cpp; path = gme/Snes_Spc.cpp; sourceTree = ""; }; 17C8F1E10CBED286008D969D /* Snes_Spc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Snes_Spc.h; path = gme/Snes_Spc.h; sourceTree = ""; }; 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Cpu.cpp; path = gme/Spc_Cpu.cpp; sourceTree = ""; }; @@ -206,14 +347,173 @@ 17C8F1E50CBED286008D969D /* Spc_Dsp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Dsp.h; path = gme/Spc_Dsp.h; sourceTree = ""; }; 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Emu.cpp; path = gme/Spc_Emu.cpp; sourceTree = ""; }; 17C8F1E70CBED286008D969D /* Spc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Emu.h; path = gme/Spc_Emu.h; sourceTree = ""; }; - 17C8F1E80CBED286008D969D /* Vgm_Emu_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Emu_Impl.cpp; path = gme/Vgm_Emu_Impl.cpp; sourceTree = ""; }; - 17C8F1E90CBED286008D969D /* Vgm_Emu_Impl.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Vgm_Emu_Impl.h; path = gme/Vgm_Emu_Impl.h; sourceTree = ""; }; 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Emu.cpp; path = gme/Vgm_Emu.cpp; sourceTree = ""; }; 17C8F1EB0CBED286008D969D /* Vgm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Vgm_Emu.h; path = gme/Vgm_Emu.h; sourceTree = ""; }; 17C8F1EC0CBED286008D969D /* Ym2413_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2413_Emu.cpp; path = gme/Ym2413_Emu.cpp; sourceTree = ""; }; 17C8F1ED0CBED286008D969D /* Ym2413_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ym2413_Emu.h; path = gme/Ym2413_Emu.h; sourceTree = ""; }; 17C8F1EE0CBED286008D969D /* Ym2612_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu.cpp; path = gme/Ym2612_Emu.cpp; sourceTree = ""; }; 17C8F1EF0CBED286008D969D /* Ym2612_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Ym2612_Emu.h; path = gme/Ym2612_Emu.h; sourceTree = ""; }; + 8370B68C17F615FD001A4D7A /* adlib.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = adlib.h; path = gme/adlib.h; sourceTree = ""; }; + 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ay_Core.cpp; path = gme/Ay_Core.cpp; sourceTree = ""; }; + 8370B68E17F615FD001A4D7A /* Ay_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ay_Core.h; path = gme/Ay_Core.h; sourceTree = ""; }; + 8370B68F17F615FD001A4D7A /* blargg_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = blargg_common.cpp; path = gme/blargg_common.cpp; sourceTree = ""; }; + 8370B69017F615FD001A4D7A /* blargg_errors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = blargg_errors.cpp; path = gme/blargg_errors.cpp; sourceTree = ""; }; + 8370B69117F615FD001A4D7A /* blargg_errors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = blargg_errors.h; path = gme/blargg_errors.h; sourceTree = ""; }; + 8370B69217F615FD001A4D7A /* Blip_Buffer_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Blip_Buffer_impl.h; path = gme/Blip_Buffer_impl.h; sourceTree = ""; }; + 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Blip_Buffer_impl2.h; path = gme/Blip_Buffer_impl2.h; sourceTree = ""; }; + 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Bml_Parser.cpp; path = gme/Bml_Parser.cpp; sourceTree = ""; }; + 8370B69517F615FD001A4D7A /* Bml_Parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Bml_Parser.h; path = gme/Bml_Parser.h; sourceTree = ""; }; + 8370B69617F615FD001A4D7A /* C140_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = C140_Emu.cpp; path = gme/C140_Emu.cpp; sourceTree = ""; }; + 8370B69717F615FD001A4D7A /* C140_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = C140_Emu.h; path = gme/C140_Emu.h; sourceTree = ""; }; + 8370B69817F615FD001A4D7A /* c140.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = c140.c; path = gme/c140.c; sourceTree = ""; }; + 8370B69917F615FD001A4D7A /* c140.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = c140.h; path = gme/c140.h; sourceTree = ""; }; + 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Chip_Resampler.h; path = gme/Chip_Resampler.h; sourceTree = ""; }; + 8370B69B17F615FD001A4D7A /* dac_control.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = dac_control.c; path = gme/dac_control.c; sourceTree = ""; }; + 8370B69C17F615FD001A4D7A /* dac_control.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dac_control.h; path = gme/dac_control.h; sourceTree = ""; }; + 8370B69D17F615FD001A4D7A /* dbopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dbopl.cpp; path = gme/dbopl.cpp; sourceTree = ""; }; + 8370B69E17F615FD001A4D7A /* dbopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dbopl.h; path = gme/dbopl.h; sourceTree = ""; }; + 8370B69F17F615FD001A4D7A /* divfix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = divfix.h; path = gme/divfix.h; sourceTree = ""; }; + 8370B6A017F615FD001A4D7A /* Downsampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Downsampler.cpp; path = gme/Downsampler.cpp; sourceTree = ""; }; + 8370B6A117F615FD001A4D7A /* Downsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Downsampler.h; path = gme/Downsampler.h; sourceTree = ""; }; + 8370B6A217F615FD001A4D7A /* emuconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = emuconfig.h; path = gme/emuconfig.h; sourceTree = ""; }; + 8370B6A317F615FD001A4D7A /* fm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fm.c; path = gme/fm.c; sourceTree = ""; }; + 8370B6A417F615FD001A4D7A /* fm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fm.h; path = gme/fm.h; sourceTree = ""; }; + 8370B6A517F615FD001A4D7A /* fm2612.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = fm2612.c; path = gme/fm2612.c; sourceTree = ""; }; + 8370B6A617F615FD001A4D7A /* fmopl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = fmopl.cpp; path = gme/fmopl.cpp; sourceTree = ""; }; + 8370B6A717F615FD001A4D7A /* fmopl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = fmopl.h; path = gme/fmopl.h; sourceTree = ""; }; + 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gb_Cpu_run.h; path = gme/Gb_Cpu_run.h; sourceTree = ""; }; + 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gbs_Core.cpp; path = gme/Gbs_Core.cpp; sourceTree = ""; }; + 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gbs_Core.h; path = gme/Gbs_Core.h; sourceTree = ""; }; + 8370B6AB17F615FD001A4D7A /* Gbs_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gbs_Cpu.cpp; path = gme/Gbs_Cpu.cpp; sourceTree = ""; }; + 8370B6AE17F615FD001A4D7A /* Gme_Loader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Gme_Loader.cpp; path = gme/Gme_Loader.cpp; sourceTree = ""; }; + 8370B6AF17F615FD001A4D7A /* Gme_Loader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Gme_Loader.h; path = gme/Gme_Loader.h; sourceTree = ""; }; + 8370B6B017F615FD001A4D7A /* Hes_Apu_Adpcm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Apu_Adpcm.cpp; path = gme/Hes_Apu_Adpcm.cpp; sourceTree = ""; }; + 8370B6B117F615FD001A4D7A /* Hes_Apu_Adpcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Apu_Adpcm.h; path = gme/Hes_Apu_Adpcm.h; sourceTree = ""; }; + 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Hes_Core.cpp; path = gme/Hes_Core.cpp; sourceTree = ""; }; + 8370B6B317F615FD001A4D7A /* Hes_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Core.h; path = gme/Hes_Core.h; sourceTree = ""; }; + 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Hes_Cpu_run.h; path = gme/Hes_Cpu_run.h; sourceTree = ""; }; + 8370B6B517F615FD001A4D7A /* i_fmpac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_fmpac.h; path = gme/i_fmpac.h; sourceTree = ""; }; + 8370B6B617F615FD001A4D7A /* i_fmunit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_fmunit.h; path = gme/i_fmunit.h; sourceTree = ""; }; + 8370B6B717F615FD001A4D7A /* i_vrc7.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = i_vrc7.h; path = gme/i_vrc7.h; sourceTree = ""; }; + 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K051649_Emu.cpp; path = gme/K051649_Emu.cpp; sourceTree = ""; }; + 8370B6B917F615FD001A4D7A /* K051649_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K051649_Emu.h; path = gme/K051649_Emu.h; sourceTree = ""; }; + 8370B6BA17F615FD001A4D7A /* k051649.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k051649.c; path = gme/k051649.c; sourceTree = ""; }; + 8370B6BB17F615FD001A4D7A /* k051649.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k051649.h; path = gme/k051649.h; sourceTree = ""; }; + 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K053260_Emu.cpp; path = gme/K053260_Emu.cpp; sourceTree = ""; }; + 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K053260_Emu.h; path = gme/K053260_Emu.h; sourceTree = ""; }; + 8370B6BE17F615FD001A4D7A /* k053260.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k053260.c; path = gme/k053260.c; sourceTree = ""; }; + 8370B6BF17F615FD001A4D7A /* k053260.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k053260.h; path = gme/k053260.h; sourceTree = ""; }; + 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = K054539_Emu.cpp; path = gme/K054539_Emu.cpp; sourceTree = ""; }; + 8370B6C117F615FD001A4D7A /* K054539_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = K054539_Emu.h; path = gme/K054539_Emu.h; sourceTree = ""; }; + 8370B6C217F615FD001A4D7A /* k054539.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = k054539.c; path = gme/k054539.c; sourceTree = ""; }; + 8370B6C317F615FD001A4D7A /* k054539.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = k054539.h; path = gme/k054539.h; sourceTree = ""; }; + 8370B6C417F615FD001A4D7A /* kmsnddev.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = kmsnddev.h; path = gme/kmsnddev.h; sourceTree = ""; }; + 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Kss_Core.cpp; path = gme/Kss_Core.cpp; sourceTree = ""; }; + 8370B6C617F615FD001A4D7A /* Kss_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Kss_Core.h; path = gme/Kss_Core.h; sourceTree = ""; }; + 8370B6C717F615FD001A4D7A /* mamedef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mamedef.h; path = gme/mamedef.h; sourceTree = ""; }; + 8370B6C817F615FD001A4D7A /* mathdefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = mathdefs.h; path = gme/mathdefs.h; sourceTree = ""; }; + 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Cpu_run.h; path = gme/Nes_Cpu_run.h; sourceTree = ""; }; + 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Fds_Apu.cpp; path = gme/Nes_Fds_Apu.cpp; sourceTree = ""; }; + 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Fds_Apu.h; path = gme/Nes_Fds_Apu.h; sourceTree = ""; }; + 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Mmc5_Apu.h; path = gme/Nes_Mmc5_Apu.h; sourceTree = ""; }; + 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Vrc7_Apu.cpp; path = gme/Nes_Vrc7_Apu.cpp; sourceTree = ""; }; + 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Vrc7_Apu.h; path = gme/Nes_Vrc7_Apu.h; sourceTree = ""; }; + 8370B6CF17F615FD001A4D7A /* nestypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nestypes.h; path = gme/nestypes.h; sourceTree = ""; }; + 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Core.cpp; path = gme/Nsf_Core.cpp; sourceTree = ""; }; + 8370B6D117F615FD001A4D7A /* Nsf_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nsf_Core.h; path = gme/Nsf_Core.h; sourceTree = ""; }; + 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Cpu.cpp; path = gme/Nsf_Cpu.cpp; sourceTree = ""; }; + 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nsf_Impl.cpp; path = gme/Nsf_Impl.cpp; sourceTree = ""; }; + 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nsf_Impl.h; path = gme/Nsf_Impl.h; sourceTree = ""; }; + 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Okim6258_Emu.cpp; path = gme/Okim6258_Emu.cpp; sourceTree = ""; }; + 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Okim6258_Emu.h; path = gme/Okim6258_Emu.h; sourceTree = ""; }; + 8370B6D717F615FD001A4D7A /* okim6258.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = okim6258.c; path = gme/okim6258.c; sourceTree = ""; }; + 8370B6D817F615FD001A4D7A /* okim6258.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = okim6258.h; path = gme/okim6258.h; sourceTree = ""; }; + 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Okim6295_Emu.cpp; path = gme/Okim6295_Emu.cpp; sourceTree = ""; }; + 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Okim6295_Emu.h; path = gme/Okim6295_Emu.h; sourceTree = ""; }; + 8370B6DB17F615FD001A4D7A /* okim6295.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = okim6295.c; path = gme/okim6295.c; sourceTree = ""; }; + 8370B6DC17F615FD001A4D7A /* okim6295.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = okim6295.h; path = gme/okim6295.h; sourceTree = ""; }; + 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Opl_Apu.cpp; path = gme/Opl_Apu.cpp; sourceTree = ""; }; + 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Opl_Apu.h; path = gme/Opl_Apu.h; sourceTree = ""; }; + 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Pwm_Emu.cpp; path = gme/Pwm_Emu.cpp; sourceTree = ""; }; + 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Pwm_Emu.h; path = gme/Pwm_Emu.h; sourceTree = ""; }; + 8370B6E117F615FE001A4D7A /* pwm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pwm.c; path = gme/pwm.c; sourceTree = ""; }; + 8370B6E217F615FE001A4D7A /* pwm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = pwm.h; path = gme/pwm.h; sourceTree = ""; }; + 8370B6E317F615FE001A4D7A /* qmix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = qmix.c; path = gme/qmix.c; sourceTree = ""; }; + 8370B6E417F615FE001A4D7A /* qmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = qmix.h; path = gme/qmix.h; sourceTree = ""; }; + 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Qsound_Apu.cpp; path = gme/Qsound_Apu.cpp; sourceTree = ""; }; + 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Qsound_Apu.h; path = gme/Qsound_Apu.h; sourceTree = ""; }; + 8370B6E717F615FE001A4D7A /* Resampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Resampler.cpp; path = gme/Resampler.cpp; sourceTree = ""; }; + 8370B6E817F615FE001A4D7A /* Resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Resampler.h; path = gme/Resampler.h; sourceTree = ""; }; + 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rf5C68_Emu.cpp; path = gme/Rf5C68_Emu.cpp; sourceTree = ""; }; + 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rf5C68_Emu.h; path = gme/Rf5C68_Emu.h; sourceTree = ""; }; + 8370B6EB17F615FE001A4D7A /* rf5c68.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = rf5c68.c; path = gme/rf5c68.c; sourceTree = ""; }; + 8370B6EC17F615FE001A4D7A /* rf5c68.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rf5c68.h; path = gme/rf5c68.h; sourceTree = ""; }; + 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rf5C164_Emu.cpp; path = gme/Rf5C164_Emu.cpp; sourceTree = ""; }; + 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rf5C164_Emu.h; path = gme/Rf5C164_Emu.h; sourceTree = ""; }; + 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Rom_Data.cpp; path = gme/Rom_Data.cpp; sourceTree = ""; }; + 8370B6F017F615FE001A4D7A /* Rom_Data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Rom_Data.h; path = gme/Rom_Data.h; sourceTree = ""; }; + 8370B6F117F615FE001A4D7A /* s_deltat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_deltat.c; path = gme/s_deltat.c; sourceTree = ""; }; + 8370B6F217F615FE001A4D7A /* s_deltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_deltat.h; path = gme/s_deltat.h; sourceTree = ""; }; + 8370B6F317F615FE001A4D7A /* s_logtbl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_logtbl.c; path = gme/s_logtbl.c; sourceTree = ""; }; + 8370B6F417F615FE001A4D7A /* s_logtbl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_logtbl.h; path = gme/s_logtbl.h; sourceTree = ""; }; + 8370B6F517F615FE001A4D7A /* s_opl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_opl.c; path = gme/s_opl.c; sourceTree = ""; }; + 8370B6F617F615FE001A4D7A /* s_opl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_opl.h; path = gme/s_opl.h; sourceTree = ""; }; + 8370B6F717F615FE001A4D7A /* s_opltbl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = s_opltbl.c; path = gme/s_opltbl.c; sourceTree = ""; }; + 8370B6F817F615FE001A4D7A /* s_opltbl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = s_opltbl.h; path = gme/s_opltbl.h; sourceTree = ""; }; + 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sap_Core.cpp; path = gme/Sap_Core.cpp; sourceTree = ""; }; + 8370B6FA17F615FE001A4D7A /* Sap_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sap_Core.h; path = gme/Sap_Core.h; sourceTree = ""; }; + 8370B6FB17F615FE001A4D7A /* scd_pcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scd_pcm.c; path = gme/scd_pcm.c; sourceTree = ""; }; + 8370B6FC17F615FE001A4D7A /* scd_pcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scd_pcm.h; path = gme/scd_pcm.h; sourceTree = ""; }; + 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SegaPcm_Emu.cpp; path = gme/SegaPcm_Emu.cpp; sourceTree = ""; }; + 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SegaPcm_Emu.h; path = gme/SegaPcm_Emu.h; sourceTree = ""; }; + 8370B6FF17F615FE001A4D7A /* segapcm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = segapcm.c; path = gme/segapcm.c; sourceTree = ""; }; + 8370B70017F615FE001A4D7A /* segapcm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = segapcm.h; path = gme/segapcm.h; sourceTree = ""; }; + 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Core.cpp; path = gme/Sgc_Core.cpp; sourceTree = ""; }; + 8370B70217F615FE001A4D7A /* Sgc_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Core.h; path = gme/Sgc_Core.h; sourceTree = ""; }; + 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Cpu.cpp; path = gme/Sgc_Cpu.cpp; sourceTree = ""; }; + 8370B70417F615FE001A4D7A /* Sgc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Emu.cpp; path = gme/Sgc_Emu.cpp; sourceTree = ""; }; + 8370B70517F615FE001A4D7A /* Sgc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Emu.h; path = gme/Sgc_Emu.h; sourceTree = ""; }; + 8370B70617F615FE001A4D7A /* Sgc_Impl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sgc_Impl.cpp; path = gme/Sgc_Impl.cpp; sourceTree = ""; }; + 8370B70717F615FE001A4D7A /* Sgc_Impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sgc_Impl.h; path = gme/Sgc_Impl.h; sourceTree = ""; }; + 8370B70817F615FE001A4D7A /* Sms_Fm_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Sms_Fm_Apu.cpp; path = gme/Sms_Fm_Apu.cpp; sourceTree = ""; }; + 8370B70917F615FE001A4D7A /* Sms_Fm_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Sms_Fm_Apu.h; path = gme/Sms_Fm_Apu.h; sourceTree = ""; }; + 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Filter.cpp; path = gme/Spc_Filter.cpp; sourceTree = ""; }; + 8370B70B17F615FE001A4D7A /* Spc_Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Filter.h; path = gme/Spc_Filter.h; sourceTree = ""; }; + 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Sfm.cpp; path = gme/Spc_Sfm.cpp; sourceTree = ""; }; + 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Sfm.h; path = gme/Spc_Sfm.h; sourceTree = ""; }; + 8370B70E17F615FE001A4D7A /* Track_Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Track_Filter.cpp; path = gme/Track_Filter.cpp; sourceTree = ""; }; + 8370B70F17F615FE001A4D7A /* Track_Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Track_Filter.h; path = gme/Track_Filter.h; sourceTree = ""; }; + 8370B71017F615FE001A4D7A /* Upsampler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Upsampler.cpp; path = gme/Upsampler.cpp; sourceTree = ""; }; + 8370B71117F615FE001A4D7A /* Upsampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Upsampler.h; path = gme/Upsampler.h; sourceTree = ""; }; + 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Core.cpp; path = gme/Vgm_Core.cpp; sourceTree = ""; }; + 8370B71317F615FE001A4D7A /* Vgm_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Vgm_Core.h; path = gme/Vgm_Core.h; sourceTree = ""; }; + 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2151_Emu.cpp; path = gme/Ym2151_Emu.cpp; sourceTree = ""; }; + 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2151_Emu.h; path = gme/Ym2151_Emu.h; sourceTree = ""; }; + 8370B71617F615FE001A4D7A /* ym2151.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ym2151.c; path = gme/ym2151.c; sourceTree = ""; }; + 8370B71717F615FE001A4D7A /* ym2151.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ym2151.h; path = gme/ym2151.h; sourceTree = ""; }; + 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2203_Emu.cpp; path = gme/Ym2203_Emu.cpp; sourceTree = ""; }; + 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2203_Emu.h; path = gme/Ym2203_Emu.h; sourceTree = ""; }; + 8370B71A17F615FE001A4D7A /* ym2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ym2413.c; path = gme/ym2413.c; sourceTree = ""; }; + 8370B71B17F615FE001A4D7A /* ym2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ym2413.h; path = gme/ym2413.h; sourceTree = ""; }; + 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2608_Emu.cpp; path = gme/Ym2608_Emu.cpp; sourceTree = ""; }; + 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2608_Emu.h; path = gme/Ym2608_Emu.h; sourceTree = ""; }; + 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2610b_Emu.cpp; path = gme/Ym2610b_Emu.cpp; sourceTree = ""; }; + 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym2610b_Emu.h; path = gme/Ym2610b_Emu.h; sourceTree = ""; }; + 8370B72017F615FE001A4D7A /* Ym2612_Emu_Gens.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu_Gens.cpp; path = gme/Ym2612_Emu_Gens.cpp; sourceTree = ""; }; + 8370B72117F615FE001A4D7A /* Ym2612_Emu_MAME.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym2612_Emu_MAME.cpp; path = gme/Ym2612_Emu_MAME.cpp; sourceTree = ""; }; + 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ym3812_Emu.cpp; path = gme/Ym3812_Emu.cpp; sourceTree = ""; }; + 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ym3812_Emu.h; path = gme/Ym3812_Emu.h; sourceTree = ""; }; + 8370B72417F615FE001A4D7A /* ymdeltat.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ymdeltat.cpp; path = gme/ymdeltat.cpp; sourceTree = ""; }; + 8370B72517F615FE001A4D7A /* ymdeltat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ymdeltat.h; path = gme/ymdeltat.h; sourceTree = ""; }; + 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ymf262_Emu.cpp; path = gme/Ymf262_Emu.cpp; sourceTree = ""; }; + 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ymf262_Emu.h; path = gme/Ymf262_Emu.h; sourceTree = ""; }; + 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Ymz280b_Emu.cpp; path = gme/Ymz280b_Emu.cpp; sourceTree = ""; }; + 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Ymz280b_Emu.h; path = gme/Ymz280b_Emu.h; sourceTree = ""; }; + 8370B72A17F615FE001A4D7A /* ymz280b.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ymz280b.c; path = gme/ymz280b.c; sourceTree = ""; }; + 8370B72B17F615FE001A4D7A /* ymz280b.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ymz280b.h; path = gme/ymz280b.h; sourceTree = ""; }; + 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu_run.h; path = gme/Z80_Cpu_run.h; sourceTree = ""; }; + 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80_Cpu.cpp; path = gme/Z80_Cpu.cpp; sourceTree = ""; }; + 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu.h; path = gme/Z80_Cpu.h; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* GME.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GME.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -298,10 +598,170 @@ 17C8F1860CBED26C008D969D /* Source */ = { isa = PBXGroup; children = ( + 8370B68C17F615FD001A4D7A /* adlib.h */, + 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */, + 8370B68E17F615FD001A4D7A /* Ay_Core.h */, + 8370B68F17F615FD001A4D7A /* blargg_common.cpp */, + 8370B69017F615FD001A4D7A /* blargg_errors.cpp */, + 8370B69117F615FD001A4D7A /* blargg_errors.h */, + 8370B69217F615FD001A4D7A /* Blip_Buffer_impl.h */, + 8370B69317F615FD001A4D7A /* Blip_Buffer_impl2.h */, + 8370B69417F615FD001A4D7A /* Bml_Parser.cpp */, + 8370B69517F615FD001A4D7A /* Bml_Parser.h */, + 8370B69617F615FD001A4D7A /* C140_Emu.cpp */, + 8370B69717F615FD001A4D7A /* C140_Emu.h */, + 8370B69817F615FD001A4D7A /* c140.c */, + 8370B69917F615FD001A4D7A /* c140.h */, + 8370B69A17F615FD001A4D7A /* Chip_Resampler.h */, + 8370B69B17F615FD001A4D7A /* dac_control.c */, + 8370B69C17F615FD001A4D7A /* dac_control.h */, + 8370B69D17F615FD001A4D7A /* dbopl.cpp */, + 8370B69E17F615FD001A4D7A /* dbopl.h */, + 8370B69F17F615FD001A4D7A /* divfix.h */, + 8370B6A017F615FD001A4D7A /* Downsampler.cpp */, + 8370B6A117F615FD001A4D7A /* Downsampler.h */, + 8370B6A217F615FD001A4D7A /* emuconfig.h */, + 8370B6A317F615FD001A4D7A /* fm.c */, + 8370B6A417F615FD001A4D7A /* fm.h */, + 8370B6A517F615FD001A4D7A /* fm2612.c */, + 8370B6A617F615FD001A4D7A /* fmopl.cpp */, + 8370B6A717F615FD001A4D7A /* fmopl.h */, + 8370B6A817F615FD001A4D7A /* Gb_Cpu_run.h */, + 8370B6A917F615FD001A4D7A /* Gbs_Core.cpp */, + 8370B6AA17F615FD001A4D7A /* Gbs_Core.h */, + 8370B6AB17F615FD001A4D7A /* Gbs_Cpu.cpp */, + 8370B6AE17F615FD001A4D7A /* Gme_Loader.cpp */, + 8370B6AF17F615FD001A4D7A /* Gme_Loader.h */, + 8370B6B017F615FD001A4D7A /* Hes_Apu_Adpcm.cpp */, + 8370B6B117F615FD001A4D7A /* Hes_Apu_Adpcm.h */, + 8370B6B217F615FD001A4D7A /* Hes_Core.cpp */, + 8370B6B317F615FD001A4D7A /* Hes_Core.h */, + 8370B6B417F615FD001A4D7A /* Hes_Cpu_run.h */, + 8370B6B517F615FD001A4D7A /* i_fmpac.h */, + 8370B6B617F615FD001A4D7A /* i_fmunit.h */, + 8370B6B717F615FD001A4D7A /* i_vrc7.h */, + 8370B6B817F615FD001A4D7A /* K051649_Emu.cpp */, + 8370B6B917F615FD001A4D7A /* K051649_Emu.h */, + 8370B6BA17F615FD001A4D7A /* k051649.c */, + 8370B6BB17F615FD001A4D7A /* k051649.h */, + 8370B6BC17F615FD001A4D7A /* K053260_Emu.cpp */, + 8370B6BD17F615FD001A4D7A /* K053260_Emu.h */, + 8370B6BE17F615FD001A4D7A /* k053260.c */, + 8370B6BF17F615FD001A4D7A /* k053260.h */, + 8370B6C017F615FD001A4D7A /* K054539_Emu.cpp */, + 8370B6C117F615FD001A4D7A /* K054539_Emu.h */, + 8370B6C217F615FD001A4D7A /* k054539.c */, + 8370B6C317F615FD001A4D7A /* k054539.h */, + 8370B6C417F615FD001A4D7A /* kmsnddev.h */, + 8370B6C517F615FD001A4D7A /* Kss_Core.cpp */, + 8370B6C617F615FD001A4D7A /* Kss_Core.h */, + 8370B6C717F615FD001A4D7A /* mamedef.h */, + 8370B6C817F615FD001A4D7A /* mathdefs.h */, + 8370B6C917F615FD001A4D7A /* Nes_Cpu_run.h */, + 8370B6CA17F615FD001A4D7A /* Nes_Fds_Apu.cpp */, + 8370B6CB17F615FD001A4D7A /* Nes_Fds_Apu.h */, + 8370B6CC17F615FD001A4D7A /* Nes_Mmc5_Apu.h */, + 8370B6CD17F615FD001A4D7A /* Nes_Vrc7_Apu.cpp */, + 8370B6CE17F615FD001A4D7A /* Nes_Vrc7_Apu.h */, + 8370B6CF17F615FD001A4D7A /* nestypes.h */, + 8370B6D017F615FD001A4D7A /* Nsf_Core.cpp */, + 8370B6D117F615FD001A4D7A /* Nsf_Core.h */, + 8370B6D217F615FD001A4D7A /* Nsf_Cpu.cpp */, + 8370B6D317F615FD001A4D7A /* Nsf_Impl.cpp */, + 8370B6D417F615FD001A4D7A /* Nsf_Impl.h */, + 8370B6D517F615FD001A4D7A /* Okim6258_Emu.cpp */, + 8370B6D617F615FD001A4D7A /* Okim6258_Emu.h */, + 8370B6D717F615FD001A4D7A /* okim6258.c */, + 8370B6D817F615FD001A4D7A /* okim6258.h */, + 8370B6D917F615FD001A4D7A /* Okim6295_Emu.cpp */, + 8370B6DA17F615FD001A4D7A /* Okim6295_Emu.h */, + 8370B6DB17F615FD001A4D7A /* okim6295.c */, + 8370B6DC17F615FD001A4D7A /* okim6295.h */, + 8370B6DD17F615FD001A4D7A /* Opl_Apu.cpp */, + 8370B6DE17F615FE001A4D7A /* Opl_Apu.h */, + 8370B6DF17F615FE001A4D7A /* Pwm_Emu.cpp */, + 8370B6E017F615FE001A4D7A /* Pwm_Emu.h */, + 8370B6E117F615FE001A4D7A /* pwm.c */, + 8370B6E217F615FE001A4D7A /* pwm.h */, + 8370B6E317F615FE001A4D7A /* qmix.c */, + 8370B6E417F615FE001A4D7A /* qmix.h */, + 8370B6E517F615FE001A4D7A /* Qsound_Apu.cpp */, + 8370B6E617F615FE001A4D7A /* Qsound_Apu.h */, + 8370B6E717F615FE001A4D7A /* Resampler.cpp */, + 8370B6E817F615FE001A4D7A /* Resampler.h */, + 8370B6E917F615FE001A4D7A /* Rf5C68_Emu.cpp */, + 8370B6EA17F615FE001A4D7A /* Rf5C68_Emu.h */, + 8370B6EB17F615FE001A4D7A /* rf5c68.c */, + 8370B6EC17F615FE001A4D7A /* rf5c68.h */, + 8370B6ED17F615FE001A4D7A /* Rf5C164_Emu.cpp */, + 8370B6EE17F615FE001A4D7A /* Rf5C164_Emu.h */, + 8370B6EF17F615FE001A4D7A /* Rom_Data.cpp */, + 8370B6F017F615FE001A4D7A /* Rom_Data.h */, + 8370B6F117F615FE001A4D7A /* s_deltat.c */, + 8370B6F217F615FE001A4D7A /* s_deltat.h */, + 8370B6F317F615FE001A4D7A /* s_logtbl.c */, + 8370B6F417F615FE001A4D7A /* s_logtbl.h */, + 8370B6F517F615FE001A4D7A /* s_opl.c */, + 8370B6F617F615FE001A4D7A /* s_opl.h */, + 8370B6F717F615FE001A4D7A /* s_opltbl.c */, + 8370B6F817F615FE001A4D7A /* s_opltbl.h */, + 8370B6F917F615FE001A4D7A /* Sap_Core.cpp */, + 8370B6FA17F615FE001A4D7A /* Sap_Core.h */, + 8370B6FB17F615FE001A4D7A /* scd_pcm.c */, + 8370B6FC17F615FE001A4D7A /* scd_pcm.h */, + 8370B6FD17F615FE001A4D7A /* SegaPcm_Emu.cpp */, + 8370B6FE17F615FE001A4D7A /* SegaPcm_Emu.h */, + 8370B6FF17F615FE001A4D7A /* segapcm.c */, + 8370B70017F615FE001A4D7A /* segapcm.h */, + 8370B70117F615FE001A4D7A /* Sgc_Core.cpp */, + 8370B70217F615FE001A4D7A /* Sgc_Core.h */, + 8370B70317F615FE001A4D7A /* Sgc_Cpu.cpp */, + 8370B70417F615FE001A4D7A /* Sgc_Emu.cpp */, + 8370B70517F615FE001A4D7A /* Sgc_Emu.h */, + 8370B70617F615FE001A4D7A /* Sgc_Impl.cpp */, + 8370B70717F615FE001A4D7A /* Sgc_Impl.h */, + 8370B70817F615FE001A4D7A /* Sms_Fm_Apu.cpp */, + 8370B70917F615FE001A4D7A /* Sms_Fm_Apu.h */, + 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */, + 8370B70B17F615FE001A4D7A /* Spc_Filter.h */, + 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */, + 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */, + 8370B70E17F615FE001A4D7A /* Track_Filter.cpp */, + 8370B70F17F615FE001A4D7A /* Track_Filter.h */, + 8370B71017F615FE001A4D7A /* Upsampler.cpp */, + 8370B71117F615FE001A4D7A /* Upsampler.h */, + 8370B71217F615FE001A4D7A /* Vgm_Core.cpp */, + 8370B71317F615FE001A4D7A /* Vgm_Core.h */, + 8370B71417F615FE001A4D7A /* Ym2151_Emu.cpp */, + 8370B71517F615FE001A4D7A /* Ym2151_Emu.h */, + 8370B71617F615FE001A4D7A /* ym2151.c */, + 8370B71717F615FE001A4D7A /* ym2151.h */, + 8370B71817F615FE001A4D7A /* Ym2203_Emu.cpp */, + 8370B71917F615FE001A4D7A /* Ym2203_Emu.h */, + 8370B71A17F615FE001A4D7A /* ym2413.c */, + 8370B71B17F615FE001A4D7A /* ym2413.h */, + 8370B71C17F615FE001A4D7A /* Ym2608_Emu.cpp */, + 8370B71D17F615FE001A4D7A /* Ym2608_Emu.h */, + 8370B71E17F615FE001A4D7A /* Ym2610b_Emu.cpp */, + 8370B71F17F615FE001A4D7A /* Ym2610b_Emu.h */, + 8370B72017F615FE001A4D7A /* Ym2612_Emu_Gens.cpp */, + 8370B72117F615FE001A4D7A /* Ym2612_Emu_MAME.cpp */, + 8370B72217F615FE001A4D7A /* Ym3812_Emu.cpp */, + 8370B72317F615FE001A4D7A /* Ym3812_Emu.h */, + 8370B72417F615FE001A4D7A /* ymdeltat.cpp */, + 8370B72517F615FE001A4D7A /* ymdeltat.h */, + 8370B72617F615FE001A4D7A /* Ymf262_Emu.cpp */, + 8370B72717F615FE001A4D7A /* Ymf262_Emu.h */, + 8370B72817F615FE001A4D7A /* Ymz280b_Emu.cpp */, + 8370B72917F615FE001A4D7A /* Ymz280b_Emu.h */, + 8370B72A17F615FE001A4D7A /* ymz280b.c */, + 8370B72B17F615FE001A4D7A /* ymz280b.h */, + 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */, + 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */, + 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */, 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */, 17C8F18C0CBED286008D969D /* Ay_Apu.h */, 17C8F18D0CBED286008D969D /* Ay_Cpu.cpp */, - 17C8F18E0CBED286008D969D /* Ay_Cpu.h */, 17C8F18F0CBED286008D969D /* Ay_Emu.cpp */, 17C8F1900CBED286008D969D /* Ay_Emu.h */, 17C8F1910CBED286008D969D /* blargg_common.h */, @@ -322,7 +782,6 @@ 17C8F1A00CBED286008D969D /* Fir_Resampler.h */, 17C8F1A10CBED286008D969D /* Gb_Apu.cpp */, 17C8F1A20CBED286008D969D /* Gb_Apu.h */, - 17C8F1A30CBED286008D969D /* gb_cpu_io.h */, 17C8F1A40CBED286008D969D /* Gb_Cpu.cpp */, 17C8F1A50CBED286008D969D /* Gb_Cpu.h */, 17C8F1A60CBED286008D969D /* Gb_Oscs.cpp */, @@ -336,13 +795,11 @@ 17C8F1B00CBED286008D969D /* Gym_Emu.h */, 17C8F1B10CBED286008D969D /* Hes_Apu.cpp */, 17C8F1B20CBED286008D969D /* Hes_Apu.h */, - 17C8F1B30CBED286008D969D /* hes_cpu_io.h */, 17C8F1B40CBED286008D969D /* Hes_Cpu.cpp */, 17C8F1B50CBED286008D969D /* Hes_Cpu.h */, 17C8F1B60CBED286008D969D /* Hes_Emu.cpp */, 17C8F1B70CBED286008D969D /* Hes_Emu.h */, 17C8F1B80CBED286008D969D /* Kss_Cpu.cpp */, - 17C8F1B90CBED286008D969D /* Kss_Cpu.h */, 17C8F1BA0CBED286008D969D /* Kss_Emu.cpp */, 17C8F1BB0CBED286008D969D /* Kss_Emu.h */, 17C8F1BC0CBED286008D969D /* Kss_Scc_Apu.cpp */, @@ -355,7 +812,6 @@ 17C8F1C40CBED286008D969D /* Music_Emu.h */, 17C8F1C50CBED286008D969D /* Nes_Apu.cpp */, 17C8F1C60CBED286008D969D /* Nes_Apu.h */, - 17C8F1C70CBED286008D969D /* nes_cpu_io.h */, 17C8F1C80CBED286008D969D /* Nes_Cpu.cpp */, 17C8F1C90CBED286008D969D /* Nes_Cpu.h */, 17C8F1CA0CBED286008D969D /* Nes_Fme7_Apu.cpp */, @@ -372,14 +828,11 @@ 17C8F1D50CBED286008D969D /* Nsfe_Emu.h */, 17C8F1D60CBED286008D969D /* Sap_Apu.cpp */, 17C8F1D70CBED286008D969D /* Sap_Apu.h */, - 17C8F1D80CBED286008D969D /* sap_cpu_io.h */, 17C8F1D90CBED286008D969D /* Sap_Cpu.cpp */, - 17C8F1DA0CBED286008D969D /* Sap_Cpu.h */, 17C8F1DB0CBED286008D969D /* Sap_Emu.cpp */, 17C8F1DC0CBED286008D969D /* Sap_Emu.h */, 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */, 17C8F1DE0CBED286008D969D /* Sms_Apu.h */, - 17C8F1DF0CBED286008D969D /* Sms_Oscs.h */, 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */, 17C8F1E10CBED286008D969D /* Snes_Spc.h */, 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */, @@ -388,8 +841,6 @@ 17C8F1E50CBED286008D969D /* Spc_Dsp.h */, 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */, 17C8F1E70CBED286008D969D /* Spc_Emu.h */, - 17C8F1E80CBED286008D969D /* Vgm_Emu_Impl.cpp */, - 17C8F1E90CBED286008D969D /* Vgm_Emu_Impl.h */, 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */, 17C8F1EB0CBED286008D969D /* Vgm_Emu.h */, 17C8F1EC0CBED286008D969D /* Ym2413_Emu.cpp */, @@ -407,59 +858,136 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 8370B76C17F615FE001A4D7A /* Nes_Cpu_run.h in Headers */, + 8370B75A17F615FE001A4D7A /* i_vrc7.h in Headers */, + 8370B7A817F615FE001A4D7A /* Sgc_Emu.h in Headers */, + 8370B74217F615FE001A4D7A /* divfix.h in Headers */, + 8370B76717F615FE001A4D7A /* kmsnddev.h in Headers */, + 8370B78D17F615FE001A4D7A /* Rf5C68_Emu.h in Headers */, + 8370B7A317F615FE001A4D7A /* segapcm.h in Headers */, + 8370B7CE17F615FE001A4D7A /* ymz280b.h in Headers */, + 8370B74717F615FE001A4D7A /* fm.h in Headers */, 17C8F1F50CBED286008D969D /* Ay_Apu.h in Headers */, - 17C8F1F70CBED286008D969D /* Ay_Cpu.h in Headers */, 17C8F1F90CBED286008D969D /* Ay_Emu.h in Headers */, + 8370B74B17F615FE001A4D7A /* Gb_Cpu_run.h in Headers */, + 8370B7A117F615FE001A4D7A /* SegaPcm_Emu.h in Headers */, 17C8F1FA0CBED286008D969D /* blargg_common.h in Headers */, + 8370B75717F615FE001A4D7A /* Hes_Cpu_run.h in Headers */, 17C8F1FB0CBED286008D969D /* blargg_config.h in Headers */, + 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */, 17C8F1FC0CBED286008D969D /* blargg_endian.h in Headers */, 17C8F1FD0CBED286008D969D /* blargg_source.h in Headers */, + 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */, + 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */, + 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */, + 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */, + 8370B76417F615FE001A4D7A /* K054539_Emu.h in Headers */, + 8370B7BC17F615FE001A4D7A /* Ym2203_Emu.h in Headers */, + 8370B78117F615FE001A4D7A /* Opl_Apu.h in Headers */, + 8370B79517F615FE001A4D7A /* s_deltat.h in Headers */, + 8370B77B17F615FE001A4D7A /* okim6258.h in Headers */, + 8370B7B417F615FE001A4D7A /* Upsampler.h in Headers */, + 8370B75417F615FE001A4D7A /* Hes_Apu_Adpcm.h in Headers */, 17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */, + 8370B77417F615FE001A4D7A /* Nsf_Core.h in Headers */, 17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */, 17C8F2030CBED286008D969D /* Data_Reader.h in Headers */, 17C8F2050CBED286008D969D /* Dual_Resampler.h in Headers */, + 8370B76617F615FE001A4D7A /* k054539.h in Headers */, + 8370B78717F615FE001A4D7A /* qmix.h in Headers */, + 8370B73517F615FE001A4D7A /* Blip_Buffer_impl.h in Headers */, + 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */, 17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */, 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */, + 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */, + 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */, + 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */, 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */, - 17C8F20C0CBED286008D969D /* gb_cpu_io.h in Headers */, + 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */, + 8370B77217F615FE001A4D7A /* nestypes.h in Headers */, 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */, 17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */, + 8370B75217F615FE001A4D7A /* Gme_Loader.h in Headers */, + 8370B72F17F615FE001A4D7A /* adlib.h in Headers */, + 8370B7B217F615FE001A4D7A /* Track_Filter.h in Headers */, + 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */, 17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */, 17C8F2140CBED286008D969D /* Gme_File.h in Headers */, + 8370B76217F615FE001A4D7A /* k053260.h in Headers */, + 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */, + 8370B75C17F615FE001A4D7A /* K051649_Emu.h in Headers */, + 8370B73F17F615FE001A4D7A /* dac_control.h in Headers */, 17C8F2160CBED286008D969D /* gme.h in Headers */, 17C8F2190CBED286008D969D /* Gym_Emu.h in Headers */, 17C8F21B0CBED286008D969D /* Hes_Apu.h in Headers */, - 17C8F21C0CBED286008D969D /* hes_cpu_io.h in Headers */, 17C8F21E0CBED286008D969D /* Hes_Cpu.h in Headers */, + 8370B76B17F615FE001A4D7A /* mathdefs.h in Headers */, + 8370B7C817F615FE001A4D7A /* ymdeltat.h in Headers */, 17C8F2200CBED286008D969D /* Hes_Emu.h in Headers */, - 17C8F2220CBED286008D969D /* Kss_Cpu.h in Headers */, + 8370B78517F615FE001A4D7A /* pwm.h in Headers */, + 8370B79717F615FE001A4D7A /* s_logtbl.h in Headers */, + 8370B77117F615FE001A4D7A /* Nes_Vrc7_Apu.h in Headers */, + 8370B74517F615FE001A4D7A /* emuconfig.h in Headers */, + 8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */, 17C8F2240CBED286008D969D /* Kss_Emu.h in Headers */, 17C8F2260CBED286008D969D /* Kss_Scc_Apu.h in Headers */, 17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */, 17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */, + 8370B78B17F615FE001A4D7A /* Resampler.h in Headers */, + 8370B74117F615FE001A4D7A /* dbopl.h in Headers */, + 8370B74D17F615FE001A4D7A /* Gbs_Core.h in Headers */, 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */, + 8370B7AC17F615FE001A4D7A /* Sms_Fm_Apu.h in Headers */, + 8370B73D17F615FE001A4D7A /* Chip_Resampler.h in Headers */, 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */, - 17C8F2300CBED286008D969D /* nes_cpu_io.h in Headers */, 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */, + 8370B73817F615FE001A4D7A /* Bml_Parser.h in Headers */, + 8370B79117F615FE001A4D7A /* Rf5C164_Emu.h in Headers */, 17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */, + 8370B75817F615FE001A4D7A /* i_fmpac.h in Headers */, + 8370B73A17F615FE001A4D7A /* C140_Emu.h in Headers */, + 8370B77F17F615FE001A4D7A /* okim6295.h in Headers */, 17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */, + 8370B73417F615FE001A4D7A /* blargg_errors.h in Headers */, 17C8F2380CBED286008D969D /* Nes_Oscs.h in Headers */, + 8370B74A17F615FE001A4D7A /* fmopl.h in Headers */, 17C8F23A0CBED286008D969D /* Nes_Vrc6_Apu.h in Headers */, 17C8F23C0CBED286008D969D /* Nsf_Emu.h in Headers */, + 8370B75917F615FE001A4D7A /* i_fmunit.h in Headers */, 17C8F23E0CBED286008D969D /* Nsfe_Emu.h in Headers */, + 8370B7BE17F615FE001A4D7A /* ym2413.h in Headers */, + 8370B73117F615FE001A4D7A /* Ay_Core.h in Headers */, + 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */, + 8370B76F17F615FE001A4D7A /* Nes_Mmc5_Apu.h in Headers */, 17C8F2400CBED286008D969D /* Sap_Apu.h in Headers */, - 17C8F2410CBED286008D969D /* sap_cpu_io.h in Headers */, - 17C8F2430CBED286008D969D /* Sap_Cpu.h in Headers */, 17C8F2450CBED286008D969D /* Sap_Emu.h in Headers */, + 8370B73C17F615FE001A4D7A /* c140.h in Headers */, + 8370B76E17F615FE001A4D7A /* Nes_Fds_Apu.h in Headers */, 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */, - 17C8F2480CBED286008D969D /* Sms_Oscs.h in Headers */, + 8370B79917F615FE001A4D7A /* s_opl.h in Headers */, + 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */, 17C8F24A0CBED286008D969D /* Snes_Spc.h in Headers */, + 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */, + 8370B7AA17F615FE001A4D7A /* Sgc_Impl.h in Headers */, 17C8F24C0CBED286008D969D /* Spc_Cpu.h in Headers */, + 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */, + 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */, + 8370B75E17F615FE001A4D7A /* k051649.h in Headers */, + 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */, + 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */, + 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */, 17C8F24E0CBED286008D969D /* Spc_Dsp.h in Headers */, + 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */, + 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */, + 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */, + 8370B76917F615FE001A4D7A /* Kss_Core.h in Headers */, + 8370B7B617F615FE001A4D7A /* Vgm_Core.h in Headers */, 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */, - 17C8F2520CBED286008D969D /* Vgm_Emu_Impl.h in Headers */, + 8370B7BA17F615FE001A4D7A /* ym2151.h in Headers */, 17C8F2540CBED286008D969D /* Vgm_Emu.h in Headers */, 17C8F2560CBED286008D969D /* Ym2413_Emu.h in Headers */, + 8370B7CC17F615FE001A4D7A /* Ymz280b_Emu.h in Headers */, + 8370B76A17F615FE001A4D7A /* mamedef.h in Headers */, 17C8F2580CBED286008D969D /* Ym2612_Emu.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -491,9 +1019,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "GME" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* GME */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -520,50 +1054,122 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8370B74917F615FE001A4D7A /* fmopl.cpp in Sources */, + 8370B78A17F615FE001A4D7A /* Resampler.cpp in Sources */, + 8370B77017F615FE001A4D7A /* Nes_Vrc7_Apu.cpp in Sources */, + 8370B7B117F615FE001A4D7A /* Track_Filter.cpp in Sources */, + 8370B74617F615FE001A4D7A /* fm.c in Sources */, + 8370B76517F615FE001A4D7A /* k054539.c in Sources */, + 8370B7BF17F615FE001A4D7A /* Ym2608_Emu.cpp in Sources */, 17C8F1F40CBED286008D969D /* Ay_Apu.cpp in Sources */, + 8370B7A017F615FE001A4D7A /* SegaPcm_Emu.cpp in Sources */, 17C8F1F60CBED286008D969D /* Ay_Cpu.cpp in Sources */, 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */, + 8370B76D17F615FE001A4D7A /* Nes_Fds_Apu.cpp in Sources */, + 8370B7B917F615FE001A4D7A /* ym2151.c in Sources */, 17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */, + 8370B79C17F615FE001A4D7A /* Sap_Core.cpp in Sources */, 17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */, + 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */, + 8370B79817F615FE001A4D7A /* s_opl.c in Sources */, + 8370B79217F615FE001A4D7A /* Rom_Data.cpp in Sources */, + 8370B7BB17F615FE001A4D7A /* Ym2203_Emu.cpp in Sources */, + 8370B74E17F615FE001A4D7A /* Gbs_Cpu.cpp in Sources */, 17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */, + 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */, + 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */, 17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */, + 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */, + 8370B74817F615FE001A4D7A /* fm2612.c in Sources */, + 8370B7A417F615FE001A4D7A /* Sgc_Core.cpp in Sources */, + 8370B79417F615FE001A4D7A /* s_deltat.c in Sources */, 17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */, 17C8F2080CBED286008D969D /* Fir_Resampler.cpp in Sources */, + 8370B77617F615FE001A4D7A /* Nsf_Impl.cpp in Sources */, + 8370B76317F615FE001A4D7A /* K054539_Emu.cpp in Sources */, + 8370B7C917F615FE001A4D7A /* Ymf262_Emu.cpp in Sources */, 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */, + 8370B74017F615FE001A4D7A /* dbopl.cpp in Sources */, + 8370B7CD17F615FE001A4D7A /* ymz280b.c in Sources */, 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */, + 8370B7A917F615FE001A4D7A /* Sgc_Impl.cpp in Sources */, 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */, + 8370B75B17F615FE001A4D7A /* K051649_Emu.cpp in Sources */, + 8370B74317F615FE001A4D7A /* Downsampler.cpp in Sources */, 17C8F2110CBED286008D969D /* Gbs_Emu.cpp in Sources */, + 8370B73317F615FE001A4D7A /* blargg_errors.cpp in Sources */, + 8370B73B17F615FE001A4D7A /* c140.c in Sources */, + 8370B77C17F615FE001A4D7A /* Okim6295_Emu.cpp in Sources */, 17C8F2130CBED286008D969D /* Gme_File.cpp in Sources */, 17C8F2150CBED286008D969D /* gme.cpp in Sources */, 17C8F2180CBED286008D969D /* Gym_Emu.cpp in Sources */, + 8370B78E17F615FE001A4D7A /* rf5c68.c in Sources */, + 8370B74C17F615FE001A4D7A /* Gbs_Core.cpp in Sources */, 17C8F21A0CBED286008D969D /* Hes_Apu.cpp in Sources */, 17C8F21D0CBED286008D969D /* Hes_Cpu.cpp in Sources */, 17C8F21F0CBED286008D969D /* Hes_Emu.cpp in Sources */, + 8370B78617F615FE001A4D7A /* qmix.c in Sources */, 17C8F2210CBED286008D969D /* Kss_Cpu.cpp in Sources */, + 8370B76117F615FE001A4D7A /* k053260.c in Sources */, + 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */, 17C8F2230CBED286008D969D /* Kss_Emu.cpp in Sources */, 17C8F2250CBED286008D969D /* Kss_Scc_Apu.cpp in Sources */, + 8370B7BD17F615FE001A4D7A /* ym2413.c in Sources */, + 8370B79617F615FE001A4D7A /* s_logtbl.c in Sources */, 17C8F2280CBED286008D969D /* M3u_Playlist.cpp in Sources */, 17C8F22A0CBED286008D969D /* Multi_Buffer.cpp in Sources */, + 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */, 17C8F22C0CBED286008D969D /* Music_Emu.cpp in Sources */, + 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */, 17C8F22E0CBED286008D969D /* Nes_Apu.cpp in Sources */, 17C8F2310CBED286008D969D /* Nes_Cpu.cpp in Sources */, 17C8F2330CBED286008D969D /* Nes_Fme7_Apu.cpp in Sources */, + 8370B7A217F615FE001A4D7A /* segapcm.c in Sources */, + 8370B7B517F615FE001A4D7A /* Vgm_Core.cpp in Sources */, + 8370B78417F615FE001A4D7A /* pwm.c in Sources */, 17C8F2350CBED286008D969D /* Nes_Namco_Apu.cpp in Sources */, + 8370B7A617F615FE001A4D7A /* Sgc_Cpu.cpp in Sources */, + 8370B77A17F615FE001A4D7A /* okim6258.c in Sources */, 17C8F2370CBED286008D969D /* Nes_Oscs.cpp in Sources */, 17C8F2390CBED286008D969D /* Nes_Vrc6_Apu.cpp in Sources */, + 8370B75317F615FE001A4D7A /* Hes_Apu_Adpcm.cpp in Sources */, + 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */, 17C8F23B0CBED286008D969D /* Nsf_Emu.cpp in Sources */, + 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */, + 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */, + 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */, + 8370B77317F615FE001A4D7A /* Nsf_Core.cpp in Sources */, + 8370B77517F615FE001A4D7A /* Nsf_Cpu.cpp in Sources */, + 8370B76817F615FE001A4D7A /* Kss_Core.cpp in Sources */, 17C8F23D0CBED286008D969D /* Nsfe_Emu.cpp in Sources */, + 8370B77E17F615FE001A4D7A /* okim6295.c in Sources */, + 8370B7A717F615FE001A4D7A /* Sgc_Emu.cpp in Sources */, 17C8F23F0CBED286008D969D /* Sap_Apu.cpp in Sources */, + 8370B75117F615FE001A4D7A /* Gme_Loader.cpp in Sources */, + 8370B73E17F615FE001A4D7A /* dac_control.c in Sources */, + 8370B73917F615FE001A4D7A /* C140_Emu.cpp in Sources */, 17C8F2420CBED286008D969D /* Sap_Cpu.cpp in Sources */, + 8370B73717F615FE001A4D7A /* Bml_Parser.cpp in Sources */, 17C8F2440CBED286008D969D /* Sap_Emu.cpp in Sources */, + 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */, + 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */, 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */, 17C8F2490CBED286008D969D /* Snes_Spc.cpp in Sources */, + 8370B7B317F615FE001A4D7A /* Upsampler.cpp in Sources */, + 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */, + 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */, 17C8F24B0CBED286008D969D /* Spc_Cpu.cpp in Sources */, + 8370B7AB17F615FE001A4D7A /* Sms_Fm_Apu.cpp in Sources */, + 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */, + 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */, + 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */, 17C8F24D0CBED286008D969D /* Spc_Dsp.cpp in Sources */, 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */, - 17C8F2510CBED286008D969D /* Vgm_Emu_Impl.cpp in Sources */, 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */, 17C8F2550CBED286008D969D /* Ym2413_Emu.cpp in Sources */, + 8370B79E17F615FE001A4D7A /* scd_pcm.c in Sources */, + 8370B75D17F615FE001A4D7A /* k051649.c in Sources */, + 8370B7B717F615FE001A4D7A /* Ym2151_Emu.cpp in Sources */, 17C8F2570CBED286008D969D /* Ym2612_Emu.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -595,10 +1201,12 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = HAVE_STDINT_H; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; @@ -620,10 +1228,12 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = "HAVE_STDINT_H;NDEBUG"; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = ""; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/GME/gme/Ay_Apu.cpp b/Frameworks/GME/gme/Ay_Apu.cpp old mode 100755 new mode 100644 index 9dc5bb28e..79c2c05f3 --- a/Frameworks/GME/gme/Ay_Apu.cpp +++ b/Frameworks/GME/gme/Ay_Apu.cpp @@ -1,8 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Ay_Apu.h" -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -22,7 +22,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Tones above this frequency are treated as disabled tone at half volume. // Power of two is more efficient (avoids division). -unsigned const inaudible_freq = 16384; +int const inaudible_freq = 16384; int const period_factor = 16; @@ -67,12 +67,18 @@ static byte const modes [8] = MODE( 0,1, 0,0, 0,0 ), }; +void Ay_Apu::set_output( Blip_Buffer* b ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, b ); +} + Ay_Apu::Ay_Apu() { // build full table of the upper 8 envelope waveforms for ( int m = 8; m--; ) { - byte* out = env.modes [m]; + byte* out = env_modes [m]; int flags = modes [m]; for ( int x = 3; --x >= 0; ) { @@ -89,27 +95,27 @@ Ay_Apu::Ay_Apu() } } - output( 0 ); + type_ = Ay8910; + set_output( NULL ); volume( 1.0 ); reset(); } void Ay_Apu::reset() { + addr_ = 0; last_time = 0; - noise.delay = 0; - noise.lfsr = 1; - - osc_t* osc = &oscs [osc_count]; - do + noise_delay = 0; + noise_lfsr = 1; + + for ( osc_t* osc = &oscs [osc_count]; osc != oscs; ) { osc--; osc->period = period_factor; - osc->delay = 0; + osc->delay = 0; osc->last_amp = 0; osc->phase = 0; } - while ( osc != oscs ); for ( int i = sizeof regs; --i >= 0; ) regs [i] = 0; @@ -117,25 +123,31 @@ void Ay_Apu::reset() write_data_( 13, 0 ); } +int Ay_Apu::read() +{ + static byte const masks [reg_count] = { + 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F, + 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00 + }; + if (!(type_ & 0x10)) return regs [addr_] & masks [addr_]; + else return regs [addr_]; +} + void Ay_Apu::write_data_( int addr, int data ) { assert( (unsigned) addr < reg_count ); if ( (unsigned) addr >= 14 ) - { - #ifdef dprintf - dprintf( "Wrote to I/O port %02X\n", (int) addr ); - #endif - } + dprintf( "Wrote to I/O port %02X\n", (int) addr ); // envelope mode if ( addr == 13 ) { if ( !(data & 8) ) // convert modes 0-7 to proper equivalents data = (data & 4) ? 15 : 9; - env.wave = env.modes [data - 7]; - env.pos = -48; - env.delay = 0; // will get set to envelope period in run_until() + env_wave = env_modes [data - 7]; + env_pos = -48; + env_delay = 0; // will get set to envelope period in run_until() } regs [addr] = data; @@ -143,7 +155,7 @@ void Ay_Apu::write_data_( int addr, int data ) int i = addr >> 1; if ( i < osc_count ) { - blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100L * period_factor) + + blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) + regs [i * 2] * period_factor; if ( !period ) period = period_factor; @@ -170,22 +182,23 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor; if ( !noise_period ) noise_period = noise_period_factor; - blip_time_t const old_noise_delay = noise.delay; - blargg_ulong const old_noise_lfsr = noise.lfsr; + blip_time_t const old_noise_delay = noise_delay; + unsigned const old_noise_lfsr = noise_lfsr; // envelope period - blip_time_t const env_period_factor = period_factor * 2; // verified - blip_time_t env_period = (regs [12] * 0x100L + regs [11]) * env_period_factor; + int env_step_scale = ((type_ & 0xF0) == 0x00) ? 1 : 0; + blip_time_t const env_period_factor = period_factor << env_step_scale; // verified + blip_time_t env_period = (regs [12] * 0x100 + regs [11]) * env_period_factor; if ( !env_period ) env_period = env_period_factor; // same as period 1 on my AY chip - if ( !env.delay ) - env.delay = env_period; + if ( !env_delay ) + env_delay = env_period; // run each osc separately for ( int index = 0; index < osc_count; index++ ) { osc_t* const osc = &oscs [index]; - int osc_mode = regs [7] >> index; + int osc_mode = regs [7] >> index; // output Blip_Buffer* const osc_output = osc->output; @@ -195,8 +208,8 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // period int half_vol = 0; - blip_time_t inaudible_period = (blargg_ulong) (osc_output->clock_rate() + - inaudible_freq) / (inaudible_freq * 2); + blip_time_t inaudible_period = (unsigned) (osc_output->clock_rate() + + inaudible_freq) / (unsigned) (inaudible_freq * 2); if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) { half_vol = 1; // Actually around 60%, but 50% is close enough @@ -206,16 +219,18 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // envelope blip_time_t start_time = last_time; blip_time_t end_time = final_end_time; - int const vol_mode = regs [0x08 + index]; - int volume = amp_table [vol_mode & 0x0F] >> half_vol; - int osc_env_pos = env.pos; - if ( vol_mode & 0x10 ) + int const vol_mode = regs [0x08 + index]; + int const vol_mode_mask = type_ == Ay8914 ? 0x30 : 0x10; + int volume = amp_table [vol_mode & 0x0F] >> half_vol + env_step_scale; + int osc_env_pos = env_pos; + if ( vol_mode & vol_mode_mask ) { - volume = env.wave [osc_env_pos] >> half_vol; + volume = env_wave [osc_env_pos] >> half_vol + env_step_scale; + if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); // use envelope only if it's a repeating wave or a ramp that hasn't finished if ( !(regs [13] & 1) || osc_env_pos < -32 ) { - end_time = start_time + env.delay; + end_time = start_time + env_delay; if ( end_time >= final_end_time ) end_time = final_end_time; @@ -237,14 +252,14 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) blip_time_t time = start_time + osc->delay; if ( osc_mode & tone_off ) // maintain tone's phase when off { - blargg_long count = (final_end_time - time + period - 1) / period; + int count = (final_end_time - time + period - 1) / period; time += count * period; osc->phase ^= count & 1; } // noise time blip_time_t ntime = final_end_time; - blargg_ulong noise_lfsr = 1; + unsigned noise_lfsr = 1; if ( !(osc_mode & noise_off) ) { ntime = start_time + old_noise_delay; @@ -311,8 +326,8 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) else { // 20 or more noise periods on average for some music - blargg_long remain = end - ntime; - blargg_long count = remain / noise_period; + int remain = end - ntime; + int count = remain / noise_period; if ( remain >= 0 ) ntime += noise_period + count * noise_period; } @@ -327,11 +342,12 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) delta = -delta; synth_.offset( time, delta, osc_output ); time += period; + + // alternate (less-efficient) implementation //phase ^= 1; } - //assert( phase == (delta > 0) ); phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1); - // (delta > 0) + check( phase == (delta > 0) ); } else { @@ -358,10 +374,11 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // next envelope step if ( ++osc_env_pos >= 0 ) osc_env_pos -= 32; - volume = env.wave [osc_env_pos] >> half_vol; + volume = env_wave [osc_env_pos] >> half_vol + env_step_scale; + if ( type_ == Ay8914 ) volume >>= 3 - ( ( vol_mode & vol_mode_mask ) >> 4 ); start_time = end_time; - end_time += env_period; + end_time += env_period; if ( end_time > final_end_time ) end_time = final_end_time; } @@ -369,27 +386,27 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) if ( !(osc_mode & noise_off) ) { - noise.delay = ntime - final_end_time; - noise.lfsr = noise_lfsr; + noise_delay = ntime - final_end_time; + this->noise_lfsr = noise_lfsr; } } // TODO: optimized saw wave envelope? // maintain envelope phase - blip_time_t remain = final_end_time - last_time - env.delay; + blip_time_t remain = final_end_time - last_time - env_delay; if ( remain >= 0 ) { - blargg_long count = (remain + env_period) / env_period; - env.pos += count; - if ( env.pos >= 0 ) - env.pos = (env.pos & 31) - 32; + int count = (remain + env_period) / env_period; + env_pos += count; + if ( env_pos >= 0 ) + env_pos = (env_pos & 31) - 32; remain -= count * env_period; assert( -remain <= env_period ); } - env.delay = -remain; - assert( env.delay > 0 ); - assert( env.pos < 0 ); + env_delay = -remain; + assert( env_delay > 0 ); + assert( env_pos < 0 ); last_time = final_end_time; } diff --git a/Frameworks/GME/gme/Ay_Apu.h b/Frameworks/GME/gme/Ay_Apu.h old mode 100755 new mode 100644 index 31956939e..314772f31 --- a/Frameworks/GME/gme/Ay_Apu.h +++ b/Frameworks/GME/gme/Ay_Apu.h @@ -1,6 +1,6 @@ // AY-3-8910 sound chip emulator -// Game_Music_Emu 0.5.2 +// $package #ifndef AY_APU_H #define AY_APU_H @@ -9,90 +9,106 @@ class Ay_Apu { public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); +// Basics + enum Ay_Apu_Type + { + Ay8910 = 0, + Ay8912, + Ay8913, + Ay8914, + Ym2149 = 0x10, + Ym3439, + Ymz284, + Ymz294, + Ym2203 = 0x20, + Ym2608, + Ym2610, + Ym2610b + }; + + void set_type( Ay_Apu_Type type ) { type_ = type; } + + // Sets buffer to generate sound into, or 0 to mute. + void set_output( Blip_Buffer* ); - // Reset sound chip + // Writes to address register + void write_addr( int data ) { addr_ = data & 0x0F; } + + // Emulates to time t, then writes to current data register + void write_data( blip_time_t t, int data ) { run_until( t ); write_data_( addr_, data ); } + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Reads from current data register + int read(); + + // Resets sound chip void reset(); - // Write to register at specified time + // Number of registers enum { reg_count = 16 }; - void write( blip_time_t time, int addr, int data ); - // Run sound to specified time, end current time frame, then start a new - // time frame at time 0. Time frames have no effect on emulation and each - // can be whatever length is convenient. - void end_frame( blip_time_t length ); - -// Additional features - - // Set sound output of specific oscillator to buffer, where index is - // 0, 1, or 2. If buffer is NULL, the specified oscillator is muted. + // Same as set_output(), but for a particular channel enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int chan, Blip_Buffer* ); - // Set overall volume (default is 1.0) - void volume( double ); + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth_.volume( 0.7/osc_count/amp_range * v ); } - // Set treble equalization (see documentation) - void treble_eq( blip_eq_t const& ); + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } +private: + // noncopyable + Ay_Apu( const Ay_Apu& ); + Ay_Apu& operator = ( const Ay_Apu& ); + +// Implementation public: Ay_Apu(); - typedef unsigned char byte; + BLARGG_DISABLE_NOTHROW + typedef BOOST::uint8_t byte; + private: struct osc_t { - blip_time_t period; - blip_time_t delay; - short last_amp; - short phase; + blip_time_t period; + blip_time_t delay; + short last_amp; + short phase; Blip_Buffer* output; } oscs [osc_count]; + + Ay_Apu_Type type_; + blip_time_t last_time; - byte latch; - byte regs [reg_count]; + byte addr_; + byte regs [reg_count]; - struct { - blip_time_t delay; - blargg_ulong lfsr; - } noise; + blip_time_t noise_delay; + unsigned noise_lfsr; - struct { - blip_time_t delay; - byte const* wave; - int pos; - byte modes [8] [48]; // values already passed through volume table - } env; + blip_time_t env_delay; + byte const* env_wave; + int env_pos; + byte env_modes [8] [48]; // values already passed through volume table - void run_until( blip_time_t ); void write_data_( int addr, int data ); + void run_until( blip_time_t ); + public: enum { amp_range = 255 }; - Blip_Synth synth_; + Blip_Synth_Norm synth_; // used by Ay_Core for beeper sound }; -inline void Ay_Apu::volume( double v ) { synth_.volume( 0.7 / osc_count / amp_range * v ); } - -inline void Ay_Apu::treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); } - -inline void Ay_Apu::write( blip_time_t time, int addr, int data ) -{ - run_until( time ); - write_data_( addr, data ); -} - -inline void Ay_Apu::osc_output( int i, Blip_Buffer* buf ) +inline void Ay_Apu::set_output( int i, Blip_Buffer* out ) { assert( (unsigned) i < osc_count ); - oscs [i].output = buf; -} - -inline void Ay_Apu::output( Blip_Buffer* buf ) -{ - osc_output( 0, buf ); - osc_output( 1, buf ); - osc_output( 2, buf ); + oscs [i].output = out; } inline void Ay_Apu::end_frame( blip_time_t time ) @@ -100,8 +116,8 @@ inline void Ay_Apu::end_frame( blip_time_t time ) if ( time > last_time ) run_until( time ); - assert( last_time >= time ); last_time -= time; + assert( last_time >= 0 ); } #endif diff --git a/Frameworks/GME/gme/Ay_Cpu.cpp b/Frameworks/GME/gme/Ay_Cpu.cpp old mode 100755 new mode 100644 index 6ff7156ba..40fbff6f6 --- a/Frameworks/GME/gme/Ay_Cpu.cpp +++ b/Frameworks/GME/gme/Ay_Cpu.cpp @@ -1,19 +1,11 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ -/* -Last validated with zexall 2006.11.21 5:26 PM -* Doesn't implement the R register or immediate interrupt after EI. -* Address wrap-around isn't completely correct, but is prevented from crashing emulator. -*/ - -#include "Ay_Cpu.h" +#include "Ay_Core.h" #include "blargg_endian.h" -#include - //#include "z80_cpu_log.h" -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -24,1642 +16,44 @@ details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define SYNC_TIME() (void) (s.time = s_time) -#define RELOAD_TIME() (void) (s_time = s.time) - -// Callbacks to emulator - -#define CPU_OUT( cpu, addr, data, TIME )\ - ay_cpu_out( cpu, TIME, addr, data ) - -#define CPU_IN( cpu, addr, TIME )\ - ay_cpu_in( cpu, addr ) - #include "blargg_source.h" -// flags, named with hex value for clarity -int const S80 = 0x80; -int const Z40 = 0x40; -int const F20 = 0x20; -int const H10 = 0x10; -int const F08 = 0x08; -int const V04 = 0x04; -int const P04 = 0x04; -int const N02 = 0x02; -int const C01 = 0x01; - -#define SZ28P( n ) szpc [n] -#define SZ28PC( n ) szpc [n] -#define SZ28C( n ) (szpc [n] & ~P04) -#define SZ28( n ) SZ28C( n ) - -#define SET_R( n ) (void) (r.r = n) -#define GET_R() (r.r) - -Ay_Cpu::Ay_Cpu() +void Ay_Core::cpu_out( time_t time, addr_t addr, int data ) { - state = &state_; - for ( int i = 0x100; --i >= 0; ) + if ( (addr & 0xFF) == 0xFE ) { - int even = 1; - for ( int p = i; p; p >>= 1 ) - even ^= p; - int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); - szpc [i] = n; - szpc [i + 0x100] = n | C01; + check( !cpc_mode ); + spectrum_mode = !cpc_mode; + + // beeper_mask and last_beeper are 0 if (cpc_mode || !beeper_output) + if ( (data &= beeper_mask) != last_beeper ) + { + last_beeper = data; + int delta = -beeper_delta; + beeper_delta = delta; + Blip_Buffer* bb = beeper_output; + bb->set_modified(); + apu_.synth_.offset( time, delta, bb ); + } + } + else + { + cpu_out_( time, addr, data ); } - szpc [0x000] |= Z40; - szpc [0x100] |= Z40; } -void Ay_Cpu::reset( void* m ) -{ - mem = (uint8_t*) m; - - check( state == &state_ ); - state = &state_; - state_.time = 0; - state_.base = 0; - end_time_ = 0; - - memset( &r, 0, sizeof r ); -} +#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data ) +#define IN_PORT( addr ) cpu_in( addr ) +#define FLAT_MEM mem +#define CPU cpu -#define TIME (s_time + s.base) -#define READ_PROG( addr ) (mem [addr]) -#define INSTR( offset ) READ_PROG( pc + (offset) ) -#define GET_ADDR() GET_LE16( &READ_PROG( pc ) ) -#define READ( addr ) READ_PROG( addr ) -#define WRITE( addr, data ) (void) (READ_PROG( addr ) = data) -#define READ_WORD( addr ) GET_LE16( &READ_PROG( addr ) ) -#define WRITE_WORD( addr, data ) SET_LE16( &READ_PROG( addr ), data ) -#define IN( addr ) CPU_IN( this, addr, TIME ) -#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) - -#if BLARGG_BIG_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [n]) -#elif BLARGG_LITTLE_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) -#else - #error "Byte order of CPU must be known" -#endif - -//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) - -// help compiler see that it can just adjust stack offset, saving an extra instruction -#define R16( n, shift, offset )\ - (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) - -#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e -#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f -#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g -#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h - -// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 -static byte const ed_dd_timing [0x100] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, -0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, -0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, -0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -bool Ay_Cpu::run( cpu_time_t end_time ) -{ - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - bool warning = false; +#define CPU_BEGIN \ +bool Ay_Core::run_cpu( time_t end_time ) \ +{\ + cpu.set_end_time( end_time );\ + byte* const mem = mem_.ram; // cache - typedef BOOST::int8_t int8_t; - - union { - regs_t rg; - pairs_t rp; - uint8_t r8_ [8]; // indexed - uint16_t r16_ [4]; - }; - rg = this->r.b; - - cpu_time_t s_time = s.time; - uint8_t* const mem = this->mem; // cache - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 iy = r.iy; - int flags = r.b.flags; - - goto loop; -jr_not_taken: - s_time -= 5; - goto loop; -call_not_taken: - s_time -= 7; -jp_not_taken: - pc += 2; -loop: - - check( (unsigned long) pc < 0x10000 ); - check( (unsigned long) sp < 0x10000 ); - check( (unsigned) flags < 0x100 ); - check( (unsigned) ix < 0x10000 ); - check( (unsigned) iy < 0x10000 ); - - fuint8 opcode; - opcode = READ_PROG( pc ); - pc++; - - static byte const base_timing [0x100] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 - 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 - 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 - 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 - 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B - 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C - 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D - 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E - 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F - }; - - fuint16 data; - data = base_timing [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = READ_PROG( pc ); - - #ifdef Z80_CPU_LOG_H - //log_opcode( opcode, READ_PROG( pc ) ); - z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); - z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), - READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Common - - case 0x00: // NOP - CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. - goto loop; - - case 0x08:{// EX AF,AF' - int temp = r.alt.b.a; - r.alt.b.a = rg.a; - rg.a = temp; - - temp = r.alt.b.flags; - r.alt.b.flags = flags; - flags = temp; - goto loop; - } - - case 0xD3: // OUT (imm),A - pc++; - OUT( data + rg.a * 0x100, rg.a ); - goto loop; - - case 0x2E: // LD L,imm - pc++; - rg.l = data; - goto loop; - - case 0x3E: // LD A,imm - pc++; - rg.a = data; - goto loop; - - case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rg.a = READ( addr ); - goto loop; - } - -// Conditional - -#define ZERO (flags & Z40) -#define CARRY (flags & C01) -#define EVEN (flags & P04) -#define MINUS (flags & S80) - -// JR -#define JR( cond ) {\ - int disp = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) )\ - goto jr_not_taken;\ - pc += disp;\ - goto loop;\ -} - - case 0x20: JR( !ZERO ) // JR NZ,disp - case 0x28: JR( ZERO ) // JR Z,disp - case 0x30: JR( !CARRY ) // JR NC,disp - case 0x38: JR( CARRY ) // JR C,disp - case 0x18: JR( true ) // JR disp - - case 0x10:{// DJNZ disp - int temp = rg.b - 1; - rg.b = temp; - JR( temp ) - } - -// JP -#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; - - case 0xC2: JP( !ZERO ) // JP NZ,addr - case 0xCA: JP( ZERO ) // JP Z,addr - case 0xD2: JP( !CARRY ) // JP NC,addr - case 0xDA: JP( CARRY ) // JP C,addr - case 0xE2: JP( !EVEN ) // JP PO,addr - case 0xEA: JP( EVEN ) // JP PE,addr - case 0xF2: JP( !MINUS ) // JP P,addr - case 0xFA: JP( MINUS ) // JP M,addr - - case 0xC3: // JP addr - pc = GET_ADDR(); - goto loop; - - case 0xE9: // JP HL - pc = rp.hl; - goto loop; - -// RET -#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; - - case 0xC0: RET( !ZERO ) // RET NZ - case 0xC8: RET( ZERO ) // RET Z - case 0xD0: RET( !CARRY ) // RET NC - case 0xD8: RET( CARRY ) // RET C - case 0xE0: RET( !EVEN ) // RET PO - case 0xE8: RET( EVEN ) // RET PE - case 0xF0: RET( !MINUS ) // RET P - case 0xF8: RET( MINUS ) // RET M - - case 0xC9: // RET - ret_taken: - pc = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// CALL -#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; - - case 0xC4: CALL( !ZERO ) // CALL NZ,addr - case 0xCC: CALL( ZERO ) // CALL Z,addr - case 0xD4: CALL( !CARRY ) // CALL NC,addr - case 0xDC: CALL( CARRY ) // CALL C,addr - case 0xE4: CALL( !EVEN ) // CALL PO,addr - case 0xEC: CALL( EVEN ) // CALL PE,addr - case 0xF4: CALL( !MINUS ) // CALL P,addr - case 0xFC: CALL( MINUS ) // CALL M,addr - - case 0xCD:{// CALL addr - call_taken: - fuint16 addr = pc + 2; - pc = GET_ADDR(); - sp = uint16_t (sp - 2); - WRITE_WORD( sp, addr ); - goto loop; - } - - case 0xFF: // RST - if ( (pc - 1) > 0xFFFF ) - { - pc = uint16_t (pc - 1); - s_time -= 11; - goto loop; - } - CASE7( C7, CF, D7, DF, E7, EF, F7 ): - data = pc; - pc = opcode & 0x38; - goto push_data; - -// PUSH/POP - case 0xF5: // PUSH AF - data = rg.a * 0x100u + flags; - goto push_data; - - case 0xC5: // PUSH BC - case 0xD5: // PUSH DE - case 0xE5: // PUSH HL - data = R16( opcode, 4, 0xC5 ); - push_data: - sp = uint16_t (sp - 2); - WRITE_WORD( sp, data ); - goto loop; - - case 0xF1: // POP AF - flags = READ( sp ); - rg.a = READ( sp + 1 ); - sp = uint16_t (sp + 2); - goto loop; - - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL - R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// ADC/ADD/SBC/SUB - case 0x96: // SUB (HL) - case 0x86: // ADD (HL) - flags &= ~C01; - case 0x9E: // SBC (HL) - case 0x8E: // ADC (HL) - data = READ( rp.hl ); - goto adc_data; - - case 0xD6: // SUB A,imm - case 0xC6: // ADD imm - flags &= ~C01; - case 0xDE: // SBC A,imm - case 0xCE: // ADC imm - pc++; - goto adc_data; - - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r - flags &= ~C01; - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r - data = R8( opcode & 7, 0 ); - adc_data: { - int result = data + (flags & C01); - data ^= rg.a; - flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes - if ( flags ) - result = -result; - result += rg.a; - data ^= result; - flags |=(data & H10) | - ((data - -0x80) >> 6 & V04) | - SZ28C( result & 0x1FF ); - rg.a = result; - goto loop; - } - -// CP - case 0xBE: // CP (HL) - data = READ( rp.hl ); - goto cp_data; - - case 0xFE: // CP imm - pc++; - goto cp_data; - - CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r - data = R8( opcode, 0xB8 ); - cp_data: { - int result = rg.a - data; - flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); - data ^= rg.a; - flags |=(((result ^ rg.a) & data) >> 5 & V04) | - (((data & H10) ^ result) & (S80 | H10)); - if ( (uint8_t) result ) - goto loop; - flags |= Z40; - goto loop; - } - -// ADD HL,rp - - case 0x39: // ADD HL,SP - data = sp; - goto add_hl_data; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - data = R16( opcode, 4, 0x09 ); - add_hl_data: { - blargg_ulong sum = rp.hl + data; - data ^= rp.hl; - rp.hl = sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((data ^ sum) >> 8 & H10); - goto loop; - } - - case 0x27:{// DAA - int a = rg.a; - if ( a > 0x99 ) - flags |= C01; - - int adjust = 0x60 & -(flags & C01); - - if ( flags & H10 || (a & 0x0F) > 9 ) - adjust |= 0x06; - - if ( flags & N02 ) - adjust = -adjust; - a += adjust; - - flags = (flags & (C01 | N02)) | - ((rg.a ^ a) & H10) | - SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - /* - case 0x27:{// DAA - // more optimized, but probably not worth the obscurity - int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags - int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 - - if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 - adjust |= 0x06; - - if ( f & N02 ) - adjust = -adjust; - int a = rg.a + adjust; - - flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - */ - -// INC/DEC - case 0x34: // INC (HL) - data = READ( rp.hl ) + 1; - WRITE( rp.hl, data ); - goto inc_set_flags; - - CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r - data = ++R8( opcode >> 3, 0 ); - inc_set_flags: - flags = (flags & C01) | - (((data & 0x0F) - 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x80 ) - goto loop; - flags |= V04; - goto loop; - - case 0x35: // DEC (HL) - data = READ( rp.hl ) - 1; - WRITE( rp.hl, data ); - goto dec_set_flags; - - CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r - data = --R8( opcode >> 3, 0 ); - dec_set_flags: - flags = (flags & C01) | N02 | - (((data & 0x0F) + 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x7F ) - goto loop; - flags |= V04; - goto loop; - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - R16( opcode, 4, 0x03 )++; - goto loop; - - case 0x33: // INC SP - sp = uint16_t (sp + 1); - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - R16( opcode, 4, 0x0B )--; - goto loop; - - case 0x3B: // DEC SP - sp = uint16_t (sp - 1); - goto loop; - -// AND - case 0xA6: // AND (HL) - data = READ( rp.hl ); - goto and_data; - - case 0xE6: // AND imm - pc++; - goto and_data; - - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r - data = R8( opcode, 0xA0 ); - and_data: - rg.a &= data; - flags = SZ28P( rg.a ) | H10; - goto loop; - -// OR - case 0xB6: // OR (HL) - data = READ( rp.hl ); - goto or_data; - - case 0xF6: // OR imm - pc++; - goto or_data; - - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r - data = R8( opcode, 0xB0 ); - or_data: - rg.a |= data; - flags = SZ28P( rg.a ); - goto loop; - -// XOR - case 0xAE: // XOR (HL) - data = READ( rp.hl ); - goto xor_data; - - case 0xEE: // XOR imm - pc++; - goto xor_data; - - CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r - data = R8( opcode, 0xA8 ); - xor_data: - rg.a ^= data; - flags = SZ28P( rg.a ); - goto loop; - -// LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r - WRITE( rp.hl, R8( opcode, 0x70 ) ); - goto loop; - - CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r - CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r - CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r - CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r - CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r - CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r - CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r - R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); - goto loop; - - CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm - R8( opcode >> 3, 0 ) = data; - pc++; - goto loop; - - case 0x36: // LD (HL),imm - pc++; - WRITE( rp.hl, data ); - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) - R8( opcode >> 3, 8 ) = READ( rp.hl ); - goto loop; - - case 0x01: // LD rp,imm - case 0x11: - case 0x21: - R16( opcode, 4, 0x01 ) = GET_ADDR(); - pc += 2; - goto loop; - - case 0x31: // LD sp,imm - sp = GET_ADDR(); - pc += 2; - goto loop; - - case 0x2A:{// LD HL,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rp.hl = READ_WORD( addr ); - goto loop; - } - - case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE( addr, rg.a ); - goto loop; - } - - case 0x22:{// LD (addr),HL - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, rp.hl ); - goto loop; - } - - case 0x02: // LD (BC),A - case 0x12: // LD (DE),A - WRITE( R16( opcode, 4, 0x02 ), rg.a ); - goto loop; - - case 0x0A: // LD A,(BC) - case 0x1A: // LD A,(DE) - rg.a = READ( R16( opcode, 4, 0x0A ) ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - -// Rotate - - case 0x07:{// RLCA - fuint16 temp = rg.a; - temp = (temp << 1) | (temp >> 7); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08 | C01)); - rg.a = temp; - goto loop; - } - - case 0x0F:{// RRCA - fuint16 temp = rg.a; - flags = (flags & (S80 | Z40 | P04)) | - (temp & C01); - temp = (temp << 7) | (temp >> 1); - flags |= temp & (F20 | F08); - rg.a = temp; - goto loop; - } - - case 0x17:{// RLA - blargg_ulong temp = (rg.a << 1) | (flags & C01); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (temp >> 8); - rg.a = temp; - goto loop; - } - - case 0x1F:{// RRA - fuint16 temp = (flags << 7) | (rg.a >> 1); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (rg.a & C01); - rg.a = temp; - goto loop; - } - -// Misc - case 0x2F:{// CPL - fuint16 temp = ~rg.a; - flags = (flags & (S80 | Z40 | P04 | C01)) | - (temp & (F20 | F08)) | - (H10 | N02); - rg.a = temp; - goto loop; - } - - case 0x3F:{// CCF - flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | - (flags << 4 & H10) | - (rg.a & (F20 | F08)); - goto loop; - } - - case 0x37: // SCF - flags = (flags & (S80 | Z40 | P04)) | C01 | - (rg.a & (F20 | F08)); - goto loop; - - case 0xDB: // IN A,(imm) - pc++; - rg.a = IN( data + rg.a * 0x100 ); - goto loop; - - case 0xE3:{// EX (SP),HL - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, rp.hl ); - rp.hl = temp; - goto loop; - } - - case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; - rp.hl = rp.de; - rp.de = temp; - goto loop; - } - - case 0xD9:{// EXX DE,HL - fuint16 temp = r.alt.w.bc; - r.alt.w.bc = rp.bc; - rp.bc = temp; - - temp = r.alt.w.de; - r.alt.w.de = rp.de; - rp.de = temp; - - temp = r.alt.w.hl; - r.alt.w.hl = rp.hl; - rp.hl = temp; - goto loop; - } - - case 0xF3: // DI - r.iff1 = 0; - r.iff2 = 0; - goto loop; - - case 0xFB: // EI - r.iff1 = 1; - r.iff2 = 1; - // TODO: delayed effect - goto loop; - - case 0x76: // HALT - goto halt; - -//////////////////////////////////////// CB prefix - { - case 0xCB: - unsigned data2; - data2 = INSTR( 1 ); - pc++; - switch ( data ) - { - - // Rotate left - - #define RLC( read, write ) {\ - fuint8 result = read;\ - result = uint8_t (result << 1) | (result >> 7);\ - flags = SZ28P( result ) | (result & C01);\ - write;\ - goto loop;\ - } - - case 0x06: // RLC (HL) - s_time += 7; - data = rp.hl; - rlc_data_addr: - RLC( READ( data ), WRITE( data, result ) ) - - CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r - uint8_t& reg = R8( data, 0 ); - RLC( reg, reg = result ) - } - - #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x16: // RL (HL) - s_time += 7; - data = rp.hl; - rl_data_addr: - RL( READ( data ), WRITE( data, result ) ) - - CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r - uint8_t& reg = R8( data, 0x10 ); - RL( reg, reg = result ) - } - - #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x26: // SLA (HL) - s_time += 7; - data = rp.hl; - sla_data_addr: - SLA( READ( data ), 0, WRITE( data, result ) ) - - CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r - uint8_t& reg = R8( data, 0x20 ); - SLA( reg, 0, reg = result ) - } - - case 0x36: // SLL (HL) - s_time += 7; - data = rp.hl; - sll_data_addr: - SLA( READ( data ), 1, WRITE( data, result ) ) - - CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r - uint8_t& reg = R8( data, 0x30 ); - SLA( reg, 1, reg = result ) - } - - // Rotate right - - #define RRC( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = uint8_t (result << 7) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x0E: // RRC (HL) - s_time += 7; - data = rp.hl; - rrc_data_addr: - RRC( READ( data ), WRITE( data, result ) ) - - CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r - uint8_t& reg = R8( data, 0x08 ); - RRC( reg, reg = result ) - } - - #define RR( read, write ) {\ - fuint8 result = read;\ - fuint8 temp = result & C01;\ - result = uint8_t (flags << 7) | (result >> 1);\ - flags = SZ28P( result ) | temp;\ - write;\ - goto loop;\ - } - - case 0x1E: // RR (HL) - s_time += 7; - data = rp.hl; - rr_data_addr: - RR( READ( data ), WRITE( data, result ) ) - - CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r - uint8_t& reg = R8( data, 0x18 ); - RR( reg, reg = result ) - } - - #define SRA( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = (result & 0x80) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x2E: // SRA (HL) - data = rp.hl; - s_time += 7; - sra_data_addr: - SRA( READ( data ), WRITE( data, result ) ) - - CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r - uint8_t& reg = R8( data, 0x28 ); - SRA( reg, reg = result ) - } - - #define SRL( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result >>= 1;\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x3E: // SRL (HL) - s_time += 7; - data = rp.hl; - srl_data_addr: - SRL( READ( data ), WRITE( data, result ) ) - - CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r - uint8_t& reg = R8( data, 0x38 ); - SRL( reg, reg = result ) - } - - // BIT - { - unsigned temp; - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) - s_time += 4; - temp = READ( rp.hl ); - flags &= C01; - goto bit_temp; - CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r - CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r - CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r - CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r - CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r - CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r - CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r - temp = R8( data & 7, 0 ); - flags = (flags & C01) | (temp & (F20 | F08)); - bit_temp: - int masked = temp & 1 << (data >> 3 & 7); - flags |=(masked & S80) | H10 | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - // SET/RES - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) - s_time += 7; - int temp = READ( rp.hl ); - int bit = 1 << (data >> 3 & 7); - temp |= bit; // SET - if ( !(data & 0x40) ) - temp ^= bit; // RES - WRITE( rp.hl, temp ); - goto loop; - } - - CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r - CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r - CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r - CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r - CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r - CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r - CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r - CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r - R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); - goto loop; - - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r - CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r - CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r - R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// ED prefix - { - case 0xED: - pc++; - s_time += ed_dd_timing [data] >> 4; - switch ( data ) - { - { - blargg_ulong temp; - case 0x72: // SBC HL,SP - case 0x7A: // ADC HL,SP - temp = sp; - if ( 0 ) - case 0x42: // SBC HL,BC - case 0x52: // SBC HL,DE - case 0x62: // SBC HL,HL - case 0x4A: // ADC HL,BC - case 0x5A: // ADC HL,DE - case 0x6A: // ADC HL,HL - temp = R16( data >> 3 & 6, 1, 0 ); - blargg_ulong sum = temp + (flags & C01); - flags = ~data >> 2 & N02; - if ( flags ) - sum = -sum; - sum += rp.hl; - temp ^= rp.hl; - temp ^= sum; - flags |=(sum >> 16 & C01) | - (temp >> 8 & H10) | - (sum >> 8 & (S80 | F20 | F08)) | - ((temp - -0x8000) >> 14 & V04); - rp.hl = sum; - if ( (uint16_t) sum ) - goto loop; - flags |= Z40; - goto loop; - } - - CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) - int temp = IN( rp.bc ); - R8( data >> 3, 8 ) = temp; - flags = (flags & C01) | SZ28P( temp ); - goto loop; - } - - case 0x71: // OUT (C),0 - rg.flags = 0; - CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r - OUT( rp.bc, R8( data >> 3, 8 ) ); - goto loop; - - { - unsigned temp; - case 0x73: // LD (ADDR),SP - temp = sp; - if ( 0 ) - case 0x43: // LD (ADDR),BC - case 0x53: // LD (ADDR),DE - temp = R16( data, 4, 0x43 ); - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, temp ); - goto loop; - } - - case 0x4B: // LD BC,(ADDR) - case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - R16( data, 4, 0x4B ) = READ_WORD( addr ); - goto loop; - } - - case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - sp = READ_WORD( addr ); - goto loop; - } - - case 0x67:{// RRD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); - temp = (rg.a & 0xF0) | (temp & 0x0F); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - case 0x6F:{// RLD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); - temp = (rg.a & 0xF0) | (temp >> 4); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG - opcode = 0x10; // flag to do SBC instead of ADC - flags &= ~C01; - data = rg.a; - rg.a = 0; - goto adc_data; - - { - int inc; - case 0xA9: // CPD - case 0xB9: // CPDR - inc = -1; - if ( 0 ) - case 0xA1: // CPI - case 0xB1: // CPIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int result = rg.a - temp; - flags = (flags & C01) | N02 | - ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); - - if ( !(uint8_t) result ) flags |= Z40; - result -= (flags & H10) >> 4; - flags |= result & F08; - flags |= result << 4 & F20; - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( flags & Z40 || data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xA8: // LDD - case 0xB8: // LDDR - inc = -1; - if ( 0 ) - case 0xA0: // LDI - case 0xB0: // LDIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - addr = rp.de; - rp.de = addr + inc; - WRITE( addr, temp ); - - temp += rg.a; - flags = (flags & (S80 | Z40 | C01)) | - (temp & F08) | (temp << 4 & F20); - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xAB: // OUTD - case 0xBB: // OTDR - inc = -1; - if ( 0 ) - case 0xA3: // OUTI - case 0xB3: // OTIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - OUT( rp.bc, temp ); - goto loop; - } - - { - int inc; - case 0xAA: // IND - case 0xBA: // INDR - inc = -1; - if ( 0 ) - case 0xA2: // INI - case 0xB2: // INIR - inc = +1; - - fuint16 addr = rp.hl; - rp.hl = addr + inc; - - int temp = IN( rp.bc ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - WRITE( addr, temp ); - goto loop; - } - - case 0x47: // LD I,A - r.i = rg.a; - goto loop; - - case 0x4F: // LD R,A - SET_R( rg.a ); - dprintf( "LD R,A not supported\n" ); - warning = true; - goto loop; - - case 0x57: // LD A,I - rg.a = r.i; - goto ld_ai_common; - - case 0x5F: // LD A,R - rg.a = GET_R(); - dprintf( "LD A,R not supported\n" ); - warning = true; - ld_ai_common: - flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); - goto loop; - - CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN - r.iff1 = r.iff2; - goto ret_taken; - - case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 - r.im = 0; - goto loop; - - case 0x56: case 0x76: // IM 1 - r.im = 1; - goto loop; - - case 0x5E: case 0x7E: // IM 2 - r.im = 2; - goto loop; - - default: - dprintf( "Opcode $ED $%02X not supported\n", data ); - warning = true; - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// DD/FD prefix - { - fuint16 ixy; - case 0xDD: - ixy = ix; - goto ix_prefix; - case 0xFD: - ixy = iy; - ix_prefix: - pc++; - unsigned data2 = READ_PROG( pc ); - s_time += ed_dd_timing [data] & 0x0F; - switch ( data ) - { - // TODO: more efficient way of avoid negative address - #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) - - #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; - - // ADD/ADC/SUB/SBC - - case 0x96: // SUB (IXY+disp) - case 0x86: // ADD (IXY+disp) - flags &= ~C01; - case 0x9E: // SBC (IXY+disp) - case 0x8E: // ADC (IXY+disp) - pc++; - opcode = data; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto adc_data; - - case 0x94: // SUB HXY - case 0x84: // ADD HXY - flags &= ~C01; - case 0x9C: // SBC HXY - case 0x8C: // ADC HXY - opcode = data; - data = ixy >> 8; - goto adc_data; - - case 0x95: // SUB LXY - case 0x85: // ADD LXY - flags &= ~C01; - case 0x9D: // SBC LXY - case 0x8D: // ADC LXY - opcode = data; - data = (uint8_t) ixy; - goto adc_data; - - { - unsigned temp; - case 0x39: // ADD IXY,SP - temp = sp; - goto add_ixy_data; - - case 0x29: // ADD IXY,HL - temp = ixy; - goto add_ixy_data; - - case 0x09: // ADD IXY,BC - case 0x19: // ADD IXY,DE - temp = R16( data, 4, 0x09 ); - add_ixy_data: { - blargg_ulong sum = ixy + temp; - temp ^= ixy; - ixy = (uint16_t) sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((temp ^ sum) >> 8 & H10); - goto set_ixy; - } - } - - // AND - case 0xA6: // AND (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto and_data; - - case 0xA4: // AND HXY - data = ixy >> 8; - goto and_data; - - case 0xA5: // AND LXY - data = (uint8_t) ixy; - goto and_data; - - // OR - case 0xB6: // OR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto or_data; - - case 0xB4: // OR HXY - data = ixy >> 8; - goto or_data; - - case 0xB5: // OR LXY - data = (uint8_t) ixy; - goto or_data; - - // XOR - case 0xAE: // XOR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto xor_data; - - case 0xAC: // XOR HXY - data = ixy >> 8; - goto xor_data; - - case 0xAD: // XOR LXY - data = (uint8_t) ixy; - goto xor_data; - - // CP - case 0xBE: // CP (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto cp_data; - - case 0xBC: // CP HXY - data = ixy >> 8; - goto cp_data; - - case 0xBD: // CP LXY - data = (uint8_t) ixy; - goto cp_data; - - // LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r - data = R8( data, 0x70 ); - if ( 0 ) - case 0x36: // LD (IXY+disp),imm - pc++, data = READ_PROG( pc ); - pc++; - WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); - goto loop; - - CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY - R8( data >> 3, 8 ) = ixy >> 8; - goto loop; - - case 0x64: // LD HXY,HXY - case 0x6D: // LD LXY,LXY - goto loop; - - CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY - R8( data >> 3, 8 ) = ixy; - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) - pc++; - R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto loop; - - case 0x26: // LD HXY,imm - pc++; - goto ld_hxy_data; - - case 0x65: // LD HXY,LXY - data2 = (uint8_t) ixy; - goto ld_hxy_data; - - CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r - data2 = R8( data, 0x60 ); - ld_hxy_data: - ixy = (uint8_t) ixy | (data2 << 8); - goto set_ixy; - - case 0x2E: // LD LXY,imm - pc++; - goto ld_lxy_data; - - case 0x6C: // LD LXY,HXY - data2 = ixy >> 8; - goto ld_lxy_data; - - CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r - data2 = R8( data, 0x68 ); - ld_lxy_data: - ixy = (ixy & 0xFF00) | data2; - set_ixy: - if ( opcode == 0xDD ) - { - ix = ixy; - goto loop; - } - iy = ixy; - goto loop; - - case 0xF9: // LD SP,IXY - sp = ixy; - goto loop; - - case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, ixy ); - goto loop; - } - - case 0x21: // LD IXY,imm - ixy = GET_ADDR(); - pc += 2; - goto set_ixy; - - case 0x2A:{// LD IXY,(addr) - fuint16 addr = GET_ADDR(); - ixy = READ_WORD( addr ); - pc += 2; - goto set_ixy; - } - - // DD/FD CB prefix - case 0xCB: { - data = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data2 = READ_PROG( pc ); - pc++; - switch ( data2 ) - { - case 0x06: goto rlc_data_addr; // RLC (IXY) - case 0x16: goto rl_data_addr; // RL (IXY) - case 0x26: goto sla_data_addr; // SLA (IXY) - case 0x36: goto sll_data_addr; // SLL (IXY) - case 0x0E: goto rrc_data_addr; // RRC (IXY) - case 0x1E: goto rr_data_addr; // RR (IXY) - case 0x2E: goto sra_data_addr; // SRA (IXY) - case 0x3E: goto srl_data_addr; // SRL (IXY) - - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) - fuint8 temp = READ( data ); - int masked = temp & 1 << (data2 >> 3 & 7); - flags = (flags & C01) | H10 | - (masked & S80) | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) - int temp = READ( data ); - int bit = 1 << (data2 >> 3 & 7); - temp |= bit; // SET - if ( !(data2 & 0x40) ) - temp ^= bit; // RES - WRITE( data, temp ); - goto loop; - } - - default: - dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); - warning = true; - goto loop; - } - assert( false ); - } - - // INC/DEC - case 0x23: // INC IXY - ixy = uint16_t (ixy + 1); - goto set_ixy; - - case 0x2B: // DEC IXY - ixy = uint16_t (ixy - 1); - goto set_ixy; - - case 0x34: // INC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) + 1; - WRITE( ixy, data ); - goto inc_set_flags; - - case 0x35: // DEC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) - 1; - WRITE( ixy, data ); - goto dec_set_flags; - - case 0x24: // INC HXY - ixy = uint16_t (ixy + 0x100); - data = ixy >> 8; - goto inc_xy_common; - - case 0x2C: // INC LXY - data = uint8_t (ixy + 1); - ixy = (ixy & 0xFF00) | data; - inc_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto inc_set_flags; - } - iy = ixy; - goto inc_set_flags; - - case 0x25: // DEC HXY - ixy = uint16_t (ixy - 0x100); - data = ixy >> 8; - goto dec_xy_common; - - case 0x2D: // DEC LXY - data = uint8_t (ixy - 1); - ixy = (ixy & 0xFF00) | data; - dec_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto dec_set_flags; - } - iy = ixy; - goto dec_set_flags; - - // PUSH/POP - case 0xE5: // PUSH IXY - data = ixy; - goto push_data; - - case 0xE1:{// POP IXY - ixy = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto set_ixy; - } - - // Misc - - case 0xE9: // JP (IXY) - pc = ixy; - goto loop; - - case 0xE3:{// EX (SP),IXY - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, ixy ); - ixy = temp; - goto set_ixy; - } - - default: - dprintf( "Unnecessary DD/FD prefix encountered\n" ); - warning = true; - pc--; - goto loop; - } - assert( false ); - } - - } - dprintf( "Unhandled main opcode: $%02X\n", opcode ); - assert( false ); - -halt: - s_time &= 3; // increment by multiple of 4 -out_of_time: - pc--; - - s.time = s_time; - rg.flags = flags; - r.ix = ix; - r.iy = iy; - r.sp = sp; - r.pc = pc; - this->r.b = rg; - this->state_ = s; - this->state = &this->state_; + #include "Z80_Cpu_run.h" return warning; } diff --git a/Frameworks/GME/gme/Ay_Emu.cpp b/Frameworks/GME/gme/Ay_Emu.cpp old mode 100755 new mode 100644 index 93582f88f..002346ef5 --- a/Frameworks/GME/gme/Ay_Emu.cpp +++ b/Frameworks/GME/gme/Ay_Emu.cpp @@ -1,404 +1,357 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Ay_Emu.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -long const spectrum_clock = 3546900; -long const cpc_clock = 2000000; - -unsigned const ram_start = 0x4000; -int const osc_count = Ay_Apu::osc_count + 1; - -Ay_Emu::Ay_Emu() -{ - beeper_output = 0; - set_type( gme_ay_type ); - - static const char* const names [osc_count] = { - "Wave 1", "Wave 2", "Wave 3", "Beeper" - }; - set_voice_names( names ); - - static int const types [osc_count] = { - wave_type | 0, wave_type | 1, wave_type | 2, mixed_type | 0 - }; - set_voice_types( types ); - set_silence_lookahead( 6 ); -} - -Ay_Emu::~Ay_Emu() { } - -// Track info - -static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int min_size ) -{ - long pos = ptr - (byte const*) file.header; - long file_size = file.end - (byte const*) file.header; - assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); - int offset = (BOOST::int16_t) get_be16( ptr ); - if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) ) - return 0; - return ptr + offset; -} - -static blargg_err_t parse_header( byte const* in, long size, Ay_Emu::file_t* out ) -{ - typedef Ay_Emu::header_t header_t; - out->header = (header_t const*) in; - out->end = in + size; - - if ( size < Ay_Emu::header_size ) - return gme_wrong_file_type; - - header_t const& h = *(header_t const*) in; - if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) - return gme_wrong_file_type; - - out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); - if ( !out->tracks ) - return "Missing track data"; - - return 0; -} - -static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int track ) -{ - Gme_File::copy_field_( out->song, (char const*) get_data( file, file.tracks + track * 4, 1 ) ); - byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 ); - if ( track_info ) - out->length = get_be16( track_info + 4 ) * (1000L / 50); // frames to msec - - Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) ); - Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) ); -} - -blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const -{ - copy_ay_fields( file, out, track ); - return 0; -} - -struct Ay_File : Gme_Info_ -{ - Ay_Emu::file_t file; - - Ay_File() { set_type( gme_ay_type ); } - - blargg_err_t load_mem_( byte const* begin, long size ) - { - RETURN_ERR( parse_header( begin, size, &file ) ); - set_track_count( file.header->max_track + 1 ); - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int track ) const - { - copy_ay_fields( file, out, track ); - return 0; - } -}; - -static Music_Emu* new_ay_emu () { return BLARGG_NEW Ay_Emu ; } -static Music_Emu* new_ay_file() { return BLARGG_NEW Ay_File; } - -gme_type_t_ const gme_ay_type [1] = { "ZX Spectrum", 0, &new_ay_emu, &new_ay_file, "AY", 1 }; - -// Setup - -blargg_err_t Ay_Emu::load_mem_( byte const* in, long size ) -{ - assert( offsetof (header_t,track_info [2]) == header_size ); - - RETURN_ERR( parse_header( in, size, &file ) ); - set_track_count( file.header->max_track + 1 ); - - if ( file.header->vers > 2 ) - set_warning( "Unknown file version" ); - - set_voice_count( osc_count ); - apu.volume( gain() ); - - return setup_buffer( spectrum_clock ); -} - -void Ay_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* ) -{ - if ( i >= Ay_Apu::osc_count ) - beeper_output = center; - else - apu.osc_output( i, center ); -} - -// Emulation - -void Ay_Emu::set_tempo_( double t ) -{ - play_period = blip_time_t (clock_rate() / 50 / t); -} - -blargg_err_t Ay_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET - memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 ); - memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start ); - memset( mem.padding1, 0xFF, sizeof mem.padding1 ); - memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 ); - - // locate data blocks - byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); - if ( !data ) return "File data missing"; - - byte const* const more_data = get_data( file, data + 10, 6 ); - if ( !more_data ) return "File data missing"; - - byte const* blocks = get_data( file, data + 12, 8 ); - if ( !blocks ) return "File data missing"; - - // initial addresses - cpu::reset( mem.ram ); - r.sp = get_be16( more_data ); - r.b.a = r.b.b = r.b.d = r.b.h = data [8]; - r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; - r.alt.w = r.w; - r.ix = r.iy = r.w.hl; - - unsigned addr = get_be16( blocks ); - if ( !addr ) return "File data missing"; - - unsigned init = get_be16( more_data + 2 ); - if ( !init ) - init = addr; - - // copy blocks into memory - do - { - blocks += 2; - unsigned len = get_be16( blocks ); blocks += 2; - if ( addr + len > 0x10000 ) - { - set_warning( "Bad data block size" ); - len = 0x10000 - addr; - } - check( len ); - byte const* in = get_data( file, blocks, 0 ); blocks += 2; - if ( len > blargg_ulong (file.end - in) ) - { - set_warning( "Missing file data" ); - len = file.end - in; - } - //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); - if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data - dprintf( "Block addr in ROM\n" ); - memcpy( mem.ram + addr, in, len ); - - if ( file.end - blocks < 8 ) - { - set_warning( "Missing file data" ); - break; - } - } - while ( (addr = get_be16( blocks )) != 0 ); - - // copy and configure driver - static byte const passive [] = { - 0xF3, // DI - 0xCD, 0, 0, // CALL init - 0xED, 0x5E, // LOOP: IM 2 - 0xFB, // EI - 0x76, // HALT - 0x18, 0xFA // JR LOOP - }; - static byte const active [] = { - 0xF3, // DI - 0xCD, 0, 0, // CALL init - 0xED, 0x56, // LOOP: IM 1 - 0xFB, // EI - 0x76, // HALT - 0xCD, 0, 0, // CALL play - 0x18, 0xF7 // JR LOOP - }; - memcpy( mem.ram, passive, sizeof passive ); - unsigned play_addr = get_be16( more_data + 4 ); - //dprintf( "Play: $%04X\n", play_addr ); - if ( play_addr ) - { - memcpy( mem.ram, active, sizeof active ); - mem.ram [ 9] = play_addr; - mem.ram [10] = play_addr >> 8; - } - mem.ram [2] = init; - mem.ram [3] = init >> 8; - - mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) - - memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh) - - beeper_delta = int (apu.amp_range * 0.65); - last_beeper = 0; - apu.reset(); - next_play = play_period; - - // start at spectrum speed - change_clock_rate( spectrum_clock ); - set_tempo( tempo() ); - - spectrum_mode = false; - cpc_mode = false; - cpc_latch = 0; - - return 0; -} - -// Emulation - -void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) -{ - if ( !cpc_mode ) - { - switch ( addr & 0xFEFF ) - { - case 0xFEFD: - spectrum_mode = true; - apu_addr = data & 0x0F; - return; - - case 0xBEFD: - spectrum_mode = true; - apu.write( time, apu_addr, data ); - return; - } - } - - if ( !spectrum_mode ) - { - switch ( addr >> 8 ) - { - case 0xF6: - switch ( data & 0xC0 ) - { - case 0xC0: - apu_addr = cpc_latch & 0x0F; - goto enable_cpc; - - case 0x80: - apu.write( time, apu_addr, cpc_latch ); - goto enable_cpc; - } - break; - - case 0xF4: - cpc_latch = data; - goto enable_cpc; - } - } - - dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); - return; - -enable_cpc: - if ( !cpc_mode ) - { - cpc_mode = true; - change_clock_rate( cpc_clock ); - set_tempo( tempo() ); - } -} - -void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) -{ - Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu); - - if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode ) - { - int delta = emu.beeper_delta; - data &= 0x10; - if ( emu.last_beeper != data ) - { - emu.last_beeper = data; - emu.beeper_delta = -delta; - emu.spectrum_mode = true; - if ( emu.beeper_output ) - emu.apu.synth_.offset( time, delta, emu.beeper_output ); - } - } - else - { - emu.cpu_out_misc( time, addr, data ); - } -} - -int ay_cpu_in( Ay_Cpu*, unsigned addr ) -{ - // keyboard read and other things - if ( (addr & 0xFF) == 0xFE ) - return 0xFF; // other values break some beeper tunes - - dprintf( "Unmapped IN : $%04X\n", addr ); - return 0xFF; -} - -blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) -{ - set_time( 0 ); - if ( !(spectrum_mode | cpc_mode) ) - duration /= 2; // until mode is set, leave room for halved clock rate - - while ( time() < duration ) - { - cpu::run( min( duration, next_play ) ); - - if ( time() >= next_play ) - { - next_play += play_period; - - if ( r.iff1 ) - { - if ( mem.ram [r.pc] == 0x76 ) - r.pc++; - - r.iff1 = r.iff2 = 0; - - mem.ram [--r.sp] = uint8_t (r.pc >> 8); - mem.ram [--r.sp] = uint8_t (r.pc); - r.pc = 0x38; - cpu::adjust_time( 12 ); - if ( r.im == 2 ) - { - cpu::adjust_time( 6 ); - unsigned addr = r.i * 0x100u + 0xFF; - r.pc = mem.ram [(addr + 1) & 0xFFFF] * 0x100u + mem.ram [addr]; - } - } - } - } - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - adjust_time( -duration ); - - apu.end_frame( duration ); - - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Ay_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: probably don't need detailed errors as to why file is corrupt + +int const spectrum_clock = 3546900; // 128K Spectrum +int const spectrum_period = 70908; + +//int const spectrum_clock = 3500000; // 48K Spectrum +//int const spectrum_period = 69888; + +int const cpc_clock = 2000000; + +Ay_Emu::Ay_Emu() +{ + core.set_cpc_callback( enable_cpc_, this ); + set_type( gme_ay_type ); + set_silence_lookahead( 6 ); +} + +Ay_Emu::~Ay_Emu() { } + +// Track info + +// Given pointer to 2-byte offset of data, returns pointer to data, or NULL if +// offset is 0 or there is less than min_size bytes of data available. +static byte const* get_data( Ay_Emu::file_t const& file, byte const ptr [], int min_size ) +{ + int offset = (BOOST::int16_t) get_be16( ptr ); + int pos = ptr - (byte const*) file.header; + int size = file.end - (byte const*) file.header; + assert( (unsigned) pos <= (unsigned) size - 2 ); + int limit = size - min_size; + if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit ) + return NULL; + return ptr + offset; +} + +static blargg_err_t parse_header( byte const in [], int size, Ay_Emu::file_t* out ) +{ + typedef Ay_Emu::header_t header_t; + if ( size < header_t::size ) + return blargg_err_file_type; + + out->header = (header_t const*) in; + out->end = in + size; + header_t const& h = *(header_t const*) in; + if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) + return blargg_err_file_type; + + out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); + if ( !out->tracks ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "missing track data" ); + + return blargg_ok; +} + +static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int track ) +{ + Gme_File::copy_field_( out->song, (char const*) get_data( file, file.tracks + track * 4, 1 ) ); + byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 ); + if ( track_info ) + out->length = get_be16( track_info + 4 ) * (1000 / 50); // frames to msec + + Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) ); + Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) ); +} + +static void hash_ay_file( Ay_Emu::file_t const& file, Gme_Info_::Hash_Function& out ) +{ + out.hash_( &file.header->vers, sizeof(file.header->vers) ); + out.hash_( &file.header->player, sizeof(file.header->player) ); + out.hash_( &file.header->unused[0], sizeof(file.header->unused) ); + out.hash_( &file.header->max_track, sizeof(file.header->max_track) ); + out.hash_( &file.header->first_track, sizeof(file.header->first_track) ); + + for ( unsigned i = 0; i <= file.header->max_track; i++ ) + { + byte const* track_info = get_data( file, file.tracks + i * 4 + 2, 14 ); + if ( track_info ) + { + out.hash_( track_info + 8, 2 ); + byte const* points = get_data( file, track_info + 10, 6 ); + if ( points ) out.hash_( points, 6 ); + + byte const* blocks = get_data( file, track_info + 12, 8 ); + if ( blocks ) + { + int addr = get_be16( blocks ); + + while ( addr ) + { + out.hash_( blocks, 4 ); + + int len = get_be16( blocks + 2 ); + + byte const* block = get_data( file, blocks + 4, len ); + if ( block ) out.hash_( block, len ); + + blocks += 6; + addr = get_be16( blocks ); + } + } + } + } +} + +blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const +{ + copy_ay_fields( file, out, track ); + return blargg_ok; +} + +struct Ay_File : Gme_Info_ +{ + Ay_Emu::file_t file; + + Ay_File() { set_type( gme_ay_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + RETURN_ERR( parse_header( begin, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + copy_ay_fields( file, out, track ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_ay_file( file, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_ay_emu () +{ + return BLARGG_NEW Ay_Emu; +} + +static Music_Emu* new_ay_file() +{ + return BLARGG_NEW Ay_File; +} + +gme_type_t_ const gme_ay_type [1] = {{ + "ZX Spectrum", + 0, + &new_ay_emu, + &new_ay_file, + "AY", + 1 +}}; + +// Setup + +blargg_err_t Ay_Emu::load_mem_( byte const in [], int size ) +{ + assert( offsetof (header_t,track_info [2]) == header_t::size ); + + RETURN_ERR( parse_header( in, size, &file ) ); + set_track_count( file.header->max_track + 1 ); + + if ( file.header->vers > 2 ) + set_warning( "Unknown file version" ); + + int const osc_count = Ay_Apu::osc_count + 1; // +1 for beeper + + set_voice_count( osc_count ); + core.apu().volume( gain() ); + + static const char* const names [osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Beeper" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+0, wave_type+1, wave_type+2, mixed_type+1 + }; + set_voice_types( types ); + + return setup_buffer( spectrum_clock ); +} + +void Ay_Emu::update_eq( blip_eq_t const& eq ) +{ + core.apu().treble_eq( eq ); +} + +void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* ) +{ + if ( i >= Ay_Apu::osc_count ) + core.set_beeper_output( center ); + else + core.apu().set_output( i, center ); +} + +void Ay_Emu::set_tempo_( double t ) +{ + int p = spectrum_period; + if ( clock_rate() != spectrum_clock ) + p = clock_rate() / 50; + + core.set_play_period( blip_time_t (p / t) ); +} + +blargg_err_t Ay_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + byte* const mem = core.mem(); + + memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET + memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 ); + memset( mem + core.ram_addr, 0x00, core.mem_size - core.ram_addr ); + + // locate data blocks + byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); + if ( !data ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + byte const* const more_data = get_data( file, data + 10, 6 ); + if ( !more_data ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + byte const* blocks = get_data( file, data + 12, 8 ); + if ( !blocks ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + // initial addresses + unsigned addr = get_be16( blocks ); + if ( !addr ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" ); + + unsigned init = get_be16( more_data + 2 ); + if ( !init ) + init = addr; + + // copy blocks into memory + do + { + blocks += 2; + unsigned len = get_be16( blocks ); blocks += 2; + if ( addr + len > core.mem_size ) + { + set_warning( "Bad data block size" ); + len = core.mem_size - addr; + } + check( len ); + byte const* in = get_data( file, blocks, 0 ); blocks += 2; + if ( len > (unsigned) (file.end - in) ) + { + set_warning( "File data missing" ); + len = file.end - in; + } + //dprintf( "addr: $%04X, len: $%04X\n", addr, len ); + if ( addr < core.ram_addr && addr >= 0x400 ) // several tracks use low data + dprintf( "Block addr in ROM\n" ); + memcpy( mem + addr, in, len ); + + if ( file.end - blocks < 8 ) + { + set_warning( "File data missing" ); + break; + } + } + while ( (addr = get_be16( blocks )) != 0 ); + + // copy and configure driver + static byte const passive [] = { + 0xF3, // DI + 0xCD, 0, 0, // CALL init + 0xED, 0x5E, // LOOP: IM 2 + 0xFB, // EI + 0x76, // HALT + 0x18, 0xFA // JR LOOP + }; + static byte const active [] = { + 0xF3, // DI + 0xCD, 0, 0, // CALL init + 0xED, 0x56, // LOOP: IM 1 + 0xFB, // EI + 0x76, // HALT + 0xCD, 0, 0, // CALL play + 0x18, 0xF7 // JR LOOP + }; + memcpy( mem, passive, sizeof passive ); + int const play_addr = get_be16( more_data + 4 ); + if ( play_addr ) + { + memcpy( mem, active, sizeof active ); + mem [ 9] = play_addr; + mem [10] = play_addr >> 8; + } + mem [2] = init; + mem [3] = init >> 8; + + mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) + + // start at spectrum speed + change_clock_rate( spectrum_clock ); + set_tempo( tempo() ); + + Ay_Core::registers_t r = { }; + r.sp = get_be16( more_data ); + r.b.a = r.b.b = r.b.d = r.b.h = data [8]; + r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; + r.alt.w = r.w; + r.ix = r.iy = r.w.hl; + + core.start_track( r, play_addr ); + + return blargg_ok; +} + +blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) +{ + core.end_frame( &duration ); + return blargg_ok; +} + +inline void Ay_Emu::enable_cpc() +{ + change_clock_rate( cpc_clock ); + set_tempo( tempo() ); +} + +void Ay_Emu::enable_cpc_( void* data ) +{ + STATIC_CAST(Ay_Emu*,data)->enable_cpc(); +} + +blargg_err_t Ay_Emu::hash_( Hash_Function& out ) const +{ + hash_ay_file( file, out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Ay_Emu.h b/Frameworks/GME/gme/Ay_Emu.h old mode 100755 new mode 100644 index ba8445d31..7da69a57e --- a/Frameworks/GME/gme/Ay_Emu.h +++ b/Frameworks/GME/gme/Ay_Emu.h @@ -1,70 +1,60 @@ // Sinclair Spectrum AY music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef AY_EMU_H #define AY_EMU_H #include "Classic_Emu.h" -#include "Ay_Apu.h" -#include "Ay_Cpu.h" +#include "Ay_Core.h" -class Ay_Emu : private Ay_Cpu, public Classic_Emu { - typedef Ay_Cpu cpu; +class Ay_Emu : public Classic_Emu { public: // AY file header - enum { header_size = 0x14 }; struct header_t { - byte tag [8]; + enum { size = 0x14 }; + + byte tag [8]; byte vers; byte player; - byte unused [2]; - byte author [2]; - byte comment [2]; + byte unused [2]; + byte author [2]; + byte comment [2]; byte max_track; byte first_track; byte track_info [2]; }; static gme_type_t static_type() { return gme_ay_type; } + +// Implementation public: Ay_Emu(); ~Ay_Emu(); + struct file_t { header_t const* header; - byte const* end; byte const* tracks; + byte const* end; // end of file data }; + + blargg_err_t hash_( Hash_Function& out ) const; + protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + private: file_t file; + Ay_Core core; - unsigned play_addr; - cpu_time_t play_period; - cpu_time_t next_play; - Blip_Buffer* beeper_output; - int beeper_delta; - int last_beeper; - int apu_addr; - int cpc_latch; - bool spectrum_mode; - bool cpc_mode; - - // large items - struct { - byte padding1 [0x100]; - byte ram [0x10000 + 0x100]; - } mem; - Ay_Apu apu; - friend void ay_cpu_out( Ay_Cpu*, cpu_time_t, unsigned addr, int data ); - void cpu_out_misc( cpu_time_t, unsigned addr, int data ); + void enable_cpc(); + static void enable_cpc_( void* data ); }; #endif diff --git a/Frameworks/GME/gme/Blip_Buffer.cpp b/Frameworks/GME/gme/Blip_Buffer.cpp old mode 100755 new mode 100644 index 6304cedb4..41cebfcea --- a/Frameworks/GME/gme/Blip_Buffer.cpp +++ b/Frameworks/GME/gme/Blip_Buffer.cpp @@ -1,446 +1,509 @@ -// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ - -#include "Blip_Buffer.h" - -#include -#include -#include -#include -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -int const silent_buf_size = 1; // size used for Silent_Blip_Buffer - -Blip_Buffer::Blip_Buffer() -{ - factor_ = LONG_MAX; - offset_ = 0; - buffer_ = 0; - buffer_size_ = 0; - sample_rate_ = 0; - reader_accum_ = 0; - bass_shift_ = 0; - clock_rate_ = 0; - bass_freq_ = 16; - length_ = 0; - - // assumptions code makes about implementation-defined features - #ifndef NDEBUG - // right shift of negative value preserves sign - buf_t_ i = -0x7FFFFFFE; - assert( (i >> 1) == -0x3FFFFFFF ); - - // casting to short truncates to 16 bits and sign-extends - i = 0x18000; - assert( (short) i == -0x8000 ); - #endif -} - -Blip_Buffer::~Blip_Buffer() -{ - if ( buffer_size_ != silent_buf_size ) - free( buffer_ ); -} - -Silent_Blip_Buffer::Silent_Blip_Buffer() -{ - factor_ = 0; - buffer_ = buf; - buffer_size_ = silent_buf_size; - memset( buf, 0, sizeof buf ); // in case machine takes exception for signed overflow -} - -void Blip_Buffer::clear( int entire_buffer ) -{ - offset_ = 0; - reader_accum_ = 0; - modified_ = 0; - if ( buffer_ ) - { - long count = (entire_buffer ? buffer_size_ : samples_avail()); - memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (buf_t_) ); - } -} - -Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec ) -{ - if ( buffer_size_ == silent_buf_size ) - { - assert( 0 ); - return "Internal (tried to resize Silent_Blip_Buffer)"; - } - - // start with maximum length that resampled time can represent - long new_size = (ULONG_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; - if ( msec != blip_max_length ) - { - long s = (new_rate * (msec + 1) + 999) / 1000; - if ( s < new_size ) - new_size = s; - else - assert( 0 ); // fails if requested buffer length exceeds limit - } - - if ( buffer_size_ != new_size ) - { - void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); - if ( !p ) - return "Out of memory"; - buffer_ = (buf_t_*) p; - } - - buffer_size_ = new_size; - assert( buffer_size_ != silent_buf_size ); - - // update things based on the sample rate - sample_rate_ = new_rate; - length_ = new_size * 1000 / new_rate - 1; - if ( msec ) - assert( length_ == msec ); // ensure length is same as that passed in - if ( clock_rate_ ) - clock_rate( clock_rate_ ); - bass_freq( bass_freq_ ); - - clear(); - - return 0; // success -} - -blip_resampled_time_t Blip_Buffer::clock_rate_factor( long rate ) const -{ - double ratio = (double) sample_rate_ / rate; - blip_long factor = (blip_long) floor( ratio * (1L << BLIP_BUFFER_ACCURACY) + 0.5 ); - assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large - return (blip_resampled_time_t) factor; -} - -void Blip_Buffer::bass_freq( int freq ) -{ - bass_freq_ = freq; - int shift = 31; - if ( freq > 0 ) - { - shift = 13; - long f = (freq << 16) / sample_rate_; - while ( (f >>= 1) && --shift ) { } - } - bass_shift_ = shift; -} - -void Blip_Buffer::end_frame( blip_time_t t ) -{ - offset_ += t * factor_; - assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length -} - -void Blip_Buffer::remove_silence( long count ) -{ - assert( count <= samples_avail() ); // tried to remove more samples than available - offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; -} - -long Blip_Buffer::count_samples( blip_time_t t ) const -{ - unsigned long last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; - unsigned long first_sample = offset_ >> BLIP_BUFFER_ACCURACY; - return (long) (last_sample - first_sample); -} - -blip_time_t Blip_Buffer::count_clocks( long count ) const -{ - if ( !factor_ ) - { - assert( 0 ); // sample rate and clock rates must be set first - return 0; - } - - if ( count > buffer_size_ ) - count = buffer_size_; - blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; - return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); -} - -void Blip_Buffer::remove_samples( long count ) -{ - if ( count ) - { - remove_silence( count ); - - // copy remaining samples to beginning and clear old samples - long remain = samples_avail() + blip_buffer_extra_; - memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); - memset( buffer_ + remain, 0, count * sizeof *buffer_ ); - } -} - -// Blip_Synth_ - -Blip_Synth_Fast_::Blip_Synth_Fast_() -{ - buf = 0; - last_amp = 0; - delta_factor = 0; -} - -void Blip_Synth_Fast_::volume_unit( double new_unit ) -{ - delta_factor = int (new_unit * (1L << blip_sample_bits) + 0.5); -} - -#if !BLIP_BUFFER_FAST - -Blip_Synth_::Blip_Synth_( short* p, int w ) : - impulses( p ), - width( w ) -{ - volume_unit_ = 0.0; - kernel_unit = 0; - buf = 0; - last_amp = 0; - delta_factor = 0; -} - -#undef PI -#define PI 3.1415926535897932384626433832795029 - -static void gen_sinc( float* out, int count, double oversample, double treble, double cutoff ) -{ - if ( cutoff >= 0.999 ) - cutoff = 0.999; - - if ( treble < -300.0 ) - treble = -300.0; - if ( treble > 5.0 ) - treble = 5.0; - - double const maxh = 4096.0; - double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); - double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); - double const to_angle = PI / 2 / maxh / oversample; - for ( int i = 0; i < count; i++ ) - { - double angle = ((i - count) * 2 + 1) * to_angle; - double c = rolloff * cos( (maxh - 1.0) * angle ) - cos( maxh * angle ); - double cos_nc_angle = cos( maxh * cutoff * angle ); - double cos_nc1_angle = cos( (maxh * cutoff - 1.0) * angle ); - double cos_angle = cos( angle ); - - c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; - double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); - double b = 2.0 - cos_angle - cos_angle; - double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; - - out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d - } -} - -void blip_eq_t::generate( float* out, int count ) const -{ - // lower cutoff freq for narrow kernels with their wider transition band - // (8 points->1.49, 16 points->1.15) - double oversample = blip_res * 2.25 / count + 0.85; - double half_rate = sample_rate * 0.5; - if ( cutoff_freq ) - oversample = half_rate / cutoff_freq; - double cutoff = rolloff_freq * oversample / half_rate; - - gen_sinc( out, count, blip_res * oversample, treble, cutoff ); - - // apply (half of) hamming window - double to_fraction = PI / (count - 1); - for ( int i = count; i--; ) - out [i] *= 0.54f - 0.46f * (float) cos( i * to_fraction ); -} - -void Blip_Synth_::adjust_impulse() -{ - // sum pairs for each phase and add error correction to end of first half - int const size = impulses_size(); - for ( int p = blip_res; p-- >= blip_res / 2; ) - { - int p2 = blip_res - 2 - p; - long error = kernel_unit; - for ( int i = 1; i < size; i += blip_res ) - { - error -= impulses [i + p ]; - error -= impulses [i + p2]; - } - if ( p == p2 ) - error /= 2; // phase = 0.5 impulse uses same half for both sides - impulses [size - blip_res + p] += (short) error; - //printf( "error: %ld\n", error ); - } - - //for ( int i = blip_res; i--; printf( "\n" ) ) - // for ( int j = 0; j < width / 2; j++ ) - // printf( "%5ld,", impulses [j * blip_res + i + 1] ); -} - -void Blip_Synth_::treble_eq( blip_eq_t const& eq ) -{ - float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; - - int const half_size = blip_res / 2 * (width - 1); - eq.generate( &fimpulse [blip_res], half_size ); - - int i; - - // need mirror slightly past center for calculation - for ( i = blip_res; i--; ) - fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; - - // starts at 0 - for ( i = 0; i < blip_res; i++ ) - fimpulse [i] = 0.0f; - - // find rescale factor - double total = 0.0; - for ( i = 0; i < half_size; i++ ) - total += fimpulse [blip_res + i]; - - //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB - //double const base_unit = 37888.0; // allows treble to +5 dB - double const base_unit = 32768.0; // necessary for blip_unscaled to work - double rescale = base_unit / 2 / total; - kernel_unit = (long) base_unit; - - // integrate, first difference, rescale, convert to int - double sum = 0.0; - double next = 0.0; - int const impulses_size = this->impulses_size(); - for ( i = 0; i < impulses_size; i++ ) - { - impulses [i] = (short) floor( (next - sum) * rescale + 0.5 ); - sum += fimpulse [i]; - next += fimpulse [i + blip_res]; - } - adjust_impulse(); - - // volume might require rescaling - double vol = volume_unit_; - if ( vol ) - { - volume_unit_ = 0.0; - volume_unit( vol ); - } -} - -void Blip_Synth_::volume_unit( double new_unit ) -{ - if ( new_unit != volume_unit_ ) - { - // use default eq if it hasn't been set yet - if ( !kernel_unit ) - treble_eq( -8.0 ); - - volume_unit_ = new_unit; - double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; - - if ( factor > 0.0 ) - { - int shift = 0; - - // if unit is really small, might need to attenuate kernel - while ( factor < 2.0 ) - { - shift++; - factor *= 2.0; - } - - if ( shift ) - { - kernel_unit >>= shift; - assert( kernel_unit > 0 ); // fails if volume unit is too low - - // keep values positive to avoid round-towards-zero of sign-preserving - // right shift for negative values - long offset = 0x8000 + (1 << (shift - 1)); - long offset2 = 0x8000 >> shift; - for ( int i = impulses_size(); i--; ) - impulses [i] = (short) (((impulses [i] + offset) >> shift) - offset2); - adjust_impulse(); - } - } - delta_factor = (int) floor( factor + 0.5 ); - //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); - } -} -#endif - -long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_samples, int stereo ) -{ - long count = samples_avail(); - if ( count > max_samples ) - count = max_samples; - - if ( count ) - { - int const bass = BLIP_READER_BASS( *this ); - BLIP_READER_BEGIN( reader, *this ); - - if ( !stereo ) - { - for ( blip_long n = count; n; --n ) - { - blip_long s = BLIP_READER_READ( reader ); - if ( (blip_sample_t) s != s ) - s = 0x7FFF - (s >> 24); - *out++ = (blip_sample_t) s; - BLIP_READER_NEXT( reader, bass ); - } - } - else - { - for ( blip_long n = count; n; --n ) - { - blip_long s = BLIP_READER_READ( reader ); - if ( (blip_sample_t) s != s ) - s = 0x7FFF - (s >> 24); - *out = (blip_sample_t) s; - out += 2; - BLIP_READER_NEXT( reader, bass ); - } - } - BLIP_READER_END( reader, *this ); - - remove_samples( count ); - } - return count; -} - -void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) -{ - if ( buffer_size_ == silent_buf_size ) - { - assert( 0 ); - return; - } - - buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; - - int const sample_shift = blip_sample_bits - 16; - int prev = 0; - while ( count-- ) - { - blip_long s = (blip_long) *in++ << sample_shift; - *out += s - prev; - prev = s; - ++out; - } - *out -= prev; -} - +// Blip_Buffer $vers. http://www.slack.net/~ant/ + +#include "Blip_Buffer.h" + +#include + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +//// Blip_Buffer + +Blip_Buffer::Blip_Buffer() +{ + factor_ = UINT_MAX/2 + 1; + buffer_ = NULL; + buffer_center_ = NULL; + buffer_size_ = 0; + sample_rate_ = 0; + bass_shift_ = 0; + clock_rate_ = 0; + bass_freq_ = 16; + length_ = 0; + + // assumptions code makes about implementation-defined features + #ifndef NDEBUG + // right shift of negative value preserves sign + int i = -0x7FFFFFFE; + assert( (i >> 1) == -0x3FFFFFFF ); + + // casting truncates and sign-extends + i = 0x18000; + assert( (BOOST::int16_t) i == -0x8000 ); + #endif + + clear(); +} + +Blip_Buffer::~Blip_Buffer() +{ + free( buffer_ ); +} + +void Blip_Buffer::clear() +{ + bool const entire_buffer = true; + + offset_ = 0; + reader_accum_ = 0; + modified_ = false; + + if ( buffer_ ) + { + int count = (entire_buffer ? buffer_size_ : samples_avail()); + memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (delta_t) ); + } +} + +blargg_err_t Blip_Buffer::set_sample_rate( int new_rate, int msec ) +{ + // Limit to maximum size that resampled time can represent + int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) - + blip_buffer_extra_ - 64; // TODO: -64 isn't needed + int new_size = (new_rate * (msec + 1) + 999) / 1000; + if ( new_size > max_size ) + new_size = max_size; + + // Resize buffer + if ( buffer_size_ != new_size ) + { + //dprintf( "%d \n", (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); + CHECK_ALLOC( p ); + buffer_ = (delta_t*) p; + buffer_center_ = buffer_ + BLIP_MAX_QUALITY/2; + buffer_size_ = new_size; + } + + // Update sample_rate and things that depend on it + sample_rate_ = new_rate; + length_ = new_size * 1000 / new_rate - 1; + if ( clock_rate_ ) + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + + clear(); + + return blargg_ok; +} + +blip_resampled_time_t Blip_Buffer::clock_rate_factor( int rate ) const +{ + double ratio = (double) sample_rate_ / rate; + int factor = (int) floor( ratio * (1 << BLIP_BUFFER_ACCURACY) + 0.5 ); + assert( factor > 0 || !sample_rate_ ); // fails if clock/output ratio is too large + return (blip_resampled_time_t) factor; +} + +void Blip_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + int shift = 31; + if ( freq > 0 && sample_rate_ ) + { + shift = 13; + int f = (freq << 16) / sample_rate_; + while ( (f >>= 1) != 0 && --shift ) { } + } + bass_shift_ = shift; +} + +void Blip_Buffer::end_frame( blip_time_t t ) +{ + offset_ += t * factor_; + assert( samples_avail() <= (int) buffer_size_ ); // fails if time is past end of buffer +} + +int Blip_Buffer::count_samples( blip_time_t t ) const +{ + blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY; + blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY; + return (int) (last_sample - first_sample); +} + +blip_time_t Blip_Buffer::count_clocks( int count ) const +{ + if ( count > buffer_size_ ) + count = buffer_size_; + blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; + return (blip_time_t) ((time - offset_ + factor_ - 1) / factor_); +} + +void Blip_Buffer::remove_samples( int count ) +{ + if ( count ) + { + remove_silence( count ); + + // copy remaining samples to beginning and clear old samples + int remain = samples_avail() + blip_buffer_extra_; + memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); + memset( buffer_ + remain, 0, count * sizeof *buffer_ ); + } +} + +int Blip_Buffer::read_samples( blip_sample_t out_ [], int max_samples, bool stereo ) +{ + int count = samples_avail(); + if ( count > max_samples ) + count = max_samples; + + if ( count ) + { + int const bass = highpass_shift(); + delta_t const* reader = read_pos() + count; + int reader_sum = integrator(); + + blip_sample_t* BLARGG_RESTRICT out = out_ + count; + if ( stereo ) + out += count; + int offset = -count; + + if ( !stereo ) + { + do + { + int s = reader_sum >> delta_bits; + + reader_sum -= reader_sum >> bass; + reader_sum += reader [offset]; + + BLIP_CLAMP( s, s ); + out [offset] = (blip_sample_t) s; + } + while ( ++offset ); + } + else + { + do + { + int s = reader_sum >> delta_bits; + + reader_sum -= reader_sum >> bass; + reader_sum += reader [offset]; + + BLIP_CLAMP( s, s ); + out [offset * 2] = (blip_sample_t) s; + } + while ( ++offset ); + } + + set_integrator( reader_sum ); + + remove_samples( count ); + } + return count; +} + +void Blip_Buffer::mix_samples( blip_sample_t const in [], int count ) +{ + delta_t* out = buffer_center_ + (offset_ >> BLIP_BUFFER_ACCURACY); + + int const sample_shift = blip_sample_bits - 16; + int prev = 0; + while ( --count >= 0 ) + { + int s = *in++ << sample_shift; + *out += s - prev; + prev = s; + ++out; + } + *out -= prev; +} + +void Blip_Buffer::save_state( blip_buffer_state_t* out ) +{ + assert( samples_avail() == 0 ); + out->offset_ = offset_; + out->reader_accum_ = reader_accum_; + memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf ); +} + +void Blip_Buffer::load_state( blip_buffer_state_t const& in ) +{ + clear(); + + offset_ = in.offset_; + reader_accum_ = in.reader_accum_; + memcpy( buffer_, in.buf, sizeof in.buf ); +} + + +//// Blip_Synth_ + +Blip_Synth_Fast_::Blip_Synth_Fast_() +{ + buf = NULL; + last_amp = 0; + delta_factor = 0; +} + +void Blip_Synth_Fast_::volume_unit( double new_unit ) +{ + delta_factor = int (new_unit * (1 << blip_sample_bits) + 0.5); +} + +#if BLIP_BUFFER_FAST + +void blip_eq_t::generate( float* out, int count ) const { } + +#else + +Blip_Synth_::Blip_Synth_( short p [], int w ) : + phases( p ), + width( w ) +{ + volume_unit_ = 0.0; + kernel_unit = 0; + buf = NULL; + last_amp = 0; + delta_factor = 0; +} + +#undef PI +#define PI 3.1415926535897932384626433832795029 + +// Generates right half of sinc kernel (including center point) with cutoff at +// sample rate / 2 / oversample. Frequency response at cutoff frequency is +// treble dB (-6=0.5,-12=0.25). Mid controls frequency that rolloff begins at, +// cut * sample rate / 2. +static void gen_sinc( float out [], int out_size, double oversample, + double treble, double mid ) +{ + if ( mid > 0.9999 ) mid = 0.9999; + if ( treble < -300.0 ) treble = -300.0; + if ( treble > 5.0 ) treble = 5.0; + + double const maxh = 4096.0; + double rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - mid) ); + double const pow_a_n = pow( rolloff, maxh - maxh * mid ); + double const to_angle = PI / maxh / oversample; + for ( int i = 1; i < out_size; i++ ) + { + double angle = i * to_angle; + double c = rolloff * cos( angle * maxh - angle ) - + cos( angle * maxh ); + double cos_nc_angle = cos( angle * maxh * mid ); + double cos_nc1_angle = cos( angle * maxh * mid - angle ); + double cos_angle = cos( angle ); + + c = c * pow_a_n - rolloff * cos_nc1_angle + cos_nc_angle; + double d = 1.0 + rolloff * (rolloff - cos_angle - cos_angle); + double b = 2.0 - cos_angle - cos_angle; + double a = 1.0 - cos_angle - cos_nc_angle + cos_nc1_angle; + + out [i] = (float) ((a * d + c * b) / (b * d)); // a / b + c / d + } + + // Approximate center by looking at two points to right. Much simpler + // and more reliable than trying to calculate it properly. + out [0] = out [1] + 0.5 * (out [1] - out [2]); +} + +// Gain is 1-2800 for beta of 0-10, instead of 1.0 as it should be, but +// this is corrected by normalization in treble_eq(). +static void kaiser_window( float io [], int count, float beta ) +{ + int const accuracy = 10; + + float const beta2 = beta * beta; + float const step = (float) 0.5 / count; + float pos = (float) 0.5; + for ( float* const end = io + count; io < end; ++io ) + { + float x = (pos - pos*pos) * beta2; + float u = x; + float k = 1; + float n = 2; + + // Keep refining until adjustment becomes small + do + { + u *= x / (n * n); + n += 1; + k += u; + } + while ( k <= u * (1 << accuracy) ); + + pos += step; + *io *= k; + } +} + +void blip_eq_t::generate( float out [], int count ) const +{ + // lower cutoff freq for narrow kernels with their wider transition band + // (8 points->1.49, 16 points->1.15) + double cutoff_adj = blip_res * 2.25 / count + 0.85; + if ( cutoff_adj < 1.02 ) + cutoff_adj = 1.02; + double half_rate = sample_rate * 0.5; + if ( cutoff_freq ) + cutoff_adj = half_rate / cutoff_freq; + double cutoff = rolloff_freq * cutoff_adj / half_rate; + + gen_sinc( out, count, oversample * cutoff_adj, treble, cutoff ); + + kaiser_window( out, count, kaiser ); +} + +void Blip_Synth_::treble_eq( blip_eq_t const& eq ) +{ + // Generate right half of kernel + int const half_size = blip_eq_t::calc_count( width ); + float fimpulse [blip_res / 2 * (BLIP_MAX_QUALITY - 1) + 1]; + eq.generate( fimpulse, half_size ); + + int i; + + // Find rescale factor. Summing from small to large (right to left) + // reduces error. + double total = 0.0; + for ( i = half_size; --i > 0; ) + total += fimpulse [i]; + total = total * 2.0 + fimpulse [0]; + + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB + //double const base_unit = 37888.0; // allows treble to +5 dB + double const base_unit = 32768.0; // necessary for blip_unscaled to work + double rescale = base_unit / total; + kernel_unit = (int) base_unit; + + // Integrate, first difference, rescale, convert to int + double sum = 0; + double next = 0; + int const size = impulses_size(); + for ( i = 0; i < size; i++ ) + { + int j = (half_size - 1) - i; + + if ( i >= blip_res ) + sum += fimpulse [j + blip_res]; + + // goes slightly past center, so it needs a little mirroring + next += fimpulse [j < 0 ? -j : j]; + + // calculate unintereleved index + int x = (~i & (blip_res - 1)) * (width >> 1) + (i >> BLIP_PHASE_BITS); + assert( (unsigned) x < (unsigned) size ); + + // flooring separately virtually eliminates error + phases [x] = (short) (int) + (floor( sum * rescale + 0.5 ) - floor( next * rescale + 0.5 )); + //phases [x] = (short) (int) + // floor( sum * rescale - next * rescale + 0.5 ); + } + + adjust_impulse(); + + // volume might require rescaling + double vol = volume_unit_; + if ( vol ) + { + volume_unit_ = 0.0; + volume_unit( vol ); + } +} + +void Blip_Synth_::adjust_impulse() +{ + int const size = impulses_size(); + int const half_width = width / 2; + + // Sum each phase as would be done when synthesizing, and correct + // any that don't add up to exactly kernel_half. + for ( int phase = blip_res / 2; --phase >= 0; ) + { + int const fwd = phase * half_width; + int const rev = size - half_width - fwd; + + int error = kernel_unit; + for ( int i = half_width; --i >= 0; ) + { + error += phases [fwd + i]; + error += phases [rev + i]; + } + phases [fwd + half_width - 1] -= (short) error; + + // Error shouldn't occur now with improved calculation + //if ( error ) printf( "error: %ld\n", error ); + } + + #if 0 + for ( int i = 0; i < blip_res; i++, printf( "\n" ) ) + for ( int j = 0; j < width / 2; j++ ) + printf( "%5d,", (int) -phases [j + width/2 * i] ); + #endif +} + +void Blip_Synth_::rescale_kernel( int shift ) +{ + // Keep values positive to avoid round-towards-zero of sign-preserving + // right shift for negative values. + int const keep_positive = 0x8000 + (1 << (shift - 1)); + + int const half_width = width / 2; + for ( int phase = blip_res; --phase >= 0; ) + { + int const fwd = phase * half_width; + + // Integrate, rescale, then differentiate again. + // If differences are rescaled directly, more error results. + int sum = keep_positive; + for ( int i = 0; i < half_width; i++ ) + { + int prev = sum; + sum += phases [fwd + i]; + phases [fwd + i] = (sum >> shift) - (prev >> shift); + } + } + + adjust_impulse(); +} + +void Blip_Synth_::volume_unit( double new_unit ) +{ + if ( volume_unit_ != new_unit ) + { + // use default eq if it hasn't been set yet + if ( !kernel_unit ) + treble_eq( -8.0 ); + + // Factor that kernel must be multiplied by + volume_unit_ = new_unit; + double factor = new_unit * (1 << blip_sample_bits) / kernel_unit; + + if ( factor > 0.0 ) + { + // If factor is low, reduce amplitude of kernel itself + int shift = 0; + while ( factor < 2.0 ) + { + shift++; + factor *= 2.0; + } + + if ( shift ) + { + kernel_unit >>= shift; + assert( kernel_unit > 0 ); // fails if volume unit is too low + + rescale_kernel( shift ); + } + } + + delta_factor = -(int) floor( factor + 0.5 ); + //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit ); + } +} +#endif diff --git a/Frameworks/GME/gme/Blip_Buffer.h b/Frameworks/GME/gme/Blip_Buffer.h old mode 100755 new mode 100644 index 9467584f4..503fc9114 --- a/Frameworks/GME/gme/Blip_Buffer.h +++ b/Frameworks/GME/gme/Blip_Buffer.h @@ -1,489 +1,198 @@ -// Band-limited sound synthesis buffer - -// Blip_Buffer 0.4.1 -#ifndef BLIP_BUFFER_H -#define BLIP_BUFFER_H - - // internal - #include - #if INT_MAX >= 0x7FFFFFFF - typedef int blip_long; - typedef unsigned blip_ulong; - #else - typedef long blip_long; - typedef unsigned long blip_ulong; - #endif - -// Time unit at source clock rate -typedef blip_long blip_time_t; - -// Output samples are 16-bit signed, with a range of -32768 to 32767 -typedef short blip_sample_t; -enum { blip_sample_max = 32767 }; - -class Blip_Buffer { -public: - typedef const char* blargg_err_t; - - // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults - // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there - // isn't enough memory, returns error without affecting current buffer setup. - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); - - // Set number of source time units per second - void clock_rate( long ); - - // End current time frame of specified duration and make its samples available - // (along with any still-unread samples) for reading with read_samples(). Begins - // a new time frame at the end of the current frame. - void end_frame( blip_time_t time ); - - // Read at most 'max_samples' out of buffer into 'dest', removing them from from - // the buffer. Returns number of samples actually read and removed. If stereo is - // true, increments 'dest' one extra time after writing each sample, to allow - // easy interleving of two channels into a stereo output buffer. - long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); - -// Additional optional features - - // Current output sample rate - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // Number of source time units per second - long clock_rate() const; - - // Set frequency high-pass filter frequency, where higher values reduce bass more - void bass_freq( int frequency ); - - // Number of samples delay from synthesis to samples read out - int output_latency() const; - - // Remove all available samples and clear buffer to silence. If 'entire_buffer' is - // false, just clears out any samples waiting rather than the entire buffer. - void clear( int entire_buffer = 1 ); - - // Number of samples available for reading with read_samples() - long samples_avail() const; - - // Remove 'count' samples from those waiting to be read - void remove_samples( long count ); - -// Experimental features - - // Count number of clocks needed until 'count' samples will be available. - // If buffer can't even hold 'count' samples, returns number of clocks until - // buffer becomes full. - blip_time_t count_clocks( long count ) const; - - // Number of raw samples that can be mixed within frame of specified duration. - long count_samples( blip_time_t duration ) const; - - // Mix 'count' samples from 'buf' into buffer. - void mix_samples( blip_sample_t const* buf, long count ); - - // not documented yet - void set_modified() { modified_ = 1; } - int clear_modified() { int b = modified_; modified_ = 0; return b; } - typedef blip_ulong blip_resampled_time_t; - void remove_silence( long count ); - blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } - blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } - blip_resampled_time_t clock_rate_factor( long clock_rate ) const; -public: - Blip_Buffer(); - ~Blip_Buffer(); - - // Deprecated - typedef blip_resampled_time_t resampled_time_t; - blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } - blargg_err_t sample_rate( long r, int msec ) { return set_sample_rate( r, msec ); } -private: - // noncopyable - Blip_Buffer( const Blip_Buffer& ); - Blip_Buffer& operator = ( const Blip_Buffer& ); -public: - typedef blip_time_t buf_t_; - blip_ulong factor_; - blip_resampled_time_t offset_; - buf_t_* buffer_; - blip_long buffer_size_; - blip_long reader_accum_; - int bass_shift_; -private: - long sample_rate_; - long clock_rate_; - int bass_freq_; - int length_; - int modified_; - friend class Blip_Reader; -}; - -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -// Number of bits in resample ratio fraction. Higher values give a more accurate ratio -// but reduce maximum buffer size. -#ifndef BLIP_BUFFER_ACCURACY - #define BLIP_BUFFER_ACCURACY 16 -#endif - -// Number bits in phase offset. Fewer than 6 bits (64 phase offsets) results in -// noticeable broadband noise when synthesizing high frequency square waves. -// Affects size of Blip_Synth objects since they store the waveform directly. -#ifndef BLIP_PHASE_BITS - #if BLIP_BUFFER_FAST - #define BLIP_PHASE_BITS 8 - #else - #define BLIP_PHASE_BITS 6 - #endif -#endif - - // Internal - typedef blip_ulong blip_resampled_time_t; - int const blip_widest_impulse_ = 16; - int const blip_buffer_extra_ = blip_widest_impulse_ + 2; - int const blip_res = 1 << BLIP_PHASE_BITS; - class blip_eq_t; - - class Blip_Synth_Fast_ { - public: - Blip_Buffer* buf; - int last_amp; - int delta_factor; - - void volume_unit( double ); - Blip_Synth_Fast_(); - void treble_eq( blip_eq_t const& ) { } - }; - - class Blip_Synth_ { - public: - Blip_Buffer* buf; - int last_amp; - int delta_factor; - - void volume_unit( double ); - Blip_Synth_( short* impulses, int width ); - void treble_eq( blip_eq_t const& ); - private: - double volume_unit_; - short* const impulses; - int const width; - blip_long kernel_unit; - int impulses_size() const { return blip_res / 2 * width + 1; } - void adjust_impulse(); - }; - -// Quality level. Start with blip_good_quality. -const int blip_med_quality = 8; -const int blip_good_quality = 12; -const int blip_high_quality = 16; - -// Range specifies the greatest expected change in amplitude. Calculate it -// by finding the difference between the maximum and minimum expected -// amplitudes (max - min). -template -class Blip_Synth { -public: - // Set overall volume of waveform - void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } - - // Configure low-pass filter (see blip_buffer.txt) - void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } - - // Get/set Blip_Buffer used for output - Blip_Buffer* output() const { return impl.buf; } - void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } - - // Update amplitude of waveform at given time. Using this requires a separate - // Blip_Synth for each waveform. - void update( blip_time_t time, int amplitude ); - -// Low-level interface - - // Add an amplitude transition of specified delta, optionally into specified buffer - // rather than the one set with output(). Delta can be positive or negative. - // The actual change in amplitude is delta * (volume / range) - void offset( blip_time_t, int delta, Blip_Buffer* ) const; - void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } - - // Works directly in terms of fractional output samples. Contact author for more info. - void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - - // Same as offset(), except code is inlined for higher performance - void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); - } - void offset_inline( blip_time_t t, int delta ) const { - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); - } - -private: -#if BLIP_BUFFER_FAST - Blip_Synth_Fast_ impl; -#else - Blip_Synth_ impl; - typedef short imp_t; - imp_t impulses [blip_res * (quality / 2) + 1]; -public: - Blip_Synth() : impl( impulses, quality ) { } -#endif -}; - -// Low-pass equalization parameters -class blip_eq_t { -public: - // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce - // treble, small positive values (0 to 5.0) increase treble. - blip_eq_t( double treble_db = 0 ); - - // See blip_buffer.txt - blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); - -private: - double treble; - long rolloff_freq; - long sample_rate; - long cutoff_freq; - void generate( float* out, int count ) const; - friend class Blip_Synth_; -}; - -int const blip_sample_bits = 30; - -// Dummy Blip_Buffer to direct sound output to, for easy muting without -// having to stop sound code. -class Silent_Blip_Buffer : public Blip_Buffer { - buf_t_ buf [blip_buffer_extra_ + 1]; -public: - // The following cannot be used (an assertion will fail if attempted): - blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); - blip_time_t count_clocks( long count ) const; - void mix_samples( blip_sample_t const* buf, long count ); - - Silent_Blip_Buffer(); -}; - - #if defined (__GNUC__) || _MSC_VER >= 1100 - #define BLIP_RESTRICT __restrict - #else - #define BLIP_RESTRICT - #endif - -// Optimized reading from Blip_Buffer, for use in custom sample output - -// Begin reading from buffer. Name should be unique to the current block. -#define BLIP_READER_BEGIN( name, blip_buffer ) \ - const Blip_Buffer::buf_t_* BLIP_RESTRICT name##_reader_buf = (blip_buffer).buffer_;\ - blip_long name##_reader_accum = (blip_buffer).reader_accum_ - -// Get value to pass to BLIP_READER_NEXT() -#define BLIP_READER_BASS( blip_buffer ) ((blip_buffer).bass_shift_) - -// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal -// code at the cost of having no bass control -int const blip_reader_default_bass = 9; - -// Current sample -#define BLIP_READER_READ( name ) (name##_reader_accum >> (blip_sample_bits - 16)) - -// Current raw sample in full internal resolution -#define BLIP_READER_READ_RAW( name ) (name##_reader_accum) - -// Advance to next sample -#define BLIP_READER_NEXT( name, bass ) \ - (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass))) - -// End reading samples from buffer. The number of samples read must now be removed -// using Blip_Buffer::remove_samples(). -#define BLIP_READER_END( name, blip_buffer ) \ - (void) ((blip_buffer).reader_accum_ = name##_reader_accum) - - -// Compatibility with older version -const long blip_unscaled = 65535; -const int blip_low_quality = blip_med_quality; -const int blip_best_quality = blip_high_quality; - -// Deprecated; use BLIP_READER macros as follows: -// Blip_Reader r; r.begin( buf ); -> BLIP_READER_BEGIN( r, buf ); -// int bass = r.begin( buf ) -> BLIP_READER_BEGIN( r, buf ); int bass = BLIP_READER_BASS( buf ); -// r.read() -> BLIP_READER_READ( r ) -// r.read_raw() -> BLIP_READER_READ_RAW( r ) -// r.next( bass ) -> BLIP_READER_NEXT( r, bass ) -// r.next() -> BLIP_READER_NEXT( r, blip_reader_default_bass ) -// r.end( buf ) -> BLIP_READER_END( r, buf ) -class Blip_Reader { -public: - int begin( Blip_Buffer& ); - blip_long read() const { return accum >> (blip_sample_bits - 16); } - blip_long read_raw() const { return accum; } - void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } - void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } - -private: - const Blip_Buffer::buf_t_* buf; - blip_long accum; -}; - -// End of public interface - -#include - -template -inline void Blip_Synth::offset_resampled( blip_resampled_time_t time, - int delta, Blip_Buffer* blip_buf ) const -{ - // Fails if time is beyond end of Blip_Buffer, due to a bug in caller code or the - // need for a longer buffer as set by set_sample_rate(). - assert( (blip_long) (time >> BLIP_BUFFER_ACCURACY) < blip_buf->buffer_size_ ); - delta *= impl.delta_factor; - blip_long* BLIP_RESTRICT buf = blip_buf->buffer_ + (time >> BLIP_BUFFER_ACCURACY); - int phase = (int) (time >> (BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS) & (blip_res - 1)); - -#if BLIP_BUFFER_FAST - blip_long left = buf [0] + delta; - - // Kind of crappy, but doing shift after multiply results in overflow. - // Alternate way of delaying multiply by delta_factor results in worse - // sub-sample resolution. - blip_long right = (delta >> BLIP_PHASE_BITS) * phase; - left -= right; - right += buf [1]; - - buf [0] = left; - buf [1] = right; -#else - - int const fwd = (blip_widest_impulse_ - quality) / 2; - int const rev = fwd + quality - 2; - int const mid = quality / 2 - 1; - - imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; - - #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - - // straight forward implementation resulted in better code on GCC for x86 - - #define ADD_IMP( out, in ) \ - buf [out] += (blip_long) imp [blip_res * (in)] * delta - - #define BLIP_FWD( i ) {\ - ADD_IMP( fwd + i, i );\ - ADD_IMP( fwd + 1 + i, i + 1 );\ - } - #define BLIP_REV( r ) {\ - ADD_IMP( rev - r, r + 1 );\ - ADD_IMP( rev + 1 - r, r );\ - } - - BLIP_FWD( 0 ) - if ( quality > 8 ) BLIP_FWD( 2 ) - if ( quality > 12 ) BLIP_FWD( 4 ) - { - ADD_IMP( fwd + mid - 1, mid - 1 ); - ADD_IMP( fwd + mid , mid ); - imp = impulses + phase; - } - if ( quality > 12 ) BLIP_REV( 6 ) - if ( quality > 8 ) BLIP_REV( 4 ) - BLIP_REV( 2 ) - - ADD_IMP( rev , 1 ); - ADD_IMP( rev + 1, 0 ); - - #else - - // for RISC processors, help compiler by reading ahead of writes - - #define BLIP_FWD( i ) {\ - blip_long t0 = i0 * delta + buf [fwd + i];\ - blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ - i0 = imp [blip_res * (i + 2)];\ - buf [fwd + i] = t0;\ - buf [fwd + 1 + i] = t1;\ - } - #define BLIP_REV( r ) {\ - blip_long t0 = i0 * delta + buf [rev - r];\ - blip_long t1 = imp [blip_res * r] * delta + buf [rev + 1 - r];\ - i0 = imp [blip_res * (r - 1)];\ - buf [rev - r] = t0;\ - buf [rev + 1 - r] = t1;\ - } - - blip_long i0 = *imp; - BLIP_FWD( 0 ) - if ( quality > 8 ) BLIP_FWD( 2 ) - if ( quality > 12 ) BLIP_FWD( 4 ) - { - blip_long t0 = i0 * delta + buf [fwd + mid - 1]; - blip_long t1 = imp [blip_res * mid] * delta + buf [fwd + mid ]; - imp = impulses + phase; - i0 = imp [blip_res * mid]; - buf [fwd + mid - 1] = t0; - buf [fwd + mid ] = t1; - } - if ( quality > 12 ) BLIP_REV( 6 ) - if ( quality > 8 ) BLIP_REV( 4 ) - BLIP_REV( 2 ) - - blip_long t0 = i0 * delta + buf [rev ]; - blip_long t1 = *imp * delta + buf [rev + 1]; - buf [rev ] = t0; - buf [rev + 1] = t1; - #endif - -#endif -} - -#undef BLIP_FWD -#undef BLIP_REV - -template -#if BLIP_BUFFER_FAST - inline -#endif -void Blip_Synth::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const -{ - offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); -} - -template -#if BLIP_BUFFER_FAST - inline -#endif -void Blip_Synth::update( blip_time_t t, int amp ) -{ - int delta = amp - impl.last_amp; - impl.last_amp = amp; - offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); -} - -inline blip_eq_t::blip_eq_t( double t ) : - treble( t ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { } -inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : - treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } - -inline int Blip_Buffer::length() const { return length_; } -inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } -inline long Blip_Buffer::sample_rate() const { return sample_rate_; } -inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } -inline long Blip_Buffer::clock_rate() const { return clock_rate_; } -inline void Blip_Buffer::clock_rate( long cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); } - -inline int Blip_Reader::begin( Blip_Buffer& blip_buf ) -{ - buf = blip_buf.buffer_; - accum = blip_buf.reader_accum_; - return blip_buf.bass_shift_; -} - -int const blip_max_length = 0; -int const blip_default_length = 250; - -#endif +// Band-limited sound synthesis buffer + +// Blip_Buffer $vers +#ifndef BLIP_BUFFER_H +#define BLIP_BUFFER_H + +#include "blargg_common.h" +#include "Blip_Buffer_impl.h" + +typedef int blip_time_t; // Source clocks in current time frame +typedef BOOST::int16_t blip_sample_t; // 16-bit signed output sample +int const blip_default_length = 1000 / 4; // Default Blip_Buffer length (1/4 second) + + +//// Sample buffer for band-limited synthesis + +class Blip_Buffer : public Blip_Buffer_ { +public: + + // Sets output sample rate and resizes and clears sample buffer + blargg_err_t set_sample_rate( int samples_per_sec, int msec_length = blip_default_length ); + + // Sets number of source time units per second + void clock_rate( int clocks_per_sec ); + + // Clears buffer and removes all samples + void clear(); + + // Use Blip_Synth to add waveform to buffer + + // Resamples to time t, then subtracts t from current time. Appends result of resampling + // to buffer for reading. + void end_frame( blip_time_t t ); + + // Number of samples available for reading with read_samples() + int samples_avail() const; + + // Reads at most n samples to out [0 to n-1] and returns number actually read. If stereo + // is true, writes to out [0], out [2], out [4] etc. instead. + int read_samples( blip_sample_t out [], int n, bool stereo = false ); + +// More features + + // Sets flag that tells some Multi_Buffer types that sound was added to buffer, + // so they know that it needs to be mixed in. Only needs to be called once + // per time frame that sound was added. Not needed if not using Multi_Buffer. + void set_modified() { modified_ = true; } + + // Sets high-pass filter frequency, from 0 to 20000 Hz, where higher values reduce bass more + void bass_freq( int frequency ); + + int length() const; // Length of buffer in milliseconds + int sample_rate() const; // Current output sample rate + int clock_rate() const; // Number of source time units per second + int output_latency() const; // Number of samples delay from offset() to read_samples() + +// Low-level features + + // Removes the first n samples + void remove_samples( int n ); + + // Returns number of clocks needed until n samples will be available. + // If buffer cannot even hold n samples, returns number of clocks + // until buffer becomes full. + blip_time_t count_clocks( int n ) const; + + // Number of samples that should be mixed before calling end_frame( t ) + int count_samples( blip_time_t t ) const; + + // Mixes n samples into buffer + void mix_samples( const blip_sample_t in [], int n ); + +// Resampled time (sorry, poor documentation right now) + + // Resampled time is fixed-point, in terms of output samples. + + // Converts clock count to resampled time + blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; } + + // Converts clock time since beginning of current time frame to resampled time + blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; } + + // Returns factor that converts clock rate to resampled time + blip_resampled_time_t clock_rate_factor( int clock_rate ) const; + +// State save/load + + // Saves state, including high-pass filter and tails of last deltas. + // All samples must have been read from buffer before calling this + // (that is, samples_avail() must return 0). + void save_state( blip_buffer_state_t* out ); + + // Loads state. State must have been saved from Blip_Buffer with same + // settings during same run of program; states can NOT be stored on disk. + // Clears buffer before loading state. + void load_state( const blip_buffer_state_t& in ); + +private: + // noncopyable + Blip_Buffer( const Blip_Buffer& ); + Blip_Buffer& operator = ( const Blip_Buffer& ); + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + Blip_Buffer(); + ~Blip_Buffer(); + void remove_silence( int n ); +}; + + +//// Adds amplitude changes to Blip_Buffer + +template class Blip_Synth; + +typedef Blip_Synth<8, 1> Blip_Synth_Fast; // faster, but less equalizer control +typedef Blip_Synth<12,1> Blip_Synth_Norm; // good for most things +typedef Blip_Synth<16,1> Blip_Synth_Good; // sharper filter cutoff + +template +class Blip_Synth { +public: + + // Sets volume of amplitude delta unit + void volume( double v ) { impl.volume_unit( 1.0 / range * v ); } + + // Configures low-pass filter + void treble_eq( const blip_eq_t& eq ) { impl.treble_eq( eq ); } + + // Gets/sets default Blip_Buffer + Blip_Buffer* output() const { return impl.buf; } + void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } + + // Extends waveform to time t at current amplitude, then changes its amplitude to a + // Using this requires a separate Blip_Synth for each waveform. + void update( blip_time_t t, int a ); + +// Low-level interface + + // If no Blip_Buffer* is specified, uses one set by output() above + + // Adds amplitude transition at time t. Delta can be positive or negative. + // The actual change in amplitude is delta * volume. + void offset( blip_time_t t, int delta, Blip_Buffer* ) const; + void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } + + // Same as offset(), except code is inlined for higher performance + void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { offset_resampled( buf->to_fixed( t ), delta, buf ); } + void offset_inline( blip_time_t t, int delta ) const { offset_resampled( impl.buf->to_fixed( t ), delta, impl.buf ); } + + // Works directly in terms of fractional output samples. Use resampled time functions in Blip_Buffer + // to convert clock counts to resampled time. + void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + +private: +#if BLIP_BUFFER_FAST + Blip_Synth_Fast_ impl; + typedef char coeff_t; +#else + Blip_Synth_ impl; + typedef short coeff_t; + // Left halves of first difference of step response for each possible phase + coeff_t phases [quality / 2 * blip_res]; +public: + Blip_Synth() : impl( phases, quality ) { } +#endif +}; + + +//// Low-pass equalization parameters + +class blip_eq_t { + double treble, kaiser; + int rolloff_freq, sample_rate, cutoff_freq; +public: + // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce + // treble, small positive values (0 to 5.0) increase treble. + blip_eq_t( double treble_db = 0 ); + + // See blip_buffer.txt + blip_eq_t( double treble, int rolloff_freq, int sample_rate, int cutoff_freq = 0, + double kaiser = 5.2 ); + + // Generate center point and right half of impulse response + virtual void generate( float out [], int count ) const; + virtual ~blip_eq_t() { } + + enum { oversample = blip_res }; + static int calc_count( int quality ) { return (quality - 1) * (oversample / 2) + 1; } +}; + +#include "Blip_Buffer_impl2.h" + +#endif diff --git a/Frameworks/GME/gme/Classic_Emu.cpp b/Frameworks/GME/gme/Classic_Emu.cpp old mode 100755 new mode 100644 index 063444fe2..f02d79b2a --- a/Frameworks/GME/gme/Classic_Emu.cpp +++ b/Frameworks/GME/gme/Classic_Emu.cpp @@ -1,11 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Classic_Emu.h" #include "Multi_Buffer.h" -#include -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -20,9 +19,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ Classic_Emu::Classic_Emu() { - buf = 0; - stereo_buffer = 0; - voice_types = 0; + buf = NULL; + stereo_buffer = NULL; + voice_types = NULL; // avoid inconsistency in our duplicated constants assert( (int) wave_type == (int) Multi_Buffer::wave_type ); @@ -33,6 +32,8 @@ Classic_Emu::Classic_Emu() Classic_Emu::~Classic_Emu() { delete stereo_buffer; + delete effects_buffer_; + effects_buffer_ = NULL; } void Classic_Emu::set_equalizer_( equalizer_t const& eq ) @@ -40,10 +41,10 @@ void Classic_Emu::set_equalizer_( equalizer_t const& eq ) Music_Emu::set_equalizer_( eq ); update_eq( eq.treble ); if ( buf ) - buf->bass_freq( equalizer().bass ); + buf->bass_freq( (int) equalizer().bass ); } -blargg_err_t Classic_Emu::set_sample_rate_( long rate ) +blargg_err_t Classic_Emu::set_sample_rate_( int rate ) { if ( !buf ) { @@ -61,11 +62,11 @@ void Classic_Emu::mute_voices_( int mask ) { if ( mask & (1 << i) ) { - set_voice( i, 0, 0, 0 ); + set_voice( i, NULL, NULL, NULL ); } else { - Multi_Buffer::channel_t ch = buf->channel( i, (voice_types ? voice_types [i] : 0) ); + Multi_Buffer::channel_t ch = buf->channel( i ); assert( (ch.center && ch.left && ch.right) || (!ch.center && !ch.left && !ch.right) ); // all or nothing set_voice( i, ch.center, ch.left, ch.right ); @@ -73,33 +74,35 @@ void Classic_Emu::mute_voices_( int mask ) } } -void Classic_Emu::change_clock_rate( long rate ) +void Classic_Emu::change_clock_rate( int rate ) { clock_rate_ = rate; buf->clock_rate( rate ); } -blargg_err_t Classic_Emu::setup_buffer( long rate ) +blargg_err_t Classic_Emu::setup_buffer( int rate ) { change_clock_rate( rate ); - RETURN_ERR( buf->set_channel_count( voice_count() ) ); + RETURN_ERR( buf->set_channel_count( voice_count(), voice_types ) ); set_equalizer( equalizer() ); buf_changed_count = buf->channels_changed_count(); - return 0; + return blargg_ok; } blargg_err_t Classic_Emu::start_track_( int track ) { RETURN_ERR( Music_Emu::start_track_( track ) ); buf->clear(); - return 0; + return blargg_ok; } -blargg_err_t Classic_Emu::play_( long count, sample_t* out ) +blargg_err_t Classic_Emu::play_( int count, sample_t out [] ) { - long remain = count; + // read from buffer, then refill buffer and repeat if necessary + int remain = count; while ( remain ) { + buf->disable_immediate_removal(); remain -= buf->read_samples( &out [count - remain], remain ); if ( remain ) { @@ -108,77 +111,14 @@ blargg_err_t Classic_Emu::play_( long count, sample_t* out ) buf_changed_count = buf->channels_changed_count(); remute_voices(); } + + // TODO: use more accurate length calculation int msec = buf->length(); - blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; + blip_time_t clocks_emulated = msec * clock_rate_ / 1000 - 100; RETURN_ERR( run_clocks( clocks_emulated, msec ) ); assert( clocks_emulated ); buf->end_frame( clocks_emulated ); } } - return 0; -} - -// Rom_Data - -blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in, - int header_size, void* header_out, int fill, long pad_size ) -{ - long file_offset = pad_size - header_size; - - rom_addr = 0; - mask = 0; - size_ = 0; - rom.clear(); - - file_size_ = in.remain(); - if ( file_size_ <= header_size ) // <= because there must be data after header - return gme_wrong_file_type; - blargg_err_t err = rom.resize( file_offset + file_size_ + pad_size ); - if ( !err ) - err = in.read( rom.begin() + file_offset, file_size_ ); - if ( err ) - { - rom.clear(); - return err; - } - - file_size_ -= header_size; - memcpy( header_out, &rom [file_offset], header_size ); - - memset( rom.begin() , fill, pad_size ); - memset( rom.end() - pad_size, fill, pad_size ); - - return 0; -} - -void Rom_Data_::set_addr_( long addr, int unit ) -{ - rom_addr = addr - unit - pad_extra; - - long rounded = (addr + file_size_ + unit - 1) / unit * unit; - if ( rounded <= 0 ) - { - rounded = 0; - } - else - { - int shift = 0; - unsigned long max_addr = (unsigned long) (rounded - 1); - while ( max_addr >> shift ) - shift++; - mask = (1L << shift) - 1; - } - - if ( addr < 0 ) - addr = 0; - size_ = rounded; - if ( rom.resize( rounded - rom_addr + pad_extra ) ) { } // OK if shrink fails - - if ( 0 ) - { - dprintf( "addr: %X\n", addr ); - dprintf( "file_size: %d\n", file_size_ ); - dprintf( "rounded: %d\n", rounded ); - dprintf( "mask: $%X\n", mask ); - } + return blargg_ok; } diff --git a/Frameworks/GME/gme/Classic_Emu.h b/Frameworks/GME/gme/Classic_Emu.h old mode 100755 new mode 100644 index 8cd822ca2..4495400f1 --- a/Frameworks/GME/gme/Classic_Emu.h +++ b/Frameworks/GME/gme/Classic_Emu.h @@ -1,6 +1,6 @@ // Common aspects of emulators which use Blip_Buffer for sound output -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef CLASSIC_EMU_H #define CLASSIC_EMU_H @@ -9,33 +9,57 @@ #include "Music_Emu.h" class Classic_Emu : public Music_Emu { +protected: +// Derived interface + + // Advertises type of sound on each voice, so Effects_Buffer can better choose + // what effect to apply (pan, echo, surround). Constant can have value added so + // that voices of the same type can be spread around the stereo sound space. + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + void set_voice_types( int const types [] ) { voice_types = types; } + + // Sets up Blip_Buffers after loading file + blargg_err_t setup_buffer( int clock_rate ); + + // Clock rate of Blip_buffers + int clock_rate() const { return clock_rate_; } + + // Changes clock rate of Blip_Buffers (experimental) + void change_clock_rate( int ); + +// Overrides should do the indicated task + + // Set Blip_Buffer(s) voice outputs to, or mute voice if pointer is NULL + virtual void set_voice( int index, Blip_Buffer* center, + Blip_Buffer* left, Blip_Buffer* right ) BLARGG_PURE( ; ) + + // Update equalization + virtual void update_eq( blip_eq_t const& ) BLARGG_PURE( ; ) + + // Start track + virtual blargg_err_t start_track_( int track ) BLARGG_PURE( ; ) + + // Run for at most msec or time_io clocks, then set time_io to number of clocks + // actually run for. After returning, Blip_Buffers have time frame of time_io clocks + // ended. + virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) BLARGG_PURE( ; ) + +// Internal public: Classic_Emu(); ~Classic_Emu(); - void set_buffer( Multi_Buffer* ); + virtual void set_buffer( Multi_Buffer* ); + protected: - // Services - enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; - void set_voice_types( int const* t ) { voice_types = t; } - blargg_err_t setup_buffer( long clock_rate ); - long clock_rate() const { return clock_rate_; } - void change_clock_rate( long ); // experimental - - // Overridable - virtual void set_voice( int index, Blip_Buffer* center, - Blip_Buffer* left, Blip_Buffer* right ) = 0; - virtual void update_eq( blip_eq_t const& ) = 0; - virtual blargg_err_t start_track_( int track ) = 0; - virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) = 0; -protected: - blargg_err_t set_sample_rate_( long sample_rate ); - void mute_voices_( int ); - void set_equalizer_( equalizer_t const& ); - blargg_err_t play_( long, sample_t* ); + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual void mute_voices_( int ); + virtual void set_equalizer_( equalizer_t const& ); + virtual blargg_err_t play_( int, sample_t [] ); + private: Multi_Buffer* buf; Multi_Buffer* stereo_buffer; // NULL if using custom buffer - long clock_rate_; + int clock_rate_; unsigned buf_changed_count; int const* voice_types; }; @@ -46,82 +70,10 @@ inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf ) buf = new_buf; } -// ROM data handler, used by several Classic_Emu derivitives. Loads file data -// with padding on both sides, allowing direct use in bank mapping. The main purpose -// is to allow all file data to be loaded with only one read() call (for efficiency). +inline void Classic_Emu::set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ) { } -class Rom_Data_ { -public: - typedef unsigned char byte; -protected: - enum { pad_extra = 8 }; - blargg_vector rom; - long file_size_; - blargg_long rom_addr; - blargg_long mask; - blargg_long size_; // TODO: eliminate - - blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out, - int fill, long pad_size ); - void set_addr_( long addr, int unit ); -}; +inline void Classic_Emu::update_eq( blip_eq_t const& ) { } -template -class Rom_Data : public Rom_Data_ { - enum { pad_size = unit + pad_extra }; -public: - // Load file data, using already-loaded header 'h' if not NULL. Copy header - // from loaded file data into *out and fill unmapped bytes with 'fill'. - blargg_err_t load( Data_Reader& in, int header_size, void* header_out, int fill ) - { - return load_rom_data_( in, header_size, header_out, fill, pad_size ); - } - - // Size of file data read in (excluding header) - long file_size() const { return file_size_; } - - // Pointer to beginning of file data - byte* begin() const { return rom.begin() + pad_size; } - - // Set address that file data should start at - void set_addr( long addr ) { set_addr_( addr, unit ); } - - // Free data - void clear() { rom.clear(); } - - // Size of data + start addr, rounded to a multiple of unit - long size() const { return size_; } - - // Pointer to unmapped page filled with same value - byte* unmapped() { return rom.begin(); } - - // Mask address to nearest power of two greater than size() - blargg_long mask_addr( blargg_long addr ) const - { - #ifdef check - check( addr <= mask ); - #endif - return addr & mask; - } - - // Pointer to page starting at addr. Returns unmapped() if outside data. - byte* at_addr( blargg_long addr ) - { - blargg_ulong offset = mask_addr( addr ) - rom_addr; - if ( offset > blargg_ulong (rom.size() - pad_size) ) - offset = 0; // unmapped - return &rom [offset]; - } -}; - -#ifndef GME_APU_HOOK - #define GME_APU_HOOK( emu, addr, data ) ((void) 0) -#endif - -#ifndef GME_FRAME_HOOK - #define GME_FRAME_HOOK( emu ) ((void) 0) -#else - #define GME_FRAME_HOOK_DEFINED 1 -#endif +inline blargg_err_t Classic_Emu::run_clocks( blip_time_t&, int ) { return blargg_ok; } #endif diff --git a/Frameworks/GME/gme/Dual_Resampler.cpp b/Frameworks/GME/gme/Dual_Resampler.cpp old mode 100755 new mode 100644 index 8644517ca..e19dfff87 --- a/Frameworks/GME/gme/Dual_Resampler.cpp +++ b/Frameworks/GME/gme/Dual_Resampler.cpp @@ -1,131 +1,317 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Dual_Resampler.h" - -#include -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -unsigned const resampler_extra = 256; - -Dual_Resampler::Dual_Resampler() { } - -Dual_Resampler::~Dual_Resampler() { } - -blargg_err_t Dual_Resampler::reset( int pairs ) -{ - // expand allocations a bit - RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) ); - resize( pairs ); - resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2); - return resampler.buffer_size( resampler_size ); -} - -void Dual_Resampler::resize( int pairs ) -{ - int new_sample_buf_size = pairs * 2; - if ( sample_buf_size != new_sample_buf_size ) - { - if ( (unsigned) new_sample_buf_size > sample_buf.size() ) - { - check( false ); - return; - } - sample_buf_size = new_sample_buf_size; - oversamples_per_frame = int (pairs * resampler.ratio()) * 2 + 2; - clear(); - } -} - -void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) -{ - long pair_count = sample_buf_size >> 1; - blip_time_t blip_time = blip_buf.count_clocks( pair_count ); - int sample_count = oversamples_per_frame - resampler.written(); - - int new_count = play_frame( blip_time, sample_count, resampler.buffer() ); - assert( new_count < resampler_size ); - - blip_buf.end_frame( blip_time ); - assert( blip_buf.samples_avail() == pair_count ); - - resampler.write( new_count ); - - long count = resampler.read( sample_buf.begin(), sample_buf_size ); - assert( count == (long) sample_buf_size ); - - mix_samples( blip_buf, out ); - blip_buf.remove_samples( pair_count ); -} - -void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_buf ) -{ - // empty extra buffer - long remain = sample_buf_size - buf_pos; - if ( remain ) - { - if ( remain > count ) - remain = count; - count -= remain; - memcpy( out, &sample_buf [buf_pos], remain * sizeof *out ); - out += remain; - buf_pos += remain; - } - - // entire frames - while ( count >= (long) sample_buf_size ) - { - play_frame_( blip_buf, out ); - out += sample_buf_size; - count -= sample_buf_size; - } - - // extra - if ( count ) - { - play_frame_( blip_buf, sample_buf.begin() ); - buf_pos = count; - memcpy( out, sample_buf.begin(), count * sizeof *out ); - out += count; - } -} - -void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out ) -{ - Blip_Reader sn; - int bass = sn.begin( blip_buf ); - const dsample_t* in = sample_buf.begin(); - - for ( int n = sample_buf_size >> 1; n--; ) - { - int s = sn.read(); - blargg_long l = (blargg_long) in [0] * 2 + s; - if ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - sn.next( bass ); - blargg_long r = (blargg_long) in [1] * 2 + s; - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - in += 2; - out [0] = l; - out [1] = r; - out += 2; - } - - sn.end( blip_buf ); -} - +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Dual_Resampler.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: fix this. hack since resampler holds back some output. +int const resampler_extra = 34; + +int const stereo = 2; + +Dual_Resampler::Dual_Resampler() { } + +Dual_Resampler::~Dual_Resampler() { } + +blargg_err_t Dual_Resampler::reset( int pairs ) +{ + // expand allocations a bit + RETURN_ERR( sample_buf.resize( (pairs + (pairs >> 2)) * 2 ) ); + resize( pairs ); + resampler_size = oversamples_per_frame + (oversamples_per_frame >> 2); + RETURN_ERR( resampler.resize_buffer( resampler_size ) ); + resampler.clear(); + return blargg_ok; +} + +void Dual_Resampler::resize( int pairs ) +{ + int new_sample_buf_size = pairs * 2; + //new_sample_buf_size = new_sample_buf_size / 4 * 4; // TODO: needed only for 3:2 downsampler + if ( sample_buf_size != new_sample_buf_size ) + { + if ( (unsigned) new_sample_buf_size > sample_buf.size() ) + { + check( false ); + return; + } + sample_buf_size = new_sample_buf_size; + oversamples_per_frame = int (pairs * resampler.rate()) * 2 + 2; + clear(); + } +} + +void Dual_Resampler::clear() +{ + buf_pos = buffered = 0; + resampler.clear(); +} + + +int Dual_Resampler::play_frame_( Stereo_Buffer& stereo_buf, dsample_t out [], Stereo_Buffer** secondary_buf_set, int secondary_buf_set_count ) +{ + int pair_count = sample_buf_size >> 1; + blip_time_t blip_time = stereo_buf.center()->count_clocks( pair_count ); + int sample_count = oversamples_per_frame - resampler.written() + resampler_extra; + + int new_count = set_callback.f( set_callback.data, blip_time, sample_count, resampler.buffer() ); + assert( new_count < resampler_size ); + + stereo_buf.end_frame( blip_time ); + assert( stereo_buf.samples_avail() == pair_count * 2 ); + if ( secondary_buf_set && secondary_buf_set_count ) + { + for ( int i = 0; i < secondary_buf_set_count; i++ ) + { + Stereo_Buffer * second_buf = secondary_buf_set[i]; + blip_time_t blip_time_2 = second_buf->center()->count_clocks( pair_count ); + second_buf->end_frame( blip_time_2 ); + assert( second_buf->samples_avail() == pair_count * 2 ); + } + } + + resampler.write( new_count ); + + int count = resampler.read( sample_buf.begin(), sample_buf_size ); + + mix_samples( stereo_buf, out, count, secondary_buf_set, secondary_buf_set_count ); + + pair_count = count >> 1; + stereo_buf.left()->remove_samples( pair_count ); + stereo_buf.right()->remove_samples( pair_count ); + stereo_buf.center()->remove_samples( pair_count ); + + if ( secondary_buf_set && secondary_buf_set_count ) + { + for ( int i = 0; i < secondary_buf_set_count; i++ ) + { + Stereo_Buffer * second_buf = secondary_buf_set[i]; + second_buf->left()->remove_samples( pair_count ); + second_buf->right()->remove_samples( pair_count ); + second_buf->center()->remove_samples( pair_count ); + } + } + + return count; +} + +void Dual_Resampler::dual_play( int count, dsample_t out [], Stereo_Buffer& stereo_buf, Stereo_Buffer** secondary_buf_set, int secondary_buf_set_count ) +{ + // empty extra buffer + int remain = buffered - buf_pos; + if ( remain ) + { + if ( remain > count ) + remain = count; + count -= remain; + memcpy( out, &sample_buf [buf_pos], remain * sizeof *out ); + out += remain; + buf_pos += remain; + } + + // entire frames + while ( count >= sample_buf_size ) + { + buf_pos = buffered = play_frame_( stereo_buf, out, secondary_buf_set, secondary_buf_set_count ); + out += buffered; + count -= buffered; + } + + while (count > 0) + { + buffered = play_frame_( stereo_buf, sample_buf.begin(), secondary_buf_set, secondary_buf_set_count ); + if ( buffered >= count ) + { + buf_pos = count; + memcpy( out, sample_buf.begin(), count * sizeof *out ); + out += count; + count = 0; + } + else + { + memcpy( out, sample_buf.begin(), buffered * sizeof *out ); + out += buffered; + count -= buffered; + } + } +} + +void Dual_Resampler::mix_samples( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count, Stereo_Buffer** secondary_buf_set, int secondary_buf_set_count ) +{ + // lol hax + if ( ((Tracked_Blip_Buffer*)stereo_buf.left())->non_silent() | ((Tracked_Blip_Buffer*)stereo_buf.right())->non_silent() ) + mix_stereo( stereo_buf, out_, count ); + else + mix_mono( stereo_buf, out_, count ); + + if ( secondary_buf_set && secondary_buf_set_count ) + { + for ( int i = 0; i < secondary_buf_set_count; i++ ) + { + Stereo_Buffer * second_buf = secondary_buf_set[i]; + if ( ((Tracked_Blip_Buffer*)second_buf->left())->non_silent() | ((Tracked_Blip_Buffer*)second_buf->right())->non_silent() ) + mix_extra_stereo( *second_buf, out_, count ); + else + mix_extra_mono( *second_buf, out_, count ); + } + } +} + +void Dual_Resampler::mix_mono( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( sn, *stereo_buf.center() ); + + count >>= 1; + BLIP_READER_ADJ_( sn, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + stereo_dsample_t const* BLARGG_RESTRICT in = + (stereo_dsample_t const*) sample_buf.begin() + count; + int offset = -count; + int const gain = gain_; + do + { + int s = BLIP_READER_READ_RAW( sn ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( sn, bass, offset ); + + int l = (in [offset] [0] * gain >> gain_bits) + s; + int r = (in [offset] [1] * gain >> gain_bits) + s; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( sn, *stereo_buf.center() ); +} + +void Dual_Resampler::mix_stereo( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( snc, *stereo_buf.center() ); + BLIP_READER_BEGIN( snl, *stereo_buf.left() ); + BLIP_READER_BEGIN( snr, *stereo_buf.right() ); + + count >>= 1; + BLIP_READER_ADJ_( snc, count ); + BLIP_READER_ADJ_( snl, count ); + BLIP_READER_ADJ_( snr, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + stereo_dsample_t const* BLARGG_RESTRICT in = + (stereo_dsample_t const*) sample_buf.begin() + count; + int offset = -count; + int const gain = gain_; + do + { + int sc = BLIP_READER_READ_RAW( snc ) >> (blip_sample_bits - 16); + int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16); + int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( snc, bass, offset ); + BLIP_READER_NEXT_IDX_( snl, bass, offset ); + BLIP_READER_NEXT_IDX_( snr, bass, offset ); + + int l = (in [offset] [0] * gain >> gain_bits) + sl + sc; + int r = (in [offset] [1] * gain >> gain_bits) + sr + sc; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( snc, *stereo_buf.center() ); + BLIP_READER_END( snl, *stereo_buf.left() ); + BLIP_READER_END( snr, *stereo_buf.right() ); +} + +void Dual_Resampler::mix_extra_mono( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( sn, *stereo_buf.center() ); + + count >>= 1; + BLIP_READER_ADJ_( sn, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + int offset = -count; + int const gain = gain_; + do + { + int s = BLIP_READER_READ_RAW( sn ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( sn, bass, offset ); + + int l = out [offset] [0] + s; + int r = out [offset] [1] + s; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( sn, *stereo_buf.center() ); +} + +void Dual_Resampler::mix_extra_stereo( Stereo_Buffer& stereo_buf, dsample_t out_ [], int count ) +{ + int const bass = BLIP_READER_BASS( *stereo_buf.center() ); + BLIP_READER_BEGIN( snc, *stereo_buf.center() ); + BLIP_READER_BEGIN( snl, *stereo_buf.left() ); + BLIP_READER_BEGIN( snr, *stereo_buf.right() ); + + count >>= 1; + BLIP_READER_ADJ_( snc, count ); + BLIP_READER_ADJ_( snl, count ); + BLIP_READER_ADJ_( snr, count ); + + typedef dsample_t stereo_dsample_t [2]; + stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count; + int offset = -count; + int const gain = gain_; + do + { + int sc = BLIP_READER_READ_RAW( snc ) >> (blip_sample_bits - 16); + int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16); + int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16); + BLIP_READER_NEXT_IDX_( snc, bass, offset ); + BLIP_READER_NEXT_IDX_( snl, bass, offset ); + BLIP_READER_NEXT_IDX_( snr, bass, offset ); + + int l = out [offset] [0] + sl + sc; + int r = out [offset] [1] + sr + sc; + + BLIP_CLAMP( l, l ); + out [offset] [0] = (blip_sample_t) l; + + BLIP_CLAMP( r, r ); + out [offset] [1] = (blip_sample_t) r; + } + while ( ++offset ); + + BLIP_READER_END( snc, *stereo_buf.center() ); + BLIP_READER_END( snl, *stereo_buf.left() ); + BLIP_READER_END( snr, *stereo_buf.right() ); +} diff --git a/Frameworks/GME/gme/Dual_Resampler.h b/Frameworks/GME/gme/Dual_Resampler.h old mode 100755 new mode 100644 index 61beb8a08..2bdfb4afa --- a/Frameworks/GME/gme/Dual_Resampler.h +++ b/Frameworks/GME/gme/Dual_Resampler.h @@ -1,50 +1,61 @@ -// Combination of Fir_Resampler and Blip_Buffer mixing. Used by Sega FM emulators. +// Combination of Fir_Resampler and Stereo_Buffer mixing. Used by Sega FM emulators. -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef DUAL_RESAMPLER_H #define DUAL_RESAMPLER_H -#include "Fir_Resampler.h" -#include "Blip_Buffer.h" +#include "Multi_Buffer.h" + +#if GME_VGM_FAST_RESAMPLER + #include "Downsampler.h" + typedef Downsampler Dual_Resampler_Downsampler; +#else + #include "Fir_Resampler.h" + typedef Fir_Resampler_Norm Dual_Resampler_Downsampler; +#endif class Dual_Resampler { public: - Dual_Resampler(); - virtual ~Dual_Resampler(); - typedef short dsample_t; - double setup( double oversample, double rolloff, double gain ); + blargg_err_t setup( double oversample, double rolloff, double gain ); + double rate() const { return resampler.rate(); } blargg_err_t reset( int max_pairs ); void resize( int pairs_per_frame ); void clear(); - void dual_play( long count, dsample_t* out, Blip_Buffer& ); + void dual_play( int count, dsample_t out [], Stereo_Buffer&, Stereo_Buffer** secondary_buf_set = NULL, int secondary_buf_set_count = 0 ); -protected: - virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; + blargg_callback set_callback; + +// Implementation +public: + Dual_Resampler(); + ~Dual_Resampler(); + private: - + enum { gain_bits = 14 }; blargg_vector sample_buf; int sample_buf_size; int oversamples_per_frame; int buf_pos; + int buffered; int resampler_size; + int gain_; - Fir_Resampler<12> resampler; - void mix_samples( Blip_Buffer&, dsample_t* ); - void play_frame_( Blip_Buffer&, dsample_t* ); + Dual_Resampler_Downsampler resampler; + void mix_samples( Stereo_Buffer&, dsample_t [], int, Stereo_Buffer**, int ); + void mix_mono( Stereo_Buffer&, dsample_t [], int ); + void mix_stereo( Stereo_Buffer&, dsample_t [], int ); + void mix_extra_mono( Stereo_Buffer&, dsample_t [], int ); + void mix_extra_stereo( Stereo_Buffer&, dsample_t [], int ); + int play_frame_( Stereo_Buffer&, dsample_t [], Stereo_Buffer**, int ); }; -inline double Dual_Resampler::setup( double oversample, double rolloff, double gain ) +inline blargg_err_t Dual_Resampler::setup( double oversample, double rolloff, double gain ) { - return resampler.time_ratio( oversample, rolloff, gain * 0.5 ); -} - -inline void Dual_Resampler::clear() -{ - buf_pos = sample_buf_size; - resampler.clear(); + gain_ = (int) ((1 << gain_bits) * gain); + return resampler.set_rate( oversample ); } #endif diff --git a/Frameworks/GME/gme/Effects_Buffer.cpp b/Frameworks/GME/gme/Effects_Buffer.cpp old mode 100755 new mode 100644 index 730f8e94c..496c00bb0 --- a/Frameworks/GME/gme/Effects_Buffer.cpp +++ b/Frameworks/GME/gme/Effects_Buffer.cpp @@ -1,529 +1,640 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Effects_Buffer.h" + +/* Copyright (C) 2006-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif -#include "Effects_Buffer.h" +int const fixed_shift = 12; +#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift)) +#define FROM_FIXED( f ) ((f) >> fixed_shift) -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -typedef blargg_long fixed_t; - -#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) -#define FMUL( x, y ) (((x) * (y)) >> 15) - -const unsigned echo_size = 4096; -const unsigned echo_mask = echo_size - 1; -BOOST_STATIC_ASSERT( (echo_size & echo_mask) == 0 ); // must be power of 2 - -const unsigned reverb_size = 8192 * 2; -const unsigned reverb_mask = reverb_size - 1; -BOOST_STATIC_ASSERT( (reverb_size & reverb_mask) == 0 ); // must be power of 2 - -Effects_Buffer::config_t::config_t() -{ - pan_1 = -0.15f; - pan_2 = 0.15f; - reverb_delay = 88.0f; - reverb_level = 0.12f; - echo_delay = 61.0f; - echo_level = 0.10f; - delay_variance = 18.0f; - effects_enabled = false; -} - -void Effects_Buffer::set_depth( double d ) -{ - float f = (float) d; - config_t c; - c.pan_1 = -0.6f * f; - c.pan_2 = 0.6f * f; - c.reverb_delay = 880 * 0.1f; - c.echo_delay = 610 * 0.1f; - if ( f > 0.5 ) - f = 0.5; // TODO: more linear reduction of extreme reverb/echo - c.reverb_level = 0.5f * f; - c.echo_level = 0.30f * f; - c.delay_variance = 180 * 0.1f; - c.effects_enabled = (d > 0.0f); - config( c ); -} - -Effects_Buffer::Effects_Buffer( bool center_only ) : Multi_Buffer( 2 ) -{ - buf_count = center_only ? max_buf_count - 4 : max_buf_count; - - echo_pos = 0; - reverb_pos = 0; - - stereo_remain = 0; - effect_remain = 0; - effects_enabled = false; - set_depth( 0 ); -} - -Effects_Buffer::~Effects_Buffer() { } - -blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) -{ - if ( !echo_buf.size() ) - RETURN_ERR( echo_buf.resize( echo_size ) ); - - if ( !reverb_buf.size() ) - RETURN_ERR( reverb_buf.resize( reverb_size ) ); - - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - - config( config_ ); - clear(); - - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Effects_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Effects_Buffer::bass_freq( int freq ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( freq ); -} - -void Effects_Buffer::clear() -{ - stereo_remain = 0; - effect_remain = 0; - if ( echo_buf.size() ) - memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); - - if ( reverb_buf.size() ) - memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); - - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -inline int pin_range( int n, int max, int min = 0 ) -{ - if ( n < min ) - return min; - if ( n > max ) - return max; - return n; -} - -void Effects_Buffer::config( const config_t& cfg ) -{ - channels_changed(); - - // clear echo and reverb buffers - if ( !config_.effects_enabled && cfg.effects_enabled && echo_buf.size() ) - { - memset( &echo_buf [0], 0, echo_size * sizeof echo_buf [0] ); - memset( &reverb_buf [0], 0, reverb_size * sizeof reverb_buf [0] ); - } - - config_ = cfg; - - if ( config_.effects_enabled ) - { - // convert to internal format - - chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); - chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0]; - - chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 ); - chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; - - chans.reverb_level = TO_FIXED( config_.reverb_level ); - chans.echo_level = TO_FIXED( config_.echo_level ); - - int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); - - int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate()); - chans.reverb_delay_l = pin_range( reverb_size - - (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); - chans.reverb_delay_r = pin_range( reverb_size + 1 - - (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); - - int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); - chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset), - echo_size - 1 ); - chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), - echo_size - 1 ); - - chan_types [0].center = &bufs [0]; - chan_types [0].left = &bufs [3]; - chan_types [0].right = &bufs [4]; - - chan_types [1].center = &bufs [1]; - chan_types [1].left = &bufs [3]; - chan_types [1].right = &bufs [4]; - - chan_types [2].center = &bufs [2]; - chan_types [2].left = &bufs [5]; - chan_types [2].right = &bufs [6]; - assert( 2 < chan_types_count ); - } - else - { - // set up outputs - for ( unsigned i = 0; i < chan_types_count; i++ ) - { - channel_t& c = chan_types [i]; - c.center = &bufs [0]; - c.left = &bufs [1]; - c.right = &bufs [2]; - } - } - - if ( buf_count < max_buf_count ) - { - for ( int i = 0; i < chan_types_count; i++ ) - { - channel_t& c = chan_types [i]; - c.left = c.center; - c.right = c.center; - } - } -} - -Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type ) -{ - int out = 2; - if ( !type ) - { - out = i % 5; - if ( out > 2 ) - out = 2; - } - else if ( !(type & noise_type) && (type & type_index_mask) % 3 != 0 ) - { - out = type & 1; - } - return chan_types [out]; -} - -void Effects_Buffer::end_frame( blip_time_t clock_count ) -{ - int bufs_used = 0; - for ( int i = 0; i < buf_count; i++ ) - { - bufs_used |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); - } - - int stereo_mask = (config_.effects_enabled ? 0x78 : 0x06); - if ( (bufs_used & stereo_mask) && buf_count == max_buf_count ) - stereo_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - if ( effects_enabled || config_.effects_enabled ) - effect_remain = bufs [0].samples_avail() + bufs [0].output_latency(); - - effects_enabled = config_.effects_enabled; -} - -long Effects_Buffer::samples_avail() const -{ - return bufs [0].samples_avail() * 2; -} - -long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) -{ - require( total_samples % 2 == 0 ); // count must be even - - long remain = bufs [0].samples_avail(); - if ( remain > (total_samples >> 1) ) - remain = (total_samples >> 1); - total_samples = remain; - while ( remain ) - { - int active_bufs = buf_count; - long count = remain; - - // optimizing mixing to skip any channels which had nothing added - if ( effect_remain ) - { - if ( count > effect_remain ) - count = effect_remain; - - if ( stereo_remain ) - { - mix_enhanced( out, count ); - } - else - { - mix_mono_enhanced( out, count ); - active_bufs = 3; - } - } - else if ( stereo_remain ) - { - mix_stereo( out, count ); - active_bufs = 3; - } - else - { - mix_mono( out, count ); - active_bufs = 1; - } - - out += count * 2; - remain -= count; - - stereo_remain -= count; - if ( stereo_remain < 0 ) - stereo_remain = 0; - - effect_remain -= count; - if ( effect_remain < 0 ) - effect_remain = 0; - - for ( int i = 0; i < buf_count; i++ ) - { - if ( i < active_bufs ) - bufs [i].remove_samples( count ); - else - bufs [i].remove_silence( count ); // keep time synchronized - } - } - - return total_samples * 2; -} - -void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [0] ); - BLIP_READER_BEGIN( c, bufs [0] ); - - // unrolled loop - for ( blargg_long n = count >> 1; n; --n ) - { - blargg_long cs0 = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - - blargg_long cs1 = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - - if ( (BOOST::int16_t) cs0 != cs0 ) - cs0 = 0x7FFF - (cs0 >> 24); - ((BOOST::uint32_t*) out) [0] = ((BOOST::uint16_t) cs0) | (cs0 << 16); - - if ( (BOOST::int16_t) cs1 != cs1 ) - cs1 = 0x7FFF - (cs1 >> 24); - ((BOOST::uint32_t*) out) [1] = ((BOOST::uint16_t) cs1) | (cs1 << 16); - out += 4; - } - - if ( count & 1 ) - { - int s = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - out [0] = s; - out [1] = s; - if ( (BOOST::int16_t) s != s ) - { - s = 0x7FFF - (s >> 24); - out [0] = s; - out [1] = s; - } - } - - BLIP_READER_END( c, bufs [0] ); -} - -void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [0] ); - BLIP_READER_BEGIN( c, bufs [0] ); - BLIP_READER_BEGIN( l, bufs [1] ); - BLIP_READER_BEGIN( r, bufs [2] ); - - while ( count-- ) - { - int cs = BLIP_READER_READ( c ); - BLIP_READER_NEXT( c, bass ); - int left = cs + BLIP_READER_READ( l ); - int right = cs + BLIP_READER_READ( r ); - BLIP_READER_NEXT( l, bass ); - BLIP_READER_NEXT( r, bass ); - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - - BLIP_READER_END( r, bufs [2] ); - BLIP_READER_END( l, bufs [1] ); - BLIP_READER_END( c, bufs [0] ); -} - -void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [2] ); - BLIP_READER_BEGIN( center, bufs [2] ); - BLIP_READER_BEGIN( sq1, bufs [0] ); - BLIP_READER_BEGIN( sq2, bufs [1] ); - - blip_sample_t* const reverb_buf = this->reverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - while ( count-- ) - { - int sum1_s = BLIP_READER_READ( sq1 ); - int sum2_s = BLIP_READER_READ( sq2 ); - - BLIP_READER_NEXT( sq1, bass ); - BLIP_READER_NEXT( sq2, bass ); - - int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + - FMUL( sum2_s, chans.pan_2_levels [0] ) + - reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - - int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + - FMUL( sum2_s, chans.pan_2_levels [1] ) + - reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - - fixed_t reverb_level = chans.reverb_level; - reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); - reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); - reverb_pos = (reverb_pos + 2) & reverb_mask; - - int sum3_s = BLIP_READER_READ( center ); - BLIP_READER_NEXT( center, bass ); - - int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); - int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - - echo_buf [echo_pos] = sum3_s; - echo_pos = (echo_pos + 1) & echo_mask; - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); -} - -void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [2] ); - BLIP_READER_BEGIN( center, bufs [2] ); - BLIP_READER_BEGIN( l1, bufs [3] ); - BLIP_READER_BEGIN( r1, bufs [4] ); - BLIP_READER_BEGIN( l2, bufs [5] ); - BLIP_READER_BEGIN( r2, bufs [6] ); - BLIP_READER_BEGIN( sq1, bufs [0] ); - BLIP_READER_BEGIN( sq2, bufs [1] ); - - blip_sample_t* const reverb_buf = this->reverb_buf.begin(); - blip_sample_t* const echo_buf = this->echo_buf.begin(); - int echo_pos = this->echo_pos; - int reverb_pos = this->reverb_pos; - - while ( count-- ) - { - int sum1_s = BLIP_READER_READ( sq1 ); - int sum2_s = BLIP_READER_READ( sq2 ); - - BLIP_READER_NEXT( sq1, bass ); - BLIP_READER_NEXT( sq2, bass ); - - int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + - FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + - reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - - int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + - FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + - reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - - BLIP_READER_NEXT( l1, bass ); - BLIP_READER_NEXT( r1, bass ); - - fixed_t reverb_level = chans.reverb_level; - reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); - reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); - reverb_pos = (reverb_pos + 2) & reverb_mask; - - int sum3_s = BLIP_READER_READ( center ); - BLIP_READER_NEXT( center, bass ); - - int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); - int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level, - echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - - BLIP_READER_NEXT( l2, bass ); - BLIP_READER_NEXT( r2, bass ); - - echo_buf [echo_pos] = sum3_s; - echo_pos = (echo_pos + 1) & echo_mask; - - if ( (BOOST::int16_t) left != left ) - left = 0x7FFF - (left >> 24); - - out [0] = left; - out [1] = right; - - out += 2; - - if ( (BOOST::int16_t) right != right ) - out [-1] = 0x7FFF - (right >> 24); - } - this->reverb_pos = reverb_pos; - this->echo_pos = echo_pos; - - BLIP_READER_END( l1, bufs [3] ); - BLIP_READER_END( r1, bufs [4] ); - BLIP_READER_END( l2, bufs [5] ); - BLIP_READER_END( r2, bufs [6] ); - BLIP_READER_END( sq1, bufs [0] ); - BLIP_READER_END( sq2, bufs [1] ); - BLIP_READER_END( center, bufs [2] ); -} +int const max_read = 2560; // determines minimum delay +Effects_Buffer::Effects_Buffer( int max_bufs, int echo_size_ ) : Multi_Buffer( stereo ) +{ + echo_size = max( max_read * (int) stereo, echo_size_ & ~1 ); + clock_rate_ = 0; + bass_freq_ = 90; + bufs = NULL; + bufs_size = 0; + bufs_max = max( max_bufs, (int) extra_chans ); + no_echo = true; + no_effects = true; + + // defaults + config_.enabled = false; + config_.delay [0] = 120; + config_.delay [1] = 122; + config_.feedback = 0.2f; + config_.treble = 0.4f; + + static float const sep = 0.8f; + config_.side_chans [0].pan = -sep; + config_.side_chans [1].pan = +sep; + config_.side_chans [0].vol = 1.0f; + config_.side_chans [1].vol = 1.0f; + + memset( &s, 0, sizeof s ); + clear(); +} + +Effects_Buffer::~Effects_Buffer() +{ + delete_bufs(); +} + +// avoid using new [] +blargg_err_t Effects_Buffer::new_bufs( int size ) +{ + bufs = (buf_t*) malloc( size * sizeof *bufs ); + CHECK_ALLOC( bufs ); + for ( int i = 0; i < size; i++ ) + new (bufs + i) buf_t; + bufs_size = size; + return blargg_ok; +} + +void Effects_Buffer::delete_bufs() +{ + if ( bufs ) + { + for ( int i = bufs_size; --i >= 0; ) + bufs [i].~buf_t(); + free( bufs ); + bufs = NULL; + } + bufs_size = 0; +} + +blargg_err_t Effects_Buffer::set_sample_rate( int rate, int msec ) +{ + // extra to allow farther past-the-end pointers + mixer.samples_read = 0; + RETURN_ERR( echo.resize( echo_size + stereo ) ); + return Multi_Buffer::set_sample_rate( rate, msec ); +} + +void Effects_Buffer::clock_rate( int rate ) +{ + clock_rate_ = rate; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( clock_rate_ ); +} + +void Effects_Buffer::bass_freq( int freq ) +{ + bass_freq_ = freq; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass_freq_ ); +} + +blargg_err_t Effects_Buffer::set_channel_count( int count, int const types [] ) +{ + RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) ); + + delete_bufs(); + + mixer.samples_read = 0; + + RETURN_ERR( chans.resize( count + extra_chans ) ); + + RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) ); + + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) ); + + for ( int i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.cfg.vol = 1.0f; + ch.cfg.pan = 0.0f; + ch.cfg.surround = false; + ch.cfg.echo = false; + } + // side channels with echo + chans [2].cfg.echo = true; + chans [3].cfg.echo = true; + + clock_rate( clock_rate_ ); + bass_freq( bass_freq_ ); + apply_config(); + clear(); + + return blargg_ok; +} + +void Effects_Buffer::clear_echo() +{ + if ( echo.size() ) + memset( echo.begin(), 0, echo.size() * sizeof echo [0] ); +} + +void Effects_Buffer::clear() +{ + echo_pos = 0; + s.low_pass [0] = 0; + s.low_pass [1] = 0; + mixer.samples_read = 0; + + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); + clear_echo(); +} + +Effects_Buffer::channel_t Effects_Buffer::channel( int i ) +{ + i += extra_chans; + require( extra_chans <= i && i < (int) chans.size() ); + return chans [i].channel; +} + + +// Configuration + +// 3 wave positions with/without surround, 2 multi (one with same config as wave) +int const simple_bufs = 3 * 2 + 2 - 1; + +Simple_Effects_Buffer::Simple_Effects_Buffer() : + Effects_Buffer( extra_chans + simple_bufs, 18 * 1024 ) +{ + config_.echo = 0.20f; + config_.stereo = 0.20f; + config_.surround = true; + config_.enabled = false; +} + +void Simple_Effects_Buffer::apply_config() +{ + Effects_Buffer::config_t& c = Effects_Buffer::config(); + + c.enabled = config_.enabled; + if ( c.enabled ) + { + c.delay [0] = 120; + c.delay [1] = 122; + c.feedback = config_.echo * 0.7f; + c.treble = 0.6f - 0.3f * config_.echo; + + float sep = config_.stereo + 0.80f; + if ( sep > 1.0f ) + sep = 1.0f; + + c.side_chans [0].pan = -sep; + c.side_chans [1].pan = +sep; + + for ( int i = channel_count(); --i >= 0; ) + { + chan_config_t& ch = Effects_Buffer::chan_config( i ); + + ch.pan = 0.0f; + ch.surround = config_.surround; + ch.echo = false; + + int const type = (channel_types() ? channel_types() [i] : 0); + if ( !(type & noise_type) ) + { + int index = (type & type_index_mask) % 6 - 3; + if ( index < 0 ) + { + index += 3; + ch.surround = false; + ch.echo = true; + } + if ( index >= 1 ) + { + ch.pan = config_.stereo; + if ( index == 1 ) + ch.pan = -ch.pan; + } + } + else if ( type & 1 ) + { + ch.surround = false; + } + } + } + + Effects_Buffer::apply_config(); +} + +int Effects_Buffer::min_delay() const +{ + require( sample_rate() ); + return max_read * 1000 / sample_rate(); +} + +int Effects_Buffer::max_delay() const +{ + require( sample_rate() ); + return (echo_size / stereo - max_read) * 1000 / sample_rate(); +} + +void Effects_Buffer::apply_config() +{ + int i; + + if ( !bufs_size ) + return; + + s.treble = TO_FIXED( config_.treble ); + + bool echo_dirty = false; + + fixed_t old_feedback = s.feedback; + s.feedback = TO_FIXED( config_.feedback ); + if ( !old_feedback && s.feedback ) + echo_dirty = true; + + // delays + for ( i = stereo; --i >= 0; ) + { + int delay = config_.delay [i] * sample_rate() / 1000 * stereo; + delay = max( delay, (int) (max_read * stereo) ); + delay = min( delay, (int) (echo_size - max_read * stereo) ); + if ( s.delay [i] != delay ) + { + s.delay [i] = delay; + echo_dirty = true; + } + } + + // side channels + for ( i = 2; --i >= 0; ) + { + chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f; + chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan; + } + + // convert volumes + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan ); + ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan ); + if ( ch.cfg.surround ) + ch.vol [0] = -ch.vol [0]; + } + + assign_buffers(); + + // set side channels + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.left = chans [ch.cfg.echo*2 ].channel.center; + ch.channel.right = chans [ch.cfg.echo*2+1].channel.center; + } + + bool old_echo = !no_echo && !no_effects; + + // determine whether effects and echo are needed at all + no_effects = true; + no_echo = true; + for ( i = chans.size(); --i >= extra_chans; ) + { + chan_t& ch = chans [i]; + if ( ch.cfg.echo && s.feedback ) + no_echo = false; + + if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + } + if ( !no_echo ) + no_effects = false; + + if ( chans [0].vol [0] != TO_FIXED( 1 ) || + chans [0].vol [1] != TO_FIXED( 0 ) || + chans [1].vol [0] != TO_FIXED( 0 ) || + chans [1].vol [1] != TO_FIXED( 1 ) ) + no_effects = false; + + if ( !config_.enabled ) + no_effects = true; + + if ( no_effects ) + { + for ( i = chans.size(); --i >= 0; ) + { + chan_t& ch = chans [i]; + ch.channel.center = &bufs [2]; + ch.channel.left = &bufs [0]; + ch.channel.right = &bufs [1]; + } + } + + mixer.bufs [0] = &bufs [0]; + mixer.bufs [1] = &bufs [1]; + mixer.bufs [2] = &bufs [2]; + + if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) ) + clear_echo(); + + channels_changed(); +} + +void Effects_Buffer::assign_buffers() +{ + // assign channels to buffers + int buf_count = 0; + for ( int i = 0; i < (int) chans.size(); i++ ) + { + // put second two side channels at end to give priority to main channels + // in case closest matching is necessary + int x = i; + if ( i > 1 ) + x += 2; + if ( x >= (int) chans.size() ) + x -= (chans.size() - 2); + chan_t& ch = chans [x]; + + int b = 0; + for ( ; b < buf_count; b++ ) + { + if ( ch.vol [0] == bufs [b].vol [0] && + ch.vol [1] == bufs [b].vol [1] && + (ch.cfg.echo == bufs [b].echo || !s.feedback) ) + break; + } + + if ( b >= buf_count ) + { + if ( buf_count < bufs_max ) + { + bufs [b].vol [0] = ch.vol [0]; + bufs [b].vol [1] = ch.vol [1]; + bufs [b].echo = ch.cfg.echo; + buf_count++; + } + else + { + // TODO: this is a mess, needs refinement + dprintf( "Effects_Buffer ran out of buffers; using closest match\n" ); + b = 0; + fixed_t best_dist = TO_FIXED( 8 ); + for ( int h = buf_count; --h >= 0; ) + { + #define CALC_LEVELS( vols, sum, diff, surround ) \ + fixed_t sum, diff;\ + bool surround = false;\ + {\ + fixed_t vol_0 = vols [0];\ + if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\ + fixed_t vol_1 = vols [1];\ + if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\ + sum = vol_0 + vol_1;\ + diff = vol_0 - vol_1;\ + } + CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround ); + CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround ); + + fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff ); + + if ( ch_surround != buf_surround ) + dist += TO_FIXED( 1 ) / 2; + + if ( s.feedback && ch.cfg.echo != bufs [h].echo ) + dist += TO_FIXED( 1 ) / 2; + + if ( best_dist > dist ) + { + best_dist = dist; + b = h; + } + } + } + } + + //dprintf( "ch %d->buf %d\n", x, b ); + ch.channel.center = &bufs [b]; + } +} + + +// Mixing + +void Effects_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +int Effects_Buffer::read_samples( blip_sample_t out [], int out_size ) +{ + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + require( pair_count * stereo == out_size ); // must read an even number of samples + if ( pair_count ) + { + if ( no_effects ) + { + mixer.read_pairs( out, pair_count ); + } + else + { + int pairs_remain = pair_count; + do + { + // mix at most max_read pairs at a time + int count = max_read; + if ( count > pairs_remain ) + count = pairs_remain; + + if ( no_echo ) + { + // optimization: clear echo here to keep mix_effects() a leaf function + echo_pos = 0; + memset( echo.begin(), 0, count * stereo * sizeof echo [0] ); + } + mix_effects( out, count ); + + int new_echo_pos = echo_pos + count * stereo; + if ( new_echo_pos >= echo_size ) + new_echo_pos -= echo_size; + echo_pos = new_echo_pos; + assert( echo_pos < echo_size ); + + out += count * stereo; + mixer.samples_read += count; + pairs_remain -= count; + } + while ( pairs_remain ); + } + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( b.non_silent() ) + b.remove_samples( mixer.samples_read ); + else + b.remove_silence( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + +void Effects_Buffer::mix_effects( blip_sample_t out_ [], int pair_count ) +{ + typedef fixed_t stereo_fixed_t [stereo]; + + // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output + int echo_phase = 1; + do + { + // mix any modified buffers + { + buf_t* buf = bufs; + int bufs_remain = bufs_size; + do + { + if ( buf->non_silent() && buf->echo == echo_phase ) + { + stereo_fixed_t* BLARGG_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos]; + int const bass = BLIP_READER_BASS( *buf ); + BLIP_READER_BEGIN( in, *buf ); + BLIP_READER_ADJ_( in, mixer.samples_read ); + fixed_t const vol_0 = buf->vol [0]; + fixed_t const vol_1 = buf->vol [1]; + + int count = (unsigned) (echo_size - echo_pos) / stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + BLIP_READER_ADJ_( in, count ); + + out += count; + int offset = -count; + do + { + fixed_t s = BLIP_READER_READ( in ); + BLIP_READER_NEXT_IDX_( in, bass, offset ); + + out [offset] [0] += s * vol_0; + out [offset] [1] += s * vol_1; + } + while ( ++offset ); + + out = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + + BLIP_READER_END( in, *buf ); + } + buf++; + } + while ( --bufs_remain ); + } + + // add echo + if ( echo_phase && !no_echo ) + { + fixed_t const feedback = s.feedback; + fixed_t const treble = s.treble; + + int i = 1; + do + { + fixed_t low_pass = s.low_pass [i]; + + fixed_t* echo_end = &echo [echo_size + i]; + fixed_t const* BLARGG_RESTRICT in_pos = &echo [echo_pos + i]; + int out_offset = echo_pos + i + s.delay [i]; + if ( out_offset >= echo_size ) + out_offset -= echo_size; + assert( out_offset < echo_size ); + fixed_t* BLARGG_RESTRICT out_pos = &echo [out_offset]; + + // break into up to three chunks to avoid having to handle wrap-around + // in middle of core loop + int remain = pair_count; + do + { + fixed_t const* pos = in_pos; + if ( pos < out_pos ) + pos = out_pos; + int count = (unsigned) ((char*) echo_end - (char const*) pos) / + (unsigned) (stereo * sizeof (fixed_t)); + if ( count > remain ) + count = remain; + remain -= count; + + in_pos += count * stereo; + out_pos += count * stereo; + int offset = -count; + do + { + low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble; + out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback; + } + while ( ++offset ); + + if ( in_pos >= echo_end ) in_pos -= echo_size; + if ( out_pos >= echo_end ) out_pos -= echo_size; + } + while ( remain ); + + s.low_pass [i] = low_pass; + } + while ( --i >= 0 ); + } + } + while ( --echo_phase >= 0 ); + + // clamp to 16 bits + { + stereo_fixed_t const* BLARGG_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos]; + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_; + int count = (unsigned) (echo_size - echo_pos) / (unsigned) stereo; + int remain = pair_count; + if ( count > remain ) + count = remain; + do + { + remain -= count; + in += count; + out += count; + int offset = -count; + do + { + fixed_t in_0 = FROM_FIXED( in [offset] [0] ); + fixed_t in_1 = FROM_FIXED( in [offset] [1] ); + + BLIP_CLAMP( in_0, in_0 ); + out [offset] [0] = (blip_sample_t) in_0; + + BLIP_CLAMP( in_1, in_1 ); + out [offset] [1] = (blip_sample_t) in_1; + } + while ( ++offset ); + + in = (stereo_fixed_t*) echo.begin(); + count = remain; + } + while ( remain ); + } +} diff --git a/Frameworks/GME/gme/Effects_Buffer.h b/Frameworks/GME/gme/Effects_Buffer.h old mode 100755 new mode 100644 index eb0aa67a3..cd6c620f8 --- a/Frameworks/GME/gme/Effects_Buffer.h +++ b/Frameworks/GME/gme/Effects_Buffer.h @@ -1,86 +1,149 @@ -// Multi-channel effects buffer with panning, echo and reverb +// Multi-channel effects buffer with echo and individual panning for each channel -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef EFFECTS_BUFFER_H #define EFFECTS_BUFFER_H #include "Multi_Buffer.h" -// Effects_Buffer uses several buffers and outputs stereo sample pairs. +// See Simple_Effects_Buffer (below) for a simpler interface + class Effects_Buffer : public Multi_Buffer { public: - // If center_only is true, only center buffers are created and - // less memory is used. - Effects_Buffer( bool center_only = false ); + // To reduce memory usage, fewer buffers can be used (with a best-fit + // approach if there are too few), and maximum echo delay can be reduced + Effects_Buffer( int max_bufs = 32, int echo_size = 24 * 1024 ); - // Channel Effect Center Pan - // --------------------------------- - // 0,5 reverb pan_1 - // 1,6 reverb pan_2 - // 2,7 echo - - // 3 echo - - // 4 echo - - - // Channel configuration - struct config_t { - double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right - double pan_2; - double echo_delay; // msec - double echo_level; // 0.0 to 1.0 - double reverb_delay; // msec - double delay_variance; // difference between left/right delays (msec) - double reverb_level; // 0.0 to 1.0 - bool effects_enabled; // if false, use optimized simple mixer - config_t(); + struct pan_vol_t + { + float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal + float pan; // -1.0 = left, 0.0 = center, +1.0 = right }; - // Set configuration of buffer - virtual void config( const config_t& ); - void set_depth( double ); + // Global configuration + struct config_t + { + bool enabled; // false = disable all effects + + // Current sound is echoed at adjustable left/right delay, + // with reduced treble and volume (feedback). + float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent + int delay [2]; // left, right delays (msec) + float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony + pan_vol_t side_chans [2]; // left and right side channel volume and pan + }; + config_t& config() { return config_; } + // Limits of delay (msec) + int min_delay() const; + int max_delay() const; + + // Per-channel configuration. Two or more channels with matching parameters are + // optimized to internally use the same buffer. + struct chan_config_t : pan_vol_t + { + // (inherited from pan_vol_t) + //float vol; // these only affect center channel + //float pan; + bool surround; // if true, negates left volume to put sound in back + bool echo; // false = channel doesn't have any echo + }; + chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; } + + // Applies any changes made to config() and chan_config() + virtual void apply_config(); + +// Implementation public: ~Effects_Buffer(); - blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); - void clock_rate( long ); + blargg_err_t set_sample_rate( int samples_per_sec, int msec = blip_default_length ); + blargg_err_t set_channel_count( int, int const* = NULL ); + void clock_rate( int ); void bass_freq( int ); void clear(); - channel_t channel( int, int ); + channel_t channel( int ); void end_frame( blip_time_t ); - long read_samples( blip_sample_t*, long ); - long samples_avail() const; + int read_samples( blip_sample_t [], int ); + int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + enum { stereo = 2 }; + typedef int fixed_t; + +protected: + enum { extra_chans = stereo * stereo }; + private: - typedef long fixed_t; - - enum { max_buf_count = 7 }; - Blip_Buffer bufs [max_buf_count]; - enum { chan_types_count = 3 }; - channel_t chan_types [3]; config_t config_; - long stereo_remain; - long effect_remain; - int buf_count; - bool effects_enabled; + int clock_rate_; + int bass_freq_; - blargg_vector reverb_buf; - blargg_vector echo_buf; - int reverb_pos; - int echo_pos; + int echo_size; + + struct chan_t + { + fixed_t vol [stereo]; + chan_config_t cfg; + channel_t channel; + }; + blargg_vector chans; + + struct buf_t : Tracked_Blip_Buffer + { + // nasty: Blip_Buffer has something called fixed_t + Effects_Buffer::fixed_t vol [stereo]; + bool echo; + + void* operator new ( size_t, void* p ) { return p; } + void operator delete ( void* ) { } + + ~buf_t() { } + }; + buf_t* bufs; + int bufs_size; + int bufs_max; // bufs_size <= bufs_max, to limit memory usage + Stereo_Mixer mixer; struct { - fixed_t pan_1_levels [2]; - fixed_t pan_2_levels [2]; - int echo_delay_l; - int echo_delay_r; - fixed_t echo_level; - int reverb_delay_l; - int reverb_delay_r; - fixed_t reverb_level; - } chans; + int delay [stereo]; + fixed_t treble; + fixed_t feedback; + fixed_t low_pass [stereo]; + } s; - void mix_mono( blip_sample_t*, blargg_long ); - void mix_stereo( blip_sample_t*, blargg_long ); - void mix_enhanced( blip_sample_t*, blargg_long ); - void mix_mono_enhanced( blip_sample_t*, blargg_long ); + blargg_vector echo; + int echo_pos; + + bool no_effects; + bool no_echo; + + void assign_buffers(); + void clear_echo(); + void mix_effects( blip_sample_t out [], int pair_count ); + blargg_err_t new_bufs( int size ); + void delete_bufs(); +}; + +// Simpler interface and lower memory usage +class Simple_Effects_Buffer : public Effects_Buffer { +public: + struct config_t + { + bool enabled; // false = disable all effects + + float echo; // 0.0 = none, 1.0 = lots + float stereo; // 0.0 = channels in center, 1.0 = channels on left/right + bool surround; // true = put some channels in back + }; + config_t& config() { return config_; } + + // Applies any changes made to config() + void apply_config(); + +// Implementation +public: + Simple_Effects_Buffer(); +private: + config_t config_; + void chan_config(); // hide }; #endif diff --git a/Frameworks/GME/gme/Fir_Resampler.cpp b/Frameworks/GME/gme/Fir_Resampler.cpp old mode 100755 new mode 100644 index 4e0a46313..5dddf29b6 --- a/Frameworks/GME/gme/Fir_Resampler.cpp +++ b/Frameworks/GME/gme/Fir_Resampler.cpp @@ -1,13 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Fir_Resampler.h" -#include -#include -#include #include -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +/* Copyright (C) 2004-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -52,148 +49,75 @@ static void gen_sinc( double rolloff, int width, double offset, double spacing, } } -Fir_Resampler_::Fir_Resampler_( int width, sample_t* impulses_ ) : +Fir_Resampler_::Fir_Resampler_( int width, sample_t impulses_ [] ) : width_( width ), - write_offset( width * stereo - stereo ), impulses( impulses_ ) { - write_pos = 0; - res = 1; - imp_phase = 0; - skip_bits = 0; - step = stereo; - ratio_ = 1.0; + imp = NULL; } -Fir_Resampler_::~Fir_Resampler_() { } - -void Fir_Resampler_::clear() +void Fir_Resampler_::clear_() { - imp_phase = 0; - if ( buf.size() ) - { - write_pos = &buf [write_offset]; - memset( buf.begin(), 0, write_offset * sizeof buf [0] ); - } + imp = impulses; + Resampler::clear_(); } -blargg_err_t Fir_Resampler_::buffer_size( int new_size ) +blargg_err_t Fir_Resampler_::set_rate_( double new_factor ) { - RETURN_ERR( buf.resize( new_size + write_offset ) ); - clear(); - return 0; -} + double const rolloff = 0.999; + double const gain = 1.0; -double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) -{ - ratio_ = new_factor; - - double fstep = 0.0; + // determine number of sub-phases that yield lowest error + double ratio_ = 0.0; + int res = -1; { double least_error = 2; double pos = 0; - res = -1; for ( int r = 1; r <= max_res; r++ ) { - pos += ratio_; + pos += new_factor; double nearest = floor( pos + 0.5 ); double error = fabs( pos - nearest ); if ( error < least_error ) { res = r; - fstep = nearest / res; + ratio_ = nearest / res; least_error = error; } } } + RETURN_ERR( Resampler::set_rate_( ratio_ ) ); - skip_bits = 0; + // how much of input is used for each output sample + int const step = stereo * (int) floor( ratio_ ); + double fraction = fmod( ratio_, 1.0 ); - step = stereo * (int) floor( fstep ); - - ratio_ = fstep; - fstep = fmod( fstep, 1.0 ); - - double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; + double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; double pos = 0.0; - input_per_cycle = 0; - for ( int i = 0; i < res; i++ ) + //int input_per_cycle = 0; + sample_t* out = impulses; + for ( int n = res; --n >= 0; ) { gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, - double (0x7FFF * gain * filter), - (int) width_, impulses + i * width_ ); + double (0x7FFF * gain * filter), (int) width_, out ); + out += width_; - pos += fstep; - input_per_cycle += step; + int cur_step = step; + pos += fraction; if ( pos >= 0.9999999 ) { pos -= 1.0; - skip_bits |= 1 << i; - input_per_cycle++; + cur_step += stereo; } + + *out++ = (cur_step - width_ * 2 + 4) * sizeof (sample_t); + *out++ = 4 * sizeof (sample_t); + //input_per_cycle += cur_step; } + // last offset moves back to beginning of impulses + out [-1] -= (char*) out - (char*) impulses; - clear(); + imp = impulses; - return ratio_; -} - -int Fir_Resampler_::input_needed( blargg_long output_count ) const -{ - blargg_long input_count = 0; - - unsigned long skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - while ( (output_count -= 2) > 0 ) - { - input_count += step + (skip & 1) * stereo; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count -= 2; - } - - long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); - if ( input_extra < 0 ) - input_extra = 0; - return input_extra; -} - -int Fir_Resampler_::avail_( blargg_long input_count ) const -{ - int cycle_count = input_count / input_per_cycle; - int output_count = cycle_count * res * stereo; - input_count -= cycle_count * input_per_cycle; - - blargg_ulong skip = skip_bits >> imp_phase; - int remain = res - imp_phase; - while ( input_count >= 0 ) - { - input_count -= step + (skip & 1) * stereo; - skip >>= 1; - if ( !--remain ) - { - skip = skip_bits; - remain = res; - } - output_count += 2; - } - return output_count; -} - -int Fir_Resampler_::skip_input( long count ) -{ - int remain = write_pos - buf.begin(); - int max_count = remain - width_ * stereo; - if ( count > max_count ) - count = max_count; - - remain -= count; - write_pos = &buf [remain]; - memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); - - return count; + return blargg_ok; } diff --git a/Frameworks/GME/gme/Fir_Resampler.h b/Frameworks/GME/gme/Fir_Resampler.h old mode 100755 new mode 100644 index 339dfce37..2344493c1 --- a/Frameworks/GME/gme/Fir_Resampler.h +++ b/Frameworks/GME/gme/Fir_Resampler.h @@ -1,171 +1,101 @@ // Finite impulse response (FIR) resampler with adjustable FIR size -// Game_Music_Emu 0.5.2 +// $package #ifndef FIR_RESAMPLER_H #define FIR_RESAMPLER_H -#include "blargg_common.h" -#include +#include "Resampler.h" + +template +class Fir_Resampler; + +// Use one of these typedefs +typedef Fir_Resampler< 8> Fir_Resampler_Fast; +typedef Fir_Resampler<16> Fir_Resampler_Norm; +typedef Fir_Resampler<24> Fir_Resampler_Good; + +// Implementation +class Fir_Resampler_ : public Resampler { +protected: + virtual blargg_err_t set_rate_( double ); + virtual void clear_(); -class Fir_Resampler_ { -public: - - // Use Fir_Resampler (below) - - // Set input/output resampling ratio and optionally low-pass rolloff and gain. - // Returns actual ratio used (rounded to internal precision). - double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); - - // Current input/output ratio - double ratio() const { return ratio_; } - -// Input - - typedef short sample_t; - - // Resize and clear input buffer - blargg_err_t buffer_size( int ); - - // Clear input buffer. At least two output samples will be available after - // two input samples are written. - void clear(); - - // Number of input samples that can be written - int max_write() const { return buf.end() - write_pos; } - - // Pointer to place to write input samples - sample_t* buffer() { return write_pos; } - - // Notify resampler that 'count' input samples have been written - void write( long count ); - - // Number of input samples in buffer - int written() const { return write_pos - &buf [write_offset]; } - - // Skip 'count' input samples. Returns number of samples actually skipped. - int skip_input( long count ); - -// Output - - // Number of extra input samples needed until 'count' output samples are available - int input_needed( blargg_long count ) const; - - // Number of output samples available - int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } - -public: - ~Fir_Resampler_(); protected: enum { stereo = 2 }; - enum { max_res = 32 }; - blargg_vector buf; - sample_t* write_pos; - int res; - int imp_phase; + enum { max_res = 32 }; // TODO: eliminate and keep impulses on freestore? + sample_t const* imp; int const width_; - int const write_offset; - blargg_ulong skip_bits; - int step; - int input_per_cycle; - double ratio_; sample_t* impulses; - Fir_Resampler_( int width, sample_t* ); - int avail_( blargg_long input_count ) const; + Fir_Resampler_( int width, sample_t [] ); }; -// Width is number of points in FIR. Must be even and 4 or more. More points give -// better quality and rolloff effectiveness, and take longer to calculate. +// Width is number of points in FIR. More points give better quality and +// rolloff effectiveness, and take longer to calculate. template class Fir_Resampler : public Fir_Resampler_ { - BOOST_STATIC_ASSERT( width >= 4 && width % 2 == 0 ); - short impulses [max_res] [width]; + enum { min_width = (width < 4 ? 4 : width) }; + enum { adj_width = min_width / 4 * 4 + 2 }; + enum { write_offset = adj_width * stereo }; + short impulses [max_res * (adj_width + 2)]; public: - Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } - - // Read at most 'count' samples. Returns number of samples actually read. - typedef short sample_t; - int read( sample_t* out, blargg_long count ); + Fir_Resampler() : Fir_Resampler_( adj_width, impulses ) { } + +protected: + virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int ); }; -// End of public interface - -inline void Fir_Resampler_::write( long count ) -{ - write_pos += count; - assert( write_pos <= buf.end() ); -} - template -int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) +Resampler::sample_t const* Fir_Resampler::resample_( sample_t** out_, + sample_t const* out_end, sample_t const in [], int in_size ) { - sample_t* out = out_begin; - const sample_t* in = buf.begin(); - sample_t* end_pos = write_pos; - blargg_ulong skip = skip_bits >> imp_phase; - sample_t const* imp = impulses [imp_phase]; - int remain = res - imp_phase; - int const step = this->step; - - count >>= 1; - - if ( end_pos - in >= width * stereo ) + in_size -= write_offset; + if ( in_size > 0 ) { - end_pos -= width * stereo; + sample_t* BLARGG_RESTRICT out = *out_; + sample_t const* const in_end = in + in_size; + sample_t const* imp = this->imp; + do { - count--; - // accumulate in extended precision - blargg_long l = 0; - blargg_long r = 0; - - const sample_t* i = in; - if ( count < 0 ) + int pt = imp [0]; + int l = pt * in [0]; + int r = pt * in [1]; + if ( out >= out_end ) break; - - for ( int n = width / 2; n; --n ) + for ( int n = (adj_width - 2) / 2; n; --n ) { - int pt0 = imp [0]; - l += pt0 * i [0]; - r += pt0 * i [1]; - int pt1 = imp [1]; + pt = imp [1]; + l += pt * in [2]; + r += pt * in [3]; + + // pre-increment more efficient on some RISC processors imp += 2; - l += pt1 * i [2]; - r += pt1 * i [3]; - i += 4; + pt = imp [0]; + r += pt * in [5]; + in += 4; + l += pt * in [0]; } + pt = imp [1]; + l += pt * in [2]; + r += pt * in [3]; - remain--; + // these two "samples" after the end of the impulse give the + // proper offsets to the next input sample and next impulse + in = (sample_t const*) ((char const*) in + imp [2]); // some negative value + imp = (sample_t const*) ((char const*) imp + imp [3]); // small positive or large negative - l >>= 15; - r >>= 15; - - in += (skip * stereo) & stereo; - skip >>= 1; - in += step; - - if ( !remain ) - { - imp = impulses [0]; - skip = skip_bits; - remain = res; - } - - out [0] = (sample_t) l; - out [1] = (sample_t) r; + out [0] = sample_t (l >> 15); + out [1] = sample_t (r >> 15); out += 2; } - while ( in <= end_pos ); + while ( in < in_end ); + + this->imp = imp; + *out_ = out; } - - imp_phase = res - remain; - - int left = write_pos - in; - write_pos = &buf [left]; - memmove( buf.begin(), in, left * sizeof *in ); - - return out - out_begin; + return in; } #endif diff --git a/Frameworks/GME/gme/Gb_Apu.cpp b/Frameworks/GME/gme/Gb_Apu.cpp old mode 100755 new mode 100644 index 932ebb838..35b77e574 --- a/Frameworks/GME/gme/Gb_Apu.cpp +++ b/Frameworks/GME/gme/Gb_Apu.cpp @@ -1,10 +1,10 @@ -// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Gb_Apu.h" -#include +//#include "gb_apu_logger.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -17,289 +17,390 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -unsigned const vol_reg = 0xFF24; -unsigned const status_reg = 0xFF26; +int const vol_reg = 0xFF24; +int const stereo_reg = 0xFF25; +int const status_reg = 0xFF26; +int const wave_ram = 0xFF30; + +int const power_mask = 0x80; + +void Gb_Apu::treble_eq( blip_eq_t const& eq ) +{ + norm_synth.treble_eq( eq ); + fast_synth.treble_eq( eq ); +} + +inline int Gb_Apu::calc_output( int osc ) const +{ + int bits = regs [stereo_reg - io_addr] >> osc; + return (bits >> 3 & 2) | (bits & 1); +} + +void Gb_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Gb_Osc& o = *oscs [i]; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; +} + +void Gb_Apu::synth_volume( int iv ) +{ + double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv; + norm_synth.volume( v ); + fast_synth.volume( v ); +} + +void Gb_Apu::apply_volume() +{ + // TODO: Doesn't handle differing left and right volumes (panning). + // Not worth the complexity. + int data = regs [vol_reg - io_addr]; + int left = data >> 4 & 7; + int right = data & 7; + //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 ); + //if ( left != right ) dprintf( "l: %d r: %d\n", left, right ); + synth_volume( max( left, right ) + 1 ); +} + +void Gb_Apu::volume( double v ) +{ + if ( volume_ != v ) + { + volume_ = v; + apply_volume(); + } +} + +void Gb_Apu::reset_regs() +{ + for ( int i = 0; i < 0x20; i++ ) + regs [i] = 0; + + square1.reset(); + square2.reset(); + wave .reset(); + noise .reset(); + + apply_volume(); +} + +void Gb_Apu::reset_lengths() +{ + square1.length_ctr = 64; + square2.length_ctr = 64; + wave .length_ctr = 256; + noise .length_ctr = 64; +} + +void Gb_Apu::reduce_clicks( bool reduce ) +{ + reduce_clicks_ = reduce; + + // Click reduction makes DAC off generate same output as volume 0 + int dac_off_amp = 0; + if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks + dac_off_amp = -Gb_Osc::dac_bias; + + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->dac_off_amp = dac_off_amp; + + // AGB always eliminates clicks on wave channel using same method + if ( wave.mode == mode_agb ) + wave.dac_off_amp = -Gb_Osc::dac_bias; +} + +void Gb_Apu::reset( mode_t mode, bool agb_wave ) +{ + // Hardware mode + if ( agb_wave ) + mode = mode_agb; // using AGB wave features implies AGB hardware + wave.agb_mask = agb_wave ? 0xFF : 0; + for ( int i = 0; i < osc_count; i++ ) + oscs [i]->mode = mode; + reduce_clicks( reduce_clicks_ ); + + // Reset state + frame_time = 0; + last_time = 0; + frame_phase = 0; + + reset_regs(); + reset_lengths(); + + // Load initial wave RAM + static byte const initial_wave [2] [16] = { + {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA}, + {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF}, + }; + for ( int b = 2; --b >= 0; ) + { + // Init both banks (does nothing if not in AGB mode) + // TODO: verify that this works + write_register( 0, 0xFF1A, b * 0x40 ); + for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ ) + write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] ); + } +} + +void Gb_Apu::set_tempo( double t ) +{ + frame_period = 4194304 / 512; // 512 Hz + if ( t != 1.0 ) + frame_period = t ? blip_time_t (frame_period / t) : blip_time_t(0); +} Gb_Apu::Gb_Apu() { - square1.synth = &square_synth; - square2.synth = &square_synth; - wave.synth = &other_synth; - noise.synth = &other_synth; + wave.wave_ram = ®s [wave_ram - io_addr]; oscs [0] = &square1; oscs [1] = &square2; oscs [2] = &wave; oscs [3] = &noise; - for ( int i = 0; i < osc_count; i++ ) + for ( int i = osc_count; --i >= 0; ) { - Gb_Osc& osc = *oscs [i]; - osc.regs = ®s [i * 5]; - osc.output = 0; - osc.outputs [0] = 0; - osc.outputs [1] = 0; - osc.outputs [2] = 0; - osc.outputs [3] = 0; + Gb_Osc& o = *oscs [i]; + o.regs = ®s [i * 5]; + o.output = NULL; + o.outputs [0] = NULL; + o.outputs [1] = NULL; + o.outputs [2] = NULL; + o.outputs [3] = NULL; + o.norm_synth = &norm_synth; + o.fast_synth = &fast_synth; } + reduce_clicks_ = false; set_tempo( 1.0 ); - volume( 1.0 ); + volume_ = 1.0; reset(); } -void Gb_Apu::treble_eq( const blip_eq_t& eq ) +void Gb_Apu::run_until_( blip_time_t end_time ) { - square_synth.treble_eq( eq ); - other_synth.treble_eq( eq ); -} - -void Gb_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - require( (center && left && right) || (!center && !left && !right) ); - Gb_Osc& osc = *oscs [index]; - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; -} - -void Gb_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); -} - -void Gb_Apu::update_volume() -{ - // TODO: doesn't handle differing left/right global volume (support would - // require modification to all oscillator code) - int data = regs [vol_reg - start_addr]; - double vol = (max( data & 7, data >> 4 & 7 ) + 1) * volume_unit; - square_synth.volume( vol ); - other_synth.volume( vol ); -} - -static unsigned char const powerup_regs [0x20] = { - 0x80,0x3F,0x00,0xFF,0xBF, // square 1 - 0xFF,0x3F,0x00,0xFF,0xBF, // square 2 - 0x7F,0xFF,0x9F,0xFF,0xBF, // wave - 0xFF,0xFF,0x00,0x00,0xBF, // noise - 0x00, // left/right enables - 0x77, // master volume - 0x80, // power - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF -}; - -void Gb_Apu::set_tempo( double t ) -{ - frame_period = 4194304 / 256; // 256 Hz - if ( t != 1.0 ) - frame_period = blip_time_t (frame_period / t); -} - -void Gb_Apu::reset() -{ - next_frame_time = 0; - last_time = 0; - frame_count = 0; - - square1.reset(); - square2.reset(); - wave.reset(); - noise.reset(); - noise.bits = 1; - wave.wave_pos = 0; - - // avoid click at beginning - regs [vol_reg - start_addr] = 0x77; - update_volume(); - - regs [status_reg - start_addr] = 0x01; // force power - write_register( 0, status_reg, 0x00 ); - - static unsigned char const initial_wave [] = { - 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table - 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA - }; - memcpy( wave.wave, initial_wave, sizeof wave.wave ); -} - -void Gb_Apu::run_until( blip_time_t end_time ) -{ - require( end_time >= last_time ); // end_time must not be before previous time - if ( end_time == last_time ) - return; + if ( !frame_period ) + frame_time += end_time - last_time; while ( true ) { - blip_time_t time = next_frame_time; - if ( time > end_time ) - time = end_time; - // run oscillators - for ( int i = 0; i < osc_count; ++i ) - { - Gb_Osc& osc = *oscs [i]; - if ( osc.output ) - { - osc.output->set_modified(); // TODO: misses optimization opportunities? - int playing = false; - if ( osc.enabled && osc.volume && - (!(osc.regs [4] & osc.len_enabled_mask) || osc.length) ) - playing = -1; - switch ( i ) - { - case 0: square1.run( last_time, time, playing ); break; - case 1: square2.run( last_time, time, playing ); break; - case 2: wave .run( last_time, time, playing ); break; - case 3: noise .run( last_time, time, playing ); break; - } - } - } + blip_time_t time = end_time; + if ( time > frame_time ) + time = frame_time; + + square1.run( last_time, time ); + square2.run( last_time, time ); + wave .run( last_time, time ); + noise .run( last_time, time ); last_time = time; if ( time == end_time ) break; - next_frame_time += frame_period; - - // 256 Hz actions - square1.clock_length(); - square2.clock_length(); - wave.clock_length(); - noise.clock_length(); - - frame_count = (frame_count + 1) & 3; - if ( frame_count == 0 ) + // run frame sequencer + assert( frame_period ); + frame_time += frame_period * Gb_Osc::clk_mul; + switch ( frame_phase++ ) { - // 64 Hz actions + case 2: + case 6: + // 128 Hz + square1.clock_sweep(); + case 0: + case 4: + // 256 Hz + square1.clock_length(); + square2.clock_length(); + wave .clock_length(); + noise .clock_length(); + break; + + case 7: + // 64 Hz + frame_phase = 0; square1.clock_envelope(); square2.clock_envelope(); - noise.clock_envelope(); + noise .clock_envelope(); } - - if ( frame_count & 1 ) - square1.clock_sweep(); // 128 Hz action } } +inline void Gb_Apu::run_until( blip_time_t time ) +{ + require( time >= last_time ); // end_time must not be before previous time + if ( time > last_time ) + run_until_( time ); +} + void Gb_Apu::end_frame( blip_time_t end_time ) { + #ifdef LOG_FRAME + LOG_FRAME( end_time ); + #endif + if ( end_time > last_time ) run_until( end_time ); - assert( next_frame_time >= end_time ); - next_frame_time -= end_time; + frame_time -= end_time; + assert( frame_time >= 0 ); - assert( last_time >= end_time ); last_time -= end_time; + assert( last_time >= 0 ); } -void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) +void Gb_Apu::silence_osc( Gb_Osc& o ) +{ + int delta = -o.last_amp; + if ( reduce_clicks_ ) + delta += o.dac_off_amp; + + if ( delta ) + { + o.last_amp = o.dac_off_amp; + if ( o.output ) + { + o.output->set_modified(); + fast_synth.offset( last_time, delta, o.output ); + } + } +} + +void Gb_Apu::apply_stereo() +{ + for ( int i = osc_count; --i >= 0; ) + { + Gb_Osc& o = *oscs [i]; + Blip_Buffer* out = o.outputs [calc_output( i )]; + if ( o.output != out ) + { + silence_osc( o ); + o.output = out; + } + } +} + +void Gb_Apu::write_register( blip_time_t time, int addr, int data ) { require( (unsigned) data < 0x100 ); - int reg = addr - start_addr; - if ( (unsigned) reg >= register_count ) + int reg = addr - io_addr; + if ( (unsigned) reg >= io_size ) + { + require( false ); return; + } + + #ifdef LOG_WRITE + LOG_WRITE( time, addr, data ); + #endif + + if ( addr < status_reg && !(regs [status_reg - io_addr] & power_mask) ) + { + // Power is off + + // length counters can only be written in DMG mode + if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) ) + return; + + if ( reg < 10 ) + data &= 0x3F; // clear square duty + } run_until( time ); - int old_reg = regs [reg]; - regs [reg] = data; - - if ( addr < vol_reg ) + if ( addr >= wave_ram ) { - write_osc( reg / 5, reg, data ); + wave.write( addr, data ); } - else if ( addr == vol_reg && data != old_reg ) // global volume + else { - // return all oscs to 0 - for ( int i = 0; i < osc_count; i++ ) + int old_data = regs [reg]; + regs [reg] = data; + + if ( addr < vol_reg ) { - Gb_Osc& osc = *oscs [i]; - int amp = osc.last_amp; - osc.last_amp = 0; - if ( amp && osc.enabled && osc.output ) - other_synth.offset( time, -amp, osc.output ); + // Oscillator + write_osc( reg, old_data, data ); } - - if ( wave.outputs [3] ) - other_synth.offset( time, 30, wave.outputs [3] ); - - update_volume(); - - if ( wave.outputs [3] ) - other_synth.offset( time, -30, wave.outputs [3] ); - - // oscs will update with new amplitude when next run - } - else if ( addr == 0xFF25 || addr == status_reg ) - { - int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; - int flags = regs [0xFF25 - start_addr] & mask; - - // left/right assignments - for ( int i = 0; i < osc_count; i++ ) + else if ( addr == vol_reg && data != old_data ) { - Gb_Osc& osc = *oscs [i]; - osc.enabled &= mask; - int bits = flags >> i; - Blip_Buffer* old_output = osc.output; - osc.output_select = (bits >> 3 & 2) | (bits & 1); - osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output ) - { - int amp = osc.last_amp; - osc.last_amp = 0; - if ( amp && old_output ) - other_synth.offset( time, -amp, old_output ); - } + // Master volume + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + apply_volume(); } - - if ( addr == status_reg && data != old_reg ) + else if ( addr == stereo_reg ) { - if ( !(data & 0x80) ) - { - for ( unsigned i = 0; i < sizeof powerup_regs; i++ ) - { - if ( i != status_reg - start_addr ) - write_register( time, i + start_addr, powerup_regs [i] ); - } - } - else - { - //dprintf( "APU powered on\n" ); - } + // Stereo panning + apply_stereo(); + } + else if ( addr == status_reg && (data ^ old_data) & power_mask ) + { + // Power control + frame_phase = 0; + for ( int i = osc_count; --i >= 0; ) + silence_osc( *oscs [i] ); + + reset_regs(); + if ( wave.mode != mode_dmg ) + reset_lengths(); + + regs [status_reg - io_addr] = data; } - } - else if ( addr >= 0xFF30 ) - { - int index = (addr & 0x0F) * 2; - wave.wave [index] = data >> 4; - wave.wave [index + 1] = data & 0x0F; } } -int Gb_Apu::read_register( blip_time_t time, unsigned addr ) +int Gb_Apu::read_register( blip_time_t time, int addr ) { - run_until( time ); + if ( addr >= status_reg ) + run_until( time ); - int index = addr - start_addr; - require( (unsigned) index < register_count ); - int data = regs [index]; + int reg = addr - io_addr; + if ( (unsigned) reg >= io_size ) + { + require( false ); + return 0; + } + if ( addr >= wave_ram ) + return wave.read( addr ); + + // Value read back has some bits always set + static byte const masks [] = { + 0x80,0x3F,0x00,0xFF,0xBF, + 0xFF,0x3F,0x00,0xFF,0xBF, + 0x7F,0xFF,0x9F,0xFF,0xBF, + 0xFF,0xFF,0x00,0x00,0xBF, + 0x00,0x00,0x70, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + int mask = masks [reg]; + if ( wave.agb_mask && (reg == 10 || reg == 12) ) + mask = 0x1F; // extra implemented bits in wave regs on AGB + int data = regs [reg] | mask; + + // Status register if ( addr == status_reg ) { - data = (data & 0x80) | 0x70; - for ( int i = 0; i < osc_count; i++ ) - { - const Gb_Osc& osc = *oscs [i]; - if ( osc.enabled && (osc.length || !(osc.regs [4] & osc.len_enabled_mask)) ) - data |= 1 << i; - } + data &= 0xF0; + data |= (int) square1.enabled << 0; + data |= (int) square2.enabled << 1; + data |= (int) wave .enabled << 2; + data |= (int) noise .enabled << 3; } return data; diff --git a/Frameworks/GME/gme/Gb_Apu.h b/Frameworks/GME/gme/Gb_Apu.h old mode 100755 new mode 100644 index e74ebc55b..ebc72687c --- a/Frameworks/GME/gme/Gb_Apu.h +++ b/Frameworks/GME/gme/Gb_Apu.h @@ -1,90 +1,193 @@ -// Nintendo Game Boy PAPU sound chip emulator +// Nintendo Game Boy sound hardware emulator with save state support -// Gb_Snd_Emu 0.1.5 +// Gb_Snd_Emu $vers #ifndef GB_APU_H #define GB_APU_H #include "Gb_Oscs.h" +struct gb_apu_state_t; + class Gb_Apu { public: +// Basics + + // Sets buffer(s) to generate sound into, or NULL to mute. If only center is not NULL, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - // Set overall volume of all oscillators, where 1.0 is full volume + // Emulates to time t, then writes data to addr + void write_register( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Clock rate sound hardware runs at + enum { clock_rate = 4194304 * GB_APU_OVERCLOCK }; + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0xFF10 }; + enum { io_size = 0x30 }; + + // Emulates to time t, then reads from addr + int read_register( blip_time_t t, int addr ); + + // Resets hardware to state after power, BEFORE boot ROM runs. Mode selects + // sound hardware. If agb_wave is true, enables AGB's extra wave features. + enum mode_t { + mode_dmg, // Game Boy monochrome + mode_cgb, // Game Boy Color + mode_agb // Game Boy Advance + }; + void reset( mode_t mode = mode_cgb, bool agb_wave = false ); + + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise + enum { osc_count = 4 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, + Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Sets overall volume, where 1.0 is normal void volume( double ); - // Set treble equalization - void treble_eq( const blip_eq_t& ); + // Sets treble equalization + void treble_eq( blip_eq_t const& ); - // Outputs can be assigned to a single buffer for mono output, or to three - // buffers for stereo output (using Stereo_Buffer to do the mixing). + // Treble and bass values for various hardware. + enum { + speaker_treble = -47, // speaker on system + speaker_bass = 2000, + dmg_treble = 0, // headphones on each system + dmg_bass = 30, + cgb_treble = 0, + cgb_bass = 300, // CGB has much less bass + agb_treble = 0, + agb_bass = 30 + }; - // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silences all oscillators. - void output( Blip_Buffer* mono ); - void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, - // silences oscillator. - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* mono ); - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Reset oscillators and internal state - void reset(); - - // Reads and writes at addr must satisfy start_addr <= addr <= end_addr - enum { start_addr = 0xFF10 }; - enum { end_addr = 0xFF3F }; - enum { register_count = end_addr - start_addr + 1 }; - - // Write 'data' to address at specified time - void write_register( blip_time_t, unsigned addr, int data ); - - // Read from address at specified time - int read_register( blip_time_t, unsigned addr ); - - // Run all oscillators up to specified time, end current time frame, then - // start a new frame at time 0. - void end_frame( blip_time_t ); + // If true, reduces clicking by disabling DAC biasing. Note that this reduces + // emulation accuracy, since the clicks are authentic. + void reduce_clicks( bool reduce = true ); + // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the + // tempo in a music player. void set_tempo( double ); -public: - Gb_Apu(); + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( gb_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( gb_apu_state_t const& in ); + private: // noncopyable Gb_Apu( const Gb_Apu& ); Gb_Apu& operator = ( const Gb_Apu& ); + +// Implementation +public: + Gb_Apu(); + // Use set_output() in place of these + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) + BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ); ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0xFF10 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0xFF3F }; ) + BLARGG_DEPRECATED_TEXT( enum { register_count = end_addr - start_addr + 1 }; ) + +private: Gb_Osc* oscs [osc_count]; - blip_time_t next_frame_time; - blip_time_t last_time; - blip_time_t frame_period; - double volume_unit; - int frame_count; + blip_time_t last_time; // time sound emulator has been run to + blip_time_t frame_period; // clocks between each frame sequencer step + double volume_; + bool reduce_clicks_; - Gb_Square square1; - Gb_Square square2; - Gb_Wave wave; - Gb_Noise noise; - BOOST::uint8_t regs [register_count]; - Gb_Square::Synth square_synth; // used by squares - Gb_Wave::Synth other_synth; // used by wave and noise + Gb_Sweep_Square square1; + Gb_Square square2; + Gb_Wave wave; + Gb_Noise noise; + blip_time_t frame_time; // time of next frame sequencer action + int frame_phase; // phase of next frame sequencer step + enum { regs_size = io_size + 0x10 }; + BOOST::uint8_t regs [regs_size];// last values written to registers - void update_volume(); + // large objects after everything else + Blip_Synth_Norm norm_synth; + Blip_Synth_Fast fast_synth; + + void reset_lengths(); + void reset_regs(); + int calc_output( int osc ) const; + void apply_stereo(); + void apply_volume(); + void synth_volume( int ); + void run_until_( blip_time_t ); void run_until( blip_time_t ); - void write_osc( int index, int reg, int data ); + void silence_osc( Gb_Osc& ); + void write_osc( int reg, int old_data, int data ); + const char* save_load( gb_apu_state_t*, bool save ); + void save_load2( gb_apu_state_t*, bool save ); + friend class Gb_Apu2; }; -inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - -inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } - -inline void Gb_Apu::volume( double vol ) +// Format of save state. Should be stable across versions of the library, +// with earlier versions properly opening later save states. Includes some +// room for expansion so the state size shouldn't increase. +struct gb_apu_state_t { - volume_unit = 0.60 / osc_count / 15 /*steps*/ / 2 /*?*/ / 8 /*master vol range*/ * vol; - update_volume(); +#if GB_APU_CUSTOM_STATE + // Values stored as plain int so your code can read/write them easily. + // Structure can NOT be written to disk, since format is not portable. + typedef int val_t; +#else + // Values written in portable little-endian format, allowing structure + // to be written directly to disk. + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414247 }; // 'GBAP' + + val_t format; // format of all following data + val_t version; // later versions just add fields to end + + unsigned char regs [0x40]; + val_t frame_time; + val_t frame_phase; + + val_t sweep_freq; + val_t sweep_delay; + val_t sweep_enabled; + val_t sweep_neg; + val_t noise_divider; + val_t wave_buf; + + val_t delay [4]; + val_t length_ctr [4]; + val_t phase [4]; + val_t enabled [4]; + + val_t env_delay [3]; + val_t env_volume [3]; + val_t env_enabled [3]; + + val_t unused [13]; // for future expansion +}; + +inline void Gb_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); } +BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c ) { set_output( c, c, c ); } ) +BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) + #endif diff --git a/Frameworks/GME/gme/Gb_Cpu.cpp b/Frameworks/GME/gme/Gb_Cpu.cpp old mode 100755 new mode 100644 index b1f22bd9a..c8b7005a0 --- a/Frameworks/GME/gme/Gb_Cpu.cpp +++ b/Frameworks/GME/gme/Gb_Cpu.cpp @@ -1,12 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Gb_Cpu.h" -#include +#include "blargg_endian.h" -//#include "gb_cpu_log.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -17,1040 +15,37 @@ details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "gb_cpu_io.h" - #include "blargg_source.h" -// Common instructions: -// -// 365880 FA LD A,IND16 -// 355863 20 JR NZ -// 313655 21 LD HL,IMM -// 274580 28 JR Z -// 252878 FE CMP IMM -// 230541 7E LD A,(HL) -// 226209 2A LD A,(HL+) -// 217467 CD CALL -// 212034 C9 RET -// 208376 CB CB prefix -// -// 27486 CB 7E BIT 7,(HL) -// 15925 CB 76 BIT 6,(HL) -// 13035 CB 19 RR C -// 11557 CB 7F BIT 7,A -// 10898 CB 37 SWAP A -// 10208 CB 66 BIT 4,(HL) - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - -inline void Gb_Cpu::set_code_page( int i, uint8_t* p ) +inline void Gb_Cpu::set_code_page( int i, void* p ) { - state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size ); + byte* p2 = STATIC_CAST(byte*,p) - GB_CPU_OFFSET( i * page_size ); + cpu_state_.code_map [i] = p2; + cpu_state->code_map [i] = p2; } void Gb_Cpu::reset( void* unmapped ) { - check( state == &state_ ); - state = &state_; + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; - state_.remain = 0; + cpu_state_.time = 0; - for ( int i = 0; i < page_count + 1; i++ ) - set_code_page( i, (uint8_t*) unmapped ); + for ( int i = 0; i < page_count + 1; ++i ) + set_code_page( i, unmapped ); memset( &r, 0, sizeof r ); - //interrupts_enabled = false; blargg_verify_byte_order(); } -void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data ) +void Gb_Cpu::map_code( addr_t start, int size, void* data ) { // address range must begin and end on page boundaries require( start % page_size == 0 ); - require( size % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= mem_size ); - unsigned first_page = start / page_size; - for ( unsigned i = size / page_size; i--; ) - set_code_page( first_page + i, (uint8_t*) data + i * page_size ); -} - -#define READ( addr ) CPU_READ( this, (addr), s.remain ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), s.remain );} -#define READ_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out ) -#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) - -unsigned const z_flag = 0x80; -unsigned const n_flag = 0x40; -unsigned const h_flag = 0x20; -unsigned const c_flag = 0x10; - -bool Gb_Cpu::run( blargg_long cycle_count ) -{ - state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; - state_t s; - this->state = &s; - memcpy( &s, &this->state_, sizeof s ); - - typedef BOOST::uint16_t uint16_t; - -#if BLARGG_BIG_ENDIAN - #define R8( n ) (r8_ [n]) -#elif BLARGG_LITTLE_ENDIAN - #define R8( n ) (r8_ [(n) ^ 1]) -#else - #error "Byte order of CPU must be known" -#endif - - union { - core_regs_t rg; // individual registers - - struct { - BOOST::uint16_t bc, de, hl, unused; // pairs - } rp; - - uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) - BOOST::uint16_t r16 [4]; // indexed pairs - }; - BOOST_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 ); - - rg = r; - unsigned pc = r.pc; - unsigned sp = r.sp; - unsigned flags = r.flags; - -loop: - - check( (unsigned long) pc < 0x10000 ); - check( (unsigned long) sp < 0x10000 ); - check( (flags & ~0xF0) == 0 ); - - uint8_t const* instr = s.code_map [pc >> page_shift]; - unsigned op; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - op = instr [pc]; - pc++; - instr += pc; - #else - instr += PAGE_OFFSET( pc ); - op = *instr++; - pc++; - #endif - -#define GET_ADDR() GET_LE16( instr ) - - if ( !--s.remain ) - goto stop; - - unsigned data; - data = *instr; - - #ifdef GB_CPU_LOG_H - gb_cpu_log( "new", pc - 1, op, data, instr [1] ); - #endif - - switch ( op ) - { - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ -{\ - pc++;\ - int offset = (BOOST::int8_t) data;\ - if ( !(cond) ) goto loop;\ - pc = uint16_t (pc + offset);\ - goto loop;\ -} - -// Most Common - - case 0x20: // JR NZ - BRANCH( !(flags & z_flag) ) - - case 0x21: // LD HL,IMM (common) - rp.hl = GET_ADDR(); - pc += 2; - goto loop; - - case 0x28: // JR Z - BRANCH( flags & z_flag ) - - { - unsigned temp; - case 0xF0: // LD A,(0xFF00+imm) - temp = data | 0xFF00; - pc++; - goto ld_a_ind_comm; - - case 0xF2: // LD A,(0xFF00+C) - temp = rg.c | 0xFF00; - goto ld_a_ind_comm; - - case 0x0A: // LD A,(BC) - temp = rp.bc; - goto ld_a_ind_comm; - - case 0x3A: // LD A,(HL-) - temp = rp.hl; - rp.hl = temp - 1; - goto ld_a_ind_comm; - - case 0x1A: // LD A,(DE) - temp = rp.de; - goto ld_a_ind_comm; - - case 0x2A: // LD A,(HL+) (common) - temp = rp.hl; - rp.hl = temp + 1; - goto ld_a_ind_comm; - - case 0xFA: // LD A,IND16 (common) - temp = GET_ADDR(); - pc += 2; - ld_a_ind_comm: - READ_FAST( temp, rg.a ); - goto loop; - } - - case 0xBE: // CMP (HL) - data = READ( rp.hl ); - goto cmp_comm; - - case 0xB8: // CMP B - case 0xB9: // CMP C - case 0xBA: // CMP D - case 0xBB: // CMP E - case 0xBC: // CMP H - case 0xBD: // CMP L - data = R8( op & 7 ); - goto cmp_comm; - - case 0xFE: // CMP IMM - pc++; - cmp_comm: - op = rg.a; - data = op - data; - sub_set_flags: - flags = ((op & 15) - (data & 15)) & h_flag; - flags |= (data >> 4) & c_flag; - flags |= n_flag; - if ( data & 0xFF ) - goto loop; - flags |= z_flag; - goto loop; - - case 0x46: // LD B,(HL) - case 0x4E: // LD C,(HL) - case 0x56: // LD D,(HL) - case 0x5E: // LD E,(HL) - case 0x66: // LD H,(HL) - case 0x6E: // LD L,(HL) - case 0x7E:{// LD A,(HL) - unsigned addr = rp.hl; - READ_FAST( addr, R8( (op >> 3) & 7 ) ); - goto loop; - } - - case 0xC4: // CNZ (next-most-common) - pc += 2; - if ( flags & z_flag ) - goto loop; - call: - pc -= 2; - case 0xCD: // CALL (most-common) - data = pc + 2; - pc = GET_ADDR(); - push: - sp = (sp - 1) & 0xFFFF; - WRITE( sp, data >> 8 ); - sp = (sp - 1) & 0xFFFF; - WRITE( sp, data & 0xFF ); - goto loop; - - case 0xC8: // RNZ (next-most-common) - if ( !(flags & z_flag) ) - goto loop; - case 0xC9: // RET (most common) - ret: - pc = READ( sp ); - pc += 0x100 * READ( sp + 1 ); - sp = (sp + 2) & 0xFFFF; - goto loop; - - case 0x00: // NOP - case 0x40: // LD B,B - case 0x49: // LD C,C - case 0x52: // LD D,D - case 0x5B: // LD E,E - case 0x64: // LD H,H - case 0x6D: // LD L,L - case 0x7F: // LD A,A - goto loop; - -// CB Instructions - - case 0xCB: - pc++; - // now data is the opcode - switch ( data ) { - - { - int temp; - case 0x46: // BIT b,(HL) - case 0x4E: - case 0x56: - case 0x5E: - case 0x66: - case 0x6E: - case 0x76: - case 0x7E: - { - unsigned addr = rp.hl; - READ_FAST( addr, temp ); - goto bit_comm; - } - - case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r - case 0x44: case 0x45: case 0x47: case 0x48: - case 0x49: case 0x4A: case 0x4B: case 0x4C: - case 0x4D: case 0x4F: case 0x50: case 0x51: - case 0x52: case 0x53: case 0x54: case 0x55: - case 0x57: case 0x58: case 0x59: case 0x5A: - case 0x5B: case 0x5C: case 0x5D: case 0x5F: - case 0x60: case 0x61: case 0x62: case 0x63: - case 0x64: case 0x65: case 0x67: case 0x68: - case 0x69: case 0x6A: case 0x6B: case 0x6C: - case 0x6D: case 0x6F: case 0x70: case 0x71: - case 0x72: case 0x73: case 0x74: case 0x75: - case 0x77: case 0x78: case 0x79: case 0x7A: - case 0x7B: case 0x7C: case 0x7D: case 0x7F: - temp = R8( data & 7 ); - bit_comm: - int bit = (~data >> 3) & 7; - flags &= ~n_flag; - flags |= h_flag | z_flag; - flags ^= (temp << bit) & z_flag; - goto loop; - } - - case 0x86: // RES b,(HL) - case 0x8E: - case 0x96: - case 0x9E: - case 0xA6: - case 0xAE: - case 0xB6: - case 0xBE: - case 0xC6: // SET b,(HL) - case 0xCE: - case 0xD6: - case 0xDE: - case 0xE6: - case 0xEE: - case 0xF6: - case 0xFE: { - int temp = READ( rp.hl ); - int bit = 1 << ((data >> 3) & 7); - temp &= ~bit; - if ( !(data & 0x40) ) - bit = 0; - WRITE( rp.hl, temp | bit ); - goto loop; - } - - case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r - case 0xC4: case 0xC5: case 0xC7: case 0xC8: - case 0xC9: case 0xCA: case 0xCB: case 0xCC: - case 0xCD: case 0xCF: case 0xD0: case 0xD1: - case 0xD2: case 0xD3: case 0xD4: case 0xD5: - case 0xD7: case 0xD8: case 0xD9: case 0xDA: - case 0xDB: case 0xDC: case 0xDD: case 0xDF: - case 0xE0: case 0xE1: case 0xE2: case 0xE3: - case 0xE4: case 0xE5: case 0xE7: case 0xE8: - case 0xE9: case 0xEA: case 0xEB: case 0xEC: - case 0xED: case 0xEF: case 0xF0: case 0xF1: - case 0xF2: case 0xF3: case 0xF4: case 0xF5: - case 0xF7: case 0xF8: case 0xF9: case 0xFA: - case 0xFB: case 0xFC: case 0xFD: case 0xFF: - R8( data & 7 ) |= 1 << ((data >> 3) & 7); - goto loop; - - case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r - case 0x84: case 0x85: case 0x87: case 0x88: - case 0x89: case 0x8A: case 0x8B: case 0x8C: - case 0x8D: case 0x8F: case 0x90: case 0x91: - case 0x92: case 0x93: case 0x94: case 0x95: - case 0x97: case 0x98: case 0x99: case 0x9A: - case 0x9B: case 0x9C: case 0x9D: case 0x9F: - case 0xA0: case 0xA1: case 0xA2: case 0xA3: - case 0xA4: case 0xA5: case 0xA7: case 0xA8: - case 0xA9: case 0xAA: case 0xAB: case 0xAC: - case 0xAD: case 0xAF: case 0xB0: case 0xB1: - case 0xB2: case 0xB3: case 0xB4: case 0xB5: - case 0xB7: case 0xB8: case 0xB9: case 0xBA: - case 0xBB: case 0xBC: case 0xBD: case 0xBF: - R8( data & 7 ) &= ~(1 << ((data >> 3) & 7)); - goto loop; - - { - int temp; - case 0x36: // SWAP (HL) - temp = READ( rp.hl ); - goto swap_comm; - - case 0x30: // SWAP B - case 0x31: // SWAP C - case 0x32: // SWAP D - case 0x33: // SWAP E - case 0x34: // SWAP H - case 0x35: // SWAP L - case 0x37: // SWAP A - temp = R8( data & 7 ); - swap_comm: - op = (temp >> 4) | (temp << 4); - flags = 0; - goto shift_comm; - } - -// Shift/Rotate - - case 0x06: // RLC (HL) - case 0x16: // RL (HL) - case 0x26: // SLA (HL) - op = READ( rp.hl ); - goto rl_comm; - - case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A - case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A - case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A - op = R8( data & 7 ); - goto rl_comm; - - case 0x3E: // SRL (HL) - data += 0x10; // bump up to 0x4n to avoid preserving sign bit - case 0x1E: // RR (HL) - case 0x0E: // RRC (HL) - case 0x2E: // SRA (HL) - op = READ( rp.hl ); - goto rr_comm; - - case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A - data += 0x10; // bump up to 0x4n - case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A - case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC A - case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A - op = R8( data & 7 ); - goto rr_comm; - - } // CB op - assert( false ); // unhandled CB op - - case 0x07: // RLCA - case 0x17: // RLA - data = op; - op = rg.a; - rl_comm: - op <<= 1; - op |= ((data & flags) >> 4) & 1; // RL and carry is set - flags = (op >> 4) & c_flag; // C = bit shifted out - if ( data < 0x10 ) // RLC - op |= op >> 8; - // SLA doesn't fill lower bit - goto shift_comm; - - case 0x0F: // RRCA - case 0x1F: // RRA - data = op; - op = rg.a; - rr_comm: - op |= (data & flags) << 4; // RR and carry is set - flags = (op << 4) & c_flag; // C = bit shifted out - if ( data < 0x10 ) // RRC - op |= op << 8; - op >>= 1; - if ( data & 0x20 ) // SRA propagates sign bit - op |= (op << 1) & 0x80; - shift_comm: - data &= 7; - if ( !(op & 0xFF) ) - flags |= z_flag; - if ( data == 6 ) - goto write_hl_op_ff; - R8( data ) = op; - goto loop; - -// Load - - case 0x70: // LD (HL),B - case 0x71: // LD (HL),C - case 0x72: // LD (HL),D - case 0x73: // LD (HL),E - case 0x74: // LD (HL),H - case 0x75: // LD (HL),L - case 0x77: // LD (HL),A - op = R8( op & 7 ); - write_hl_op_ff: - WRITE( rp.hl, op & 0xFF ); - goto loop; - - case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r - case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F: - case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57: - case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F: - case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67: - case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F: - case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: - R8( (op >> 3) & 7 ) = R8( op & 7 ); - goto loop; - - case 0x08: // LD IND16,SP - data = GET_ADDR(); - pc += 2; - WRITE( data, sp&0xFF ); - data++; - WRITE( data, sp >> 8 ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - - case 0x31: // LD SP,IMM - sp = GET_ADDR(); - pc += 2; - goto loop; - - case 0x01: // LD BC,IMM - case 0x11: // LD DE,IMM - r16 [op >> 4] = GET_ADDR(); - pc += 2; - goto loop; - - { - unsigned temp; - case 0xE0: // LD (0xFF00+imm),A - temp = data | 0xFF00; - pc++; - goto write_data_rg_a; - - case 0xE2: // LD (0xFF00+C),A - temp = rg.c | 0xFF00; - goto write_data_rg_a; - - case 0x32: // LD (HL-),A - temp = rp.hl; - rp.hl = temp - 1; - goto write_data_rg_a; - - case 0x02: // LD (BC),A - temp = rp.bc; - goto write_data_rg_a; - - case 0x12: // LD (DE),A - temp = rp.de; - goto write_data_rg_a; - - case 0x22: // LD (HL+),A - temp = rp.hl; - rp.hl = temp + 1; - goto write_data_rg_a; - - case 0xEA: // LD IND16,A (common) - temp = GET_ADDR(); - pc += 2; - write_data_rg_a: - WRITE( temp, rg.a ); - goto loop; - } - - case 0x06: // LD B,IMM - rg.b = data; - pc++; - goto loop; - - case 0x0E: // LD C,IMM - rg.c = data; - pc++; - goto loop; - - case 0x16: // LD D,IMM - rg.d = data; - pc++; - goto loop; - - case 0x1E: // LD E,IMM - rg.e = data; - pc++; - goto loop; - - case 0x26: // LD H,IMM - rg.h = data; - pc++; - goto loop; - - case 0x2E: // LD L,IMM - rg.l = data; - pc++; - goto loop; - - case 0x36: // LD (HL),IMM - WRITE( rp.hl, data ); - pc++; - goto loop; - - case 0x3E: // LD A,IMM - rg.a = data; - pc++; - goto loop; - -// Increment/Decrement - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - r16 [op >> 4]++; - goto loop; - - case 0x33: // INC SP - sp = (sp + 1) & 0xFFFF; - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - r16 [op >> 4]--; - goto loop; - - case 0x3B: // DEC SP - sp = (sp - 1) & 0xFFFF; - goto loop; - - case 0x34: // INC (HL) - op = rp.hl; - data = READ( op ); - data++; - WRITE( op, data & 0xFF ); - goto inc_comm; - - case 0x04: // INC B - case 0x0C: // INC C (common) - case 0x14: // INC D - case 0x1C: // INC E - case 0x24: // INC H - case 0x2C: // INC L - case 0x3C: // INC A - op = (op >> 3) & 7; - R8( op ) = data = R8( op ) + 1; - inc_comm: - flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); - goto loop; - - case 0x35: // DEC (HL) - op = rp.hl; - data = READ( op ); - data--; - WRITE( op, data & 0xFF ); - goto dec_comm; - - case 0x05: // DEC B - case 0x0D: // DEC C - case 0x15: // DEC D - case 0x1D: // DEC E - case 0x25: // DEC H - case 0x2D: // DEC L - case 0x3D: // DEC A - op = (op >> 3) & 7; - data = R8( op ) - 1; - R8( op ) = data; - dec_comm: - flags = (flags & c_flag) | n_flag | (((data & 15) + 0x31) & h_flag); - if ( data & 0xFF ) - goto loop; - flags |= z_flag; - goto loop; - -// Add 16-bit - - { - blargg_ulong temp; // need more than 16 bits for carry - unsigned prev; - - case 0xF8: // LD HL,SP+imm - temp = BOOST::int8_t (data); // sign-extend to 16 bits - pc++; - flags = 0; - temp += sp; - prev = sp; - goto add_16_hl; - - case 0xE8: // ADD SP,IMM - temp = BOOST::int8_t (data); // sign-extend to 16 bits - pc++; - flags = 0; - temp += sp; - prev = sp; - sp = temp & 0xFFFF; - goto add_16_comm; - - case 0x39: // ADD HL,SP - temp = sp; - goto add_hl_comm; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - temp = r16 [op >> 4]; - add_hl_comm: - prev = rp.hl; - temp += prev; - flags &= z_flag; - add_16_hl: - rp.hl = temp; - add_16_comm: - flags |= (temp >> 12) & c_flag; - flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; - goto loop; - } - - case 0x86: // ADD (HL) - data = READ( rp.hl ); - goto add_comm; - - case 0x80: // ADD B - case 0x81: // ADD C - case 0x82: // ADD D - case 0x83: // ADD E - case 0x84: // ADD H - case 0x85: // ADD L - case 0x87: // ADD A - data = R8( op & 7 ); - goto add_comm; - - case 0xC6: // ADD IMM - pc++; - add_comm: - flags = rg.a; - data += flags; - flags = ((data & 15) - (flags & 15)) & h_flag; - flags |= (data >> 4) & c_flag; - rg.a = data; - if ( data & 0xFF ) - goto loop; - flags |= z_flag; - goto loop; - -// Add/Subtract - - case 0x8E: // ADC (HL) - data = READ( rp.hl ); - goto adc_comm; - - case 0x88: // ADC B - case 0x89: // ADC C - case 0x8A: // ADC D - case 0x8B: // ADC E - case 0x8C: // ADC H - case 0x8D: // ADC L - case 0x8F: // ADC A - data = R8( op & 7 ); - goto adc_comm; - - case 0xCE: // ADC IMM - pc++; - adc_comm: - data += (flags >> 4) & 1; - data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? - goto add_comm; - - case 0x96: // SUB (HL) - data = READ( rp.hl ); - goto sub_comm; - - case 0x90: // SUB B - case 0x91: // SUB C - case 0x92: // SUB D - case 0x93: // SUB E - case 0x94: // SUB H - case 0x95: // SUB L - case 0x97: // SUB A - data = R8( op & 7 ); - goto sub_comm; - - case 0xD6: // SUB IMM - pc++; - sub_comm: - op = rg.a; - data = op - data; - rg.a = data; - goto sub_set_flags; - - case 0x9E: // SBC (HL) - data = READ( rp.hl ); - goto sbc_comm; - - case 0x98: // SBC B - case 0x99: // SBC C - case 0x9A: // SBC D - case 0x9B: // SBC E - case 0x9C: // SBC H - case 0x9D: // SBC L - case 0x9F: // SBC A - data = R8( op & 7 ); - goto sbc_comm; - - case 0xDE: // SBC IMM - pc++; - sbc_comm: - data += (flags >> 4) & 1; - data &= 0xFF; // to do: does carry get set when sum + carry = 0x100? - goto sub_comm; - -// Logical - - case 0xA0: // AND B - case 0xA1: // AND C - case 0xA2: // AND D - case 0xA3: // AND E - case 0xA4: // AND H - case 0xA5: // AND L - data = R8( op & 7 ); - goto and_comm; - - case 0xA6: // AND (HL) - data = READ( rp.hl ); - pc--; - case 0xE6: // AND IMM - pc++; - and_comm: - rg.a &= data; - case 0xA7: // AND A - flags = h_flag | (((rg.a - 1) >> 1) & z_flag); - goto loop; - - case 0xB0: // OR B - case 0xB1: // OR C - case 0xB2: // OR D - case 0xB3: // OR E - case 0xB4: // OR H - case 0xB5: // OR L - data = R8( op & 7 ); - goto or_comm; - - case 0xB6: // OR (HL) - data = READ( rp.hl ); - pc--; - case 0xF6: // OR IMM - pc++; - or_comm: - rg.a |= data; - case 0xB7: // OR A - flags = ((rg.a - 1) >> 1) & z_flag; - goto loop; - - case 0xA8: // XOR B - case 0xA9: // XOR C - case 0xAA: // XOR D - case 0xAB: // XOR E - case 0xAC: // XOR H - case 0xAD: // XOR L - data = R8( op & 7 ); - goto xor_comm; - - case 0xAE: // XOR (HL) - data = READ( rp.hl ); - pc--; - case 0xEE: // XOR IMM - pc++; - xor_comm: - data ^= rg.a; - rg.a = data; - data--; - flags = (data >> 1) & z_flag; - goto loop; - - case 0xAF: // XOR A - rg.a = 0; - flags = z_flag; - goto loop; - -// Stack - - case 0xF1: // POP FA - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL (common) - data = READ( sp ); - r16 [(op >> 4) & 3] = data + 0x100 * READ( sp + 1 ); - sp = (sp + 2) & 0xFFFF; - if ( op != 0xF1 ) - goto loop; - flags = rg.flags & 0xF0; - goto loop; - - case 0xC5: // PUSH BC - data = rp.bc; - goto push; - - case 0xD5: // PUSH DE - data = rp.de; - goto push; - - case 0xE5: // PUSH HL - data = rp.hl; - goto push; - - case 0xF5: // PUSH FA - data = (flags << 8) | rg.a; - goto push; - -// Flow control - - case 0xFF: - if ( pc == idle_addr + 1 ) - goto stop; - case 0xC7: case 0xCF: case 0xD7: case 0xDF: // RST - case 0xE7: case 0xEF: case 0xF7: - data = pc; - pc = (op & 0x38) + rst_base; - goto push; - - case 0xCC: // CZ - pc += 2; - if ( flags & z_flag ) - goto call; - goto loop; - - case 0xD4: // CNC - pc += 2; - if ( !(flags & c_flag) ) - goto call; - goto loop; - - case 0xDC: // CC - pc += 2; - if ( flags & c_flag ) - goto call; - goto loop; - - case 0xD9: // RETI - //interrupts_enabled = 1; - goto ret; - - case 0xC0: // RZ - if ( !(flags & z_flag) ) - goto ret; - goto loop; - - case 0xD0: // RNC - if ( !(flags & c_flag) ) - goto ret; - goto loop; - - case 0xD8: // RC - if ( flags & c_flag ) - goto ret; - goto loop; - - case 0x18: // JR - BRANCH( true ) - - case 0x30: // JR NC - BRANCH( !(flags & c_flag) ) - - case 0x38: // JR C - BRANCH( flags & c_flag ) - - case 0xE9: // JP_HL - pc = rp.hl; - goto loop; - - case 0xC3: // JP (next-most-common) - pc = GET_ADDR(); - goto loop; - - case 0xC2: // JP NZ - pc += 2; - if ( !(flags & z_flag) ) - goto jp_taken; - goto loop; - - case 0xCA: // JP Z (most common) - pc += 2; - if ( !(flags & z_flag) ) - goto loop; - jp_taken: - pc -= 2; - pc = GET_ADDR(); - goto loop; - - case 0xD2: // JP NC - pc += 2; - if ( !(flags & c_flag) ) - goto jp_taken; - goto loop; - - case 0xDA: // JP C - pc += 2; - if ( flags & c_flag ) - goto jp_taken; - goto loop; - -// Flags - - case 0x2F: // CPL - rg.a = ~rg.a; - flags |= n_flag | h_flag; - goto loop; - - case 0x3F: // CCF - flags = (flags ^ c_flag) & ~(n_flag | h_flag); - goto loop; - - case 0x37: // SCF - flags = (flags | c_flag) & ~(n_flag | h_flag); - goto loop; - - case 0xF3: // DI - //interrupts_enabled = 0; - goto loop; - - case 0xFB: // EI - //interrupts_enabled = 1; - goto loop; - -// Special - - case 0xDD: case 0xD3: case 0xDB: case 0xE3: case 0xE4: // ? - case 0xEB: case 0xEC: case 0xF4: case 0xFD: case 0xFC: - case 0x10: // STOP - case 0x27: // DAA (I'll have to implement this eventually...) - case 0xBF: - case 0xED: // Z80 prefix - case 0x76: // HALT - s.remain++; - goto stop; - } - - // If this fails then the case above is missing an opcode - assert( false ); - -stop: - pc--; - - // copy state back - STATIC_CAST(core_regs_t&,r) = rg; - r.pc = pc; - r.sp = sp; - r.flags = flags; - - this->state = &state_; - memcpy( &this->state_, &s, sizeof this->state_ ); - - return s.remain > 0; + for ( int offset = 0; offset < size; offset += page_size ) + set_code_page( (start + offset) >> page_bits, STATIC_CAST(char*,data) + offset ); } diff --git a/Frameworks/GME/gme/Gb_Cpu.h b/Frameworks/GME/gme/Gb_Cpu.h old mode 100755 new mode 100644 index 953fbaf50..eb9fc81a1 --- a/Frameworks/GME/gme/Gb_Cpu.h +++ b/Frameworks/GME/gme/Gb_Cpu.h @@ -1,93 +1,82 @@ // Nintendo Game Boy CPU emulator -// Treats every instruction as taking 4 cycles -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef GB_CPU_H #define GB_CPU_H #include "blargg_common.h" -#include "blargg_endian.h" - -typedef unsigned gb_addr_t; // 16-bit CPU address class Gb_Cpu { - enum { clocks_per_instr = 4 }; public: - typedef BOOST::uint8_t uint8_t; + typedef int addr_t; + typedef BOOST::uint8_t byte; - // Clear registers and map all pages to unmapped - void reset( void* unmapped = 0 ); + enum { mem_size = 0x10000 }; - // Map code memory (memory accessed via the program counter). Start and size + // Clears registers and map all pages to unmapped + void reset( void* unmapped = NULL ); + + // Maps code memory (memory accessed via the program counter). Start and size // must be multiple of page_size. - enum { page_size = 0x2000 }; - void map_code( gb_addr_t start, unsigned size, void* code ); + enum { page_bits = 13 }; + enum { page_size = 1 << page_bits }; + void map_code( addr_t start, int size, void* code ); - uint8_t* get_code( gb_addr_t ); + // Accesses emulated memory as CPU does + byte* get_code( addr_t ); - // Push a byte on the stack - void push_byte( int ); - - // Game Boy Z80 registers. *Not* kept updated during a call to run(). + // Game Boy Z-80 registers. NOT kept updated during emulation. struct core_regs_t { - #if BLARGG_BIG_ENDIAN - uint8_t b, c, d, e, h, l, flags, a; - #else - uint8_t c, b, e, d, l, h, a, flags; - #endif + BOOST::uint16_t bc, de, hl, fa; }; struct registers_t : core_regs_t { - long pc; // more than 16 bits to allow overflow detection + int pc; // more than 16 bits to allow overflow detection BOOST::uint16_t sp; }; registers_t r; - // Interrupt enable flag set by EI and cleared by DI - //bool interrupts_enabled; // unused + // Base address for RST vectors, to simplify GBS player (normally 0) + addr_t rst_base; - // Base address for RST vectors (normally 0) - gb_addr_t rst_base; + // Current time. + int time() const { return cpu_state->time; } - // If CPU executes opcode 0xFF at this address, it treats as illegal instruction - enum { idle_addr = 0xF00D }; + // Changes time. Must not be called during emulation. + // Should be negative, because emulation stops once it becomes >= 0. + void set_time( int t ) { cpu_state->time = t; } - // Run CPU for at least 'count' cycles and return false, or return true if - // illegal instruction is encountered. - bool run( blargg_long count ); - - // Number of clock cycles remaining for most recent run() call - blargg_long remain() const { return state->remain * clocks_per_instr; } - - // Can read this many bytes past end of a page + // Emulator reads this many bytes past end of a page enum { cpu_padding = 8 }; + +// Implementation public: - Gb_Cpu() : rst_base( 0 ) { state = &state_; } - enum { page_shift = 13 }; - enum { page_count = 0x10000 >> page_shift }; -private: - // noncopyable - Gb_Cpu( const Gb_Cpu& ); - Gb_Cpu& operator = ( const Gb_Cpu& ); + Gb_Cpu() : rst_base( 0 ) { cpu_state = &cpu_state_; } + enum { page_count = mem_size >> page_bits }; - struct state_t { - uint8_t* code_map [page_count + 1]; - blargg_long remain; + struct cpu_state_t { + byte* code_map [page_count + 1]; + int time; }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; + cpu_state_t* cpu_state; // points to state_ or a local copy within run() + cpu_state_t cpu_state_; - void set_code_page( int, uint8_t* ); +private: + void set_code_page( int, void* ); }; -inline BOOST::uint8_t* Gb_Cpu::get_code( gb_addr_t addr ) +#define GB_CPU_PAGE( addr ) ((unsigned) (addr) >> Gb_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define GB_CPU_OFFSET( addr ) (addr) +#else + #define GB_CPU_OFFSET( addr ) ((addr) & (Gb_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t* Gb_Cpu::get_code( addr_t addr ) { - return state->code_map [addr >> page_shift] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; + return cpu_state_.code_map [GB_CPU_PAGE( addr )] + GB_CPU_OFFSET( addr ); } #endif diff --git a/Frameworks/GME/gme/Gb_Oscs.cpp b/Frameworks/GME/gme/Gb_Oscs.cpp old mode 100755 new mode 100644 index 735653fa9..08e06e31b --- a/Frameworks/GME/gme/Gb_Oscs.cpp +++ b/Frameworks/GME/gme/Gb_Oscs.cpp @@ -1,10 +1,8 @@ -// Gb_Snd_Emu 0.1.5. http://www.slack.net/~ant/ +// Gb_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Gb_Apu.h" -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -17,320 +15,698 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// Gb_Osc +bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games +bool const cgb_05 = false; // enables CGB-05 zombie behavior + +int const trigger_mask = 0x80; +int const length_enabled = 0x40; void Gb_Osc::reset() { - delay = 0; + output = NULL; last_amp = 0; - length = 0; - output_select = 3; - output = outputs [output_select]; + delay = 0; + phase = 0; + enabled = false; } -void Gb_Osc::clock_length() +inline void Gb_Osc::update_amp( blip_time_t time, int new_amp ) { - if ( (regs [4] & len_enabled_mask) && length ) - length--; -} - -// Gb_Env - -void Gb_Env::clock_envelope() -{ - if ( env_delay && !--env_delay ) + output->set_modified(); + int delta = new_amp - last_amp; + if ( delta ) { - env_delay = regs [2] & 7; - int v = volume - 1 + (regs [2] >> 2 & 2); - if ( (unsigned) v < 15 ) - volume = v; + last_amp = new_amp; + fast_synth->offset( time, delta, output ); } } -bool Gb_Env::write_register( int reg, int data ) +// Units + +void Gb_Osc::clock_length() { + if ( (regs [4] & length_enabled) && length_ctr ) + { + if ( --length_ctr <= 0 ) + enabled = false; + } +} + +inline int Gb_Env::reload_env_timer() +{ + int raw = regs [2] & 7; + env_delay = (raw ? raw : 8); + return raw; +} + +void Gb_Env::clock_envelope() +{ + if ( env_enabled && --env_delay <= 0 && reload_env_timer() ) + { + int v = volume + (regs [2] & 0x08 ? +1 : -1); + if ( 0 <= v && v <= 15 ) + volume = v; + else + env_enabled = false; + } +} + +inline void Gb_Sweep_Square::reload_sweep_timer() +{ + sweep_delay = (regs [0] & period_mask) >> 4; + if ( !sweep_delay ) + sweep_delay = 8; +} + +void Gb_Sweep_Square::calc_sweep( bool update ) +{ + int const shift = regs [0] & shift_mask; + int const delta = sweep_freq >> shift; + sweep_neg = (regs [0] & 0x08) != 0; + int const freq = sweep_freq + (sweep_neg ? -delta : delta); + + if ( freq > 0x7FF ) + { + enabled = false; + } + else if ( shift && update ) + { + sweep_freq = freq; + + regs [3] = freq & 0xFF; + regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07); + } +} + +void Gb_Sweep_Square::clock_sweep() +{ + if ( --sweep_delay <= 0 ) + { + reload_sweep_timer(); + if ( sweep_enabled && (regs [0] & period_mask) ) + { + calc_sweep( true ); + calc_sweep( false ); + } + } +} + +int Gb_Wave::access( int addr ) const +{ + if ( enabled ) + { + addr = phase & (bank_size - 1); + if ( mode == Gb_Apu::mode_dmg ) + { + addr++; + if ( delay > clk_mul ) + return -1; // can only access within narrow time window while playing + } + addr >>= 1; + } + return addr & 0x0F; +} + +// write_register + +int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data ) +{ + int data = regs [4]; + + if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr ) + { + if ( (data & length_enabled) || cgb_02 ) + length_ctr--; + } + + if ( data & trigger_mask ) + { + enabled = true; + if ( !length_ctr ) + { + length_ctr = max_len; + if ( (frame_phase & 1) && (data & length_enabled) ) + length_ctr--; + } + } + + if ( !length_ctr ) + enabled = false; + + return data & trigger_mask; +} + +inline void Gb_Env::zombie_volume( int old, int data ) +{ + int v = volume; + if ( mode == Gb_Apu::mode_agb || cgb_05 ) + { + // CGB-05 behavior, very close to AGB behavior as well + if ( (old ^ data) & 8 ) + { + if ( !(old & 8) ) + { + v++; + if ( old & 7 ) + v++; + } + + v = 16 - v; + } + else if ( (old & 0x0F) == 8 ) + { + v++; + } + } + else + { + // CGB-04&02 behavior, very close to MGB behavior as well + if ( !(old & 7) && env_enabled ) + v++; + else if ( !(old & 8) ) + v += 2; + + if ( (old ^ data) & 8 ) + v = 16 - v; + } + volume = v & 0x0F; +} + +bool Gb_Env::write_register( int frame_phase, int reg, int old, int data ) +{ + int const max_len = 64; + switch ( reg ) { case 1: - length = 64 - (regs [1] & 0x3F); + length_ctr = max_len - (data & (max_len - 1)); break; case 2: - if ( !(data >> 4) ) + if ( !dac_enabled() ) enabled = false; + + zombie_volume( old, data ); + + if ( (data & 7) && env_delay == 8 ) + { + env_delay = 1; + clock_envelope(); // TODO: really happens at next length clock + } break; case 4: - if ( data & trigger ) + if ( write_trig( frame_phase, max_len, old ) ) { - env_delay = regs [2] & 7; volume = regs [2] >> 4; - enabled = true; - if ( length == 0 ) - length = 64; + reload_env_timer(); + env_enabled = true; + if ( frame_phase == 7 ) + env_delay++; + if ( !dac_enabled() ) + enabled = false; return true; } } return false; } -// Gb_Square - -void Gb_Square::reset() +bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data ) { - phase = 0; - sweep_freq = 0; - sweep_delay = 0; - Gb_Env::reset(); + bool result = Gb_Env::write_register( frame_phase, reg, old_data, data ); + if ( result ) + delay = (delay & (4 * clk_mul - 1)) + period(); + return result; } -void Gb_Square::clock_sweep() +inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data ) { - int sweep_period = (regs [0] & period_mask) >> 4; - if ( sweep_period && sweep_delay && !--sweep_delay ) + if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) ) { - sweep_delay = sweep_period; - regs [3] = sweep_freq & 0xFF; - regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); - - int offset = sweep_freq >> (regs [0] & shift_mask); - if ( regs [0] & 0x08 ) - offset = -offset; - sweep_freq += offset; - - if ( sweep_freq < 0 ) - { - sweep_freq = 0; - } - else if ( sweep_freq >= 2048 ) - { - sweep_delay = 0; // don't modify channel frequency any further - sweep_freq = 2048; // silence sound immediately - } + phase = 0x7FFF; + delay += 8 * clk_mul; } } -void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) +inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data ) { - if ( sweep_freq == 2048 ) - playing = false; + if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) ) + enabled = false; // sweep negate disabled after used - static unsigned char const table [4] = { 1, 2, 4, 6 }; - int const duty = table [regs [1] >> 6]; - int amp = volume & playing; - if ( phase >= duty ) - amp = -amp; - - int frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 + if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) ) { - // really high frequency results in DC at half volume - amp = volume >> 1; - playing = false; + sweep_freq = frequency(); + sweep_neg = false; + reload_sweep_timer(); + sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0; + if ( regs [0] & shift_mask ) + calc_sweep( false ); } - - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - int const period = (2048 - frequency) * 4; - Blip_Buffer* const output = this->output; - int phase = this->phase; - int delta = amp * 2; - do - { - phase = (phase + 1) & 7; - if ( phase == 0 || phase == duty ) - { - delta = -delta; - synth->offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->phase = phase; - last_amp = delta >> 1; - } - delay = time - end_time; } -// Gb_Noise - -void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) +void Gb_Wave::corrupt_wave() { - int amp = volume & playing; - int tap = 13 - (regs [3] & 8); - if ( bits >> tap & 2 ) - amp = -amp; - - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( !playing ) - time = end_time; - - if ( time < end_time ) - { - static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; - int period = table [regs [3] & 7] << (regs [3] >> 4); - - // keep parallel resampled time to eliminate time conversion in the loop - Blip_Buffer* const output = this->output; - const blip_resampled_time_t resampled_period = - output->resampled_duration( period ); - blip_resampled_time_t resampled_time = output->resampled_time( time ); - unsigned bits = this->bits; - int delta = amp * 2; - - do - { - unsigned changed = (bits >> tap) + 1; - time += period; - bits <<= 1; - if ( changed & 2 ) - { - delta = -delta; - bits |= 1; - synth->offset_resampled( resampled_time, delta, output ); - } - resampled_time += resampled_period; - } - while ( time < end_time ); - - this->bits = bits; - last_amp = delta >> 1; - } - delay = time - end_time; + int pos = ((phase + 1) & (bank_size - 1)) >> 1; + if ( pos < 4 ) + wave_ram [0] = wave_ram [pos]; + else + for ( int i = 4; --i >= 0; ) + wave_ram [i] = wave_ram [(pos & ~3) + i]; } -// Gb_Wave - -inline void Gb_Wave::write_register( int reg, int data ) +inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data ) { + int const max_len = 256; + switch ( reg ) { case 0: - if ( !(data & 0x80) ) + if ( !dac_enabled() ) enabled = false; break; case 1: - length = 256 - regs [1]; - break; - - case 2: - volume = data >> 5 & 3; + length_ctr = max_len - data; break; case 4: - if ( data & trigger & regs [0] ) + bool was_enabled = enabled; + if ( write_trig( frame_phase, max_len, old_data ) ) { - wave_pos = 0; - enabled = true; - if ( length == 0 ) - length = 256; + if ( !dac_enabled() ) + enabled = false; + else if ( mode == Gb_Apu::mode_dmg && was_enabled && + (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul ) + corrupt_wave(); + + phase = 0; + delay = period() + 6 * clk_mul; } } } -void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) +void Gb_Apu::write_osc( int reg, int old_data, int data ) { - int volume_shift = (volume - 1) & 7; // volume = 0 causes shift = 7 - int frequency; + int index = (reg * 3 + 3) >> 4; // avoids divide + assert( index == reg / 5 ); + reg -= index * 5; + switch ( index ) { - int amp = (wave [wave_pos] >> volume_shift & playing) * 2; - - frequency = this->frequency(); - if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 + case 0: square1.write_register( frame_phase, reg, old_data, data ); break; + case 1: square2.write_register( frame_phase, reg, old_data, data ); break; + case 2: wave .write_register( frame_phase, reg, old_data, data ); break; + case 3: noise .write_register( frame_phase, reg, old_data, data ); break; + } +} + +// Synthesis + +void Gb_Square::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc duty and phase + static byte const duty_offsets [4] = { 1, 1, 3, 7 }; + static byte const duties [4] = { 1, 2, 4, 6 }; + int const duty_code = regs [1] >> 6; + int duty_offset = duty_offsets [duty_code]; + int duty = duties [duty_code]; + if ( mode == Gb_Apu::mode_agb ) + { + // AGB uses inverted duty + duty_offset -= duty; + duty = 8 - duty; + } + int ph = (this->phase + duty_offset) & 7; + + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) { - amp = 30 >> volume_shift & playing; - playing = false; - } - - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + // Play inaudible frequencies as constant amplitude + if ( frequency() >= 0x7FA && delay < 32 * clk_mul ) + { + amp += (vol * duty) >> 3; + vol = 0; + } + + if ( ph < duty ) + { + amp += vol; + vol = -vol; + } } + update_amp( time, amp ); } + // Generate wave time += delay; - if ( !playing ) - time = end_time; - if ( time < end_time ) { - Blip_Buffer* const output = this->output; - int const period = (2048 - frequency) * 2; - int wave_pos = (this->wave_pos + 1) & (wave_size - 1); - - do + int const per = this->period(); + if ( !vol ) { - int amp = (wave [wave_pos] >> volume_shift) * 2; - wave_pos = (wave_pos + 1) & (wave_size - 1); - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset_inline( time, delta, output ); - } - time += period; + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + #endif } - while ( time < end_time ); - - this->wave_pos = (wave_pos - 1) & (wave_size - 1); + else + { + // Output amplitude transitions + int delta = vol; + do + { + ph = (ph + 1) & 7; + if ( ph == 0 || ph == duty ) + { + norm_synth->offset_inline( time, delta, out ); + delta = -delta; + } + time += per; + } + while ( time < end_time ); + + if ( delta != vol ) + last_amp -= delta; + } + this->phase = (ph - duty_offset) & 7; } delay = time - end_time; } -// Gb_Apu::write_osc - -void Gb_Apu::write_osc( int index, int reg, int data ) +#if !GB_APU_FAST +// Quickly runs LFSR for a large number of clocks. For use when noise is generating +// no sound. +static unsigned run_lfsr( unsigned s, unsigned mask, int count ) { - reg -= index * 5; - Gb_Square* sq = &square2; - switch ( index ) + bool const optimized = true; // set to false to use only unoptimized loop in middle + + // optimization used in several places: + // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n) + + if ( mask == 0x4000 && optimized ) { - case 0: - sq = &square1; - case 1: - if ( sq->write_register( reg, data ) && index == 0 ) + if ( count >= 32767 ) + count %= 32767; + + // Convert from Fibonacci to Galois configuration, + // shifted left 1 bit + s ^= (s & 1) * 0x8000; + + // Each iteration is equivalent to clocking LFSR 255 times + while ( (count -= 255) > 0 ) + s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3); + count += 255; + + // Each iteration is equivalent to clocking LFSR 15 times + // (interesting similarity to single clocking below) + while ( (count -= 15) > 0 ) + s ^= ((s & 2) * (3 << 13)) ^ (s >> 1); + count += 15; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 2) * (3 << 13)) ^ (s >> 1); + + // Convert back to Fibonacci configuration + s &= 0x7FFF; + } + else if ( count < 8 || !optimized ) + { + // won't fully replace upper 8 bits, so have to do the unoptimized way + while ( --count >= 0 ) + s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2)); + } + else + { + if ( count > 127 ) { - square1.sweep_freq = square1.frequency(); - if ( (regs [0] & sq->period_mask) && (regs [0] & sq->shift_mask) ) + count %= 127; + if ( !count ) + count = 127; // must run at least once + } + + // Need to keep one extra bit of history + s = s << 1 & 0xFF; + + // Convert from Fibonacci to Galois configuration, + // shifted left 2 bits + s ^= (s & 2) * 0x80; + + // Each iteration is equivalent to clocking LFSR 7 times + // (interesting similarity to single clocking below) + while ( (count -= 7) > 0 ) + s ^= ((s & 4) * (3 << 5)) ^ (s >> 1); + count += 7; + + // Remaining singles + while ( --count >= 0 ) + s = ((s & 4) * (3 << 5)) ^ (s >> 1); + + // Convert back to Fibonacci configuration and + // repeat last 8 bits above significant 7 + s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F); + } + + return s; +} +#endif + +void Gb_Noise::run( blip_time_t time, blip_time_t end_time ) +{ + // Determine what will be generated + int vol = 0; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + if ( enabled ) + vol = this->volume; + + amp = -dac_bias; + if ( mode == Gb_Apu::mode_agb ) + amp = -(vol >> 1); + + if ( !(phase & 1) ) { - square1.sweep_delay = 1; // cause sweep to recalculate now - square1.clock_sweep(); + amp += vol; + vol = -vol; } } - break; - - case 2: - wave.write_register( reg, data ); - break; - - case 3: - if ( noise.write_register( reg, data ) ) - noise.bits = 0x7FFF; + + // AGB negates final output + if ( mode == Gb_Apu::mode_agb ) + { + vol = -vol; + amp = -amp; + } + + update_amp( time, amp ); } + + // Run timer and calculate time of next LFSR clock + static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 }; + int const period1 = period1s [regs [3] & 7] * clk_mul; + + #if GB_APU_FAST + time += delay; + #else + { + int extra = (end_time - time) - delay; + int const per2 = this->period2(); + time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1; + + int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1); + divider = (divider - count) & period2_mask; + delay = count * period1 - extra; + } + #endif + + // Generate wave + if ( time < end_time ) + { + unsigned const mask = this->lfsr_mask(); + unsigned bits = this->phase; + + int per = period2( period1 * 8 ); + #if GB_APU_FAST + // Noise can be THE biggest time hog; adjust as necessary + int const min_period = 24; + if ( per < min_period ) + per = min_period; + #endif + if ( period2_index() >= 0xE ) + { + time = end_time; + } + else if ( !vol ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + time += (blip_time_t) count * per; + bits = run_lfsr( bits, ~mask, count ); + #endif + } + else + { + Blip_Synth_Fast const* const synth = fast_synth; // cache + + // Output amplitude transitions + int delta = -vol; + do + { + unsigned changed = bits + 1; + bits = bits >> 1 & mask; + if ( changed & 2 ) + { + bits |= ~mask; + delta = -delta; + synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + + if ( delta == vol ) + last_amp += delta; + } + this->phase = bits; + } + + #if GB_APU_FAST + delay = time - end_time; + #endif +} + +void Gb_Wave::run( blip_time_t time, blip_time_t end_time ) +{ + // Calc volume +#if GB_APU_NO_AGB + static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 }; + int const volume_idx = regs [2] >> 5 & 3; + int const volume_shift = shifts [volume_idx]; + int const volume_mul = 1; +#else + static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 }; + int const volume_shift = 2 + 4; + int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB + int const volume_mul = volumes [volume_idx]; +#endif + + // Determine what will be generated + int playing = false; + Blip_Buffer* const out = this->output; + if ( out ) + { + int amp = dac_off_amp; + if ( dac_enabled() ) + { + // Play inaudible frequencies as constant amplitude + amp = 8 << 4; // really depends on average of all samples in wave + + // if delay is larger, constant amplitude won't start yet + if ( frequency() <= 0x7FB || delay > 15 * clk_mul ) + { + if ( volume_mul && volume_shift != 4+4 ) + playing = (int) enabled; + + amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing; + } + + amp = ((amp * volume_mul) >> volume_shift) - dac_bias; + } + update_amp( time, amp ); + } + + // Generate wave + time += delay; + if ( time < end_time ) + { + byte const* wave = this->wave_ram; + + // wave size and bank + #if GB_APU_NO_AGB + int const wave_mask = 0x1F; + int const swap_banks = 0; + #else + int const size20_mask = 0x20; + int const flags = regs [0] & agb_mask; + int const wave_mask = (flags & size20_mask) | 0x1F; + int swap_banks = 0; + if ( flags & bank40_mask ) + { + swap_banks = flags & size20_mask; + wave += bank_size/2 - (swap_banks >> 1); + } + #endif + + int ph = this->phase ^ swap_banks; + ph = (ph + 1) & wave_mask; // pre-advance + + int const per = this->period(); + if ( !playing ) + { + #if GB_APU_FAST + time = end_time; + #else + // Maintain phase when not playing + int count = (end_time - time + per - 1) / per; + ph += count; // will be masked below + time += (blip_time_t) count * per; + #endif + } + else + { + Blip_Synth_Fast const* const synth = fast_synth; // cache + + // Output amplitude transitions + int lamp = this->last_amp + dac_bias; + do + { + // Extract nibble + int nibble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0; + ph = (ph + 1) & wave_mask; + + // Scale by volume + int amp = (nibble * volume_mul) >> volume_shift; + + int delta = amp - lamp; + if ( delta ) + { + lamp = amp; + synth->offset_inline( time, delta, out ); + } + time += per; + } + while ( time < end_time ); + this->last_amp = lamp - dac_bias; + } + ph = (ph - 1) & wave_mask; // undo pre-advance and mask position + + // Keep track of last byte read + if ( enabled ) + sample_buf = wave [ph >> 1]; + + this->phase = ph ^ swap_banks; // undo swapped banks + } + delay = time - end_time; } diff --git a/Frameworks/GME/gme/Gb_Oscs.h b/Frameworks/GME/gme/Gb_Oscs.h old mode 100755 new mode 100644 index d7f88ea14..fb29853e3 --- a/Frameworks/GME/gme/Gb_Oscs.h +++ b/Frameworks/GME/gme/Gb_Oscs.h @@ -1,83 +1,188 @@ // Private oscillators used by Gb_Apu -// Gb_Snd_Emu 0.1.5 +// Gb_Snd_Emu $vers #ifndef GB_OSCS_H #define GB_OSCS_H #include "blargg_common.h" #include "Blip_Buffer.h" -struct Gb_Osc -{ - enum { trigger = 0x80 }; - enum { len_enabled_mask = 0x40 }; +#ifndef GB_APU_OVERCLOCK + #define GB_APU_OVERCLOCK 1 +#endif + +#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1) + #error "GB_APU_OVERCLOCK must be a power of 2" +#endif + +class Gb_Osc { +protected: - Blip_Buffer* outputs [4]; // NULL, right, left, center - Blip_Buffer* output; - int output_select; - BOOST::uint8_t* regs; // osc's 5 registers - - int delay; - int last_amp; - int volume; - int length; - int enabled; - - void reset(); - void clock_length(); + // 11-bit frequency in NRx3 and NRx4 int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } -}; - -struct Gb_Env : Gb_Osc -{ - int env_delay; + void update_amp( blip_time_t, int new_amp ); + int write_trig( int frame_phase, int max_len, int old_data ); +public: + + enum { clk_mul = GB_APU_OVERCLOCK }; + enum { dac_bias = 7 }; + + Blip_Buffer* outputs [4];// NULL, right, left, center + Blip_Buffer* output; // where to output sound + BOOST::uint8_t* regs; // osc's 5 registers + int mode; // mode_dmg, mode_cgb, mode_agb + int dac_off_amp;// amplitude when DAC is off + int last_amp; // current amplitude in Blip_Buffer + Blip_Synth_Norm const* norm_synth; + Blip_Synth_Fast const* fast_synth; + + int delay; // clocks until frequency timer expires + int length_ctr; // length counter + unsigned phase; // waveform phase (or equivalent) + bool enabled; // internal enabled flag + + void clock_length(); void reset(); - void clock_envelope(); - bool write_register( int, int ); }; -struct Gb_Square : Gb_Env -{ +class Gb_Env : public Gb_Osc { +public: + int env_delay; + int volume; + bool env_enabled; + + void clock_envelope(); + bool write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + env_delay = 0; + volume = 0; + Gb_Osc::reset(); + } +protected: + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [2] & 0xF8; } +private: + void zombie_volume( int old, int data ); + int reload_env_timer(); +}; + +class Gb_Square : public Gb_Env { +public: + bool write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + void reset() + { + Gb_Env::reset(); + delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger) + } +private: + // Frequency timer period + int period() const { return (2048 - frequency()) * (4 * clk_mul); } +}; + +class Gb_Sweep_Square : public Gb_Square { +public: + int sweep_freq; + int sweep_delay; + bool sweep_enabled; + bool sweep_neg; + + void clock_sweep(); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + sweep_freq = 0; + sweep_delay = 0; + sweep_enabled = false; + sweep_neg = false; + Gb_Square::reset(); + } +private: enum { period_mask = 0x70 }; enum { shift_mask = 0x07 }; - typedef Blip_Synth Synth; - Synth const* synth; - int sweep_delay; - int sweep_freq; - int phase; - - void reset(); - void clock_sweep(); - void run( blip_time_t, blip_time_t, int playing ); + void calc_sweep( bool update ); + void reload_sweep_timer(); }; -struct Gb_Noise : Gb_Env -{ - typedef Blip_Synth Synth; - Synth const* synth; - unsigned bits; +class Gb_Noise : public Gb_Env { +public: - void run( blip_time_t, blip_time_t, int playing ); + int divider; // noise has more complex frequency divider setup + + void run( blip_time_t, blip_time_t ); + void write_register( int frame_phase, int reg, int old_data, int data ); + + void reset() + { + divider = 0; + Gb_Env::reset(); + delay = 4 * clk_mul; // TODO: remove? + } +private: + enum { period2_mask = 0x1FFFF }; + + int period2_index() const { return regs [3] >> 4; } + int period2( int base = 8 ) const { return base << period2_index(); } + unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; } }; -struct Gb_Wave : Gb_Osc -{ - typedef Blip_Synth Synth; - Synth const* synth; - int wave_pos; - enum { wave_size = 32 }; - BOOST::uint8_t wave [wave_size]; +class Gb_Wave : public Gb_Osc { +public: + int sample_buf; // last wave RAM byte read (hardware has this as well) - void write_register( int, int ); - void run( blip_time_t, blip_time_t, int playing ); + void write_register( int frame_phase, int reg, int old_data, int data ); + void run( blip_time_t, blip_time_t ); + + // Reads/writes wave RAM + int read( int addr ) const; + void write( int addr, int data ); + + void reset() + { + sample_buf = 0; + Gb_Osc::reset(); + } + +private: + enum { bank40_mask = 0x40 }; + enum { bank_size = 32 }; + + int agb_mask; // 0xFF if AGB features enabled, 0 otherwise + BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU + + friend class Gb_Apu; + + // Frequency timer period + int period() const { return (2048 - frequency()) * (2 * clk_mul); } + + // Non-zero if DAC is enabled + int dac_enabled() const { return regs [0] & 0x80; } + + void corrupt_wave(); + + BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; } + + // Wave index that would be accessed, or -1 if no access would occur + int access( int addr ) const; }; -inline void Gb_Env::reset() +inline int Gb_Wave::read( int addr ) const { - env_delay = 0; - Gb_Osc::reset(); + int index = access( addr ); + return (index < 0 ? 0xFF : wave_bank() [index]); +} + +inline void Gb_Wave::write( int addr, int data ) +{ + int index = access( addr ); + if ( index >= 0 ) + wave_bank() [index] = data;; } #endif diff --git a/Frameworks/GME/gme/Gbs_Emu.cpp b/Frameworks/GME/gme/Gbs_Emu.cpp old mode 100755 new mode 100644 index 30a147e5f..383066e03 --- a/Frameworks/GME/gme/Gbs_Emu.cpp +++ b/Frameworks/GME/gme/Gbs_Emu.cpp @@ -1,288 +1,167 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gbs_Emu.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gbs_Emu.h" + +/* Copyright (C) 2003-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000 }; -Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 300 }; +Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000, 0,0,0,0,0,0,0,0 }; +Gbs_Emu::equalizer_t const Gbs_Emu::cgb_eq = { 0.0, 300, 0,0,0,0,0,0,0,0 }; +Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 30, 0,0,0,0,0,0,0,0 }; // DMG Gbs_Emu::Gbs_Emu() { - set_type( gme_gbs_type ); - - static const char* const names [Gb_Apu::osc_count] = { - "Square 1", "Square 2", "Wave", "Noise" - }; - set_voice_names( names ); - - static int const types [Gb_Apu::osc_count] = { - wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0 - }; - set_voice_types( types ); - - set_silence_lookahead( 6 ); - set_max_initial_silence( 21 ); + sound_hardware = sound_gbs; + enable_clicking( false ); + set_type( gme_gbs_type ); + set_silence_lookahead( 6 ); + set_max_initial_silence( 21 ); set_gain( 1.2 ); - - static equalizer_t const eq = { -1.0, 120 }; + + // kind of midway between headphones and speaker + static equalizer_t const eq = { -1.0, 120, 0,0,0,0,0,0,0,0 }; set_equalizer( eq ); } -Gbs_Emu::~Gbs_Emu() { } - -void Gbs_Emu::unload() -{ - rom.clear(); - Music_Emu::unload(); -} - -// Track info - -static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) -{ - GME_COPY_FIELD( h, out, game ); - GME_COPY_FIELD( h, out, author ); - GME_COPY_FIELD( h, out, copyright ); -} - -blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const -{ - copy_gbs_fields( header_, out ); - return 0; -} - -static blargg_err_t check_gbs_header( void const* header ) -{ - if ( memcmp( header, "GBS", 3 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Gbs_File : Gme_Info_ -{ - Gbs_Emu::header_t h; - - Gbs_File() { set_type( gme_gbs_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - - set_track_count( h.track_count ); - return check_gbs_header( &h ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_gbs_fields( h, out ); - return 0; - } -}; - -static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } -static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } - -gme_type_t_ const gme_gbs_type [1] = { "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }; - -// Setup - -blargg_err_t Gbs_Emu::load_( Data_Reader& in ) -{ - assert( offsetof (header_t,copyright [32]) == header_size ); - RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); - - set_track_count( header_.track_count ); - RETURN_ERR( check_gbs_header( &header_ ) ); - - if ( header_.vers != 1 ) - set_warning( "Unknown file version" ); - - if ( header_.timer_mode & 0x78 ) - set_warning( "Invalid timer mode" ); - - unsigned load_addr = get_le16( header_.load_addr ); - if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || - load_addr < 0x400 ) - set_warning( "Invalid load/init/play address" ); - - set_voice_count( Gb_Apu::osc_count ); - - apu.volume( gain() ); - - return setup_buffer( 4194304 ); -} - -void Gbs_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - apu.osc_output( i, c, l, r ); -} - -// Emulation - -// see gb_cpu_io.h for read/write functions - -void Gbs_Emu::set_bank( int n ) -{ - blargg_long addr = rom.mask_addr( n * (blargg_long) bank_size ); - if ( addr == 0 && rom.size() > bank_size ) - { - // TODO: what is the correct behavior? Current Game & Watch Gallery - // rip requires that this have no effect or set to bank 1. - //dprintf( "Selected ROM bank 0\n" ); - return; - //n = 1; - } - cpu::map_code( bank_size, bank_size, rom.at_addr( addr ) ); -} - -void Gbs_Emu::update_timer() -{ - if ( header_.timer_mode & 0x04 ) - { - static byte const rates [4] = { 10, 4, 6, 8 }; - int shift = rates [ram [hi_page + 7] & 3] - (header_.timer_mode >> 7); - play_period = (256L - ram [hi_page + 6]) << shift; - } - else - { - play_period = 70224; // 59.73 Hz - } - if ( tempo() != 1.0 ) - play_period = blip_time_t (play_period / tempo()); -} - -static BOOST::uint8_t const sound_data [Gb_Apu::register_count] = { - 0x80, 0xBF, 0x00, 0x00, 0xBF, // square 1 - 0x00, 0x3F, 0x00, 0x00, 0xBF, // square 2 - 0x7F, 0xFF, 0x9F, 0x00, 0xBF, // wave - 0x00, 0xFF, 0x00, 0x00, 0xBF, // noise - 0x77, 0xF3, 0xF1, // vin/volume, status, power mode - 0, 0, 0, 0, 0, 0, 0, 0, 0, // unused - 0xAC, 0xDD, 0xDA, 0x48, 0x36, 0x02, 0xCF, 0x16, // waveform data - 0x2C, 0x04, 0xE5, 0x2C, 0xAC, 0xDD, 0xDA, 0x48 -}; - -void Gbs_Emu::cpu_jsr( gb_addr_t addr ) -{ - check( cpu::r.sp == get_le16( header_.stack_ptr ) ); - cpu::r.pc = addr; - cpu_write( --cpu::r.sp, idle_addr >> 8 ); - cpu_write( --cpu::r.sp, idle_addr&0xFF ); -} - -void Gbs_Emu::set_tempo_( double t ) -{ - apu.set_tempo( t ); - update_timer(); -} - -blargg_err_t Gbs_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( ram, 0, 0x4000 ); - memset( ram + 0x4000, 0xFF, 0x1F80 ); - memset( ram + 0x5F80, 0, sizeof ram - 0x5F80 ); - ram [hi_page] = 0; // joypad reads back as 0 - - apu.reset(); - for ( int i = 0; i < (int) sizeof sound_data; i++ ) - apu.write_register( 0, i + apu.start_addr, sound_data [i] ); - - cpu::reset( rom.unmapped() ); - - unsigned load_addr = get_le16( header_.load_addr ); - cpu::rst_base = load_addr; - rom.set_addr( load_addr ); - - cpu::map_code( ram_addr, 0x10000 - ram_addr, ram ); - cpu::map_code( 0, bank_size, rom.at_addr( 0 ) ); - set_bank( rom.size() > bank_size ); - - ram [hi_page + 6] = header_.timer_modulo; - ram [hi_page + 7] = header_.timer_mode; - update_timer(); - next_play = play_period; - - cpu::r.a = track; - cpu::r.pc = idle_addr; - cpu::r.sp = get_le16( header_.stack_ptr ); - cpu_time = 0; - cpu_jsr( get_le16( header_.init_addr ) ); - - return 0; -} - -blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) -{ - cpu_time = 0; - while ( cpu_time < duration ) - { - long count = duration - cpu_time; - cpu_time = duration; - bool result = cpu::run( count ); - cpu_time -= cpu::remain(); - - if ( result ) - { - if ( cpu::r.pc == idle_addr ) - { - if ( next_play > duration ) - { - cpu_time = duration; - break; - } - - if ( cpu_time < next_play ) - cpu_time = next_play; - next_play += play_period; - cpu_jsr( get_le16( header_.play_addr ) ); - GME_FRAME_HOOK( this ); - // TODO: handle timer rates different than 60 Hz - } - else if ( cpu::r.pc > 0xFFFF ) - { - dprintf( "PC wrapped around\n" ); - cpu::r.pc &= 0xFFFF; - } - else - { - set_warning( "Emulation error (illegal/unsupported instruction)" ); - dprintf( "Bad opcode $%.2x at $%.4x\n", - (int) *cpu::get_code( cpu::r.pc ), (int) cpu::r.pc ); - cpu::r.pc = (cpu::r.pc + 1) & 0xFFFF; - cpu_time += 6; - } - } - } - - duration = cpu_time; - next_play -= cpu_time; - if ( next_play < 0 ) // could go negative if routine is taking too long to return - next_play = 0; - apu.end_frame( cpu_time ); - - return 0; -} +Gbs_Emu::~Gbs_Emu() { } + +void Gbs_Emu::unload() +{ + core_.unload(); + Music_Emu::unload(); +} + +// Track info + +static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out ) +{ + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, author ); + GME_COPY_FIELD( h, out, copyright ); +} + +static void hash_gbs_file( Gbs_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.track_count, sizeof(h.track_count) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.stack_ptr[0], sizeof(h.stack_ptr) ); + out.hash_( &h.timer_modulo, sizeof(h.timer_modulo) ); + out.hash_( &h.timer_mode, sizeof(h.timer_mode) ); + out.hash_( data, data_size ); +} + +blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const +{ + copy_gbs_fields( header(), out ); + return blargg_ok; +} + +struct Gbs_File : Gme_Info_ +{ + Gbs_Emu::header_t const* h; + + Gbs_File() { set_type( gme_gbs_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( Gbs_Emu::header_t * ) begin; + + set_track_count( h->track_count ); + if ( !h->valid_tag() ) + return blargg_err_file_type; + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_gbs_fields( Gbs_Emu::header_t( *h ), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_gbs_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; } +static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; } + +gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }}; + +// Setup + +blargg_err_t Gbs_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core_.load( in ) ); + set_warning( core_.warning() ); + set_track_count( header().track_count ); + set_voice_count( Gb_Apu::osc_count ); + core_.apu().volume( gain() ); + + static const char* const names [Gb_Apu::osc_count] = { + "Square 1", "Square 2", "Wave", "Noise" + }; + set_voice_names( names ); + + static int const types [Gb_Apu::osc_count] = { + wave_type+1, wave_type+2, wave_type+3, mixed_type+1 + }; + set_voice_types( types ); + + return setup_buffer( 4194304 ); +} + +void Gbs_Emu::update_eq( blip_eq_t const& eq ) +{ + core_.apu().treble_eq( eq ); +} + +void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + core_.apu().set_output( i, c, l, r ); +} + +void Gbs_Emu::set_tempo_( double t ) +{ + core_.set_tempo( t ); +} + +blargg_err_t Gbs_Emu::start_track_( int track ) +{ + sound_t mode = sound_hardware; + if ( mode == sound_gbs ) + mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg; + + RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) ); + + // clear buffer AFTER track is started, eliminating initial click + return Classic_Emu::start_track_( track ); +} + +blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) +{ + return core_.end_frame( duration ); +} + +blargg_err_t Gbs_Emu::hash_( Hash_Function& out ) const +{ + hash_gbs_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Gbs_Emu.h b/Frameworks/GME/gme/Gbs_Emu.h old mode 100755 new mode 100644 index 93fe691e5..b070582b7 --- a/Frameworks/GME/gme/Gbs_Emu.h +++ b/Frameworks/GME/gme/Gbs_Emu.h @@ -1,88 +1,63 @@ // Nintendo Game Boy GBS music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef GBS_EMU_H #define GBS_EMU_H #include "Classic_Emu.h" -#include "Gb_Apu.h" -#include "Gb_Cpu.h" +#include "Gbs_Core.h" -class Gbs_Emu : private Gb_Cpu, public Classic_Emu { - typedef Gb_Cpu cpu; +class Gbs_Emu : public Classic_Emu { public: - // Equalizer profiles for Game Boy Color speaker and headphones + // Equalizer profiles for Game Boy speaker and headphones static equalizer_t const handheld_eq; static equalizer_t const headphones_eq; + static equalizer_t const cgb_eq; // Game Boy Color headphones have less bass - // GBS file header - enum { header_size = 112 }; - struct header_t - { - char tag [3]; - byte vers; - byte track_count; - byte first_track; - byte load_addr [2]; - byte init_addr [2]; - byte play_addr [2]; - byte stack_ptr [2]; - byte timer_modulo; - byte timer_mode; - char game [32]; - char author [32]; - char copyright [32]; - }; + // GBS file header (see Gbs_Core.h) + typedef Gbs_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return header_; } + header_t const& header() const { return core_.header(); } + + // Selects which sound hardware to use. AGB hardware is cleaner than the + // others. Doesn't take effect until next start_track(). + enum sound_t { + sound_dmg = Gb_Apu::mode_dmg, // Game Boy monochrome + sound_cgb = Gb_Apu::mode_cgb, // Game Boy Color + sound_agb = Gb_Apu::mode_agb, // Game Boy Advance + sound_gbs // Use DMG/CGB based on GBS (default) + }; + void set_sound( sound_t s ) { sound_hardware = s; } + + // If true, makes APU more accurate, which results in more clicking. + void enable_clicking( bool enable = true ) { core_.apu().reduce_clicks( !enable ); } static gme_type_t static_type() { return gme_gbs_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } + Gbs_Core& core() { return core_; } + + blargg_err_t hash_( Hash_Function& ) const; + +// Internal public: Gbs_Emu(); ~Gbs_Emu(); + protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); + // Overrides + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual void unload(); + private: - // rom - enum { bank_size = 0x4000 }; - Rom_Data rom; - void set_bank( int ); - - // timer - blip_time_t cpu_time; - blip_time_t play_period; - blip_time_t next_play; - void update_timer(); - - header_t header_; - void cpu_jsr( gb_addr_t ); - -public: private: friend class Gb_Cpu; - blip_time_t clock() const { return cpu_time - cpu::remain(); } - - enum { joypad_addr = 0xFF00 }; - enum { ram_addr = 0xA000 }; - enum { hi_page = 0xFF00 - ram_addr }; - byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; - Gb_Apu apu; - - int cpu_read( gb_addr_t ); - void cpu_write( gb_addr_t, int ); + sound_t sound_hardware; + Gbs_Core core_; }; #endif diff --git a/Frameworks/GME/gme/Gme_File.cpp b/Frameworks/GME/gme/Gme_File.cpp old mode 100755 new mode 100644 index 6821c3a56..377f04e76 --- a/Frameworks/GME/gme/Gme_File.cpp +++ b/Frameworks/GME/gme/Gme_File.cpp @@ -1,216 +1,183 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gme_File.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -const char gme_wrong_file_type [] = "Wrong file type for this emulator"; - -void Gme_File::clear_playlist() -{ - playlist.clear(); - clear_playlist_(); - track_count_ = raw_track_count_; -} - -void Gme_File::unload() -{ - clear_playlist(); // *before* clearing track count - track_count_ = 0; - raw_track_count_ = 0; - file_data.clear(); -} - -Gme_File::Gme_File() -{ - type_ = 0; - user_data_ = 0; - user_cleanup_ = 0; - unload(); // clears fields - blargg_verify_byte_order(); // used by most emulator types, so save them the trouble -} - -Gme_File::~Gme_File() -{ - if ( user_cleanup_ ) - user_cleanup_( user_data_ ); -} - -blargg_err_t Gme_File::load_mem_( byte const* data, long size ) -{ - require( data != file_data.begin() ); // load_mem_() or load_() must be overridden - Mem_File_Reader in( data, size ); - return load_( in ); -} - -blargg_err_t Gme_File::load_( Data_Reader& in ) -{ - RETURN_ERR( file_data.resize( in.remain() ) ); - RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); - return load_mem_( file_data.begin(), file_data.size() ); -} - -// public load functions call this at beginning -void Gme_File::pre_load() { unload(); } - -void Gme_File::post_load_() { } - -// public load functions call this at end -blargg_err_t Gme_File::post_load( blargg_err_t err ) -{ - if ( !track_count() ) - set_track_count( type()->track_count ); - if ( !err ) - post_load_(); - else - unload(); - - return err; -} - -// Public load functions - -blargg_err_t Gme_File::load_mem( void const* in, long size ) -{ - pre_load(); - return post_load( load_mem_( (byte const*) in, size ) ); -} - -blargg_err_t Gme_File::load( Data_Reader& in ) -{ - pre_load(); - return post_load( load_( in ) ); -} - -blargg_err_t Gme_File::load_file( const char* path ) -{ - pre_load(); - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - return post_load( load_( in ) ); -} - -blargg_err_t Gme_File::load_remaining_( void const* h, long s, Data_Reader& in ) -{ - Remaining_Reader rem( h, s, &in ); - return load( rem ); -} - -// Track info - -void Gme_File::copy_field_( char* out, const char* in, int in_size ) -{ - if ( !in || !*in ) - return; - - // remove spaces/junk from beginning - while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) - { - in++; - in_size--; - } - - // truncate - if ( in_size > max_field_ ) - in_size = max_field_; - - // find terminator - int len = 0; - while ( len < in_size && in [len] ) - len++; - - // remove spaces/junk from end - while ( len && unsigned (in [len - 1]) <= ' ' ) - len--; - - // copy - out [len] = 0; - memcpy( out, in, len ); - - // strip out stupid fields that should have been left blank - if ( !strcmp( out, "?" ) || !strcmp( out, "" ) || !strcmp( out, "< ? >" ) ) - out [0] = 0; -} - -void Gme_File::copy_field_( char* out, const char* in ) -{ - copy_field_( out, in, max_field_ ); -} - -blargg_err_t Gme_File::remap_track_( int* track_io ) const -{ - if ( (unsigned) *track_io >= (unsigned) track_count() ) - return "Invalid track"; - - if ( (unsigned) *track_io < (unsigned) playlist.size() ) - { - M3u_Playlist::entry_t const& e = playlist [*track_io]; - *track_io = 0; - if ( e.track >= 0 ) - { - *track_io = e.track; - if ( !(type_->flags_ & 0x02) ) - *track_io -= e.decimal_track; - } - if ( *track_io >= raw_track_count_ ) - return "Invalid track in m3u playlist"; - } - else - { - check( !playlist.size() ); - } - return 0; -} - -blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const -{ - out->track_count = track_count(); - out->length = -1; - out->loop_length = -1; - out->intro_length = -1; - out->song [0] = 0; - - out->game [0] = 0; - out->author [0] = 0; - out->copyright [0] = 0; - out->comment [0] = 0; - out->dumper [0] = 0; - out->system [0] = 0; - - copy_field_( out->system, type()->system ); - - int remapped = track; - RETURN_ERR( remap_track_( &remapped ) ); - RETURN_ERR( track_info_( out, remapped ) ); - - // override with m3u info - if ( playlist.size() ) - { - M3u_Playlist::info_t const& i = playlist.info(); - copy_field_( out->game , i.title ); - copy_field_( out->author, i.engineer ); - copy_field_( out->author, i.composer ); - copy_field_( out->dumper, i.ripping ); - - M3u_Playlist::entry_t const& e = playlist [track]; - copy_field_( out->song, e.name ); - if ( e.length >= 0 ) out->length = e.length * 1000L; - if ( e.intro >= 0 ) out->intro_length = e.intro * 1000L; - if ( e.loop >= 0 ) out->loop_length = e.loop * 1000L; - } - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gme_File.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +void Gme_File::unload() +{ + clear_playlist(); // BEFORE clearing track count + track_count_ = 0; + raw_track_count_ = 0; + Gme_Loader::unload(); +} + +Gme_File::Gme_File() +{ + type_ = NULL; + user_data_ = NULL; + user_cleanup_ = NULL; + Gme_File::unload(); // clears fields +} + +Gme_File::~Gme_File() +{ + if ( user_cleanup_ ) + user_cleanup_( user_data_ ); +} + +blargg_err_t Gme_File::post_load() +{ + if ( !track_count() ) + set_track_count( type()->track_count ); + return Gme_Loader::post_load(); +} + +void Gme_File::clear_playlist() +{ + playlist.clear(); + clear_playlist_(); + track_count_ = raw_track_count_; +} + +void Gme_File::copy_field_( char out [], const char* in, int in_size ) +{ + if ( !in || !*in ) + return; + + // remove spaces/junk from beginning + while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) + { + in++; + in_size--; + } + + // truncate + if ( in_size > max_field_ ) + in_size = max_field_; + + // find terminator + int len = 0; + while ( len < in_size && in [len] ) + len++; + + // remove spaces/junk from end + while ( len && unsigned (in [len - 1]) <= ' ' ) + len--; + + // copy + out [len] = 0; + memcpy( out, in, len ); + + // strip out stupid fields that should have been left blank + if ( !strcmp( out, "?" ) || !strcmp( out, "" ) || !strcmp( out, "< ? >" ) ) + out [0] = 0; +} + +void Gme_File::copy_field_( char out [], const char* in ) +{ + copy_field_( out, in, max_field_ ); +} + +blargg_err_t Gme_File::remap_track_( int* track_io ) const +{ + if ( (unsigned) *track_io >= (unsigned) track_count() ) + return BLARGG_ERR( BLARGG_ERR_CALLER, "invalid track" ); + + if ( (unsigned) *track_io < (unsigned) playlist.size() ) + { + M3u_Playlist::entry_t const& e = playlist [*track_io]; + *track_io = 0; + if ( e.track >= 0 ) + { + *track_io = e.track; + // TODO: really needs to be removed? + //if ( !(type_->flags_ & 0x02) ) + // *track_io -= e.decimal_track; + } + if ( *track_io >= raw_track_count_ ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "invalid track in m3u playlist" ); + } + else + { + check( !playlist.size() ); + } + return blargg_ok; +} + +blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const +{ + out->track_count = track_count(); + out->length = -1; + out->loop_length = -1; + out->intro_length = -1; + out->fade_length = -1; + out->play_length = -1; + out->repeat_count = -1; + out->song [0] = 0; + out->game [0] = 0; + out->author [0] = 0; + out->composer [0] = 0; + out->engineer [0] = 0; + out->sequencer [0] = 0; + out->tagger [0] = 0; + out->copyright [0] = 0; + out->date [0] = 0; + out->comment [0] = 0; + out->dumper [0] = 0; + out->system [0] = 0; + out->disc [0] = 0; + out->track [0] = 0; + out->ost [0] = 0; + + copy_field_( out->system, type()->system ); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + RETURN_ERR( track_info_( out, remapped ) ); + + // override with m3u info + if ( playlist.size() ) + { + M3u_Playlist::info_t const& i = playlist.info(); + copy_field_( out->game , i.title ); + copy_field_( out->author , i.artist ); + copy_field_( out->engineer , i.engineer ); + copy_field_( out->composer , i.composer ); + copy_field_( out->sequencer, i.sequencer ); + copy_field_( out->copyright, i.copyright ); + copy_field_( out->dumper , i.ripping ); + copy_field_( out->tagger , i.tagging ); + copy_field_( out->date , i.date ); + + M3u_Playlist::entry_t const& e = playlist [track]; + if ( e.length >= 0 ) out->length = e.length; + if ( e.intro >= 0 ) out->intro_length = e.intro; + if ( e.loop >= 0 ) out->loop_length = e.loop; + if ( e.fade >= 0 ) out->fade_length = e.fade; + if ( e.repeat >= 0 ) out->repeat_count = e.repeat; + copy_field_( out->song, e.name ); + } + + // play_length + out->play_length = out->length; + if ( out->play_length <= 0 ) + { + out->play_length = out->intro_length + 2 * out->loop_length; // intro + 2 loops + if ( out->play_length <= 0 ) + out->play_length = 150 * 1000; // 2.5 minutes + } + + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Gme_File.h b/Frameworks/GME/gme/Gme_File.h old mode 100755 new mode 100644 index a535633bc..8f9e86e32 --- a/Frameworks/GME/gme/Gme_File.h +++ b/Frameworks/GME/gme/Gme_File.h @@ -1,145 +1,153 @@ -// Common interface to game music file loading and information - -// Game_Music_Emu 0.5.2 -#ifndef GME_FILE_H -#define GME_FILE_H - -#include "gme.h" -#include "blargg_common.h" -#include "Data_Reader.h" -#include "M3u_Playlist.h" - -// Error returned if file is wrong type -//extern const char gme_wrong_file_type []; // declared in gme.h - -struct Gme_File { -public: -// File loading - - // Each loads game music data from a file and returns an error if - // file is wrong type or is seriously corrupt. They also set warning - // string for minor problems. - - // Load from file on disk - blargg_err_t load_file( const char* path ); - - // Load from custom data source (see Data_Reader.h) - blargg_err_t load( Data_Reader& ); - - // Load from file already read into memory. Keeps pointer to data, so you - // must not free it until you're done with the file. - blargg_err_t load_mem( void const* data, long size ); - - // Load an m3u playlist. Must be done after loading main music file. - blargg_err_t load_m3u( const char* path ); - blargg_err_t load_m3u( Data_Reader& in ); - - // Clears any loaded m3u playlist and any internal playlist that the music - // format supports (NSFE for example). - void clear_playlist(); - -// Informational - - // Type of emulator. For example if this returns gme_nsfe_type, this object - // is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary. - gme_type_t type() const; - - // Most recent warning string, or NULL if none. Clears current warning after - // returning. - const char* warning(); - - // Number of tracks or 0 if no file has been loaded - int track_count() const; - - // Get information for a track (length, name, author, etc.) - // See gme.h for definition of struct track_info_t. - blargg_err_t track_info( track_info_t* out, int track ) const; - -// User data/cleanup - - // Set/get pointer to data you want to associate with this emulator. - // You can use this for whatever you want. - void set_user_data( void* p ) { user_data_ = p; } - void* user_data() const { return user_data_; } - - // Register cleanup function to be called when deleting emulator, or NULL to - // clear it. Passes user_data to cleanup function. - void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } - -public: - // deprecated - int error_count() const; // use warning() -public: - Gme_File(); - virtual ~Gme_File(); - BLARGG_DISABLE_NOTHROW - typedef BOOST::uint8_t byte; -protected: - // Services - void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } - void set_warning( const char* s ) { warning_ = s; } - void set_type( gme_type_t t ) { type_ = t; } - blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining ); - - // Overridable - virtual void unload(); // called before loading file and if loading fails - virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() - virtual blargg_err_t load_mem_( byte const* data, long size ); // use data in memory - virtual blargg_err_t track_info_( track_info_t* out, int track ) const = 0; - virtual void pre_load(); - virtual void post_load_(); - virtual void clear_playlist_() { } - -public: - blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu -private: - // noncopyable - Gme_File( const Gme_File& ); - Gme_File& operator = ( const Gme_File& ); - - gme_type_t type_; - int track_count_; - int raw_track_count_; - const char* warning_; - void* user_data_; - gme_user_cleanup_t user_cleanup_; - M3u_Playlist playlist; - char playlist_warning [64]; - blargg_vector file_data; // only if loaded into memory using default load - - blargg_err_t load_m3u_( blargg_err_t ); - blargg_err_t post_load( blargg_err_t err ); -public: - // track_info field copying - enum { max_field_ = 255 }; - static void copy_field_( char* out, const char* in ); - static void copy_field_( char* out, const char* in, int len ); -}; - -Music_Emu* gme_new_( Music_Emu*, long sample_rate ); - -#define GME_COPY_FIELD( in, out, name ) \ - { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } - -#ifndef GME_FILE_READER - #ifdef HAVE_ZLIB_H - #define GME_FILE_READER Gzip_File_Reader - #else - #define GME_FILE_READER Std_File_Reader - #endif -#elif defined (GME_FILE_READER_INCLUDE) - #include GME_FILE_READER_INCLUDE -#endif - -inline gme_type_t Gme_File::type() const { return type_; } -inline int Gme_File::error_count() const { return warning_ != 0; } -inline int Gme_File::track_count() const { return track_count_; } - -inline const char* Gme_File::warning() -{ - const char* s = warning_; - warning_ = 0; - return s; -} - -#endif +// Common interface for track information + +// Game_Music_Emu $vers +#ifndef GME_FILE_H +#define GME_FILE_H + +#include "gme.h" +#include "Gme_Loader.h" +#include "M3u_Playlist.h" + +struct track_info_t +{ + int track_count; + + /* times in milliseconds; -1 if unknown */ + int length; /* total length, if file specifies it */ + int intro_length; /* length of song up to looping section */ + int loop_length; /* length of looping section */ + int fade_length; + int repeat_count; + + /* Length if available, otherwise intro_length+loop_length*2 if available, + otherwise a default of 150000 (2.5 minutes). */ + int play_length; + + /* empty string if not available */ + char system [256]; + char game [256]; + char song [256]; + char author [256]; + char composer [256]; + char engineer [256]; + char sequencer [256]; + char tagger [256]; + char copyright [256]; + char date [256]; + char comment [256]; + char dumper [256]; + char disc [256]; + char track [256]; + char ost [256]; +}; +enum { gme_max_field = 255 }; + +class Gme_File : public Gme_Loader { +public: + // Type of emulator. For example if this returns gme_nsfe_type, this object + // is an NSFE emulator, and you can downcast to an Nsfe_Emu* if necessary. + gme_type_t type() const; + + // Loads an m3u playlist. Must be done AFTER loading main music file. + blargg_err_t load_m3u( const char path [] ); + blargg_err_t load_m3u( Data_Reader& in ); + + // Clears any loaded m3u playlist and any internal playlist that the music + // format supports (NSFE for example). + void clear_playlist(); + + // Number of tracks or 0 if no file has been loaded + int track_count() const; + + // Gets information for a track (length, name, author, etc.) + // See gme.h for definition of struct track_info_t. + blargg_err_t track_info( track_info_t* out, int track ) const; + +// User data/cleanup + + // Sets/gets pointer to data you want to associate with this emulator. + // You can use this for whatever you want. + void set_user_data( void* p ) { user_data_ = p; } + void* user_data() const { return user_data_; } + + // Registers cleanup function to be called when deleting emulator, or NULL to + // clear it. Passes user_data to cleanup function. + void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } + +public: + Gme_File(); + ~Gme_File(); + +protected: + // Services + void set_type( gme_type_t t ) { type_ = t; } + void set_track_count( int n ) { track_count_ = raw_track_count_ = n; } + + // Must be overridden + virtual blargg_err_t track_info_( track_info_t* out, int track ) const BLARGG_PURE( ; ) + + // Optionally overridden + virtual void clear_playlist_() { } + +protected: // Gme_Loader overrides + virtual void unload(); + virtual blargg_err_t post_load(); + +protected: + blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu +private: + gme_type_t type_; + void* user_data_; + gme_user_cleanup_t user_cleanup_; + int track_count_; + int raw_track_count_; + M3u_Playlist playlist; + char playlist_warning [64]; + + blargg_err_t load_m3u_( blargg_err_t ); + +public: + // track_info field copying + enum { max_field_ = 255 }; + static void copy_field_( char out [], const char* in ); + static void copy_field_( char out [], const char* in, int len ); +}; + +struct gme_type_t_ +{ + const char* system; /* name of system this music file type is generally for */ + int track_count; /* non-zero for formats with a fixed number of tracks */ + Music_Emu* (*new_emu)(); /* Create new emulator for this type (C++ only) */ + Music_Emu* (*new_info)();/* Create new info reader for this type (C++ only) */ + + /* internal */ + const char* extension_; + int flags_; +}; + +/* Emulator type constants for each supported file type */ +extern const gme_type_t_ + gme_ay_type [1], + gme_gbs_type [1], + gme_gym_type [1], + gme_hes_type [1], + gme_kss_type [1], + gme_nsf_type [1], + gme_nsfe_type [1], + gme_sap_type [1], + gme_sfm_type [1], + gme_sgc_type [1], + gme_spc_type [1], + gme_vgm_type [1], + gme_vgz_type [1]; + +#define GME_COPY_FIELD( in, out, name ) \ + { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); } + +inline gme_type_t Gme_File::type() const { return type_; } + +inline int Gme_File::track_count() const { return track_count_; } + +inline blargg_err_t Gme_File::track_info_( track_info_t*, int ) const { return blargg_ok; } + +#endif diff --git a/Frameworks/GME/gme/Gym_Emu.cpp b/Frameworks/GME/gme/Gym_Emu.cpp old mode 100755 new mode 100644 index 499a9ca2a..bcac416c3 --- a/Frameworks/GME/gme/Gym_Emu.cpp +++ b/Frameworks/GME/gme/Gym_Emu.cpp @@ -1,379 +1,428 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Gym_Emu.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -double const min_tempo = 0.25; -double const oversample_factor = 5 / 3.0; -double const fm_gain = 3.0; - -const long base_clock = 53700300; -const long clock_rate = base_clock / 15; - -Gym_Emu::Gym_Emu() -{ - data = 0; - pos = 0; - set_type( gme_gym_type ); - - static const char* const names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - set_voice_names( names ); - set_silence_lookahead( 1 ); // tracks should already be trimmed -} - -Gym_Emu::~Gym_Emu() { } - -// Track info - -static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out ) -{ - if ( !memcmp( h.tag, "GYMX", 4 ) ) - { - length = length * 50 / 3; // 1000 / 60 - long loop = get_le32( h.loop_start ); - if ( loop ) - { - out->intro_length = loop * 50 / 3; - out->loop_length = length - out->intro_length; - } - else - { - out->length = length; - out->intro_length = length; // make it clear that track is no longer than length - out->loop_length = 0; - } - - // more stupidity where the field should have been left - if ( strcmp( h.song, "Unknown Song" ) ) - GME_COPY_FIELD( h, out, song ); - - if ( strcmp( h.game, "Unknown Game" ) ) - GME_COPY_FIELD( h, out, game ); - - if ( strcmp( h.copyright, "Unknown Publisher" ) ) - GME_COPY_FIELD( h, out, copyright ); - - if ( strcmp( h.dumper, "Unknown Person" ) ) - GME_COPY_FIELD( h, out, dumper ); - - if ( strcmp( h.comment, "Header added by YMAMP" ) ) - GME_COPY_FIELD( h, out, comment ); - } -} - -blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const -{ - get_gym_info( header_, track_length(), out ); - return 0; -} - -static long gym_track_length( byte const* p, byte const* end ) -{ - long time = 0; - while ( p < end ) - { - switch ( *p++ ) - { - case 0: - time++; - break; - - case 1: - case 2: - p += 2; - break; - - case 3: - p += 1; - break; - } - } - return time; -} - -long Gym_Emu::track_length() const { return gym_track_length( data, data_end ); } - -static blargg_err_t check_header( byte const* in, long size, int* data_offset = 0 ) -{ - if ( size < 4 ) - return gme_wrong_file_type; - - if ( memcmp( in, "GYMX", 4 ) == 0 ) - { - if ( size < Gym_Emu::header_size + 1 ) - return gme_wrong_file_type; - - if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) - return "Packed GYM file not supported"; - - if ( data_offset ) - *data_offset = Gym_Emu::header_size; - } - else if ( *in > 3 ) - { - return gme_wrong_file_type; - } - - return 0; -} - -struct Gym_File : Gme_Info_ -{ - byte const* file_begin; - byte const* file_end; - int data_offset; - - Gym_File() { set_type( gme_gym_type ); } - - blargg_err_t load_mem_( byte const* in, long size ) - { - file_begin = in; - file_end = in + size; - data_offset = 0; - return check_header( in, size, &data_offset ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - long length = gym_track_length( &file_begin [data_offset], file_end ); - get_gym_info( *(Gym_Emu::header_t const*) file_begin, length, out ); - return 0; - } -}; - -static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; } -static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } - -gme_type_t_ const gme_gym_type [1] = { "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }; - -// Setup - -blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate ) -{ - blip_eq_t eq( -32, 8000, sample_rate ); - apu.treble_eq( eq ); - dac_synth.treble_eq( eq ); - apu.volume( 0.135 * fm_gain * gain() ); - dac_synth.volume( 0.125 / 256 * fm_gain * gain() ); - double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() ); - fm_sample_rate = sample_rate * factor; - - RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); - blip_buf.clock_rate( clock_rate ); - - RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) ); - RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) ); - - return 0; -} - -void Gym_Emu::set_tempo_( double t ) -{ - if ( t < min_tempo ) - { - set_tempo( min_tempo ); - return; - } - - if ( blip_buf.sample_rate() ) - { - clocks_per_frame = long (clock_rate / 60 / tempo()); - Dual_Resampler::resize( long (sample_rate() / (60.0 * tempo())) ); - } -} - -void Gym_Emu::mute_voices_( int mask ) -{ - Music_Emu::mute_voices_( mask ); - fm.mute_voices( mask ); - dac_muted = (mask & 0x40) != 0; - apu.output( (mask & 0x80) ? 0 : &blip_buf ); -} - -blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) -{ - assert( offsetof (header_t,packed [4]) == header_size ); - int offset = 0; - RETURN_ERR( check_header( in, size, &offset ) ); - set_voice_count( 8 ); - - data = in + offset; - data_end = in + size; - loop_begin = 0; - - if ( offset ) - header_ = *(header_t const*) in; - else - memset( &header_, 0, sizeof header_ ); - - return 0; -} - -// Emulation - -blargg_err_t Gym_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - - pos = data; - loop_remain = get_le32( header_.loop_start ); - - prev_dac_count = 0; - dac_enabled = false; - dac_amp = -1; - - fm.reset(); - apu.reset(); - blip_buf.clear(); - Dual_Resampler::clear(); - return 0; -} - -void Gym_Emu::run_dac( int dac_count ) -{ - // Guess beginning and end of sample and adjust rate and buffer position accordingly. - - // count dac samples in next frame - int next_dac_count = 0; - const byte* p = this->pos; - int cmd; - while ( (cmd = *p++) != 0 ) - { - int data = *p++; - if ( cmd <= 2 ) - ++p; - if ( cmd == 1 && data == 0x2A ) - next_dac_count++; - } - - // detect beginning and end of sample - int rate_count = dac_count; - int start = 0; - if ( !prev_dac_count && next_dac_count && dac_count < next_dac_count ) - { - rate_count = next_dac_count; - start = next_dac_count - dac_count; - } - else if ( prev_dac_count && !next_dac_count && dac_count < prev_dac_count ) - { - rate_count = prev_dac_count; - } - - // Evenly space samples within buffer section being used - blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count; - - blip_resampled_time_t time = blip_buf.resampled_time( 0 ) + - period * start + (period >> 1); - - int dac_amp = this->dac_amp; - if ( dac_amp < 0 ) - dac_amp = dac_buf [0]; - - for ( int i = 0; i < dac_count; i++ ) - { - int delta = dac_buf [i] - dac_amp; - dac_amp += delta; - dac_synth.offset_resampled( time, delta, &blip_buf ); - time += period; - } - this->dac_amp = dac_amp; -} - -void Gym_Emu::parse_frame() -{ - int dac_count = 0; - const byte* pos = this->pos; - - if ( loop_remain && !--loop_remain ) - loop_begin = pos; // find loop on first time through sequence - - int cmd; - while ( (cmd = *pos++) != 0 ) - { - int data = *pos++; - if ( cmd == 1 ) - { - int data2 = *pos++; - if ( data != 0x2A ) - { - if ( data == 0x2B ) - dac_enabled = (data2 & 0x80) != 0; - - fm.write0( data, data2 ); - } - else if ( dac_count < (int) sizeof dac_buf ) - { - dac_buf [dac_count] = data2; - dac_count += dac_enabled; - } - } - else if ( cmd == 2 ) - { - fm.write1( data, *pos++ ); - } - else if ( cmd == 3 ) - { - apu.write_data( 0, data ); - } - else - { - // to do: many GYM streams are full of errors, and error count should - // reflect cases where music is really having problems - //log_error(); - --pos; // put data back - } - } - - // loop - if ( pos >= data_end ) - { - check( pos == data_end ); - - if ( loop_begin ) - pos = loop_begin; - else - set_track_ended(); - } - this->pos = pos; - - // dac - if ( dac_count && !dac_muted ) - run_dac( dac_count ); - prev_dac_count = dac_count; -} - -int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) -{ - if ( !track_ended() ) - parse_frame(); - - apu.end_frame( blip_time ); - - memset( buf, 0, sample_count * sizeof *buf ); - fm.run( sample_count >> 1, buf ); - - return sample_count; -} - -blargg_err_t Gym_Emu::play_( long count, sample_t* out ) -{ - Dual_Resampler::dual_play( count, out, blip_buf ); - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Gym_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +double const min_tempo = 0.25; +double const oversample = 5 / 3.0; +double const fm_gain = 3.0; + +int const base_clock = 53700300; +int const clock_rate = base_clock / 15; + +Gym_Emu::Gym_Emu() +{ + resampler.set_callback( play_frame_, this ); + pos = NULL; + disable_oversampling_ = false; + set_type( gme_gym_type ); + set_silence_lookahead( 1 ); // tracks should already be trimmed + pcm_buf = stereo_buf.center(); +} + +Gym_Emu::~Gym_Emu() { } + +// Track info + +static void get_gym_info( Gym_Emu::header_t const& h, int length, track_info_t* out ) +{ + if ( 0 != memcmp( h.tag, "GYMX", 4 ) ) + return; + + length = length * 50 / 3; // 1000 / 60 + int loop = get_le32( h.loop_start ); + if ( loop ) + { + out->intro_length = loop * 50 / 3; + out->loop_length = length - out->intro_length; + } + else + { + out->length = length; + out->intro_length = length; // make it clear that track is no longer than length + out->loop_length = 0; + } + + // more stupidity where the field should have been left blank + if ( strcmp( h.song, "Unknown Song" ) ) + GME_COPY_FIELD( h, out, song ); + + if ( strcmp( h.game, "Unknown Game" ) ) + GME_COPY_FIELD( h, out, game ); + + if ( strcmp( h.copyright, "Unknown Publisher" ) ) + GME_COPY_FIELD( h, out, copyright ); + + if ( strcmp( h.dumper, "Unknown Person" ) ) + GME_COPY_FIELD( h, out, dumper ); + + if ( strcmp( h.comment, "Header added by YMAMP" ) ) + GME_COPY_FIELD( h, out, comment ); +} + +static void hash_gym_file( Gym_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.loop_start[0], sizeof(h.loop_start) ); + out.hash_( &h.packed[0], sizeof(h.packed) ); + out.hash_( data, data_size ); +} + +static int gym_track_length( byte const p [], byte const* end ) +{ + int time = 0; + while ( p < end ) + { + switch ( *p++ ) + { + case 0: + time++; + break; + + case 1: + case 2: + p += 2; + break; + + case 3: + p += 1; + break; + } + } + return time; +} + +blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const +{ + get_gym_info( header_, gym_track_length( log_begin(), file_end() ), out ); + return blargg_ok; +} + +static blargg_err_t check_header( byte const in [], int size, int* data_offset = NULL ) +{ + if ( size < 4 ) + return blargg_err_file_type; + + if ( memcmp( in, "GYMX", 4 ) == 0 ) + { + if ( size < Gym_Emu::header_t::size + 1 ) + return blargg_err_file_type; + + if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "packed GYM file" ); + + if ( data_offset ) + *data_offset = Gym_Emu::header_t::size; + } + else if ( *in > 3 ) + { + return blargg_err_file_type; + } + + return blargg_ok; +} + +struct Gym_File : Gme_Info_ +{ + int data_offset; + + Gym_File() { set_type( gme_gym_type ); } + + blargg_err_t load_mem_( byte const in [], int size ) + { + data_offset = 0; + return check_header( in, size, &data_offset ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + int length = gym_track_length( &file_begin() [data_offset], file_end() ); + get_gym_info( *(Gym_Emu::header_t const*) file_begin(), length, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + Gym_Emu::header_t const* h = ( Gym_Emu::header_t const* ) file_begin(); + byte const* data = &file_begin() [data_offset]; + + hash_gym_file( *h, data, file_end() - data, out ); + + return blargg_ok; + } +}; + +static Music_Emu* new_gym_emu () { return BLARGG_NEW Gym_Emu ; } +static Music_Emu* new_gym_file() { return BLARGG_NEW Gym_File; } + +gme_type_t_ const gme_gym_type [1] = {{ "Sega Genesis", 1, &new_gym_emu, &new_gym_file, "GYM", 0 }}; + +// Setup + +blargg_err_t Gym_Emu::set_sample_rate_( int sample_rate ) +{ + blip_eq_t eq( -32, 8000, sample_rate ); + apu.treble_eq( eq ); + pcm_synth.treble_eq( eq ); + + apu.volume( 0.135 * fm_gain * gain() ); + + double factor = oversample; + if ( disable_oversampling_ ) + factor = (double) base_clock / 7 / 144 / sample_rate; + RETURN_ERR( resampler.setup( factor, 0.990, fm_gain * gain() ) ); + factor = resampler.rate(); + double fm_rate = sample_rate * factor; + + RETURN_ERR( stereo_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); + stereo_buf.clock_rate( clock_rate ); + + RETURN_ERR( fm.set_rate( fm_rate, base_clock / 7.0 ) ); + RETURN_ERR( resampler.reset( (int) (1.0 / 60 / min_tempo * sample_rate) ) ); + + return blargg_ok; +} + +void Gym_Emu::set_tempo_( double t ) +{ + if ( t < min_tempo ) + { + set_tempo( min_tempo ); + return; + } + + if ( stereo_buf.sample_rate() ) + { + double denom = tempo() * 60; + clocks_per_frame = (int) (clock_rate / denom); + resampler.resize( (int) (sample_rate() / denom) ); + } +} + +void Gym_Emu::mute_voices_( int mask ) +{ + Music_Emu::mute_voices_( mask ); + fm.mute_voices( mask ); + apu.set_output( (mask & 0x80) ? 0 : stereo_buf.center() ); + pcm_synth.volume( (mask & 0x40) ? 0.0 : 0.125 / 256 * fm_gain * gain() ); +} + +blargg_err_t Gym_Emu::load_mem_( byte const in [], int size ) +{ + assert( offsetof (header_t,packed [4]) == header_t::size ); + log_offset = 0; + RETURN_ERR( check_header( in, size, &log_offset ) ); + + loop_begin = NULL; + + static const char* const names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + set_voice_names( names ); + + set_voice_count( 8 ); + + if ( log_offset ) + header_ = *(header_t const*) in; + else + memset( &header_, 0, sizeof header_ ); + + return blargg_ok; +} + +// Emulation + +blargg_err_t Gym_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + + pos = log_begin(); + loop_remain = get_le32( header_.loop_start ); + + prev_pcm_count = 0; + pcm_enabled = 0; + pcm_amp = -1; + + fm.reset(); + apu.reset(); + stereo_buf.clear(); + resampler.clear(); + pcm_buf = stereo_buf.center(); + return blargg_ok; +} + +void Gym_Emu::run_pcm( byte const pcm_in [], int pcm_count ) +{ + // Guess beginning and end of sample and adjust rate and buffer position accordingly. + + // count dac samples in next frame + int next_pcm_count = 0; + const byte* p = this->pos; + int cmd; + while ( (cmd = *p++) != 0 ) + { + int data = *p++; + if ( cmd <= 2 ) + ++p; + if ( cmd == 1 && data == 0x2A ) + next_pcm_count++; + } + + // detect beginning and end of sample + int rate_count = pcm_count; + int start = 0; + if ( !prev_pcm_count && next_pcm_count && pcm_count < next_pcm_count ) + { + rate_count = next_pcm_count; + start = next_pcm_count - pcm_count; + } + else if ( prev_pcm_count && !next_pcm_count && pcm_count < prev_pcm_count ) + { + rate_count = prev_pcm_count; + } + + // Evenly space samples within buffer section being used + blip_resampled_time_t period = pcm_buf->resampled_duration( clocks_per_frame ) / rate_count; + + blip_resampled_time_t time = pcm_buf->resampled_time( 0 ) + period * start + (unsigned) period / 2; + + int pcm_amp = this->pcm_amp; + if ( pcm_amp < 0 ) + pcm_amp = pcm_in [0]; + + for ( int i = 0; i < pcm_count; i++ ) + { + int delta = pcm_in [i] - pcm_amp; + pcm_amp += delta; + pcm_synth.offset_resampled( time, delta, pcm_buf ); + time += period; + } + this->pcm_amp = pcm_amp; + pcm_buf->set_modified(); +} + +void Gym_Emu::parse_frame() +{ + byte pcm [1024]; // all PCM writes for frame + int pcm_size = 0; + const byte* pos = this->pos; + + if ( loop_remain && !--loop_remain ) + loop_begin = pos; // find loop on first time through sequence + + int cmd; + while ( (cmd = *pos++) != 0 ) + { + int data = *pos++; + if ( cmd == 1 ) + { + int data2 = *pos++; + if ( data == 0x2A ) + { + pcm [pcm_size] = data2; + if ( pcm_size < (int) sizeof pcm - 1 ) + pcm_size += pcm_enabled; + } + else + { + if ( data == 0x2B ) + pcm_enabled = data2 >> 7 & 1; + + fm.write0( data, data2 ); + } + } + else if ( cmd == 2 ) + { + int data2 = *pos++; + if ( data == 0xB6 ) + { + Blip_Buffer * pcm_buf = NULL; + switch ( data2 >> 6 ) + { + case 0: pcm_buf = NULL; break; + case 1: pcm_buf = stereo_buf.right(); break; + case 2: pcm_buf = stereo_buf.left(); break; + case 3: pcm_buf = stereo_buf.center(); break; + } + /*if ( this->pcm_buf != pcm_buf ) + { + if ( this->pcm_buf ) pcm_synth.offset_inline( 0, -pcm_amp, this->pcm_buf ); + if ( pcm_buf ) pcm_synth.offset_inline( 0, pcm_amp, pcm_buf ); + }*/ + this->pcm_buf = pcm_buf; + } + fm.write1( data, data2 ); + } + else if ( cmd == 3 ) + { + apu.write_data( 0, data ); + } + else + { + // to do: many GYM streams are full of errors, and error count should + // reflect cases where music is really having problems + //log_error(); + --pos; // put data back + } + } + + if ( pos >= file_end() ) + { + // Reached end + check( pos == file_end() ); + + if ( loop_begin ) + pos = loop_begin; + else + set_track_ended(); + } + this->pos = pos; + + // PCM + if ( pcm_buf && pcm_size ) + run_pcm( pcm, pcm_size ); + prev_pcm_count = pcm_size; +} + +inline int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ) +{ + if ( !track_ended() ) + parse_frame(); + + apu.end_frame( blip_time ); + + memset( buf, 0, sample_count * sizeof *buf ); + fm.run( sample_count >> 1, buf ); + + return sample_count; +} + +int Gym_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) +{ + return STATIC_CAST(Gym_Emu*,p)->play_frame( a, b, c ); +} + +blargg_err_t Gym_Emu::play_( int count, sample_t out [] ) +{ + resampler.dual_play( count, out, stereo_buf ); + return blargg_ok; +} + +blargg_err_t Gym_Emu::hash_( Hash_Function& out ) const +{ + hash_gym_file( header(), log_begin(), file_end() - log_begin(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Gym_Emu.h b/Frameworks/GME/gme/Gym_Emu.h old mode 100755 new mode 100644 index 05419ea20..cd28a29cb --- a/Frameworks/GME/gme/Gym_Emu.h +++ b/Frameworks/GME/gme/Gym_Emu.h @@ -1,82 +1,88 @@ -// Sega Genesis/Mega Drive GYM music file emulator -// Includes with PCM timing recovery to improve sample quality. - -// Game_Music_Emu 0.5.2 -#ifndef GYM_EMU_H -#define GYM_EMU_H - -#include "Dual_Resampler.h" -#include "Ym2612_Emu.h" -#include "Music_Emu.h" -#include "Sms_Apu.h" - -class Gym_Emu : public Music_Emu, private Dual_Resampler { -public: - // GYM file header - enum { header_size = 428 }; - struct header_t - { - char tag [4]; - char song [32]; - char game [32]; - char copyright [32]; - char emulator [32]; - char dumper [32]; - char comment [256]; - byte loop_start [4]; // in 1/60 seconds, 0 if not looped - byte packed [4]; - }; - - // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_gym_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - enum { gym_rate = 60 }; - long track_length() const; // use track_info() - -public: - Gym_Emu(); - ~Gym_Emu(); -protected: - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t set_sample_rate_( long sample_rate ); - blargg_err_t start_track_( int ); - blargg_err_t play_( long count, sample_t* ); - void mute_voices_( int ); - void set_tempo_( double ); - int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); -private: - // sequence data begin, loop begin, current position, end - const byte* data; - const byte* loop_begin; - const byte* pos; - const byte* data_end; - blargg_long loop_remain; // frames remaining until loop beginning has been located - header_t header_; - double fm_sample_rate; - blargg_long clocks_per_frame; - void parse_frame(); - - // dac (pcm) - int dac_amp; - int prev_dac_count; - bool dac_enabled; - bool dac_muted; - void run_dac( int ); - - // sound - Blip_Buffer blip_buf; - Ym2612_Emu fm; - Blip_Synth dac_synth; - Sms_Apu apu; - byte dac_buf [1024]; -}; - -#endif +// Sega Genesis/Mega Drive GYM music file emulator +// Performs PCM timing recovery to improve sample quality. + +// Game_Music_Emu $vers +#ifndef GYM_EMU_H +#define GYM_EMU_H + +#include "Dual_Resampler.h" +#include "Ym2612_Emu.h" +#include "Music_Emu.h" +#include "Sms_Apu.h" + +class Gym_Emu : public Music_Emu { +public: + + // GYM file header (optional; many files have NO header at all) + struct header_t + { + enum { size = 428 }; + + char tag [ 4]; + char song [ 32]; + char game [ 32]; + char copyright [ 32]; + char emulator [ 32]; + char dumper [ 32]; + char comment [256]; + byte loop_start [ 4]; // in 1/60 seconds, 0 if not looped + byte packed [ 4]; + }; + + // Header for currently loaded file + header_t const& header() const { return header_; } + + static gme_type_t static_type() { return gme_gym_type; } + + // Disables running FM chips at higher than normal rate. Will result in slightly + // more aliasing of high notes. + void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } + + blargg_err_t hash_( Hash_Function& ) const; + +// Implementation +public: + Gym_Emu(); + ~Gym_Emu(); + +protected: + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int count, sample_t [] ); + virtual void mute_voices_( int ); + virtual void set_tempo_( double ); + +private: + // Log + byte const* pos; // current position + byte const* loop_begin; + int log_offset; // size of header (0 or header_t::size) + int loop_remain; // frames remaining until loop_begin has been located + int clocks_per_frame; + + bool disable_oversampling_; + + // PCM + int pcm_amp; + int prev_pcm_count; // for detecting beginning/end of group of samples + int pcm_enabled; + + // large objects + Dual_Resampler resampler; + Stereo_Buffer stereo_buf; + Blip_Buffer * pcm_buf; + Ym2612_Emu fm; + Sms_Apu apu; + Blip_Synth_Fast pcm_synth; + header_t header_; + + byte const* log_begin() const { return file_begin() + log_offset; } + void parse_frame(); + void run_pcm( byte const in [], int count ); + int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ); + static int play_frame_( void*, blip_time_t, int, sample_t [] ); +}; + +#endif diff --git a/Frameworks/GME/gme/Hes_Apu.cpp b/Frameworks/GME/gme/Hes_Apu.cpp old mode 100755 new mode 100644 index 223891212..edbbf1f80 --- a/Frameworks/GME/gme/Hes_Apu.cpp +++ b/Frameworks/GME/gme/Hes_Apu.cpp @@ -1,10 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Hes_Apu.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -21,17 +19,15 @@ bool const center_waves = true; // reduces asymmetry and clamping when starting Hes_Apu::Hes_Apu() { - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; - osc->outputs [0] = 0; - osc->outputs [1] = 0; - osc->chans [0] = 0; - osc->chans [1] = 0; - osc->chans [2] = 0; + osc->output [0] = NULL; + osc->output [1] = NULL; + osc->outputs [0] = NULL; + osc->outputs [1] = NULL; + osc->outputs [2] = NULL; } - while ( osc != oscs ); reset(); } @@ -41,150 +37,192 @@ void Hes_Apu::reset() latch = 0; balance = 0xFF; - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; - memset( osc, 0, offsetof (Hes_Osc,outputs) ); - osc->noise_lfsr = 1; - osc->control = 0x40; - osc->balance = 0xFF; + memset( osc, 0, offsetof (Osc,output) ); + osc->lfsr = 0; + osc->control = 0x40; + osc->balance = 0xFF; } - while ( osc != oscs ); -} - -void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - require( (unsigned) index < osc_count ); - oscs [index].chans [0] = center; - oscs [index].chans [1] = left; - oscs [index].chans [2] = right; - Hes_Osc* osc = &oscs [osc_count]; - do - { - osc--; - balance_changed( *osc ); - } - while ( osc != oscs ); + // Only last two oscs support noise + oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR + oscs [osc_count - 1].lfsr = 0x200C3; } -void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) +void Hes_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - Blip_Buffer* const osc_outputs_0 = outputs [0]; // cache often-used values - if ( osc_outputs_0 && control & 0x80 ) + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( !center || !left || !right ) { - int dac = this->dac; - - int const volume_0 = volume [0]; + left = center; + right = center; + } + + Osc& o = oscs [i]; + o.outputs [0] = center; + o.outputs [1] = left; + o.outputs [2] = right; + balance_changed( o ); +} + +void Hes_Apu::run_osc( Blip_Synth_Fast& syn, Osc& o, blip_time_t end_time ) +{ + int vol0 = o.volume [0]; + int vol1 = o.volume [1]; + int dac = o.dac; + + Blip_Buffer* out0 = o.output [0]; // cache often-used values + Blip_Buffer* out1 = o.output [1]; + if ( !(o.control & 0x80) ) + out0 = NULL; + + if ( out0 ) + { + // Update amplitudes + if ( out1 ) { - int delta = dac * volume_0 - last_amp [0]; + int delta = dac * vol1 - o.last_amp [1]; if ( delta ) - synth_.offset( last_time, delta, osc_outputs_0 ); - osc_outputs_0->set_modified(); + { + syn.offset( o.last_time, delta, out1 ); + out1->set_modified(); + } + } + int delta = dac * vol0 - o.last_amp [0]; + if ( delta ) + { + syn.offset( o.last_time, delta, out0 ); + out0->set_modified(); } - Blip_Buffer* const osc_outputs_1 = outputs [1]; - int const volume_1 = volume [1]; - if ( osc_outputs_1 ) - { - int delta = dac * volume_1 - last_amp [1]; - if ( delta ) - synth_.offset( last_time, delta, osc_outputs_1 ); - osc_outputs_1->set_modified(); - } + // Don't generate if silent + if ( !(vol0 | vol1) ) + out0 = NULL; + } + + // Generate noise + int noise = 0; + if ( o.lfsr ) + { + noise = o.noise & 0x80; - blip_time_t time = last_time + delay; + blip_time_t time = o.last_time + o.noise_delay; if ( time < end_time ) { - if ( noise & 0x80 ) + int period = (~o.noise & 0x1F) * 128; + if ( !period ) + period = 64; + + if ( noise && out0 ) { - if ( volume_0 | volume_1 ) + unsigned lfsr = o.lfsr; + do { - // noise - int const period = (32 - (noise & 0x1F)) * 64; // TODO: correct? - unsigned noise_lfsr = this->noise_lfsr; - do - { - int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); - // Implemented using "Galios configuration" - // TODO: find correct LFSR algorithm - noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); - //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - synth_.offset( time, delta * volume_0, osc_outputs_0 ); - if ( osc_outputs_1 ) - synth_.offset( time, delta * volume_1, osc_outputs_1 ); - } - time += period; - } - while ( time < end_time ); + int new_dac = -(lfsr & 1); + lfsr = (lfsr >> 1) ^ (0x30061 & new_dac); - this->noise_lfsr = noise_lfsr; - assert( noise_lfsr ); + int delta = (new_dac &= 0x1F) - dac; + if ( delta ) + { + dac = new_dac; + syn.offset( time, delta * vol0, out0 ); + if ( out1 ) + syn.offset( time, delta * vol1, out1 ); + } + time += period; } + while ( time < end_time ); + + if ( !lfsr ) + { + lfsr = 1; + check( false ); + } + o.lfsr = lfsr; + + out0->set_modified(); + if ( out1 ) + out1->set_modified(); } - else if ( !(control & 0x40) ) + else { - // wave - int phase = (this->phase + 1) & 0x1F; // pre-advance for optimal inner loop - int period = this->period * 2; - if ( period >= 14 && (volume_0 | volume_1) ) - { - do - { - int new_dac = wave [phase]; - phase = (phase + 1) & 0x1F; - int delta = new_dac - dac; - if ( delta ) - { - dac = new_dac; - synth_.offset( time, delta * volume_0, osc_outputs_0 ); - if ( osc_outputs_1 ) - synth_.offset( time, delta * volume_1, osc_outputs_1 ); - } - time += period; - } - while ( time < end_time ); - } - else - { - if ( !period ) - { - // TODO: Gekisha Boy assumes that period = 0 silences wave - //period = 0x1000 * 2; - period = 1; - //if ( !(volume_0 | volume_1) ) - // dprintf( "Used period 0\n" ); - } - - // maintain phase when silent - blargg_long count = (end_time - time + period - 1) / period; - phase += count; // phase will be masked below - time += count * period; - } - this->phase = (phase - 1) & 0x1F; // undo pre-advance + // Maintain phase when silent + int count = (end_time - time + period - 1) / period; + time += count * period; + + // not worth it + //while ( count-- ) + // o.lfsr = (o.lfsr >> 1) ^ (0x30061 * (o.lfsr & 1)); } } - time -= end_time; - if ( time < 0 ) - time = 0; - delay = time; - - this->dac = dac; - last_amp [0] = dac * volume_0; - last_amp [1] = dac * volume_1; + o.noise_delay = time - end_time; } - last_time = end_time; + + // Generate wave + blip_time_t time = o.last_time + o.delay; + if ( time < end_time ) + { + int phase = (o.phase + 1) & 0x1F; // pre-advance for optimal inner loop + int period = o.period * 2; + + if ( period >= 14 && out0 && !((o.control & 0x40) | noise) ) + { + do + { + int new_dac = o.wave [phase]; + phase = (phase + 1) & 0x1F; + int delta = new_dac - dac; + if ( delta ) + { + dac = new_dac; + syn.offset( time, delta * vol0, out0 ); + if ( out1 ) + syn.offset( time, delta * vol1, out1 ); + } + time += period; + } + while ( time < end_time ); + out0->set_modified(); + if ( out1 ) + out1->set_modified(); + } + else + { + // Maintain phase when silent + int count = end_time - time; + if ( !period ) + period = 1; + count = (count + period - 1) / period; + + phase += count; // phase will be masked below + time += count * period; + } + + // TODO: Find whether phase increments even when both volumes are zero. + // CAN'T simply check for out0 being non-NULL, since it could be NULL + // if channel is muted in player, but still has non-zero volume. + // City Hunter breaks when this check is removed. + if ( !(o.control & 0x40) && (vol0 | vol1) ) + o.phase = (phase - 1) & 0x1F; // undo pre-advance + } + o.delay = time - end_time; + check( o.delay >= 0 ); + + o.last_time = end_time; + o.dac = dac; + o.last_amp [0] = dac * vol0; + o.last_amp [1] = dac * vol1; } -void Hes_Apu::balance_changed( Hes_Osc& osc ) +void Hes_Apu::balance_changed( Osc& osc ) { static short const log_table [32] = { // ~1.5 db per step - #define ENTRY( factor ) short (factor * Hes_Osc::amp_range / 31.0 + 0.5) + #define ENTRY( factor ) short (factor * amp_range / 31.0 + 0.5) ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ), ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ), ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ), @@ -204,27 +242,40 @@ void Hes_Apu::balance_changed( Hes_Osc& osc ) int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); if ( right < 0 ) right = 0; - left = log_table [left ]; - right = log_table [right]; - // optimizing for the common case of being centered also allows easy // panning using Effects_Buffer - osc.outputs [0] = osc.chans [0]; // center - osc.outputs [1] = 0; - if ( left != right ) + + // Separate balance into center volume and additional on either left or right + osc.output [0] = osc.outputs [0]; // center + osc.output [1] = osc.outputs [2]; // right + int base = log_table [left ]; + int side = log_table [right] - base; + if ( side < 0 ) { - osc.outputs [0] = osc.chans [1]; // left - osc.outputs [1] = osc.chans [2]; // right + base += side; + side = -side; + osc.output [1] = osc.outputs [1]; // left + } + + // Optimize when output is far left, center, or far right + if ( !base || osc.output [0] == osc.output [1] ) + { + base += side; + side = 0; + osc.output [0] = osc.output [1]; + osc.output [1] = NULL; + osc.last_amp [1] = 0; } if ( center_waves ) { - osc.last_amp [0] += (left - osc.volume [0]) * 16; - osc.last_amp [1] += (right - osc.volume [1]) * 16; + // TODO: this can leave a non-zero level in a buffer (minor) + osc.last_amp [0] += (base - osc.volume [0]) * 16; + osc.last_amp [1] += (side - osc.volume [1]) * 16; } - osc.volume [0] = left; - osc.volume [1] = right; + osc.volume [0] = base; + osc.volume [1] = side; } void Hes_Apu::write_data( blip_time_t time, int addr, int data ) @@ -239,20 +290,18 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) { balance = data; - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; - osc->run_until( synth, time ); + run_osc( synth, *osc, time ); balance_changed( *oscs ); } - while ( osc != oscs ); } } else if ( latch < osc_count ) { - Hes_Osc& osc = oscs [latch]; - osc.run_until( synth, time ); + Osc& osc = oscs [latch]; + run_osc( synth, osc, time ); switch ( addr ) { case 0x802: @@ -289,8 +338,7 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) break; case 0x807: - if ( &osc >= &oscs [4] ) - osc.noise = data; + osc.noise = data; break; case 0x809: @@ -302,14 +350,12 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) void Hes_Apu::end_frame( blip_time_t end_time ) { - Hes_Osc* osc = &oscs [osc_count]; - do + for ( Osc* osc = &oscs [osc_count]; osc != oscs; ) { osc--; if ( end_time > osc->last_time ) - osc->run_until( synth, end_time ); - assert( osc->last_time >= end_time ); + run_osc( synth, *osc, end_time ); osc->last_time -= end_time; + check( osc->last_time >= 0 ); } - while ( osc != oscs ); } diff --git a/Frameworks/GME/gme/Hes_Apu.h b/Frameworks/GME/gme/Hes_Apu.h old mode 100755 new mode 100644 index ca0c932fd..e6f099cef --- a/Frameworks/GME/gme/Hes_Apu.h +++ b/Frameworks/GME/gme/Hes_Apu.h @@ -1,66 +1,87 @@ // Turbo Grafx 16 (PC Engine) PSG sound chip emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef HES_APU_H #define HES_APU_H #include "blargg_common.h" #include "Blip_Buffer.h" -struct Hes_Osc -{ - unsigned char wave [32]; - short volume [2]; - int last_amp [2]; - int delay; - int period; - unsigned char noise; - unsigned char phase; - unsigned char balance; - unsigned char dac; - blip_time_t last_time; - - Blip_Buffer* outputs [2]; - Blip_Buffer* chans [3]; - unsigned noise_lfsr; - unsigned char control; - - enum { amp_range = 0x8000 }; - typedef Blip_Synth synth_t; - - void run_until( synth_t& synth, blip_time_t ); -}; - class Hes_Apu { public: - void treble_eq( blip_eq_t const& ); - void volume( double ); +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); - enum { osc_count = 6 }; - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); +// More features + + // Resets sound chip void reset(); - enum { start_addr = 0x0800 }; - enum { end_addr = 0x0809 }; - void write_data( blip_time_t, int addr, int data ); + // Same as set_output(), but for a particular channel + enum { osc_count = 6 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - void end_frame( blip_time_t ); + // Sets treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + // Sets overall volume, where 1.0 is normal + void volume( double v ) { synth.volume( 1.8 / osc_count / amp_range * v ); } + + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0x0800 }; + enum { io_size = 10 }; + +// Implementation public: Hes_Apu(); + typedef BOOST::uint8_t byte; + private: - Hes_Osc oscs [osc_count]; + enum { amp_range = 0x8000 }; + struct Osc + { + byte wave [32]; + int delay; + int period; + int phase; + + int noise_delay; + byte noise; + unsigned lfsr; + + byte control; + byte balance; + byte dac; + short volume [2]; + int last_amp [2]; + + blip_time_t last_time; + Blip_Buffer* output [2]; + Blip_Buffer* outputs [3]; + }; + Osc oscs [osc_count]; int latch; int balance; - Hes_Osc::synth_t synth; + Blip_Synth_Fast synth; - void balance_changed( Hes_Osc& ); - void recalc_chans(); + void balance_changed( Osc& ); + static void run_osc( Blip_Synth_Fast&, Osc&, blip_time_t ); }; -inline void Hes_Apu::volume( double v ) { synth.volume( 1.8 / osc_count / Hes_Osc::amp_range * v ); } - -inline void Hes_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } +inline void Hes_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} #endif diff --git a/Frameworks/GME/gme/Hes_Cpu.cpp b/Frameworks/GME/gme/Hes_Cpu.cpp old mode 100755 new mode 100644 index 2615a0bb9..5f70ad45f --- a/Frameworks/GME/gme/Hes_Cpu.cpp +++ b/Frameworks/GME/gme/Hes_Cpu.cpp @@ -1,12 +1,13 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Hes_Cpu.h" #include "blargg_endian.h" +#include "Hes_Core.h" //#include "hes_cpu_log.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -17,1287 +18,106 @@ details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -// TODO: support T flag, including clearing it at appropriate times? - -// all zero-page should really use whatever is at page 1, but that would -// reduce efficiency quite a bit -int const ram_addr = 0x2000; - -#define FLUSH_TIME() (void) (s.time = s_time) -#define CACHE_TIME() (void) (s_time = s.time) - -#include "hes_cpu_io.h" - #include "blargg_source.h" -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif +#define PAGE HES_CPU_PAGE -// status flags -int const st_n = 0x80; -int const st_v = 0x40; -int const st_t = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; - -void Hes_Cpu::reset() +int Hes_Core::read_mem( addr_t addr ) { - check( state == &state_ ); - state = &state_; - - state_.time = 0; - state_.base = 0; - irq_time_ = future_hes_time; - end_time_ = future_hes_time; - - r.status = st_i; - r.sp = 0; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - - blargg_verify_byte_order(); + check( addr < 0x10000 ); + int result = *cpu.get_code( addr ); + if ( cpu.mmr [PAGE( addr )] == 0xFF ) + result = read_mem_( addr ); + return result; } -void Hes_Cpu::set_mmr( int reg, int bank ) +void Hes_Core::write_mem( addr_t addr, int data ) { - assert( (unsigned) reg <= page_count ); // allow page past end to be set - assert( (unsigned) bank < 0x100 ); - mmr [reg] = bank; - uint8_t const* code = CPU_SET_MMR( this, reg, bank ); - state->code_map [reg] = code - PAGE_OFFSET( reg << page_shift ); + check( addr < 0x10000 ); + byte* out = write_pages [PAGE( addr )]; + if ( out ) + out [addr & (cpu.page_size - 1)] = data; + else if ( cpu.mmr [PAGE( addr )] == 0xFF ) + write_mem_( addr, data ); } -#define TIME (s_time + s.base) - -#define READ( addr ) CPU_READ( this, (addr), TIME ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} -#define READ_LOW( addr ) (ram [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) -#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - -bool Hes_Cpu::run( hes_time_t end_time ) +void Hes_Core::set_mmr( int page, int bank ) { - bool illegal_encountered = false; - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - // even on x86, using s.time in place of s_time was slower - fint16 s_time = s.time; - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; - SET_SP( r.sp ); - - #define IS_NEG (nz & 0x8080) - - #define CALC_STATUS( out ) do {\ - out = status & (st_v | st_d | st_i);\ - out |= ((nz >> 8) | nz) & st_n;\ - out |= c >> 8 & st_c;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & (st_v | st_d | st_i);\ - nz = in << 8;\ - c = nz;\ - nz |= ~in & st_z;\ - } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + write_pages [page] = 0; + byte* data = rom.at_addr( bank * cpu.page_size ); + if ( bank >= 0x80 ) { - fuint8 temp = r.status; - SET_STATUS( temp ); + data = 0; + switch ( bank ) + { + case 0xF8: + data = ram; + break; + + case 0xF9: + case 0xFA: + case 0xFB: + data = &sgx [(bank - 0xF9) * cpu.page_size]; + break; + + default: + if ( bank != 0xFF ) + dprintf( "Unmapped bank $%02X\n", bank ); + data = rom.unmapped(); + goto end; + } + + write_pages [page] = data; } - - goto loop; -branch_not_taken: - s_time -= 2; -loop: - - #ifndef NDEBUG - { - hes_time_t correct = end_time_; - if ( !(status & st_i) && correct > irq_time_ ) - correct = irq_time_; - check( s.base == correct ); - /* - static long count; - if ( count == 1844 ) Debugger(); - if ( s.base != correct ) dprintf( "%ld\n", count ); - count++; - */ - } - #endif +end: + cpu.set_mmr( page, bank, data ); +} - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - - uint8_t const* instr = s.code_map [pc >> page_shift]; - fuint8 opcode; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - opcode = instr [pc]; - pc++; - instr += pc; - #else - instr += PAGE_OFFSET( pc ); - opcode = *instr++; - pc++; - #endif - - // TODO: each reference lists slightly different timing values, ugh - static uint8_t const clock_table [256] = - {// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,6,// 0 - 4,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,6,// 1 - 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,6,// 2 - 4,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,6,// 3 - 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,6,// 4 - 4,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,6,// 5 - 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,6,// 6 - 4,7,7,17,4,4,6,7,2,5,4,2,7,5,7,6,// 7 - 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// 8 - 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// 9 - 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,6,// A - 4,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,6,// B - 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// C - 4,7,7,17,2,4,6,7,2,5,3,2,2,5,7,6,// D - 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,6,// E - 4,7,7,17,2,4,6,7,2,5,4,2,2,5,7,6 // F - }; // 0x00 was 8 - - fuint16 data; - data = clock_table [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = *instr; - - #ifdef HES_CPU_LOG_H - log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2], - instr [3], instr [4], instr [5] ); - //log_opcode( opcode ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Macros - -#define GET_MSB() (instr [1]) -#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB()); -#define GET_ADDR() GET_LE16( instr ) - -// TODO: is the penalty really always added? the original 6502 was much better -//#define PAGE_CROSS_PENALTY( lsb ) (void) (s_time += (lsb) >> 8) -#define PAGE_CROSS_PENALTY( lsb ) - -// Branch - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ +#define READ_FAST( addr, out ) \ {\ - fint16 offset = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) ) goto branch_not_taken;\ - pc = BOOST::uint16_t (pc + offset);\ - goto loop;\ + out = READ_CODE( addr );\ + if ( CPU.mmr [PAGE( addr )] == 0xFF )\ + {\ + FLUSH_TIME();\ + out = read_mem_( addr );\ + CACHE_TIME();\ + }\ } - case 0xF0: // BEQ - BRANCH( !((uint8_t) nz) ); - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x10: // BPL - BRANCH( !IS_NEG ); - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x80: // BRA - branch_taken: - BRANCH( true ); - - case 0xFF: - if ( pc == idle_addr + 1 ) - goto idle_done; - case 0x0F: // BBRn - case 0x1F: - case 0x2F: - case 0x3F: - case 0x4F: - case 0x5F: - case 0x6F: - case 0x7F: - case 0x8F: // BBSn - case 0x9F: - case 0xAF: - case 0xBF: - case 0xCF: - case 0xDF: - case 0xEF: { - fuint16 t = 0x101 * READ_LOW( data ); - t ^= 0xFF; - pc++; - data = GET_MSB(); - BRANCH( t & (1 << (opcode >> 4)) ) - } - - case 0x4C: // JMP abs - pc = GET_ADDR(); - goto loop; - - case 0x7C: // JMP (ind+X) - data += x; - case 0x6C:{// JMP (ind) - data += 0x100 * GET_MSB(); - pc = GET_LE16( &READ_PROG( data ) ); - goto loop; - } - -// Subroutine - - case 0x44: // BSR - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, pc ); - goto branch_taken; - - case 0x20: { // JSR - fuint16 temp = pc + 1; - pc = GET_ADDR(); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x60: // RTS - pc = 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); - pc += 1 + READ_LOW( sp ); - sp = (sp - 0xFE) | 0x100; - goto loop; - - case 0x00: // BRK - goto handle_brk; - -// Common - - case 0xBD:{// LDA abs,X - PAGE_CROSS_PENALTY( data + x ); - fuint16 addr = GET_ADDR() + x; - pc += 2; - CPU_READ_FAST( this, addr, TIME, nz ); - a = nz; - goto loop; - } - - case 0x9D:{// STA abs,X - fuint16 addr = GET_ADDR() + x; - pc += 2; - CPU_WRITE_FAST( this, addr, a, TIME ); - goto loop; - } - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xAE:{// LDX abs - fuint16 addr = GET_ADDR(); - pc += 2; - CPU_READ_FAST( this, addr, TIME, nz ); - x = nz; - goto loop; - } - - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - -// Load/store - - { - fuint16 addr; - case 0x91: // STA (ind),Y - addr = 0x100 * READ_LOW( uint8_t (data + 1) ); - addr += READ_LOW( data ) + y; - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - data = uint8_t (data + x); - case 0x92: // STA (ind) - addr = 0x100 * READ_LOW( uint8_t (data + 1) ); - addr += READ_LOW( data ); - pc++; - goto sta_ptr; - - case 0x99: // STA abs,Y - data += y; - case 0x8D: // STA abs - addr = data + 0x100 * GET_MSB(); - pc += 2; - sta_ptr: - CPU_WRITE_FAST( this, addr, a, TIME ); - goto loop; - } - - { - fuint16 addr; - case 0xA1: // LDA (ind,X) - data = uint8_t (data + x); - case 0xB2: // LDA (ind) - addr = 0x100 * READ_LOW( uint8_t (data + 1) ); - addr += READ_LOW( data ); - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - PAGE_CROSS_PENALTY( addr ); - addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); - pc++; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - data += y; - PAGE_CROSS_PENALTY( data ); - case 0xAD: // LDA abs - addr = data + 0x100 * GET_MSB(); - pc += 2; - a_nz_read_addr: - CPU_READ_FAST( this, addr, TIME, nz ); - a = nz; - goto loop; - } - - case 0xBE:{// LDX abs,y - PAGE_CROSS_PENALTY( data + y ); - fuint16 addr = GET_ADDR() + y; - pc += 2; - FLUSH_TIME(); - x = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (data + x) ); - pc++; - goto loop; - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - -// Bit operations - - case 0x3C: // BIT abs,x - data += x; - case 0x2C:{// BIT abs - fuint16 addr; - ADD_PAGE( addr ); - FLUSH_TIME(); - nz = READ( addr ); - CACHE_TIME(); - goto bit_common; - } - case 0x34: // BIT zp,x - data = uint8_t (data + x); - case 0x24: // BIT zp - data = READ_LOW( data ); - case 0x89: // BIT imm - nz = data; - bit_common: - pc++; - status &= ~st_v; - status |= nz & st_v; - if ( nz & a ) - goto loop; // Z should be clear, and nz must be non-zero if nz & a is - nz <<= 8; // set Z flag without affecting N flag - goto loop; - - { - fuint16 addr; - - case 0xB3: // TST abs,x - addr = GET_MSB() + x; - goto tst_abs; - - case 0x93: // TST abs - addr = GET_MSB(); - tst_abs: - addr += 0x100 * instr [2]; - pc++; - FLUSH_TIME(); - nz = READ( addr ); - CACHE_TIME(); - goto tst_common; - } - - case 0xA3: // TST zp,x - nz = READ_LOW( uint8_t (GET_MSB() + x) ); - goto tst_common; - - case 0x83: // TST zp - nz = READ_LOW( GET_MSB() ); - tst_common: - pc += 2; - status &= ~st_v; - status |= nz & st_v; - if ( nz & data ) - goto loop; // Z should be clear, and nz must be non-zero if nz & data is - nz <<= 8; // set Z flag without affecting N flag - goto loop; - - { - fuint16 addr; - case 0x0C: // TSB abs - case 0x1C: // TRB abs - addr = GET_ADDR(); - pc++; - goto txb_addr; - - // TODO: everyone lists different behaviors for the status flags, ugh - case 0x04: // TSB zp - case 0x14: // TRB zp - addr = data + ram_addr; - txb_addr: - FLUSH_TIME(); - nz = a | READ( addr ); - if ( opcode & 0x10 ) - nz ^= a; // bits from a will already be set, so this clears them - status &= ~st_v; - status |= nz & st_v; - pc++; - WRITE( addr, nz ); - CACHE_TIME(); - goto loop; - } - - case 0x07: // RMBn - case 0x17: - case 0x27: - case 0x37: - case 0x47: - case 0x57: - case 0x67: - case 0x77: - pc++; - READ_LOW( data ) &= ~(1 << (opcode >> 4)); - goto loop; - - case 0x87: // SMBn - case 0x97: - case 0xA7: - case 0xB7: - case 0xC7: - case 0xD7: - case 0xE7: - case 0xF7: - pc++; - READ_LOW( data ) |= 1 << ((opcode >> 4) - 8); - goto loop; - -// Load/store - - case 0x9E: // STZ abs,x - data += x; - case 0x9C: // STZ abs - ADD_PAGE( data ); - pc++; - FLUSH_TIME(); - WRITE( data, 0 ); - CACHE_TIME(); - goto loop; - - case 0x74: // STZ zp,x - data = uint8_t (data + x); - case 0x64: // STZ zp - pc++; - WRITE_LOW( data, 0 ); - goto loop; - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (data + y); - case 0xA6: // LDX zp - data = READ_LOW( data ); - case 0xA2: // LDX #imm - pc++; - x = data; - nz = data; - goto loop; - - case 0xB4: // LDY zp,x - data = uint8_t (data + x); - case 0xA4: // LDY zp - data = READ_LOW( data ); - case 0xA0: // LDY #imm - pc++; - y = data; - nz = data; - goto loop; - - case 0xBC: // LDY abs,X - data += x; - PAGE_CROSS_PENALTY( data ); - case 0xAC:{// LDY abs - fuint16 addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - fuint16 addr = GET_ADDR(); - pc += 2; - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - fuint16 addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( addr ); - CACHE_TIME(); - goto cpx_data; - } - - case 0xE4: // CPX zp - data = READ_LOW( data ); - case 0xE0: // CPX #imm - cpx_data: - nz = x - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - fuint16 addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( addr ); - CACHE_TIME(); - goto cpy_data; - } - - case 0xC4: // CPY zp - data = READ_LOW( data ); - case 0xC0: // CPY #imm - cpy_data: - nz = y - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - -// Logical - -#define ARITH_ADDR_MODES( op )\ - case op - 0x04: /* (ind,x) */\ - data = uint8_t (data + x);\ - case op + 0x0D: /* (ind) */\ - data = 0x100 * READ_LOW( uint8_t (data + 1) ) + READ_LOW( data );\ - goto ptr##op;\ - case op + 0x0C:{/* (ind),y */\ - fuint16 temp = READ_LOW( data ) + y;\ - PAGE_CROSS_PENALTY( temp );\ - data = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - goto ptr##op;\ +#define WRITE_FAST( addr, data ) \ +{\ + int page = PAGE( addr );\ + byte* out = write_pages [page];\ + addr &= CPU.page_size - 1;\ + if ( out )\ + {\ + out [addr] = data;\ }\ - case op + 0x10: /* zp,X */\ - data = uint8_t (data + x);\ - case op + 0x00: /* zp */\ - data = READ_LOW( data );\ - goto imm##op;\ - case op + 0x14: /* abs,Y */\ - data += y;\ - goto ind##op;\ - case op + 0x18: /* abs,X */\ - data += x;\ - ind##op:\ - PAGE_CROSS_PENALTY( data );\ - case op + 0x08: /* abs */\ - ADD_PAGE( data );\ - ptr##op:\ + else if ( CPU.mmr [page] == 0xFF )\ + {\ FLUSH_TIME();\ - data = READ( data );\ + write_mem_( addr, data );\ CACHE_TIME();\ - case op + 0x04: /* imm */\ - imm##op: + }\ +} - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - ARITH_ADDR_MODES( 0x25 ) // AND - nz = (a &= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x45 ) // EOR - nz = (a ^= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x05 ) // ORA - nz = (a |= data); - pc++; - goto loop; - -// Add/subtract +#define READ_LOW( addr ) (ram [addr]) +#define WRITE_LOW( addr, data ) (ram [addr] = data) +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) write_mem( addr, data ) +#define WRITE_VDP( addr, data ) write_vdp( addr, data ) +#define CPU_DONE( result_out ) { FLUSH_TIME(); result_out = cpu_done(); CACHE_TIME(); } +#define SET_MMR( reg, bank ) set_mmr( reg, bank ) - ARITH_ADDR_MODES( 0xE5 ) // SBC - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - if ( status & st_d ) - dprintf( "Decimal mode not supported\n" ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= ov >> 2 & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) nz; - goto loop; - } - -// Shift/rotate +#define CPU cpu +#define IDLE_ADDR idle_addr - case 0x4A: // LSR A - c = 0; - case 0x6A: // ROR A - nz = c >> 1 & 0x80; - c = a << 8; - nz |= a >> 1; - a = nz; - goto loop; - - case 0x0A: // ASL A - nz = a << 1; - c = nz; - a = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } +#define CPU_BEGIN \ +bool Hes_Core::run_cpu( time_t end_time )\ +{\ + cpu.set_end_time( end_time ); - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - ADD_PAGE( data ); - FLUSH_TIME(); - int temp = READ( data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - goto rotate_common; - } - - case 0x3E: // ROL abs,X - data += x; - goto rol_abs; - - case 0x1E: // ASL abs,X - data += x; - case 0x0E: // ASL abs - c = 0; - case 0x2E: // ROL abs - rol_abs: - ADD_PAGE( data ); - nz = c >> 8 & 1; - FLUSH_TIME(); - nz |= (c = READ( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (data + x); - case 0x46: // LSR zp - c = 0; - case 0x66: // ROR zp - ror_zp: { - int temp = READ_LOW( data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - goto write_nz_zp; - } - - case 0x36: // ROL zp,x - data = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (data + x); - case 0x06: // ASL zp - c = 0; - case 0x26: // ROL zp - rol_zp: - nz = c >> 8 & 1; - nz |= (c = READ_LOW( data ) << 1); - goto write_nz_zp; - -// Increment/decrement - -#define INC_DEC_AXY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - - case 0x1A: // INA - INC_DEC_AXY( a, +1 ) - - case 0xE8: // INX - INC_DEC_AXY( x, +1 ) - - case 0xC8: // INY - INC_DEC_AXY( y, +1 ) - - case 0x3A: // DEA - INC_DEC_AXY( a, -1 ) - - case 0xCA: // DEX - INC_DEC_AXY( x, -1 ) - - case 0x88: // DEY - INC_DEC_AXY( y, -1 ) - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = (unsigned) -1; - add_nz_zp: - nz += READ_LOW( data ); - write_nz_zp: - pc++; - WRITE_LOW( data, nz ); - goto loop; - - case 0xFE: // INC abs,x - data = x + GET_ADDR(); - goto inc_ptr; - - case 0xEE: // INC abs - data = GET_ADDR(); - inc_ptr: - nz = 1; - goto inc_common; - - case 0xDE: // DEC abs,x - data = x + GET_ADDR(); - goto dec_ptr; - - case 0xCE: // DEC abs - data = GET_ADDR(); - dec_ptr: - nz = (unsigned) -1; - inc_common: - FLUSH_TIME(); - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xA8: // TAY - y = a; - nz = a; - goto loop; - - case 0x98: // TYA - a = y; - nz = y; - goto loop; - - case 0xAA: // TAX - x = a; - nz = a; - goto loop; - - case 0x8A: // TXA - a = x; - nz = x; - goto loop; - - case 0x9A: // TXS - SET_SP( x ); // verified (no flag change) - goto loop; - - case 0xBA: // TSX - x = nz = GET_SP(); - goto loop; - - #define SWAP_REGS( r1, r2 ) {\ - fuint8 t = r1;\ - r1 = r2;\ - r2 = t;\ - goto loop;\ - } - - case 0x02: // SXY - SWAP_REGS( x, y ); - - case 0x22: // SAX - SWAP_REGS( a, x ); - - case 0x42: // SAY - SWAP_REGS( a, y ); - - case 0x62: // CLA - a = 0; - goto loop; - - case 0x82: // CLX - x = 0; - goto loop; - - case 0xC2: // CLY - y = 0; - goto loop; - -// Stack - - case 0x48: // PHA - PUSH( a ); - goto loop; - - case 0xDA: // PHX - PUSH( x ); - goto loop; - - case 0x5A: // PHY - PUSH( y ); - goto loop; - - case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - this->r.status = status; // update externally-visible I flag - if ( (data ^ status) & st_i ) - { - hes_time_t new_time = end_time_; - if ( !(status & st_i) && new_time > irq_time_ ) - new_time = irq_time_; - blargg_long delta = s.base - new_time; - s.base = new_time; - s_time += delta; - } - goto loop; - } - - #define POP() READ_LOW( sp ); sp = (sp - 0xFF) | 0x100 - - case 0x68: // PLA - a = nz = POP(); - goto loop; - - case 0xFA: // PLX - x = nz = POP(); - goto loop; - - case 0x7A: // PLY - y = nz = POP(); - goto loop; - - case 0x28:{// PLP - fuint8 temp = POP(); - fuint8 changed = status ^ temp; - SET_STATUS( temp ); - if ( !(changed & st_i) ) - goto loop; // I flag didn't change - if ( status & st_i ) - goto handle_sei; - goto handle_cli; - } - #undef POP - - case 0x08: { // PHP - fuint8 temp; - CALC_STATUS( temp ); - PUSH( temp | st_b ); - goto loop; - } - -// Flags - - case 0x38: // SEC - c = (unsigned) ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) - { - if ( TIME < irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = irq_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - if ( delta >= s_time + 1 ) - { - // delayed irq until after next instruction - s.base += s_time + 1; - s_time = -1; - irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop - goto loop; - } - delayed_cli: - dprintf( "Delayed CLI not supported\n" ); // TODO: implement - goto loop; - } - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - dprintf( "Delayed SEI not supported\n" ); // TODO: implement - goto loop; - } - -// Special - - case 0x53:{// TAM - fuint8 const bits = data; // avoid using data across function call - pc++; - for ( int i = 0; i < 8; i++ ) - if ( bits & (1 << i) ) - set_mmr( i, a ); - goto loop; - } - - case 0x43:{// TMA - pc++; - byte const* in = mmr; - do - { - if ( data & 1 ) - a = *in; - in++; - } - while ( (data >>= 1) != 0 ); - goto loop; - } - - case 0x03: // ST0 - case 0x13: // ST1 - case 0x23:{// ST2 - fuint16 addr = opcode >> 4; - if ( addr ) - addr++; - pc++; - FLUSH_TIME(); - CPU_WRITE_VDP( this, addr, data, TIME ); - CACHE_TIME(); - goto loop; - } - - case 0xEA: // NOP - goto loop; - - case 0x54: // CSL - dprintf( "CSL not supported\n" ); - illegal_encountered = true; - goto loop; - - case 0xD4: // CSH - goto loop; - - case 0xF4: { // SET - //fuint16 operand = GET_MSB(); - dprintf( "SET not handled\n" ); - //switch ( data ) - //{ - //} - illegal_encountered = true; - goto loop; - } - -// Block transfer - - { - fuint16 in_alt; - fint16 in_inc; - fuint16 out_alt; - fint16 out_inc; - - case 0xE3: // TIA - in_alt = 0; - goto bxfer_alt; - - case 0xF3: // TAI - in_alt = 1; - bxfer_alt: - in_inc = in_alt ^ 1; - out_alt = in_inc; - out_inc = in_alt; - goto bxfer; - - case 0xD3: // TIN - in_inc = 1; - out_inc = 0; - goto bxfer_no_alt; - - case 0xC3: // TDD - in_inc = -1; - out_inc = -1; - goto bxfer_no_alt; - - case 0x73: // TII - in_inc = 1; - out_inc = 1; - bxfer_no_alt: - in_alt = 0; - out_alt = 0; - bxfer: - fuint16 in = GET_LE16( instr + 0 ); - fuint16 out = GET_LE16( instr + 2 ); - int count = GET_LE16( instr + 4 ); - if ( !count ) - count = 0x10000; - pc += 6; - WRITE_LOW( 0x100 | (sp - 1), y ); - WRITE_LOW( 0x100 | (sp - 2), a ); - WRITE_LOW( 0x100 | (sp - 3), x ); - FLUSH_TIME(); - do - { - // TODO: reads from $0800-$1400 in I/O page return 0 and don't access I/O - fuint8 t = READ( in ); - in += in_inc; - in &= 0xFFFF; - s.time += 6; - if ( in_alt ) - in_inc = -in_inc; - WRITE( out, t ); - out += out_inc; - out &= 0xFFFF; - if ( out_alt ) - out_inc = -out_inc; - } - while ( --count ); - CACHE_TIME(); - goto loop; - } - -// Illegal - - default: - assert( (unsigned) opcode <= 0xFF ); - dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 ); - illegal_encountered = true; - goto loop; - } - assert( false ); - - int result_; -handle_brk: - pc++; - result_ = 6; - -interrupt: - { - s_time += 7; - - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - pc = GET_LE16( &READ_PROG( 0xFFF0 ) + result_ ); - - sp = (sp - 3) | 0x100; - fuint8 temp; - CALC_STATUS( temp ); - if ( result_ == 6 ) - temp |= st_b; - WRITE_LOW( sp, temp ); - - status &= ~st_d; - status |= st_i; - this->r.status = status; // update externally-visible I flag - - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - goto loop; - } - -idle_done: - s_time = 0; -out_of_time: - pc--; - FLUSH_TIME(); - CPU_DONE( this, TIME, result_ ); - CACHE_TIME(); - if ( result_ > 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - - s.time = s_time; - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; + #include "Hes_Cpu_run.h" return illegal_encountered; } - diff --git a/Frameworks/GME/gme/Hes_Cpu.h b/Frameworks/GME/gme/Hes_Cpu.h old mode 100755 new mode 100644 index 9cb8b006b..b182390cc --- a/Frameworks/GME/gme/Hes_Cpu.h +++ b/Frameworks/GME/gme/Hes_Cpu.h @@ -1,124 +1,139 @@ // PC Engine CPU emulator for use with HES music files -// Game_Music_Emu 0.5.2 +// $package #ifndef HES_CPU_H #define HES_CPU_H #include "blargg_common.h" -typedef blargg_long hes_time_t; // clock cycle count -typedef unsigned hes_addr_t; // 16-bit address -enum { future_hes_time = LONG_MAX / 2 + 1 }; - class Hes_Cpu { public: - typedef BOOST::uint8_t uint8_t; + typedef BOOST::uint8_t byte; + typedef int time_t; + typedef int addr_t; + enum { future_time = INT_MAX/2 + 1 }; void reset(); - enum { page_size = 0x2000 }; - enum { page_shift = 13 }; - enum { page_count = 8 }; - void set_mmr( int reg, int bank ); + enum { page_bits = 13 }; + enum { page_size = 1 << page_bits }; + enum { page_count = 0x10000 / page_size }; + void set_mmr( int reg, int bank, void const* code ); - uint8_t const* get_code( hes_addr_t ); + byte const* get_code( addr_t ); - uint8_t ram [page_size]; - - // not kept updated during a call to run() + // NOT kept updated during emulation. struct registers_t { BOOST::uint16_t pc; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; + byte a; + byte x; + byte y; + byte flags; + byte sp; }; registers_t r; // page mapping registers - uint8_t mmr [page_count + 1]; - - // Set end_time and run CPU from current time. Returns true if any illegal - // instructions were encountered. - bool run( hes_time_t end_time ); + byte mmr [page_count + 1]; // Time of beginning of next instruction to be executed - hes_time_t time() const { return state->time + state->base; } - void set_time( hes_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } + time_t time() const { return cpu_state->time + cpu_state->base; } + void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } + void adjust_time( int delta ) { cpu_state->time += delta; } - hes_time_t irq_time() const { return irq_time_; } - void set_irq_time( hes_time_t ); + // Clocks past end (negative if before) + int time_past_end() const { return cpu_state->time; } - hes_time_t end_time() const { return end_time_; } - void set_end_time( hes_time_t ); + // Time of next IRQ + time_t irq_time() const { return irq_time_; } + void set_irq_time( time_t ); - void end_frame( hes_time_t ); + // Emulation stops once time >= end_time + time_t end_time() const { return end_time_; } + void set_end_time( time_t ); - // Attempt to execute instruction here results in CPU advancing time to - // lesser of irq_time() and end_time() (or end_time() if IRQs are - // disabled) - enum { idle_addr = 0x1FFF }; + // Subtracts t from all times + void end_frame( time_t t ); // Can read this many bytes past end of a page enum { cpu_padding = 8 }; -public: - Hes_Cpu() { state = &state_; } - enum { irq_inhibit = 0x04 }; private: // noncopyable Hes_Cpu( const Hes_Cpu& ); Hes_Cpu& operator = ( const Hes_Cpu& ); - - struct state_t { - uint8_t const* code_map [page_count + 1]; - hes_time_t base; - blargg_long time; + + +// Implementation +public: + Hes_Cpu() { cpu_state = &cpu_state_; } + enum { irq_inhibit_mask = 0x04 }; + + struct cpu_state_t { + byte const* code_map [page_count + 1]; + time_t base; + int time; }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - hes_time_t irq_time_; - hes_time_t end_time_; + cpu_state_t* cpu_state; // points to cpu_state_ or a local copy + cpu_state_t cpu_state_; + time_t irq_time_; + time_t end_time_; +private: void set_code_page( int, void const* ); - inline int update_end_time( hes_time_t end, hes_time_t irq ); + inline void update_end_time( time_t end, time_t irq ); }; -inline BOOST::uint8_t const* Hes_Cpu::get_code( hes_addr_t addr ) +#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> Hes_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define HES_CPU_OFFSET( addr ) (addr) +#else + #define HES_CPU_OFFSET( addr ) ((addr) & (Hes_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t const* Hes_Cpu::get_code( addr_t addr ) { - return state->code_map [addr >> page_shift] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; + return cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr ); } -inline int Hes_Cpu::update_end_time( hes_time_t t, hes_time_t irq ) +inline void Hes_Cpu::update_end_time( time_t end, time_t irq ) { - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - int delta = state->base - t; - state->base = t; - return delta; + if ( end > irq && !(r.flags & irq_inhibit_mask) ) + end = irq; + + cpu_state->time += cpu_state->base - end; + cpu_state->base = end; } -inline void Hes_Cpu::set_irq_time( hes_time_t t ) +inline void Hes_Cpu::set_irq_time( time_t t ) { - state->time += update_end_time( end_time_, (irq_time_ = t) ); + irq_time_ = t; + update_end_time( end_time_, t ); } -inline void Hes_Cpu::set_end_time( hes_time_t t ) +inline void Hes_Cpu::set_end_time( time_t t ) { - state->time += update_end_time( (end_time_ = t), irq_time_ ); + end_time_ = t; + update_end_time( t, irq_time_ ); } -inline void Hes_Cpu::end_frame( hes_time_t t ) +inline void Hes_Cpu::end_frame( time_t t ) { - assert( state == &state_ ); - state_.base -= t; - if ( irq_time_ < future_hes_time ) irq_time_ -= t; - if ( end_time_ < future_hes_time ) end_time_ -= t; + assert( cpu_state == &cpu_state_ ); + cpu_state_.base -= t; + if ( irq_time_ < future_time ) irq_time_ -= t; + if ( end_time_ < future_time ) end_time_ -= t; +} + +inline void Hes_Cpu::set_mmr( int reg, int bank, void const* code ) +{ + assert( (unsigned) reg <= page_count ); // allow page past end to be set + assert( (unsigned) bank < 0x100 ); + mmr [reg] = bank; + byte const* p = STATIC_CAST(byte const*,code) - HES_CPU_OFFSET( reg << page_bits ); + cpu_state->code_map [reg] = p; + cpu_state_.code_map [reg] = p; } #endif diff --git a/Frameworks/GME/gme/Hes_Emu.cpp b/Frameworks/GME/gme/Hes_Emu.cpp old mode 100755 new mode 100644 index fafb2666d..266c14394 --- a/Frameworks/GME/gme/Hes_Emu.cpp +++ b/Frameworks/GME/gme/Hes_Emu.cpp @@ -1,529 +1,192 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Hes_Emu.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -int const timer_mask = 0x04; -int const vdp_mask = 0x02; -int const i_flag_mask = 0x04; -int const unmapped = 0xFF; - -long const period_60hz = 262 * 455L; // scanlines * clocks per scanline - -Hes_Emu::Hes_Emu() -{ - timer.raw_load = 0; - set_type( gme_hes_type ); - - static const char* const names [Hes_Apu::osc_count] = { - "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2" - }; - set_voice_names( names ); - - static int const types [Hes_Apu::osc_count] = { - wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, - mixed_type | 0, mixed_type | 1 - }; - set_voice_types( types ); - set_silence_lookahead( 6 ); - set_gain( 1.11 ); -} - -Hes_Emu::~Hes_Emu() { } - -void Hes_Emu::unload() -{ - rom.clear(); - Music_Emu::unload(); -} - -// Track info - -static byte const* copy_field( byte const* in, char* out ) -{ - if ( in ) - { - int len = 0x20; - if ( in [0x1F] && !in [0x2F] ) - len = 0x30; // fields are sometimes 16 bytes longer (ugh) - - // since text fields are where any data could be, detect non-text - // and fields with data after zero byte terminator - - int i = 0; - for ( i = 0; i < len && in [i]; i++ ) - if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text - return 0; // non-ASCII found - - for ( ; i < len; i++ ) - if ( in [i] ) - return 0; // data after terminator - - Gme_File::copy_field_( out, (char const*) in, len ); - in += len; - } - return in; -} - -static void copy_hes_fields( byte const* in, track_info_t* out ) -{ - if ( *in >= ' ' ) - { - in = copy_field( in, out->game ); - in = copy_field( in, out->author ); - in = copy_field( in, out->copyright ); - } -} - -blargg_err_t Hes_Emu::track_info_( track_info_t* out, int ) const -{ - copy_hes_fields( rom.begin() + 0x20, out ); - return 0; -} - -static blargg_err_t check_hes_header( void const* header ) -{ - if ( memcmp( header, "HESM", 4 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Hes_File : Gme_Info_ -{ - struct header_t { - char header [Hes_Emu::header_size]; - char unused [0x20]; - byte fields [0x30 * 3]; - } h; - - Hes_File() { set_type( gme_hes_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 ); - blargg_err_t err = in.read( &h, sizeof h ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - return check_hes_header( &h ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_hes_fields( h.fields, out ); - return 0; - } -}; - -static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } -static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } - -gme_type_t_ const gme_hes_type [1] = { "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }; - -// Setup - -blargg_err_t Hes_Emu::load_( Data_Reader& in ) -{ - assert( offsetof (header_t,unused [4]) == header_size ); - RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) ); - - RETURN_ERR( check_hes_header( header_.tag ) ); - - if ( header_.vers != 0 ) - set_warning( "Unknown file version" ); - - if ( memcmp( header_.data_tag, "DATA", 4 ) ) - set_warning( "Data header missing" ); - - if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) - set_warning( "Unknown header data" ); - - // File spec supports multiple blocks, but I haven't found any, and - // many files have bad sizes in the only block, so it's simpler to - // just try to load the damn data as best as possible. - - long addr = get_le32( header_.addr ); - long size = get_le32( header_.size ); - long const rom_max = 0x100000; - if ( addr & ~(rom_max - 1) ) - { - set_warning( "Invalid address" ); - addr &= rom_max - 1; - } - if ( (unsigned long) (addr + size) > (unsigned long) rom_max ) - set_warning( "Invalid size" ); - - if ( size != rom.file_size() ) - { - if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) - set_warning( "Multiple DATA not supported" ); - else if ( size < rom.file_size() ) - set_warning( "Extra file data" ); - else - set_warning( "Missing file data" ); - } - - rom.set_addr( addr ); - - set_voice_count( apu.osc_count ); - - apu.volume( gain() ); - - return setup_buffer( 7159091 ); -} - -void Hes_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); -} - -void Hes_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - apu.osc_output( i, center, left, right ); -} - -// Emulation - -void Hes_Emu::recalc_timer_load() -{ - timer.load = timer.raw_load * timer_base + 1; -} - -void Hes_Emu::set_tempo_( double t ) -{ - play_period = hes_time_t (period_60hz / t); - timer_base = int (1024 / t); - recalc_timer_load(); -} - -blargg_err_t Hes_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( ram, 0, sizeof ram ); // some HES music relies on zero fill - memset( sgx, 0, sizeof sgx ); - - apu.reset(); - cpu::reset(); - - for ( unsigned i = 0; i < sizeof header_.banks; i++ ) - set_mmr( i, header_.banks [i] ); - set_mmr( page_count, 0xFF ); // unmapped beyond end of address space - - irq.disables = timer_mask | vdp_mask; - irq.timer = future_hes_time; - irq.vdp = future_hes_time; - - timer.enabled = false; - timer.raw_load= 0x80; - timer.count = timer.load; - timer.fired = false; - timer.last_time = 0; - - vdp.latch = 0; - vdp.control = 0; - vdp.next_vbl = 0; - - ram [0x1FF] = (idle_addr - 1) >> 8; - ram [0x1FE] = (idle_addr - 1) & 0xFF; - r.sp = 0xFD; - r.pc = get_le16( header_.init_addr ); - r.a = track; - - recalc_timer_load(); - last_frame_hook = 0; - - return 0; -} - -// Hardware - -void Hes_Emu::cpu_write_vdp( int addr, int data ) -{ - switch ( addr ) - { - case 0: - vdp.latch = data & 0x1F; - break; - - case 2: - if ( vdp.latch == 5 ) - { - if ( data & 0x04 ) - set_warning( "Scanline interrupt unsupported" ); - run_until( time() ); - vdp.control = data; - irq_changed(); - } - else - { - dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data ); - } - break; - - case 3: - dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); - break; - } -} - -void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) -{ - if ( unsigned (addr - apu.start_addr) <= apu.end_addr - apu.start_addr ) - { - GME_APU_HOOK( this, addr - apu.start_addr, data ); - // avoid going way past end when a long block xfer is writing to I/O space - hes_time_t t = min( time(), end_time() + 8 ); - apu.write_data( t, addr, data ); - return; - } - - hes_time_t time = this->time(); - switch ( addr ) - { - case 0x0000: - case 0x0002: - case 0x0003: - cpu_write_vdp( addr, data ); - return; - - case 0x0C00: { - run_until( time ); - timer.raw_load = (data & 0x7F) + 1; - recalc_timer_load(); - timer.count = timer.load; - break; - } - - case 0x0C01: - data &= 1; - if ( timer.enabled == data ) - return; - run_until( time ); - timer.enabled = data; - if ( data ) - timer.count = timer.load; - break; - - case 0x1402: - run_until( time ); - irq.disables = data; - if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values - dprintf( "Int mask: $%02X\n", data ); - break; - - case 0x1403: - run_until( time ); - if ( timer.enabled ) - timer.count = timer.load; - timer.fired = false; - break; - -#ifndef NDEBUG - case 0x1000: // I/O port - case 0x0402: // palette - case 0x0403: - case 0x0404: - case 0x0405: - return; - - default: - dprintf( "unmapped write $%04X <- $%02X\n", addr, data ); - return; -#endif - } - - irq_changed(); -} - -int Hes_Emu::cpu_read_( hes_addr_t addr ) -{ - hes_time_t time = this->time(); - addr &= page_size - 1; - switch ( addr ) - { - case 0x0000: - if ( irq.vdp > time ) - return 0; - irq.vdp = future_hes_time; - run_until( time ); - irq_changed(); - return 0x20; - - case 0x0002: - case 0x0003: - dprintf( "VDP read not supported: %d\n", addr ); - return 0; - - case 0x0C01: - //return timer.enabled; // TODO: remove? - case 0x0C00: - run_until( time ); - dprintf( "Timer count read\n" ); - return (unsigned) (timer.count - 1) / timer_base; - - case 0x1402: - return irq.disables; - - case 0x1403: - { - int status = 0; - if ( irq.timer <= time ) status |= timer_mask; - if ( irq.vdp <= time ) status |= vdp_mask; - return status; - } - - #ifndef NDEBUG - case 0x1000: // I/O port - case 0x180C: // CD-ROM - case 0x180D: - break; - - default: - dprintf( "unmapped read $%04X\n", addr ); - #endif - } - - return unmapped; -} - -// see hes_cpu_io.h for core read/write functions - -// Emulation - -void Hes_Emu::run_until( hes_time_t present ) -{ - while ( vdp.next_vbl < present ) - vdp.next_vbl += play_period; - - hes_time_t elapsed = present - timer.last_time; - if ( elapsed > 0 ) - { - if ( timer.enabled ) - { - timer.count -= elapsed; - if ( timer.count <= 0 ) - timer.count += timer.load; - } - timer.last_time = present; - } -} - -void Hes_Emu::irq_changed() -{ - hes_time_t present = time(); - - if ( irq.timer > present ) - { - irq.timer = future_hes_time; - if ( timer.enabled && !timer.fired ) - irq.timer = present + timer.count; - } - - if ( irq.vdp > present ) - { - irq.vdp = future_hes_time; - if ( vdp.control & 0x08 ) - irq.vdp = vdp.next_vbl; - } - - hes_time_t time = future_hes_time; - if ( !(irq.disables & timer_mask) ) time = irq.timer; - if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); - - set_irq_time( time ); -} - -int Hes_Emu::cpu_done() -{ - check( time() >= end_time() || - (!(r.status & i_flag_mask) && time() >= irq_time()) ); - - if ( !(r.status & i_flag_mask) ) - { - hes_time_t present = time(); - - if ( irq.timer <= present && !(irq.disables & timer_mask) ) - { - timer.fired = true; - irq.timer = future_hes_time; - irq_changed(); // overkill, but not worth writing custom code - #if GME_FRAME_HOOK_DEFINED - { - unsigned const threshold = period_60hz / 30; - unsigned long elapsed = present - last_frame_hook; - if ( elapsed - period_60hz + threshold / 2 < threshold ) - { - last_frame_hook = present; - GME_FRAME_HOOK( this ); - } - } - #endif - return 0x0A; - } - - if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) - { - // work around for bugs with music not acknowledging VDP - //run_until( present ); - //irq.vdp = future_hes_time; - //irq_changed(); - #if GME_FRAME_HOOK_DEFINED - last_frame_hook = present; - GME_FRAME_HOOK( this ); - #endif - return 0x08; - } - } - return 0; -} - -static void adjust_time( blargg_long& time, hes_time_t delta ) -{ - if ( time < future_hes_time ) - { - time -= delta; - if ( time < 0 ) - time = 0; - } -} - -blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) -{ - blip_time_t const duration = duration_; // cache - - if ( cpu::run( duration ) ) - set_warning( "Emulation error (illegal instruction)" ); - - check( time() >= duration ); - //check( time() - duration < 20 ); // Txx instruction could cause going way over - - run_until( duration ); - - // end time frame - timer.last_time -= duration; - vdp.next_vbl -= duration; - #if GME_FRAME_HOOK_DEFINED - last_frame_hook -= duration; - #endif - cpu::end_frame( duration ); - ::adjust_time( irq.timer, duration ); - ::adjust_time( irq.vdp, duration ); - apu.end_frame( duration ); - - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Hes_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Hes_Emu::Hes_Emu() +{ + set_type( gme_hes_type ); + set_silence_lookahead( 6 ); + set_gain( 1.11 ); +} + +Hes_Emu::~Hes_Emu() { } + +void Hes_Emu::unload() +{ + core.unload(); + Music_Emu::unload(); +} + +static byte const* copy_field( byte const in [], char* out ) +{ + if ( in ) + { + int len = 0x20; + if ( in [0x1F] && !in [0x2F] ) + len = 0x30; // fields are sometimes 16 bytes longer (ugh) + + // since text fields are where any data could be, detect non-text + // and fields with data after zero byte terminator + + int i = 0; + for ( ; i < len && in [i]; i++ ) + if ( (unsigned) (in [i] - ' ') >= 0xFF - ' ' ) // also treat 0xFF as non-text + return 0; // non-ASCII found + + for ( ; i < len; i++ ) + if ( in [i] ) + return 0; // data after terminator + + Gme_File::copy_field_( out, (char const*) in, len ); + in += len; + } + return in; +} + +static byte const* copy_hes_fields( byte const in [], track_info_t* out ) +{ + byte const* in_offset = in; + if ( *in_offset >= ' ' ) + { + in_offset = copy_field( in_offset, out->game ); + in_offset = copy_field( in_offset, out->author ); + in_offset = copy_field( in_offset, out->copyright ); + } + return in_offset ? in_offset : in; +} + +static void hash_hes_file( Hes_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.banks[0], sizeof(h.banks) ); + out.hash_( &h.data_size[0], sizeof(h.data_size) ); + out.hash_( &h.addr[0], sizeof(h.addr) ); + out.hash_( &h.unused[0], sizeof(h.unused) ); + out.hash_( data, Hes_Core::info_offset ); + + track_info_t temp; // GCC whines about passing a pointer to a temporary here + byte const* more_data = copy_hes_fields( data + Hes_Core::info_offset, &temp ); + out.hash_( more_data, data_size - ( more_data - data ) ); +} + +blargg_err_t Hes_Emu::track_info_( track_info_t* out, int ) const +{ + copy_hes_fields( core.data() + core.info_offset, out ); + return blargg_ok; +} + +struct Hes_File : Gme_Info_ +{ + enum { fields_offset = Hes_Core::header_t::size + Hes_Core::info_offset }; + + union header_t { + Hes_Core::header_t header; + byte data [fields_offset + 0x30 * 3]; + } const* h; + + Hes_File() + { + set_type( gme_hes_type ); + } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( header_t const* ) begin; + + if ( !h->header.valid_tag() ) + return blargg_err_file_type; + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_hes_fields( h->data + fields_offset, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_hes_file( h->header, file_begin() + h->header.size, file_end() - file_begin() - h->header.size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_hes_emu () { return BLARGG_NEW Hes_Emu ; } +static Music_Emu* new_hes_file() { return BLARGG_NEW Hes_File; } + +gme_type_t_ const gme_hes_type [1] = {{ "PC Engine", 256, &new_hes_emu, &new_hes_file, "HES", 1 }}; + +blargg_err_t Hes_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core.load( in ) ); + + static const char* const names [Hes_Apu::osc_count + Hes_Apu_Adpcm::osc_count] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2", "ADPCM" + }; + set_voice_names( names ); + + static int const types [Hes_Apu::osc_count + Hes_Apu_Adpcm::osc_count] = { + wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2 + }; + set_voice_types( types ); + + set_voice_count( core.apu().osc_count + core.adpcm().osc_count ); + core.apu().volume( gain() ); + core.adpcm().volume( gain() ); + + return setup_buffer( 7159091 ); +} + +void Hes_Emu::update_eq( blip_eq_t const& eq ) +{ + core.apu().treble_eq( eq ); + core.adpcm().treble_eq( eq ); +} + +void Hes_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < core.apu().osc_count ) + core.apu().set_output( i, c, l, r ); + else if ( i == core.apu().osc_count ) + core.adpcm().set_output( 0, c, l, r ); +} + +void Hes_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Hes_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + return core.start_track( track ); +} + +blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) +{ + return core.end_frame( duration_ ); +} + +blargg_err_t Hes_Emu::hash_( Hash_Function& out ) const +{ + hash_hes_file( header(), core.data(), core.data_size(), out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Hes_Emu.h b/Frameworks/GME/gme/Hes_Emu.h old mode 100755 new mode 100644 index 9951eb6a2..50568da31 --- a/Frameworks/GME/gme/Hes_Emu.h +++ b/Frameworks/GME/gme/Hes_Emu.h @@ -1,94 +1,42 @@ // TurboGrafx-16/PC Engine HES music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef HES_EMU_H #define HES_EMU_H #include "Classic_Emu.h" -#include "Hes_Apu.h" -#include "Hes_Cpu.h" +#include "Hes_Core.h" -class Hes_Emu : private Hes_Cpu, public Classic_Emu { - typedef Hes_Cpu cpu; +class Hes_Emu : public Classic_Emu { public: - // HES file header - enum { header_size = 0x20 }; - struct header_t - { - byte tag [4]; - byte vers; - byte first_track; - byte init_addr [2]; - byte banks [8]; - byte data_tag [4]; - byte size [4]; - byte addr [4]; - byte unused [4]; - }; + + static gme_type_t static_type() { return gme_hes_type; } + + // HES file header (see Hes_Core.h) + typedef Hes_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return header_; } - - static gme_type_t static_type() { return gme_hes_type; } + header_t const& header() const { return core.header(); } + blargg_err_t hash_( Hash_Function& ) const; + +// Implementation public: Hes_Emu(); ~Hes_Emu(); + virtual void unload(); + protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); -public: private: friend class Hes_Cpu; - byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space - - int cpu_read_( hes_addr_t ); - int cpu_read( hes_addr_t ); - void cpu_write_( hes_addr_t, int data ); - void cpu_write( hes_addr_t, int ); - void cpu_write_vdp( int addr, int data ); - byte const* cpu_set_mmr( int page, int bank ); - int cpu_done(); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + private: - Rom_Data rom; - header_t header_; - hes_time_t play_period; - hes_time_t last_frame_hook; - int timer_base; - - struct { - hes_time_t last_time; - blargg_long count; - blargg_long load; - int raw_load; - byte enabled; - byte fired; - } timer; - - struct { - hes_time_t next_vbl; - byte latch; - byte control; - } vdp; - - struct { - hes_time_t timer; - hes_time_t vdp; - byte disables; - } irq; - - void recalc_timer_load(); - - // large items - Hes_Apu apu; - byte sgx [3 * page_size + cpu_padding]; - - void irq_changed(); - void run_until( hes_time_t ); + Hes_Core core; }; #endif diff --git a/Frameworks/GME/gme/Kss_Cpu.cpp b/Frameworks/GME/gme/Kss_Cpu.cpp old mode 100755 new mode 100644 index 37c4a7241..8abffca1f --- a/Frameworks/GME/gme/Kss_Cpu.cpp +++ b/Frameworks/GME/gme/Kss_Cpu.cpp @@ -1,1706 +1,35 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -/* -Last validated with zexall 2006.11.14 2:19 PM -* Doesn't implement the R register or immediate interrupt after EI. -* Address wrap-around isn't completely correct, but is prevented from crashing emulator. -*/ - -#include "Kss_Cpu.h" - -#include "blargg_endian.h" -#include - -//#include "z80_cpu_log.h" - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#define SYNC_TIME() (void) (s.time = s_time) -#define RELOAD_TIME() (void) (s_time = s.time) - -// Callbacks to emulator - -#define CPU_OUT( cpu, addr, data, time )\ - kss_cpu_out( this, time, addr, data ) - -#define CPU_IN( cpu, addr, time )\ - kss_cpu_in( this, time, addr ) - -#define CPU_WRITE( cpu, addr, data, time )\ - (SYNC_TIME(), kss_cpu_write( this, addr, data )) - -#include "blargg_source.h" - -// flags, named with hex value for clarity -int const S80 = 0x80; -int const Z40 = 0x40; -int const F20 = 0x20; -int const H10 = 0x10; -int const F08 = 0x08; -int const V04 = 0x04; -int const P04 = 0x04; -int const N02 = 0x02; -int const C01 = 0x01; - -#define SZ28P( n ) szpc [n] -#define SZ28PC( n ) szpc [n] -#define SZ28C( n ) (szpc [n] & ~P04) -#define SZ28( n ) SZ28C( n ) - -#define SET_R( n ) (void) (r.r = n) -#define GET_R() (r.r) - -Kss_Cpu::Kss_Cpu() -{ - state = &state_; - - for ( int i = 0x100; --i >= 0; ) - { - int even = 1; - for ( int p = i; p; p >>= 1 ) - even ^= p; - int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04); - szpc [i] = n; - szpc [i + 0x100] = n | C01; - } - szpc [0x000] |= Z40; - szpc [0x100] |= Z40; -} - -inline void Kss_Cpu::set_page( int i, void* write, void const* read ) -{ - blargg_long offset = KSS_CPU_PAGE_OFFSET( i * (blargg_long) page_size ); - state->write [i] = (byte *) write - offset; - state->read [i] = (byte const*) read - offset; -} - -void Kss_Cpu::reset( void* unmapped_write, void const* unmapped_read ) -{ - check( state == &state_ ); - state = &state_; - state_.time = 0; - state_.base = 0; - end_time_ = 0; - - for ( int i = 0; i < page_count + 1; i++ ) - set_page( i, unmapped_write, unmapped_read ); - - memset( &r, 0, sizeof r ); -} - -void Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ) -{ - // address range must begin and end on page boundaries - require( addr % page_size == 0 ); - require( size % page_size == 0 ); - - unsigned first_page = addr / page_size; - for ( unsigned i = size / page_size; i--; ) - { - blargg_long offset = i * (blargg_long) page_size; - set_page( first_page + i, (byte*) write + offset, (byte const*) read + offset ); - } -} - -#define TIME (s_time + s.base) -#define RW_MEM( addr, rw ) (s.rw [(addr) >> page_shift] [KSS_CPU_PAGE_OFFSET( addr )]) -#define READ_PROG( addr ) RW_MEM( addr, read ) -#define READ( addr ) READ_PROG( addr ) -//#define WRITE( addr, data ) (void) (RW_MEM( addr, write ) = data) -#define WRITE( addr, data ) CPU_WRITE( this, addr, data, TIME ) -#define READ_WORD( addr ) GET_LE16( &READ( addr ) ) -#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data ) -#define IN( addr ) CPU_IN( this, addr, TIME ) -#define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) - -#if BLARGG_BIG_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [n]) -#elif BLARGG_LITTLE_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) -#else - #error "Byte order of CPU must be known" -#endif - -//#define R16( n, shift, offset ) (r16_ [((n) >> shift) - (offset >> shift)]) - -// help compiler see that it can just adjust stack offset, saving an extra instruction -#define R16( n, shift, offset )\ - (*(uint16_t*) ((char*) r16_ - (offset >> (shift - 1)) + ((n) >> (shift - 1)))) - -#define CASE5( a, b, c, d, e ) case 0x##a:case 0x##b:case 0x##c:case 0x##d:case 0x##e -#define CASE6( a, b, c, d, e, f ) CASE5( a, b, c, d, e ): case 0x##f -#define CASE7( a, b, c, d, e, f, g ) CASE6( a, b, c, d, e, f ): case 0x##g -#define CASE8( a, b, c, d, e, f, g, h ) CASE7( a, b, c, d, e, f, g ): case 0x##h - -// high four bits are $ED time - 8, low four bits are $DD/$FD time - 8 -static byte const ed_dd_timing [0x100] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x0C,0x02,0x00,0x00,0x03,0x00,0x00,0x07,0x0C,0x02,0x00,0x00,0x03,0x00, -0x00,0x00,0x00,0x00,0x0F,0x0F,0x0B,0x00,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x10, -0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0xA0, -0x4B,0x4B,0x7B,0xCB,0x0B,0x6B,0x00,0x0B,0x40,0x40,0x70,0xC0,0x00,0x60,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x00, -0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x0B,0x00, -0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00,0xD0,0xD0,0xD0,0xD0,0x00,0x00,0x0B,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x06,0x00,0x0F,0x00,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00, -}; - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -bool Kss_Cpu::run( cpu_time_t end_time ) -{ - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - bool warning = false; - - typedef BOOST::int8_t int8_t; - - union { - regs_t rg; - pairs_t rp; - uint8_t r8_ [8]; // indexed - uint16_t r16_ [4]; - }; - rg = this->r.b; - - cpu_time_t s_time = s.time; - fuint16 pc = r.pc; - fuint16 sp = r.sp; - fuint16 ix = r.ix; // TODO: keep in memory for direct access? - fuint16 iy = r.iy; - int flags = r.b.flags; - - goto loop; -jr_not_taken: - s_time -= 5; - goto loop; -call_not_taken: - s_time -= 7; -jp_not_taken: - pc += 2; -loop: - - check( (unsigned long) pc < 0x10000 ); - check( (unsigned long) sp < 0x10000 ); - check( (unsigned) flags < 0x100 ); - check( (unsigned) ix < 0x10000 ); - check( (unsigned) iy < 0x10000 ); - - uint8_t const* instr = s.read [pc >> page_shift]; -#define GET_ADDR() GET_LE16( instr ) - - fuint8 opcode; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - opcode = instr [pc]; - pc++; - instr += pc; - #else - instr += KSS_CPU_PAGE_OFFSET( pc ); - opcode = *instr++; - pc++; - #endif - - static byte const base_timing [0x100] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 - 13,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1 - 12,10,16, 6, 4, 4, 7, 4,12,11,16, 6, 4, 4, 7, 4, // 2 - 12,10,13, 6,11,11,10, 4,12,11,13, 6, 4, 4, 7, 4, // 3 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 4 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 5 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 6 - 7, 7, 7, 7, 7, 7, 4, 7, 4, 4, 4, 4, 4, 4, 7, 4, // 7 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 8 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // 9 - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // A - 4, 4, 4, 4, 4, 4, 7, 4, 4, 4, 4, 4, 4, 4, 7, 4, // B - 11,10,10,10,17,11, 7,11,11,10,10, 8,17,17, 7,11, // C - 11,10,10,11,17,11, 7,11,11, 4,10,11,17, 8, 7,11, // D - 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E - 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F - }; - - fuint16 data; - data = base_timing [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = READ_PROG( pc ); - - #ifdef Z80_CPU_LOG_H - //log_opcode( opcode, READ_PROG( pc ) ); - z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); - z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), - READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); - #endif - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Common - - case 0x00: // NOP - CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. - goto loop; - - case 0x08:{// EX AF,AF' - int temp = r.alt.b.a; - r.alt.b.a = rg.a; - rg.a = temp; - - temp = r.alt.b.flags; - r.alt.b.flags = flags; - flags = temp; - goto loop; - } - - case 0xD3: // OUT (imm),A - pc++; - OUT( data + rg.a * 0x100, rg.a ); - goto loop; - - case 0x2E: // LD L,imm - pc++; - rg.l = data; - goto loop; - - case 0x3E: // LD A,imm - pc++; - rg.a = data; - goto loop; - - case 0x3A:{// LD A,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rg.a = READ( addr ); - goto loop; - } - -// Conditional - -#define ZERO (flags & Z40) -#define CARRY (flags & C01) -#define EVEN (flags & P04) -#define MINUS (flags & S80) - -// JR -// TODO: more efficient way to handle negative branch that wraps PC around -#define JR( cond ) {\ - int offset = (BOOST::int8_t) data;\ - pc++;\ - if ( !(cond) )\ - goto jr_not_taken;\ - pc = uint16_t (pc + offset);\ - goto loop;\ -} - - case 0x20: JR( !ZERO ) // JR NZ,disp - case 0x28: JR( ZERO ) // JR Z,disp - case 0x30: JR( !CARRY ) // JR NC,disp - case 0x38: JR( CARRY ) // JR C,disp - case 0x18: JR( true ) // JR disp - - case 0x10:{// DJNZ disp - int temp = rg.b - 1; - rg.b = temp; - JR( temp ) - } - -// JP -#define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; - - case 0xC2: JP( !ZERO ) // JP NZ,addr - case 0xCA: JP( ZERO ) // JP Z,addr - case 0xD2: JP( !CARRY ) // JP NC,addr - case 0xDA: JP( CARRY ) // JP C,addr - case 0xE2: JP( !EVEN ) // JP PO,addr - case 0xEA: JP( EVEN ) // JP PE,addr - case 0xF2: JP( !MINUS ) // JP P,addr - case 0xFA: JP( MINUS ) // JP M,addr - - case 0xC3: // JP addr - pc = GET_ADDR(); - goto loop; - - case 0xE9: // JP HL - pc = rp.hl; - goto loop; - -// RET -#define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; - - case 0xC0: RET( !ZERO ) // RET NZ - case 0xC8: RET( ZERO ) // RET Z - case 0xD0: RET( !CARRY ) // RET NC - case 0xD8: RET( CARRY ) // RET C - case 0xE0: RET( !EVEN ) // RET PO - case 0xE8: RET( EVEN ) // RET PE - case 0xF0: RET( !MINUS ) // RET P - case 0xF8: RET( MINUS ) // RET M - - case 0xC9: // RET - ret_taken: - pc = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// CALL -#define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; - - case 0xC4: CALL( !ZERO ) // CALL NZ,addr - case 0xCC: CALL( ZERO ) // CALL Z,addr - case 0xD4: CALL( !CARRY ) // CALL NC,addr - case 0xDC: CALL( CARRY ) // CALL C,addr - case 0xE4: CALL( !EVEN ) // CALL PO,addr - case 0xEC: CALL( EVEN ) // CALL PE,addr - case 0xF4: CALL( !MINUS ) // CALL P,addr - case 0xFC: CALL( MINUS ) // CALL M,addr - - case 0xCD:{// CALL addr - call_taken: - fuint16 addr = pc + 2; - pc = GET_ADDR(); - sp = uint16_t (sp - 2); - WRITE_WORD( sp, addr ); - goto loop; - } - - case 0xFF: // RST - if ( pc > idle_addr ) - goto hit_idle_addr; - CASE7( C7, CF, D7, DF, E7, EF, F7 ): - data = pc; - pc = opcode & 0x38; - goto push_data; - -// PUSH/POP - case 0xF5: // PUSH AF - data = rg.a * 0x100u + flags; - goto push_data; - - case 0xC5: // PUSH BC - case 0xD5: // PUSH DE - case 0xE5: // PUSH HL - data = R16( opcode, 4, 0xC5 ); - push_data: - sp = uint16_t (sp - 2); - WRITE_WORD( sp, data ); - goto loop; - - case 0xF1: // POP AF - flags = READ( sp ); - rg.a = READ( sp + 1 ); - sp = uint16_t (sp + 2); - goto loop; - - case 0xC1: // POP BC - case 0xD1: // POP DE - case 0xE1: // POP HL - R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto loop; - -// ADC/ADD/SBC/SUB - case 0x96: // SUB (HL) - case 0x86: // ADD (HL) - flags &= ~C01; - case 0x9E: // SBC (HL) - case 0x8E: // ADC (HL) - data = READ( rp.hl ); - goto adc_data; - - case 0xD6: // SUB A,imm - case 0xC6: // ADD imm - flags &= ~C01; - case 0xDE: // SBC A,imm - case 0xCE: // ADC imm - pc++; - goto adc_data; - - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r - flags &= ~C01; - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // SBC r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // ADC r - data = R8( opcode & 7, 0 ); - adc_data: { - int result = data + (flags & C01); - data ^= rg.a; - flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes - if ( flags ) - result = -result; - result += rg.a; - data ^= result; - flags |=(data & H10) | - ((data - -0x80) >> 6 & V04) | - SZ28C( result & 0x1FF ); - rg.a = result; - goto loop; - } - -// CP - case 0xBE: // CP (HL) - data = READ( rp.hl ); - goto cp_data; - - case 0xFE: // CP imm - pc++; - goto cp_data; - - CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r - data = R8( opcode, 0xB8 ); - cp_data: { - int result = rg.a - data; - flags = N02 | (data & (F20 | F08)) | (result >> 8 & C01); - data ^= rg.a; - flags |=(((result ^ rg.a) & data) >> 5 & V04) | - (((data & H10) ^ result) & (S80 | H10)); - if ( (uint8_t) result ) - goto loop; - flags |= Z40; - goto loop; - } - -// ADD HL,rp - - case 0x39: // ADD HL,SP - data = sp; - goto add_hl_data; - - case 0x09: // ADD HL,BC - case 0x19: // ADD HL,DE - case 0x29: // ADD HL,HL - data = R16( opcode, 4, 0x09 ); - add_hl_data: { - blargg_ulong sum = rp.hl + data; - data ^= rp.hl; - rp.hl = sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((data ^ sum) >> 8 & H10); - goto loop; - } - - case 0x27:{// DAA - int a = rg.a; - if ( a > 0x99 ) - flags |= C01; - - int adjust = 0x60 & -(flags & C01); - - if ( flags & H10 || (a & 0x0F) > 9 ) - adjust |= 0x06; - - if ( flags & N02 ) - adjust = -adjust; - a += adjust; - - flags = (flags & (C01 | N02)) | - ((rg.a ^ a) & H10) | - SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - /* - case 0x27:{// DAA - // more optimized, but probably not worth the obscurity - int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags - int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 - - if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 - adjust |= 0x06; - - if ( f & N02 ) - adjust = -adjust; - int a = rg.a + adjust; - - flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); - rg.a = a; - goto loop; - } - */ - -// INC/DEC - case 0x34: // INC (HL) - data = READ( rp.hl ) + 1; - WRITE( rp.hl, data ); - goto inc_set_flags; - - CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r - data = ++R8( opcode >> 3, 0 ); - inc_set_flags: - flags = (flags & C01) | - (((data & 0x0F) - 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x80 ) - goto loop; - flags |= V04; - goto loop; - - case 0x35: // DEC (HL) - data = READ( rp.hl ) - 1; - WRITE( rp.hl, data ); - goto dec_set_flags; - - CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r - data = --R8( opcode >> 3, 0 ); - dec_set_flags: - flags = (flags & C01) | N02 | - (((data & 0x0F) + 1) & H10) | - SZ28( (uint8_t) data ); - if ( data != 0x7F ) - goto loop; - flags |= V04; - goto loop; - - case 0x03: // INC BC - case 0x13: // INC DE - case 0x23: // INC HL - R16( opcode, 4, 0x03 )++; - goto loop; - - case 0x33: // INC SP - sp = uint16_t (sp + 1); - goto loop; - - case 0x0B: // DEC BC - case 0x1B: // DEC DE - case 0x2B: // DEC HL - R16( opcode, 4, 0x0B )--; - goto loop; - - case 0x3B: // DEC SP - sp = uint16_t (sp - 1); - goto loop; - -// AND - case 0xA6: // AND (HL) - data = READ( rp.hl ); - goto and_data; - - case 0xE6: // AND imm - pc++; - goto and_data; - - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r - data = R8( opcode, 0xA0 ); - and_data: - rg.a &= data; - flags = SZ28P( rg.a ) | H10; - goto loop; - -// OR - case 0xB6: // OR (HL) - data = READ( rp.hl ); - goto or_data; - - case 0xF6: // OR imm - pc++; - goto or_data; - - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r - data = R8( opcode, 0xB0 ); - or_data: - rg.a |= data; - flags = SZ28P( rg.a ); - goto loop; - -// XOR - case 0xAE: // XOR (HL) - data = READ( rp.hl ); - goto xor_data; - - case 0xEE: // XOR imm - pc++; - goto xor_data; - - CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r - data = R8( opcode, 0xA8 ); - xor_data: - rg.a ^= data; - flags = SZ28P( rg.a ); - goto loop; - -// LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r - WRITE( rp.hl, R8( opcode, 0x70 ) ); - goto loop; - - CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r - CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r - CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r - CASE6( 58, 59, 5A, 5C, 5D, 5F ): // LD E,r - CASE6( 60, 61, 62, 63, 65, 67 ): // LD H,r - CASE6( 68, 69, 6A, 6B, 6C, 6F ): // LD L,r - CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r - R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); - goto loop; - - CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm - R8( opcode >> 3, 0 ) = data; - pc++; - goto loop; - - case 0x36: // LD (HL),imm - pc++; - WRITE( rp.hl, data ); - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) - R8( opcode >> 3, 8 ) = READ( rp.hl ); - goto loop; - - case 0x01: // LD rp,imm - case 0x11: - case 0x21: - R16( opcode, 4, 0x01 ) = GET_ADDR(); - pc += 2; - goto loop; - - case 0x31: // LD sp,imm - sp = GET_ADDR(); - pc += 2; - goto loop; - - case 0x2A:{// LD HL,(addr) - fuint16 addr = GET_ADDR(); - pc += 2; - rp.hl = READ_WORD( addr ); - goto loop; - } - - case 0x32:{// LD (addr),A - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE( addr, rg.a ); - goto loop; - } - - case 0x22:{// LD (addr),HL - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, rp.hl ); - goto loop; - } - - case 0x02: // LD (BC),A - case 0x12: // LD (DE),A - WRITE( R16( opcode, 4, 0x02 ), rg.a ); - goto loop; - - case 0x0A: // LD A,(BC) - case 0x1A: // LD A,(DE) - rg.a = READ( R16( opcode, 4, 0x0A ) ); - goto loop; - - case 0xF9: // LD SP,HL - sp = rp.hl; - goto loop; - -// Rotate - - case 0x07:{// RLCA - fuint16 temp = rg.a; - temp = (temp << 1) | (temp >> 7); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08 | C01)); - rg.a = temp; - goto loop; - } - - case 0x0F:{// RRCA - fuint16 temp = rg.a; - flags = (flags & (S80 | Z40 | P04)) | - (temp & C01); - temp = (temp << 7) | (temp >> 1); - flags |= temp & (F20 | F08); - rg.a = temp; - goto loop; - } - - case 0x17:{// RLA - blargg_ulong temp = (rg.a << 1) | (flags & C01); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (temp >> 8); - rg.a = temp; - goto loop; - } - - case 0x1F:{// RRA - fuint16 temp = (flags << 7) | (rg.a >> 1); - flags = (flags & (S80 | Z40 | P04)) | - (temp & (F20 | F08)) | - (rg.a & C01); - rg.a = temp; - goto loop; - } - -// Misc - case 0x2F:{// CPL - fuint16 temp = ~rg.a; - flags = (flags & (S80 | Z40 | P04 | C01)) | - (temp & (F20 | F08)) | - (H10 | N02); - rg.a = temp; - goto loop; - } - - case 0x3F:{// CCF - flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | - (flags << 4 & H10) | - (rg.a & (F20 | F08)); - goto loop; - } - - case 0x37: // SCF - flags = (flags & (S80 | Z40 | P04)) | C01 | - (rg.a & (F20 | F08)); - goto loop; - - case 0xDB: // IN A,(imm) - pc++; - rg.a = IN( data + rg.a * 0x100 ); - goto loop; - - case 0xE3:{// EX (SP),HL - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, rp.hl ); - rp.hl = temp; - goto loop; - } - - case 0xEB:{// EX DE,HL - fuint16 temp = rp.hl; - rp.hl = rp.de; - rp.de = temp; - goto loop; - } - - case 0xD9:{// EXX DE,HL - fuint16 temp = r.alt.w.bc; - r.alt.w.bc = rp.bc; - rp.bc = temp; - - temp = r.alt.w.de; - r.alt.w.de = rp.de; - rp.de = temp; - - temp = r.alt.w.hl; - r.alt.w.hl = rp.hl; - rp.hl = temp; - goto loop; - } - - case 0xF3: // DI - r.iff1 = 0; - r.iff2 = 0; - goto loop; - - case 0xFB: // EI - r.iff1 = 1; - r.iff2 = 1; - // TODO: delayed effect - goto loop; - - case 0x76: // HALT - goto halt; - -//////////////////////////////////////// CB prefix - { - case 0xCB: - unsigned data2; - data2 = instr [1]; - pc++; - switch ( data ) - { - - // Rotate left - - #define RLC( read, write ) {\ - fuint8 result = read;\ - result = uint8_t (result << 1) | (result >> 7);\ - flags = SZ28P( result ) | (result & C01);\ - write;\ - goto loop;\ - } - - case 0x06: // RLC (HL) - s_time += 7; - data = rp.hl; - rlc_data_addr: - RLC( READ( data ), WRITE( data, result ) ) - - CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r - uint8_t& reg = R8( data, 0 ); - RLC( reg, reg = result ) - } - - #define RL( read, write ) {\ - fuint16 result = (read << 1) | (flags & C01);\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x16: // RL (HL) - s_time += 7; - data = rp.hl; - rl_data_addr: - RL( READ( data ), WRITE( data, result ) ) - - CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r - uint8_t& reg = R8( data, 0x10 ); - RL( reg, reg = result ) - } - - #define SLA( read, add, write ) {\ - fuint16 result = (read << 1) | add;\ - flags = SZ28PC( result );\ - write;\ - goto loop;\ - } - - case 0x26: // SLA (HL) - s_time += 7; - data = rp.hl; - sla_data_addr: - SLA( READ( data ), 0, WRITE( data, result ) ) - - CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r - uint8_t& reg = R8( data, 0x20 ); - SLA( reg, 0, reg = result ) - } - - case 0x36: // SLL (HL) - s_time += 7; - data = rp.hl; - sll_data_addr: - SLA( READ( data ), 1, WRITE( data, result ) ) - - CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r - uint8_t& reg = R8( data, 0x30 ); - SLA( reg, 1, reg = result ) - } - - // Rotate right - - #define RRC( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = uint8_t (result << 7) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x0E: // RRC (HL) - s_time += 7; - data = rp.hl; - rrc_data_addr: - RRC( READ( data ), WRITE( data, result ) ) - - CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r - uint8_t& reg = R8( data, 0x08 ); - RRC( reg, reg = result ) - } - - #define RR( read, write ) {\ - fuint8 result = read;\ - fuint8 temp = result & C01;\ - result = uint8_t (flags << 7) | (result >> 1);\ - flags = SZ28P( result ) | temp;\ - write;\ - goto loop;\ - } - - case 0x1E: // RR (HL) - s_time += 7; - data = rp.hl; - rr_data_addr: - RR( READ( data ), WRITE( data, result ) ) - - CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r - uint8_t& reg = R8( data, 0x18 ); - RR( reg, reg = result ) - } - - #define SRA( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result = (result & 0x80) | (result >> 1);\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x2E: // SRA (HL) - data = rp.hl; - s_time += 7; - sra_data_addr: - SRA( READ( data ), WRITE( data, result ) ) - - CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r - uint8_t& reg = R8( data, 0x28 ); - SRA( reg, reg = result ) - } - - #define SRL( read, write ) {\ - fuint8 result = read;\ - flags = result & C01;\ - result >>= 1;\ - flags |= SZ28P( result );\ - write;\ - goto loop;\ - } - - case 0x3E: // SRL (HL) - s_time += 7; - data = rp.hl; - srl_data_addr: - SRL( READ( data ), WRITE( data, result ) ) - - CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r - uint8_t& reg = R8( data, 0x38 ); - SRL( reg, reg = result ) - } - - // BIT - { - unsigned temp; - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL) - s_time += 4; - temp = READ( rp.hl ); - flags &= C01; - goto bit_temp; - CASE7( 40, 41, 42, 43, 44, 45, 47 ): // BIT 0,r - CASE7( 48, 49, 4A, 4B, 4C, 4D, 4F ): // BIT 1,r - CASE7( 50, 51, 52, 53, 54, 55, 57 ): // BIT 2,r - CASE7( 58, 59, 5A, 5B, 5C, 5D, 5F ): // BIT 3,r - CASE7( 60, 61, 62, 63, 64, 65, 67 ): // BIT 4,r - CASE7( 68, 69, 6A, 6B, 6C, 6D, 6F ): // BIT 5,r - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // BIT 6,r - CASE7( 78, 79, 7A, 7B, 7C, 7D, 7F ): // BIT 7,r - temp = R8( data & 7, 0 ); - flags = (flags & C01) | (temp & (F20 | F08)); - bit_temp: - int masked = temp & 1 << (data >> 3 & 7); - flags |=(masked & S80) | H10 | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - // SET/RES - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) - s_time += 7; - int temp = READ( rp.hl ); - int bit = 1 << (data >> 3 & 7); - temp |= bit; // SET - if ( !(data & 0x40) ) - temp ^= bit; // RES - WRITE( rp.hl, temp ); - goto loop; - } - - CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r - CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r - CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r - CASE7( D8, D9, DA, DB, DC, DD, DF ): // SET 3,r - CASE7( E0, E1, E2, E3, E4, E5, E7 ): // SET 4,r - CASE7( E8, E9, EA, EB, EC, ED, EF ): // SET 5,r - CASE7( F0, F1, F2, F3, F4, F5, F7 ): // SET 6,r - CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r - R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); - goto loop; - - CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r - CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r - CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r - CASE7( 98, 99, 9A, 9B, 9C, 9D, 9F ): // RES 3,r - CASE7( A0, A1, A2, A3, A4, A5, A7 ): // RES 4,r - CASE7( A8, A9, AA, AB, AC, AD, AF ): // RES 5,r - CASE7( B0, B1, B2, B3, B4, B5, B7 ): // RES 6,r - CASE7( B8, B9, BA, BB, BC, BD, BF ): // RES 7,r - R8( data & 7, 0 ) &= ~(1 << (data >> 3 & 7)); - goto loop; - } - assert( false ); - } - -#undef GET_ADDR -#define GET_ADDR() GET_LE16( instr + 1 ) - -//////////////////////////////////////// ED prefix - { - case 0xED: - pc++; - s_time += ed_dd_timing [data] >> 4; - switch ( data ) - { - { - blargg_ulong temp; - case 0x72: // SBC HL,SP - case 0x7A: // ADC HL,SP - temp = sp; - if ( 0 ) - case 0x42: // SBC HL,BC - case 0x52: // SBC HL,DE - case 0x62: // SBC HL,HL - case 0x4A: // ADC HL,BC - case 0x5A: // ADC HL,DE - case 0x6A: // ADC HL,HL - temp = R16( data >> 3 & 6, 1, 0 ); - blargg_ulong sum = temp + (flags & C01); - flags = ~data >> 2 & N02; - if ( flags ) - sum = -sum; - sum += rp.hl; - temp ^= rp.hl; - temp ^= sum; - flags |=(sum >> 16 & C01) | - (temp >> 8 & H10) | - (sum >> 8 & (S80 | F20 | F08)) | - ((temp - -0x8000) >> 14 & V04); - rp.hl = sum; - if ( (uint16_t) sum ) - goto loop; - flags |= Z40; - goto loop; - } - - CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) - int temp = IN( rp.bc ); - R8( data >> 3, 8 ) = temp; - flags = (flags & C01) | SZ28P( temp ); - goto loop; - } - - case 0x71: // OUT (C),0 - rg.flags = 0; - CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r - OUT( rp.bc, R8( data >> 3, 8 ) ); - goto loop; - - { - unsigned temp; - case 0x73: // LD (ADDR),SP - temp = sp; - if ( 0 ) - case 0x43: // LD (ADDR),BC - case 0x53: // LD (ADDR),DE - temp = R16( data, 4, 0x43 ); - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, temp ); - goto loop; - } - - case 0x4B: // LD BC,(ADDR) - case 0x5B:{// LD DE,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - R16( data, 4, 0x4B ) = READ_WORD( addr ); - goto loop; - } - - case 0x7B:{// LD SP,(ADDR) - fuint16 addr = GET_ADDR(); - pc += 2; - sp = READ_WORD( addr ); - goto loop; - } - - case 0x67:{// RRD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); - temp = (rg.a & 0xF0) | (temp & 0x0F); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - case 0x6F:{// RLD - fuint8 temp = READ( rp.hl ); - WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); - temp = (rg.a & 0xF0) | (temp >> 4); - flags = (flags & C01) | SZ28P( temp ); - rg.a = temp; - goto loop; - } - - CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG - opcode = 0x10; // flag to do SBC instead of ADC - flags &= ~C01; - data = rg.a; - rg.a = 0; - goto adc_data; - - { - int inc; - case 0xA9: // CPD - case 0xB9: // CPDR - inc = -1; - if ( 0 ) - case 0xA1: // CPI - case 0xB1: // CPIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int result = rg.a - temp; - flags = (flags & C01) | N02 | - ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); - - if ( !(uint8_t) result ) flags |= Z40; - result -= (flags & H10) >> 4; - flags |= result & F08; - flags |= result << 4 & F20; - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( flags & Z40 || data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xA8: // LDD - case 0xB8: // LDDR - inc = -1; - if ( 0 ) - case 0xA0: // LDI - case 0xB0: // LDIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - addr = rp.de; - rp.de = addr + inc; - WRITE( addr, temp ); - - temp += rg.a; - flags = (flags & (S80 | Z40 | C01)) | - (temp & F08) | (temp << 4 & F20); - if ( !--rp.bc ) - goto loop; - - flags |= V04; - if ( data < 0xB0 ) - goto loop; - - pc -= 2; - s_time += 5; - goto loop; - } - - { - int inc; - case 0xAB: // OUTD - case 0xBB: // OTDR - inc = -1; - if ( 0 ) - case 0xA3: // OUTI - case 0xB3: // OTIR - inc = +1; - fuint16 addr = rp.hl; - rp.hl = addr + inc; - int temp = READ( addr ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - OUT( rp.bc, temp ); - goto loop; - } - - { - int inc; - case 0xAA: // IND - case 0xBA: // INDR - inc = -1; - if ( 0 ) - case 0xA2: // INI - case 0xB2: // INIR - inc = +1; - - fuint16 addr = rp.hl; - rp.hl = addr + inc; - - int temp = IN( rp.bc ); - - int b = --rg.b; - flags = (temp >> 6 & N02) | SZ28( b ); - if ( b && data >= 0xB0 ) - { - pc -= 2; - s_time += 5; - } - - WRITE( addr, temp ); - goto loop; - } - - case 0x47: // LD I,A - r.i = rg.a; - goto loop; - - case 0x4F: // LD R,A - SET_R( rg.a ); - dprintf( "LD R,A not supported\n" ); - warning = true; - goto loop; - - case 0x57: // LD A,I - rg.a = r.i; - goto ld_ai_common; - - case 0x5F: // LD A,R - rg.a = GET_R(); - dprintf( "LD A,R not supported\n" ); - warning = true; - ld_ai_common: - flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); - goto loop; - - CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN - r.iff1 = r.iff2; - goto ret_taken; - - case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 - r.im = 0; - goto loop; - - case 0x56: case 0x76: // IM 1 - r.im = 1; - goto loop; - - case 0x5E: case 0x7E: // IM 2 - r.im = 2; - goto loop; - - default: - dprintf( "Opcode $ED $%02X not supported\n", data ); - warning = true; - goto loop; - } - assert( false ); - } - -//////////////////////////////////////// DD/FD prefix - { - fuint16 ixy; - case 0xDD: - ixy = ix; - goto ix_prefix; - case 0xFD: - ixy = iy; - ix_prefix: - pc++; - unsigned data2 = READ_PROG( pc ); - s_time += ed_dd_timing [data] & 0x0F; - switch ( data ) - { - // TODO: more efficient way of avoid negative address - // TODO: avoid using this as argument to READ() since it is evaluated twice - #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) - - #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; - - // ADD/ADC/SUB/SBC - - case 0x96: // SUB (IXY+disp) - case 0x86: // ADD (IXY+disp) - flags &= ~C01; - case 0x9E: // SBC (IXY+disp) - case 0x8E: // ADC (IXY+disp) - pc++; - opcode = data; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto adc_data; - - case 0x94: // SUB HXY - case 0x84: // ADD HXY - flags &= ~C01; - case 0x9C: // SBC HXY - case 0x8C: // ADC HXY - opcode = data; - data = ixy >> 8; - goto adc_data; - - case 0x95: // SUB LXY - case 0x85: // ADD LXY - flags &= ~C01; - case 0x9D: // SBC LXY - case 0x8D: // ADC LXY - opcode = data; - data = (uint8_t) ixy; - goto adc_data; - - { - unsigned temp; - case 0x39: // ADD IXY,SP - temp = sp; - goto add_ixy_data; - - case 0x29: // ADD IXY,HL - temp = ixy; - goto add_ixy_data; - - case 0x09: // ADD IXY,BC - case 0x19: // ADD IXY,DE - temp = R16( data, 4, 0x09 ); - add_ixy_data: { - blargg_ulong sum = ixy + temp; - temp ^= ixy; - ixy = (uint16_t) sum; - flags = (flags & (S80 | Z40 | V04)) | - (sum >> 16) | - (sum >> 8 & (F20 | F08)) | - ((temp ^ sum) >> 8 & H10); - goto set_ixy; - } - } - - // AND - case 0xA6: // AND (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto and_data; - - case 0xA4: // AND HXY - data = ixy >> 8; - goto and_data; - - case 0xA5: // AND LXY - data = (uint8_t) ixy; - goto and_data; - - // OR - case 0xB6: // OR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto or_data; - - case 0xB4: // OR HXY - data = ixy >> 8; - goto or_data; - - case 0xB5: // OR LXY - data = (uint8_t) ixy; - goto or_data; - - // XOR - case 0xAE: // XOR (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto xor_data; - - case 0xAC: // XOR HXY - data = ixy >> 8; - goto xor_data; - - case 0xAD: // XOR LXY - data = (uint8_t) ixy; - goto xor_data; - - // CP - case 0xBE: // CP (IXY+disp) - pc++; - data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto cp_data; - - case 0xBC: // CP HXY - data = ixy >> 8; - goto cp_data; - - case 0xBD: // CP LXY - data = (uint8_t) ixy; - goto cp_data; - - // LD - CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r - data = R8( data, 0x70 ); - if ( 0 ) - case 0x36: // LD (IXY+disp),imm - pc++, data = READ_PROG( pc ); - pc++; - WRITE( IXY_DISP( ixy, (int8_t) data2 ), data ); - goto loop; - - CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY - R8( data >> 3, 8 ) = ixy >> 8; - goto loop; - - case 0x64: // LD HXY,HXY - case 0x6D: // LD LXY,LXY - goto loop; - - CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY - R8( data >> 3, 8 ) = ixy; - goto loop; - - CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) - pc++; - R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); - goto loop; - - case 0x26: // LD HXY,imm - pc++; - goto ld_hxy_data; - - case 0x65: // LD HXY,LXY - data2 = (uint8_t) ixy; - goto ld_hxy_data; - - CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r - data2 = R8( data, 0x60 ); - ld_hxy_data: - ixy = (uint8_t) ixy | (data2 << 8); - goto set_ixy; - - case 0x2E: // LD LXY,imm - pc++; - goto ld_lxy_data; - - case 0x6C: // LD LXY,HXY - data2 = ixy >> 8; - goto ld_lxy_data; - - CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r - data2 = R8( data, 0x68 ); - ld_lxy_data: - ixy = (ixy & 0xFF00) | data2; - set_ixy: - if ( opcode == 0xDD ) - { - ix = ixy; - goto loop; - } - iy = ixy; - goto loop; - - case 0xF9: // LD SP,IXY - sp = ixy; - goto loop; - - case 0x22:{// LD (ADDR),IXY - fuint16 addr = GET_ADDR(); - pc += 2; - WRITE_WORD( addr, ixy ); - goto loop; - } - - case 0x21: // LD IXY,imm - ixy = GET_ADDR(); - pc += 2; - goto set_ixy; - - case 0x2A:{// LD IXY,(addr) - fuint16 addr = GET_ADDR(); - ixy = READ_WORD( addr ); - pc += 2; - goto set_ixy; - } - - // DD/FD CB prefix - case 0xCB: { - data = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data2 = READ_PROG( pc ); - pc++; - switch ( data2 ) - { - case 0x06: goto rlc_data_addr; // RLC (IXY) - case 0x16: goto rl_data_addr; // RL (IXY) - case 0x26: goto sla_data_addr; // SLA (IXY) - case 0x36: goto sll_data_addr; // SLL (IXY) - case 0x0E: goto rrc_data_addr; // RRC (IXY) - case 0x1E: goto rr_data_addr; // RR (IXY) - case 0x2E: goto sra_data_addr; // SRA (IXY) - case 0x3E: goto srl_data_addr; // SRL (IXY) - - CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) - fuint8 temp = READ( data ); - int masked = temp & 1 << (data2 >> 3 & 7); - flags = (flags & C01) | H10 | - (masked & S80) | - ((masked - 1) >> 8 & (Z40 | P04)); - goto loop; - } - - CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) - CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) - int temp = READ( data ); - int bit = 1 << (data2 >> 3 & 7); - temp |= bit; // SET - if ( !(data2 & 0x40) ) - temp ^= bit; // RES - WRITE( data, temp ); - goto loop; - } - - default: - dprintf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); - warning = true; - goto loop; - } - assert( false ); - } - - // INC/DEC - case 0x23: // INC IXY - ixy = uint16_t (ixy + 1); - goto set_ixy; - - case 0x2B: // DEC IXY - ixy = uint16_t (ixy - 1); - goto set_ixy; - - case 0x34: // INC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) + 1; - WRITE( ixy, data ); - goto inc_set_flags; - - case 0x35: // DEC (IXY+disp) - ixy = IXY_DISP( ixy, (int8_t) data2 ); - pc++; - data = READ( ixy ) - 1; - WRITE( ixy, data ); - goto dec_set_flags; - - case 0x24: // INC HXY - ixy = uint16_t (ixy + 0x100); - data = ixy >> 8; - goto inc_xy_common; - - case 0x2C: // INC LXY - data = uint8_t (ixy + 1); - ixy = (ixy & 0xFF00) | data; - inc_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto inc_set_flags; - } - iy = ixy; - goto inc_set_flags; - - case 0x25: // DEC HXY - ixy = uint16_t (ixy - 0x100); - data = ixy >> 8; - goto dec_xy_common; - - case 0x2D: // DEC LXY - data = uint8_t (ixy - 1); - ixy = (ixy & 0xFF00) | data; - dec_xy_common: - if ( opcode == 0xDD ) - { - ix = ixy; - goto dec_set_flags; - } - iy = ixy; - goto dec_set_flags; - - // PUSH/POP - case 0xE5: // PUSH IXY - data = ixy; - goto push_data; - - case 0xE1:{// POP IXY - ixy = READ_WORD( sp ); - sp = uint16_t (sp + 2); - goto set_ixy; - } - - // Misc - - case 0xE9: // JP (IXY) - pc = ixy; - goto loop; - - case 0xE3:{// EX (SP),IXY - fuint16 temp = READ_WORD( sp ); - WRITE_WORD( sp, ixy ); - ixy = temp; - goto set_ixy; - } - - default: - dprintf( "Unnecessary DD/FD prefix encountered\n" ); - warning = true; - pc--; - goto loop; - } - assert( false ); - } - - } - dprintf( "Unhandled main opcode: $%02X\n", opcode ); - assert( false ); - -hit_idle_addr: - s_time -= 11; - goto out_of_time; -halt: - s_time &= 3; // increment by multiple of 4 -out_of_time: - pc--; - - s.time = s_time; - rg.flags = flags; - r.ix = ix; - r.iy = iy; - r.sp = sp; - r.pc = pc; - this->r.b = rg; - this->state_ = s; - this->state = &this->state_; - - return warning; -} +// $package. http://www.slack.net/~ant/ + +#include "Kss_Core.h" + +#include "blargg_endian.h" +//#include "z80_cpu_log.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data ) +#define IN_PORT( addr ) cpu_in( TIME(), addr ) +#define WRITE_MEM( addr, data ) {FLUSH_TIME(); cpu_write( addr, data );} +#define IDLE_ADDR idle_addr +#define CPU cpu + +#define CPU_BEGIN \ +bool Kss_Core::run_cpu( time_t end_time )\ +{\ + cpu.set_end_time( end_time ); + + #include "Z80_Cpu_run.h" + + return warning; +} diff --git a/Frameworks/GME/gme/Kss_Emu.cpp b/Frameworks/GME/gme/Kss_Emu.cpp old mode 100755 new mode 100644 index 1b26ef0b8..e7643b50c --- a/Frameworks/GME/gme/Kss_Emu.cpp +++ b/Frameworks/GME/gme/Kss_Emu.cpp @@ -1,11 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Kss_Emu.h" #include "blargg_endian.h" -#include -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2009 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -18,397 +17,477 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -long const clock_rate = 3579545; -int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; +#define IF_PTR( ptr ) if ( ptr ) (ptr) -Kss_Emu::Kss_Emu() -{ - sn = 0; - set_type( gme_kss_type ); - set_silence_lookahead( 6 ); - static const char* const names [osc_count] = { - "Square 1", "Square 2", "Square 3", - "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" - }; - set_voice_names( names ); - - static int const types [osc_count] = { - wave_type | 0, wave_type | 1, wave_type | 2, - wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 - }; - set_voice_types( types ); - - memset( unmapped_read, 0xFF, sizeof unmapped_read ); +int const clock_rate = 3579545; + +#define FOR_EACH_APU( macro )\ +{\ + macro( sms.psg );\ + macro( sms.fm );\ + macro( msx.psg );\ + macro( msx.scc );\ + macro( msx.music );\ + macro( msx.audio );\ } -Kss_Emu::~Kss_Emu() { unload(); } +Kss_Emu::Kss_Emu() : + core( this ) +{ + #define ACTION( apu ) { core.apu = NULL; } + FOR_EACH_APU( ACTION ); + #undef ACTION + + set_type( gme_kss_type ); +} + +Kss_Emu::~Kss_Emu() +{ + unload(); +} + +inline void Kss_Emu::Core::unload() +{ + #define ACTION( ptr ) { delete (ptr); (ptr) = 0; } + FOR_EACH_APU( ACTION ); + #undef ACTION +} void Kss_Emu::unload() { - delete sn; - sn = 0; + core.unload(); Classic_Emu::unload(); } // Track info -static void copy_kss_fields( Kss_Emu::header_t const& h, track_info_t* out ) +static void copy_kss_fields( Kss_Core::header_t const& h, track_info_t* out ) { const char* system = "MSX"; + if ( h.device_flags & 0x02 ) { system = "Sega Master System"; if ( h.device_flags & 0x04 ) system = "Game Gear"; + + if ( h.device_flags & 0x01 ) + system = "Sega Mark III"; + } + else + { + if ( h.device_flags & 0x09 ) + system = "MSX + FM Sound"; } Gme_File::copy_field_( out->system, system ); } +static void hash_kss_file( Kss_Core::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.load_size[0], sizeof(h.load_size) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.first_bank, sizeof(h.first_bank) ); + out.hash_( &h.bank_mode, sizeof(h.bank_mode) ); + out.hash_( &h.extra_header, sizeof(h.extra_header) ); + out.hash_( &h.device_flags, sizeof(h.device_flags) ); + + out.hash_( data, data_size ); +} + blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const { - copy_kss_fields( header_, out ); - return 0; + copy_kss_fields( header(), out ); +// TODO: remove +//if ( msx.music ) strcpy( out->system, "msxmusic" ); +//if ( msx.audio ) strcpy( out->system, "msxaudio" ); +//if ( sms.fm ) strcpy( out->system, "fmunit" ); + return blargg_ok; } static blargg_err_t check_kss_header( void const* header ) { if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) ) - return gme_wrong_file_type; - return 0; + return blargg_err_file_type; + + return blargg_ok; } struct Kss_File : Gme_Info_ { - Kss_Emu::header_t header_; - + Kss_Emu::header_t const* header_; + Kss_File() { set_type( gme_kss_type ); } - - blargg_err_t load_( Data_Reader& in ) + + blargg_err_t load_mem_( byte const begin [], int size ) { - blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - return check_kss_header( &header_ ); + header_ = ( Kss_Emu::header_t const* ) begin; + + if ( header_->tag [3] == 'X' && header_->extra_header == 0x10 ) + set_track_count( get_le16( header_->last_track ) + 1 ); + + return check_kss_header( header_ ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { - copy_kss_fields( header_, out ); - return 0; + copy_kss_fields( *header_, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_kss_file( *header_, file_begin() + Kss_Core::header_t::base_size, file_end() - file_begin() - Kss_Core::header_t::base_size, out ); + return blargg_ok; } }; static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; } static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; } -gme_type_t_ const gme_kss_type [1] = { "MSX", 256, &new_kss_emu, &new_kss_file, "KSS", 0x03 }; +gme_type_t_ const gme_kss_type [1] = {{ + "MSX", + 256, + &new_kss_emu, + &new_kss_file, + "KSS", + 0x03 +}}; // Setup -void Kss_Emu::update_gain() +void Kss_Emu::Core::update_gain_() { - double g = gain() * 1.4; - if ( scc_accessed ) - g *= 1.5; - ay.volume( g ); - scc.volume( g ); - if ( sn ) - sn->volume( g ); + double g = emu.gain(); + if ( msx.music || msx.audio || sms.fm ) + { + g *= 0.3; + } + else + { + g *= 1.2; + if ( scc_accessed ) + g *= 1.4; + } + + #define ACTION( apu ) IF_PTR( apu )->volume( g ) + FOR_EACH_APU( ACTION ); + #undef ACTION +} + +static blargg_err_t new_opl_apu( Opl_Apu::type_t type, Opl_Apu** out ) +{ + check( !*out ); + CHECK_ALLOC( *out = BLARGG_NEW( Opl_Apu ) ); + blip_time_t const period = 72; + int const rate = clock_rate / period; + return (*out)->init( rate * period, rate, period, type ); } blargg_err_t Kss_Emu::load_( Data_Reader& in ) { - memset( &header_, 0, sizeof header_ ); - assert( offsetof (header_t,device_flags) == header_size - 1 ); - assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); - RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) ); - - RETURN_ERR( check_kss_header( header_.tag ) ); - - if ( header_.tag [3] == 'C' ) + RETURN_ERR( core.load( in ) ); + set_warning( core.warning() ); + + set_track_count( get_le16( header().last_track ) + 1 ); + + core.scc_enabled = false; + if ( header().device_flags & 0x02 ) // Sega Master System { - if ( header_.extra_header ) + int const osc_count = Sms_Apu::osc_count + Opl_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", "Noise", "FM" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0 + }; + set_voice_types( types ); + + // sms.psg + set_voice_count( Sms_Apu::osc_count ); + check( !core.sms.psg ); + CHECK_ALLOC( core.sms.psg = BLARGG_NEW Sms_Apu ); + + // sms.fm + if ( header().device_flags & 0x01 ) { - header_.extra_header = 0; - set_warning( "Unknown data in header" ); + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_smsfmunit, &core.sms.fm ) ); } - if ( header_.device_flags & ~0x0F ) + + } + else // MSX + { + int const osc_count = Ay_Apu::osc_count + Opl_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", "FM" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, wave_type+0 + }; + set_voice_types( types ); + + // msx.psg + set_voice_count( Ay_Apu::osc_count ); + check( !core.msx.psg ); + CHECK_ALLOC( core.msx.psg = BLARGG_NEW Ay_Apu ); + + if ( header().device_flags & 0x10 ) + set_warning( "MSX stereo not supported" ); + + // msx.music + if ( header().device_flags & 0x01 ) { - header_.device_flags &= 0x0F; - set_warning( "Unknown data in header" ); + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_msxmusic, &core.msx.music ) ); + } + + // msx.audio + if ( header().device_flags & 0x08 ) + { + set_voice_count( osc_count ); + RETURN_ERR( new_opl_apu( Opl_Apu::type_msxaudio, &core.msx.audio ) ); + } + + if ( !(header().device_flags & 0x80) ) + { + if ( !(header().device_flags & 0x84) ) + core.scc_enabled = core.scc_enabled_true; + + // msx.scc + check( !core.msx.scc ); + CHECK_ALLOC( core.msx.scc = BLARGG_NEW Scc_Apu ); + + int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; + static const char* const names [osc_count] = { + "Square 1", "Square 2", "Square 3", + "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" + }; + set_voice_names( names ); + + static int const types [osc_count] = { + wave_type+1, wave_type+3, wave_type+2, + wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7, + }; + set_voice_types( types ); + + set_voice_count( osc_count ); } } - else + + set_silence_lookahead( 6 ); + if ( core.sms.fm || core.msx.music || core.msx.audio ) { - ext_header_t& ext = header_; - memcpy( &ext, rom.begin(), min( (int) ext_header_size, (int) header_.extra_header ) ); - if ( header_.extra_header > 0x10 ) - set_warning( "Unknown data in header" ); + if ( !Opl_Apu::supported() ) + set_warning( "FM sound not supported" ); + else + set_silence_lookahead( 3 ); // Opl_Apu is really slow } - - if ( header_.device_flags & 0x09 ) - set_warning( "FM sound not supported" ); - - scc_enabled = 0xC000; - if ( header_.device_flags & 0x04 ) - scc_enabled = 0; - - if ( header_.device_flags & 0x02 && !sn ) - CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) ); - - set_voice_count( osc_count ); - + return setup_buffer( ::clock_rate ); } void Kss_Emu::update_eq( blip_eq_t const& eq ) { - ay.treble_eq( eq ); - scc.treble_eq( eq ); - if ( sn ) - sn->treble_eq( eq ); + #define ACTION( apu ) IF_PTR( core.apu )->treble_eq( eq ) + FOR_EACH_APU( ACTION ); + #undef ACTION } void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - int i2 = i - ay.osc_count; - if ( i2 >= 0 ) - scc.osc_output( i2, center ); - else - ay.osc_output( i, center ); - if ( sn && i < sn->osc_count ) - sn->osc_output( i, center, left, right ); -} + if ( core.sms.psg ) // Sega Master System + { + i -= core.sms.psg->osc_count; + if ( i < 0 ) + { + core.sms.psg->set_output( i + core.sms.psg->osc_count, center, left, right ); + return; + } -// Emulation + if ( core.sms.fm && i < core.sms.fm->osc_count ) + core.sms.fm->set_output( i, center, NULL, NULL ); + } + else if ( core.msx.psg ) // MSX + { + i -= core.msx.psg->osc_count; + if ( i < 0 ) + { + core.msx.psg->set_output( i + core.msx.psg->osc_count, center ); + return; + } + + if ( core.msx.scc && i < core.msx.scc->osc_count ) core.msx.scc ->set_output( i, center ); + if ( core.msx.music && i < core.msx.music->osc_count ) core.msx.music->set_output( i, center, NULL, NULL ); + if ( core.msx.audio && i < core.msx.audio->osc_count ) core.msx.audio->set_output( i, center, NULL, NULL ); + } +} void Kss_Emu::set_tempo_( double t ) { - blip_time_t period = - (header_.device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); - play_period = blip_time_t (period / t); + int period = (header().device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60); + core.set_play_period( (Kss_Core::time_t) (period / t) ); } blargg_err_t Kss_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - memset( ram, 0xC9, 0x4000 ); - memset( ram + 0x4000, 0, sizeof ram - 0x4000 ); - - // copy driver code to lo RAM - static byte const bios [] = { - 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG - 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG - }; - static byte const vectors [] = { - 0xC3, 0x01, 0x00, // $0093: WRTPSG vector - 0xC3, 0x09, 0x00, // $0096: RDPSG vector - }; - memcpy( ram + 0x01, bios, sizeof bios ); - memcpy( ram + 0x93, vectors, sizeof vectors ); - - // copy non-banked data into RAM - unsigned load_addr = get_le16( header_.load_addr ); - long orig_load_size = get_le16( header_.load_size ); - long load_size = min( orig_load_size, rom.file_size() ); - load_size = min( load_size, long (mem_size - load_addr) ); - if ( load_size != orig_load_size ) - set_warning( "Excessive data size" ); - memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size ); - - rom.set_addr( -load_size - header_.extra_header ); - - // check available bank data - blargg_long const bank_size = this->bank_size(); - int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size; - bank_count = header_.bank_mode & 0x7F; - if ( bank_count > max_banks ) - { - bank_count = max_banks; - set_warning( "Bank data missing" ); - } - //dprintf( "load_size : $%X\n", load_size ); - //dprintf( "bank_size : $%X\n", bank_size ); - //dprintf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); - - ram [idle_addr] = 0xFF; - cpu::reset( unmapped_write, unmapped_read ); - cpu::map_mem( 0, mem_size, ram, ram ); - - ay.reset(); - scc.reset(); - if ( sn ) - sn->reset(); - r.sp = 0xF380; - ram [--r.sp] = idle_addr >> 8; - ram [--r.sp] = idle_addr & 0xFF; - r.b.a = track; - r.pc = get_le16( header_.init_addr ); - next_play = play_period; - scc_accessed = false; - gain_updated = false; - update_gain(); - ay_latch = 0; - - return 0; + #define ACTION( apu ) IF_PTR( core.apu )->reset() + FOR_EACH_APU( ACTION ); + #undef ACTION + + core.scc_accessed = false; + core.update_gain_(); + + return core.start_track( track ); } -void Kss_Emu::set_bank( int logical, int physical ) +void Kss_Emu::Core::cpu_write_( addr_t addr, int data ) { - unsigned const bank_size = this->bank_size(); - - unsigned addr = 0x8000; - if ( logical && bank_size == 8 * 1024 ) - addr = 0xA000; - - physical -= header_.first_bank; - if ( (unsigned) physical >= (unsigned) bank_count ) - { - byte* data = ram + addr; - cpu::map_mem( addr, bank_size, data, data ); - } - else - { - long phys = physical * (blargg_long) bank_size; - for ( unsigned offset = 0; offset < bank_size; offset += page_size ) - cpu::map_mem( addr + offset, page_size, - unmapped_write, rom.at_addr( phys + offset ) ); - } -} + // TODO: SCC+ support -void Kss_Emu::cpu_write( unsigned addr, int data ) -{ data &= 0xFF; switch ( addr ) { case 0x9000: set_bank( 0, data ); return; - + case 0xB000: set_bank( 1, data ); return; + + case 0xBFFE: // selects between mapping areas (we just always enable both) + if ( data == 0 || data == 0x20 ) + return; } - - int scc_addr = (addr & 0xDFFF) ^ 0x9800; - if ( scc_addr < scc.reg_count ) + + int scc_addr = (addr & 0xDFFF) - 0x9800; + if ( (unsigned) scc_addr < 0xB0 && msx.scc ) { scc_accessed = true; - scc.write( time(), scc_addr, data ); + //if ( (unsigned) (scc_addr - 0x90) < 0x10 ) + // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F + if ( scc_addr < Scc_Apu::reg_count ) + msx.scc->write( cpu.time(), addr, data ); return; } - + dprintf( "LD ($%04X),$%02X\n", addr, data ); } -void kss_cpu_write( Kss_Cpu* cpu, unsigned addr, int data ) +void Kss_Emu::Core::cpu_write( addr_t addr, int data ) { - *cpu->write( addr ) = data; - if ( (addr & STATIC_CAST(Kss_Emu&,*cpu).scc_enabled) == 0x8000 ) - STATIC_CAST(Kss_Emu&,*cpu).cpu_write( addr, data ); + *cpu.write( addr ) = data; + if ( (addr & scc_enabled) == 0x8000 ) + cpu_write_( addr, data ); } -void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) +void Kss_Emu::Core::cpu_out( time_t time, addr_t addr, int data ) { data &= 0xFF; - Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); switch ( addr & 0xFF ) { case 0xA0: - emu.ay_latch = data & 0x0F; + if ( msx.psg ) + msx.psg->write_addr( data ); return; - + case 0xA1: - GME_APU_HOOK( &emu, emu.ay_latch, data ); - emu.ay.write( time, emu.ay_latch, data ); + if ( msx.psg ) + msx.psg->write_data( time, data ); return; - + case 0x06: - if ( emu.sn && (emu.header_.device_flags & 0x04) ) + if ( sms.psg && (header().device_flags & 0x04) ) { - emu.sn->write_ggstereo( time, data ); + sms.psg->write_ggstereo( time, data ); return; } break; - + case 0x7E: case 0x7F: - if ( emu.sn ) + if ( sms.psg ) { - GME_APU_HOOK( &emu, 16, data ); - emu.sn->write_data( time, data ); + sms.psg->write_data( time, data ); return; } break; - + + #define OPL_WRITE_HANDLER( base, opl )\ + case base : if ( opl ) { opl->write_addr( data ); return; } break;\ + case base+1: if ( opl ) { opl->write_data( time, data ); return; } break; + + OPL_WRITE_HANDLER( 0x7C, msx.music ) + OPL_WRITE_HANDLER( 0xC0, msx.audio ) + OPL_WRITE_HANDLER( 0xF0, sms.fm ) + case 0xFE: - emu.set_bank( 0, data ); + set_bank( 0, data ); return; - + #ifndef NDEBUG - case 0xF1: // FM data - if ( data ) - break; // trap non-zero data - case 0xF0: // FM addr case 0xA8: // PPI return; #endif } - - dprintf( "OUT $%04X,$%02X\n", addr, data ); + + Kss_Core::cpu_out( time, addr, data ); } -int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr ) +int Kss_Emu::Core::cpu_in( time_t time, addr_t addr ) { - //Kss_Emu& emu = STATIC_CAST(Kss_Emu&,*cpu); - //switch ( addr & 0xFF ) - //{ - //} - - dprintf( "IN $%04X\n", addr ); - return 0; + switch ( addr & 0xFF ) + { + case 0xC0: + case 0xC1: + if ( msx.audio ) + return msx.audio->read( time, addr & 1 ); + break; + + case 0xA2: + if ( msx.psg ) + return msx.psg->read(); + break; + + #ifndef NDEBUG + case 0xA8: // PPI + return 0; + #endif + } + + return Kss_Core::cpu_in( time, addr ); } -// Emulation +void Kss_Emu::Core::update_gain() +{ + if ( scc_accessed ) + { + dprintf( "SCC accessed\n" ); + update_gain_(); + } +} blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) { - while ( time() < duration ) - { - blip_time_t end = min( duration, next_play ); - cpu::run( min( duration, next_play ) ); - if ( r.pc == idle_addr ) - set_time( end ); - - if ( time() >= next_play ) - { - next_play += play_period; - if ( r.pc == idle_addr ) - { - if ( !gain_updated ) - { - gain_updated = true; - if ( scc_accessed ) - update_gain(); - } - - ram [--r.sp] = idle_addr >> 8; - ram [--r.sp] = idle_addr & 0xFF; - r.pc = get_le16( header_.play_addr ); - GME_FRAME_HOOK( this ); - } - } - } - - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - adjust_time( -duration ); - ay.end_frame( duration ); - scc.end_frame( duration ); - if ( sn ) - sn->end_frame( duration ); - - return 0; + RETURN_ERR( core.end_frame( duration ) ); + + #define ACTION( apu ) IF_PTR( core.apu )->end_frame( duration ) + FOR_EACH_APU( ACTION ); + #undef ACTION + + return blargg_ok; } + +blargg_err_t Kss_Emu::hash_( Hash_Function& out ) const +{ + hash_kss_file( header(), core.rom_().begin(), core.rom_().file_size(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Kss_Emu.h b/Frameworks/GME/gme/Kss_Emu.h old mode 100755 new mode 100644 index 4d8463abd..cfc7d0730 --- a/Frameworks/GME/gme/Kss_Emu.h +++ b/Frameworks/GME/gme/Kss_Emu.h @@ -1,96 +1,79 @@ // MSX computer KSS music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef KSS_EMU_H #define KSS_EMU_H #include "Classic_Emu.h" +#include "Kss_Core.h" #include "Kss_Scc_Apu.h" -#include "Kss_Cpu.h" #include "Sms_Apu.h" #include "Ay_Apu.h" +#include "Opl_Apu.h" -class Kss_Emu : private Kss_Cpu, public Classic_Emu { - typedef Kss_Cpu cpu; +class Kss_Emu : public Classic_Emu { public: - // KSS file header - enum { header_size = 0x10 }; - struct header_t - { - byte tag [4]; - byte load_addr [2]; - byte load_size [2]; - byte init_addr [2]; - byte play_addr [2]; - byte first_bank; - byte bank_mode; - byte extra_header; - byte device_flags; - }; - - enum { ext_header_size = 0x10 }; - struct ext_header_t - { - byte data_size [4]; - byte unused [4]; - byte first_track [2]; - byte last_tack [2]; - byte psg_vol; - byte scc_vol; - byte msx_music_vol; - byte msx_audio_vol; - }; - - struct composite_header_t : header_t, ext_header_t { }; + // KSS file header (see Kss_Core.h) + typedef Kss_Core::header_t header_t; // Header for currently loaded file - composite_header_t const& header() const { return header_; } + header_t const& header() const { return core.header(); } + + blargg_err_t hash_( Hash_Function& ) const; static gme_type_t static_type() { return gme_kss_type; } + +// Implementation public: Kss_Emu(); ~Kss_Emu(); + protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual void unload(); + private: - Rom_Data rom; - composite_header_t header_; - - bool scc_accessed; - bool gain_updated; - void update_gain(); - - unsigned scc_enabled; // 0 or 0xC000 - byte const* bank_data; - int bank_count; - void set_bank( int logical, int physical ); - blargg_long bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); } - - blip_time_t play_period; - blip_time_t next_play; - int ay_latch; - - friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); - friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); - void cpu_write( unsigned addr, int data ); - friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); - - // large items - enum { mem_size = 0x10000 }; - byte ram [mem_size + cpu_padding]; - - Ay_Apu ay; - Scc_Apu scc; - Sms_Apu* sn; - byte unmapped_read [0x100]; - byte unmapped_write [page_size]; + struct Core; + friend struct Core; + struct Core : Kss_Core { + Kss_Emu& emu; + + // detection of tunes that use SCC so they can be made louder + bool scc_accessed; + + enum { scc_enabled_true = 0xC000 }; + unsigned scc_enabled; // 0 or 0xC000 + int ay_latch; + + struct { + Sms_Apu* psg; + Opl_Apu* fm; + } sms; + + struct { + Ay_Apu* psg; + Scc_Apu* scc; + Opl_Apu* music; + Opl_Apu* audio; + } msx; + + Core( Kss_Emu* e ) : emu( *e ) { } + + virtual void cpu_write( addr_t, int ); + virtual int cpu_in( time_t, addr_t ); + virtual void cpu_out( time_t, addr_t, int ); + virtual void update_gain(); + + void cpu_write_( addr_t addr, int data ); + void update_gain_(); + void unload(); + } core; }; #endif diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.cpp b/Frameworks/GME/gme/Kss_Scc_Apu.cpp old mode 100755 new mode 100644 index 1660ac3da..60c53293e --- a/Frameworks/GME/gme/Kss_Scc_Apu.cpp +++ b/Frameworks/GME/gme/Kss_Scc_Apu.cpp @@ -1,8 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Kss_Scc_Apu.h" -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -17,79 +17,106 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Tones above this frequency are treated as disabled tone at half volume. // Power of two is more efficient (avoids division). -unsigned const inaudible_freq = 16384; +int const inaudible_freq = 16384; int const wave_size = 0x20; +void Scc_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +void Scc_Apu::volume( double v ) +{ + synth.volume( 0.43 / osc_count / amp_range * v ); +} + +void Scc_Apu::reset() +{ + last_time = 0; + + for ( int i = osc_count; --i >= 0; ) + memset( &oscs [i], 0, offsetof (osc_t,output) ); + + memset( regs, 0, sizeof regs ); +} + +Scc_Apu::Scc_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + void Scc_Apu::run_until( blip_time_t end_time ) { for ( int index = 0; index < osc_count; index++ ) { osc_t& osc = oscs [index]; - + Blip_Buffer* const output = osc.output; if ( !output ) continue; - output->set_modified(); - - blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 + - regs [0x80 + index * 2] + 1; + + blip_time_t period = (regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 + + regs [0xA0 + index * 2] + 1; int volume = 0; - if ( regs [0x8F] & (1 << index) ) + if ( regs [0xAF] & (1 << index) ) { - blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() + - inaudible_freq * 32) / (inaudible_freq * 16); + blip_time_t inaudible_period = (unsigned) (output->clock_rate() + + inaudible_freq * 32) / (unsigned) (inaudible_freq * 16); if ( period > inaudible_period ) - volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15); + volume = (regs [0xAA + index] & 0x0F) * (amp_range / 256 / 15); } - + BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size; - if ( index == osc_count - 1 ) - wave -= wave_size; // last two oscs share wave + /*if ( index == osc_count - 1 ) + wave -= wave_size; // last two oscs share same wave RAM*/ + { - int amp = wave [osc.phase] * volume; - int delta = amp - osc.last_amp; + int delta = wave [osc.phase] * volume - osc.last_amp; if ( delta ) { - osc.last_amp = amp; + osc.last_amp += delta; + output->set_modified(); synth.offset( last_time, delta, output ); } } - + blip_time_t time = last_time + osc.delay; if ( time < end_time ) { + int phase = osc.phase; if ( !volume ) { // maintain phase - blargg_long count = (end_time - time + period - 1) / period; - osc.phase = (osc.phase + count) & (wave_size - 1); - time += count * period; + int count = (end_time - time + period - 1) / period; + phase += count; // will be masked below + time += count * period; } else { - - int phase = osc.phase; int last_wave = wave [phase]; phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop - do { - int amp = wave [phase]; + int delta = wave [phase] - last_wave; phase = (phase + 1) & (wave_size - 1); - int delta = amp - last_wave; if ( delta ) { - last_wave = amp; - synth.offset( time, delta * volume, output ); + last_wave += delta; + synth.offset_inline( time, delta * volume, output ); } time += period; } while ( time < end_time ); - - osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance - osc.last_amp = wave [phase] * volume; + + osc.last_amp = last_wave * volume; + output->set_modified(); + phase--; // undo pre-advance } + osc.phase = phase & (wave_size - 1); } osc.delay = time - end_time; } diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.h b/Frameworks/GME/gme/Kss_Scc_Apu.h old mode 100755 new mode 100644 index 03a6a1080..a48ff5396 --- a/Frameworks/GME/gme/Kss_Scc_Apu.h +++ b/Frameworks/GME/gme/Kss_Scc_Apu.h @@ -1,45 +1,53 @@ // Konami SCC sound chip emulator -// Game_Music_Emu 0.5.2 +// $package #ifndef KSS_SCC_APU_H #define KSS_SCC_APU_H #include "blargg_common.h" #include "Blip_Buffer.h" -#include class Scc_Apu { public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); - - // Reset sound chip - void reset(); - - // Write to register at specified time - enum { reg_count = 0x90 }; - void write( blip_time_t time, int reg, int data ); - - // Run sound to specified time, end current time frame, then start a new - // time frame at time 0. Time frames have no effect on emulation and each - // can be whatever length is convenient. - void end_frame( blip_time_t length ); +// Basics -// Additional features - - // Set sound output of specific oscillator to buffer, where index is - // 0 to 4. If buffer is NULL, the specified oscillator is muted. + // Sets buffer to generate sound into, or 0 to mute. + void set_output( Blip_Buffer* ); + + // Emulates to time t, then writes data to reg + enum { reg_count = 0xB0 }; // 0 <= reg < reg_count + void write( blip_time_t t, int reg, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip + void reset(); + + // Same as set_output(), but for a particular channel enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* ); - - // Set overall volume (default is 1.0) + void set_output( int chan, Blip_Buffer* ); + + // Set overall volume, where 1.0 is normal void volume( double ); - - // Set treble equalization (see documentation) - void treble_eq( blip_eq_t const& ); - + + // Set treble equalization + void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } + +private: + // noncopyable + Scc_Apu( const Scc_Apu& ); + Scc_Apu& operator = ( const Scc_Apu& ); + + +// Implementation public: Scc_Apu(); + BLARGG_DISABLE_NOTHROW + private: enum { amp_range = 0x8000 }; struct osc_t @@ -52,16 +60,12 @@ private: osc_t oscs [osc_count]; blip_time_t last_time; unsigned char regs [reg_count]; - Blip_Synth synth; - + Blip_Synth_Fast synth; + void run_until( blip_time_t ); }; -inline void Scc_Apu::volume( double v ) { synth.volume( 0.43 / osc_count / amp_range * v ); } - -inline void Scc_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - -inline void Scc_Apu::osc_output( int index, Blip_Buffer* b ) +inline void Scc_Apu::set_output( int index, Blip_Buffer* b ) { assert( (unsigned) index < osc_count ); oscs [index].output = b; @@ -69,38 +73,39 @@ inline void Scc_Apu::osc_output( int index, Blip_Buffer* b ) inline void Scc_Apu::write( blip_time_t time, int addr, int data ) { - assert( (unsigned) addr < reg_count ); + //assert( (unsigned) addr < reg_count ); + assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) ); run_until( time ); - regs [addr] = data; + + addr -= 0x9800; + if ( ( unsigned ) addr < 0x90 ) + { + if ( ( unsigned ) addr < 0x60 ) + regs [addr] = data; + else if ( ( unsigned ) addr < 0x80 ) + { + regs [addr] = regs[addr + 0x20] = data; + } + else if ( ( unsigned ) addr < 0x90 ) + { + regs [addr + 0x20] = data; + } + } + else + { + addr -= 0xB800 - 0x9800; + if ( ( unsigned ) addr < 0xB0 ) + regs [addr] = data; + } } inline void Scc_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until( end_time ); + last_time -= end_time; assert( last_time >= 0 ); } -inline void Scc_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - oscs [i].output = buf; -} - -inline Scc_Apu::Scc_Apu() -{ - output( 0 ); -} - -inline void Scc_Apu::reset() -{ - last_time = 0; - - for ( int i = 0; i < osc_count; i++ ) - memset( &oscs [i], 0, offsetof (osc_t,output) ); - - memset( regs, 0, sizeof regs ); -} - #endif diff --git a/Frameworks/GME/gme/M3u_Playlist.cpp b/Frameworks/GME/gme/M3u_Playlist.cpp old mode 100755 new mode 100644 index 0a1475db3..cfcf4d94f --- a/Frameworks/GME/gme/M3u_Playlist.cpp +++ b/Frameworks/GME/gme/M3u_Playlist.cpp @@ -1,426 +1,476 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "M3u_Playlist.h" -#include "Music_Emu.h" - -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -// gme functions defined here to avoid linking in m3u code unless it's used - -blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) -{ - require( raw_track_count_ ); // file must be loaded first - - if ( !err ) - { - if ( playlist.size() ) - track_count_ = playlist.size(); - - int line = playlist.first_error(); - if ( line ) - { - // avoid using bloated printf() - char* out = &playlist_warning [sizeof playlist_warning]; - *--out = 0; - do { - *--out = line % 10 + '0'; - } while ( (line /= 10) > 0 ); - - static const char str [] = "Problem in m3u at line "; - out -= sizeof str - 1; - memcpy( out, str, sizeof str - 1 ); - set_warning( out ); - } - } - return err; -} - -blargg_err_t Gme_File::load_m3u( const char* path ) { return load_m3u_( playlist.load( path ) ); } - -blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } - -gme_err_t gme_load_m3u( Music_Emu* me, const char* path ) { return me->load_m3u( path ); } - -gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) -{ - Mem_File_Reader in( data, size ); - return me->load_m3u( in ); -} - - - -static char* skip_white( char* in ) -{ - while ( *in == ' ' ) - in++; - return in; -} - -inline unsigned from_dec( unsigned n ) { return n - '0'; } - -static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) -{ - entry.file = in; - entry.type = ""; - char* out = in; - while ( 1 ) - { - int c = *in; - if ( !c ) break; - in++; - - if ( c == ',' ) // commas in filename - { - char* p = skip_white( in ); - if ( *p == '$' || from_dec( *p ) <= 9 ) - { - in = p; - break; - } - } - - if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix - { - entry.type = ++in; - while ( (c = *in) != 0 && c != ',' ) - in++; - if ( c == ',' ) - { - *in++ = 0; // terminate type - in = skip_white( in ); - } - break; - } - - if ( c == '\\' ) // \ prefix for special characters - { - c = *in; - if ( !c ) break; - in++; - } - *out++ = (char) c; - } - *out = 0; // terminate string - return in; -} - -static char* next_field( char* in, int* result ) -{ - while ( 1 ) - { - in = skip_white( in ); - - if ( !*in ) - break; - - if ( *in == ',' ) - { - in++; - break; - } - - *result = 1; - in++; - } - return skip_white( in ); -} - -static char* parse_int_( char* in, int* out ) -{ - int n = 0; - while ( 1 ) - { - unsigned d = from_dec( *in ); - if ( d > 9 ) - break; - in++; - n = n * 10 + d; - *out = n; - } - return in; -} - -static char* parse_int( char* in, int* out, int* result ) -{ - return next_field( parse_int_( in, out ), result ); -} - -// Returns 16 or greater if not hex -inline int from_hex_char( int h ) -{ - h -= 0x30; - if ( (unsigned) h > 9 ) - h = ((h - 0x11) & 0xDF) + 10; - return h; -} - -static char* parse_track( char* in, M3u_Playlist::entry_t& entry, int* result ) -{ - if ( *in == '$' ) - { - in++; - int n = 0; - while ( 1 ) - { - int h = from_hex_char( *in ); - if ( h > 15 ) - break; - in++; - n = n * 16 + h; - entry.track = n; - } - } - else - { - in = parse_int_( in, &entry.track ); - if ( entry.track >= 0 ) - entry.decimal_track = 1; - } - return next_field( in, result ); -} - -static char* parse_time_( char* in, int* out ) -{ - *out = -1; - int n = -1; - in = parse_int_( in, &n ); - if ( n >= 0 ) - { - *out = n; - if ( *in == ':' ) - { - n = -1; - in = parse_int_( in + 1, &n ); - if ( n >= 0 ) - *out = *out * 60 + n; - } - } - return in; -} - -static char* parse_time( char* in, int* out, int* result ) -{ - return next_field( parse_time_( in, out ), result ); -} - -static char* parse_name( char* in ) -{ - char* out = in; - while ( 1 ) - { - int c = *in; - if ( !c ) break; - in++; - - if ( c == ',' ) // commas in string - { - char* p = skip_white( in ); - if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 ) - { - in = p; - break; - } - } - - if ( c == '\\' ) // \ prefix for special characters - { - c = *in; - if ( !c ) break; - in++; - } - *out++ = (char) c; - } - *out = 0; // terminate string - return in; -} - -static int parse_line( char* in, M3u_Playlist::entry_t& entry ) -{ - int result = 0; - - // file - entry.file = in; - entry.type = ""; - in = parse_filename( in, entry ); - - // track - entry.track = -1; - entry.decimal_track = 0; - in = parse_track( in, entry, &result ); - - // name - entry.name = in; - in = parse_name( in ); - - // time - entry.length = -1; - in = parse_time( in, &entry.length, &result ); - - // loop - entry.intro = -1; - entry.loop = -1; - if ( *in == '-' ) - { - entry.loop = entry.length; - in++; - } - else - { - in = parse_time_( in, &entry.loop ); - if ( entry.loop >= 0 ) - { - entry.intro = 0; - if ( *in == '-' ) // trailing '-' means that intro length was specified - { - in++; - entry.intro = entry.loop; - entry.loop = entry.length - entry.intro; - } - } - } - in = next_field( in, &result ); - - // fade - entry.fade = -1; - in = parse_time( in, &entry.fade, &result ); - - // repeat - entry.repeat = -1; - in = parse_int( in, &entry.repeat, &result ); - - return result; -} - -static void parse_comment( char* in, M3u_Playlist::info_t& info, bool first ) -{ - in = skip_white( in + 1 ); - const char* field = in; - while ( *in && *in != ':' ) - in++; - - if ( *in == ':' ) - { - const char* text = skip_white( in + 1 ); - if ( *text ) - { - *in = 0; - if ( !strcmp( "Composer", field ) ) info.composer = text; - else if ( !strcmp( "Engineer", field ) ) info.engineer = text; - else if ( !strcmp( "Ripping" , field ) ) info.ripping = text; - else if ( !strcmp( "Tagging" , field ) ) info.tagging = text; - else - text = 0; - if ( text ) - return; - *in = ':'; - } - } - - if ( first ) - info.title = field; -} - -blargg_err_t M3u_Playlist::parse_() -{ - info_.title = ""; - info_.composer = ""; - info_.engineer = ""; - info_.ripping = ""; - info_.tagging = ""; - - int const CR = 13; - int const LF = 10; - - data.end() [-1] = LF; // terminate input - - first_error_ = 0; - bool first_comment = true; - int line = 0; - int count = 0; - char* in = data.begin(); - while ( in < data.end() ) - { - // find end of line and terminate it - line++; - char* begin = in; - while ( *in != CR && *in != LF ) - { - if ( !*in ) - return "Not an m3u playlist"; - in++; - } - if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line - *in++ = 0; - *in++ = 0; - - // parse line - if ( *begin == '#' ) - { - parse_comment( begin, info_, first_comment ); - first_comment = false; - } - else if ( *begin ) - { - if ( (int) entries.size() <= count ) - RETURN_ERR( entries.resize( count * 2 + 64 ) ); - - if ( !parse_line( begin, entries [count] ) ) - count++; - else if ( !first_error_ ) - first_error_ = line; - first_comment = false; - } - } - if ( count <= 0 ) - return "Not an m3u playlist"; - - if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) ) - info_.title = ""; - - return entries.resize( count ); -} - -blargg_err_t M3u_Playlist::parse() -{ - blargg_err_t err = parse_(); - if ( err ) - { - entries.clear(); - data.clear(); - } - return err; -} - -blargg_err_t M3u_Playlist::load( Data_Reader& in ) -{ - RETURN_ERR( data.resize( in.remain() + 1 ) ); - RETURN_ERR( in.read( data.begin(), data.size() - 1 ) ); - return parse(); -} - -blargg_err_t M3u_Playlist::load( const char* path ) -{ - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - return load( in ); -} - -blargg_err_t M3u_Playlist::load( void const* in, long size ) -{ - RETURN_ERR( data.resize( size + 1 ) ); - memcpy( data.begin(), in, size ); - return parse(); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "M3u_Playlist.h" +#include "Music_Emu.h" + +/* Copyright (C) 2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// gme functions defined here to avoid linking in m3u code unless it's used + +blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) +{ + if ( !err ) + { + require( raw_track_count_ ); // file must be loaded first + if ( playlist.size() ) + track_count_ = playlist.size(); + + int line = playlist.first_error(); + if ( line ) + { + // avoid using bloated printf() + char* out = &playlist_warning [sizeof playlist_warning]; + *--out = 0; + do { + *--out = line % 10 + '0'; + } while ( (line /= 10) > 0 ); + + static const char str [] = "Problem in m3u at line "; + out -= sizeof str - 1; + memcpy( out, str, sizeof str - 1 ); + set_warning( out ); + } + } + return err; +} + +blargg_err_t Gme_File::load_m3u( const char path [] ) { return load_m3u_( playlist.load( path ) ); } + +blargg_err_t Gme_File::load_m3u( Data_Reader& in ) { return load_m3u_( playlist.load( in ) ); } + +gme_err_t gme_load_m3u( Music_Emu* me, const char path [] ) { return me->load_m3u( path ); } + +gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) +{ + Mem_File_Reader in( data, size ); + return me->load_m3u( in ); +} + +static char* skip_white( char* in ) +{ + while ( unsigned (*in - 1) <= ' ' - 1 ) + in++; + return in; +} + +inline unsigned from_dec( unsigned n ) { return n - '0'; } + +static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) +{ + entry.file = in; + entry.type = ""; + char* out = in; + while ( 1 ) + { + int c = *in; + if ( !c ) break; + in++; + + if ( c == ',' ) // commas in filename + { + char* p = skip_white( in ); + if ( *p == '$' || from_dec( *p ) <= 9 ) + { + in = p; + break; + } + } + + if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix + { + entry.type = ++in; + while ( (c = *in) != 0 && c != ',' ) + in++; + if ( c == ',' ) + { + *in++ = 0; // terminate type + in = skip_white( in ); + } + break; + } + + if ( c == '\\' ) // \ prefix for special characters + { + c = *in; + if ( !c ) break; + in++; + } + *out++ = (char) c; + } + *out = 0; // terminate string + return in; +} + +static char* next_field( char* in, int* result ) +{ + while ( 1 ) + { + in = skip_white( in ); + + if ( !*in ) + break; + + if ( *in == ',' ) + { + in++; + break; + } + + *result = 1; + in++; + } + return skip_white( in ); +} + +static char* parse_int_( char* in, int* out ) +{ + int n = 0; + while ( 1 ) + { + unsigned d = from_dec( *in ); + if ( d > 9 ) + break; + in++; + n = n * 10 + d; + *out = n; + } + return in; +} + +static char* parse_int( char* in, int* out, int* result ) +{ + return next_field( parse_int_( in, out ), result ); +} + +// Returns 16 or greater if not hex +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static char* parse_track( char* in, M3u_Playlist::entry_t& entry, int* result ) +{ + if ( *in == '$' ) + { + in++; + int n = 0; + while ( 1 ) + { + int h = from_hex_char( *in ); + if ( h > 15 ) + break; + in++; + n = n * 16 + h; + entry.track = n; + } + } + else + { + in = parse_int_( in, &entry.track ); + if ( entry.track >= 0 ) + entry.decimal_track = 1; + } + return next_field( in, result ); +} + +static char* parse_time_( char* in, int* out ) +{ + *out = -1; + int n = -1; + in = parse_int_( in, &n ); + if ( n >= 0 ) + { + *out = n; + while ( *in == ':' ) + { + n = -1; + in = parse_int_( in + 1, &n ); + if ( n >= 0 ) + *out = *out * 60 + n; + } + *out *= 1000; + if ( *in == '.' ) + { + n = -1; + in = parse_int_( in + 1, &n ); + if ( n >= 0 ) + *out = *out + n; + } + } + return in; +} + +static char* parse_time( char* in, int* out, int* result ) +{ + return next_field( parse_time_( in, out ), result ); +} + +static char* parse_name( char* in ) +{ + char* out = in; + while ( 1 ) + { + int c = *in; + if ( !c ) break; + in++; + + if ( c == ',' ) // commas in string + { + char* p = skip_white( in ); + if ( *p == ',' || *p == '-' || from_dec( *p ) <= 9 ) + { + in = p; + break; + } + } + + if ( c == '\\' ) // \ prefix for special characters + { + c = *in; + if ( !c ) break; + in++; + } + *out++ = (char) c; + } + *out = 0; // terminate string + return in; +} + +static int parse_line( char* in, M3u_Playlist::entry_t& entry ) +{ + int result = 0; + + // file + entry.file = in; + entry.type = ""; + in = parse_filename( in, entry ); + + // track + entry.track = -1; + entry.decimal_track = 0; + in = parse_track( in, entry, &result ); + + // name + entry.name = in; + in = parse_name( in ); + + // time + entry.length = -1; + in = parse_time( in, &entry.length, &result ); + + // loop + entry.intro = -1; + entry.loop = -1; + if ( *in == '-' ) + { + entry.loop = entry.length; + in++; + } + else + { + in = parse_time_( in, &entry.loop ); + if ( entry.loop >= 0 ) + { + entry.intro = entry.length - entry.loop; + if ( *in == '-' ) // trailing '-' means that intro length was specified + { + in++; + entry.intro = entry.loop; + entry.loop = entry.length - entry.intro; + } + } + } + in = next_field( in, &result ); + + // fade + entry.fade = -1; + in = parse_time( in, &entry.fade, &result ); + + // repeat + entry.repeat = -1; + in = parse_int( in, &entry.repeat, &result ); + + return result; +} + +static void parse_comment( char* in, M3u_Playlist::info_t& info, char *& last_comment_value, bool first ) +{ + in = skip_white( in + 1 ); + const char* field = in; + if ( *field != '@' ) + while ( *in && *in != ':' ) + in++; + + if ( *in == ':' ) + { + const char* text = skip_white( in + 1 ); + if ( *text ) + { + *in = 0; + if ( !strcmp( "Composer" , field ) ) info.composer = text; + else if ( !strcmp( "Engineer" , field ) ) info.engineer = text; + else if ( !strcmp( "Ripping" , field ) ) info.ripping = text; + else if ( !strcmp( "Tagging" , field ) ) info.tagging = text; + else if ( !strcmp( "Game" , field ) ) info.title = text; + else if ( !strcmp( "Artist" , field ) ) info.artist = text; + else if ( !strcmp( "Copyright", field ) ) info.copyright = text; + else + text = 0; + if ( text ) + return; + *in = ':'; + } + } + else if ( *field == '@' ) + { + ++field; + in = (char*)field; + while ( *in && *in > ' ' ) + in++; + const char* text = skip_white( in ); + if ( *text ) + { + char saved = *in; + *in = 0; + if ( !strcmp( "TITLE" , field ) ) info.title = text; + else if ( !strcmp( "ARTIST", field ) ) info.artist = text; + else if ( !strcmp( "DATE", field ) ) info.date = text; + else if ( !strcmp( "COMPOSER", field ) ) info.composer = text; + else if ( !strcmp( "SEQUENCER", field ) ) info.sequencer = text; + else if ( !strcmp( "ENGINEER", field ) ) info.engineer = text; + else if ( !strcmp( "RIPPER", field ) ) info.ripping = text; + else if ( !strcmp( "TAGGER", field ) ) info.tagging = text; + else + text = 0; + if ( text ) + { + last_comment_value = (char*)text; + return; + } + *in = saved; + } + } + else if ( last_comment_value ) + { + size_t len = strlen( last_comment_value ); + last_comment_value[ len ] = ','; + last_comment_value[ len + 1 ] = ' '; + size_t field_len = strlen( field ); + memmove( last_comment_value + len + 2, field, field_len ); + last_comment_value[ len + 2 + field_len ] = 0; + return; + } + + if ( first ) + info.title = field; +} + +blargg_err_t M3u_Playlist::parse_() +{ + info_.title = ""; + info_.artist = ""; + info_.date = ""; + info_.composer = ""; + info_.sequencer = ""; + info_.engineer = ""; + info_.ripping = ""; + info_.tagging = ""; + info_.copyright = ""; + + int const CR = 13; + int const LF = 10; + + data.end() [-1] = LF; // terminate input + + first_error_ = 0; + bool first_comment = true; + int line = 0; + int count = 0; + char* in = data.begin(); + char* last_comment_value = 0; + while ( in < data.end() ) + { + // find end of line and terminate it + line++; + char* begin = in; + while ( *in != CR && *in != LF ) + { + if ( !*in ) + return blargg_err_file_type; + in++; + } + if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line + *in++ = 0; + *in++ = 0; + + // parse line + if ( *begin == '#' ) + { + parse_comment( begin, info_, last_comment_value, first_comment ); + first_comment = false; + } + else if ( *begin ) + { + if ( (int) entries.size() <= count ) + RETURN_ERR( entries.resize( count * 2 + 64 ) ); + + if ( !parse_line( begin, entries [count] ) ) + count++; + else if ( !first_error_ ) + first_error_ = line; + first_comment = false; + } + else last_comment_value = 0; + } + if ( count <= 0 ) + return blargg_err_file_type; + + // Treat first comment as title only if another field is also specified + if ( !(info_.artist [0] | info_.composer [0] | info_.date [0] | info_.engineer [0] | info_.ripping [0] | info_.sequencer [0] | info_.tagging [0] | info_.copyright[0]) ) + info_.title = ""; + + return entries.resize( count ); +} + +blargg_err_t M3u_Playlist::parse() +{ + blargg_err_t err = parse_(); + if ( err ) + clear_(); + return err; +} + +blargg_err_t M3u_Playlist::load( Data_Reader& in ) +{ + RETURN_ERR( data.resize( in.remain() + 1 ) ); + RETURN_ERR( in.read( data.begin(), data.size() - 1 ) ); + return parse(); +} + +blargg_err_t M3u_Playlist::load( const char path [] ) +{ + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + return load( in ); +} + +blargg_err_t M3u_Playlist::load( void const* in, long size ) +{ + RETURN_ERR( data.resize( size + 1 ) ); + memcpy( data.begin(), in, size ); + return parse(); +} diff --git a/Frameworks/GME/gme/M3u_Playlist.h b/Frameworks/GME/gme/M3u_Playlist.h old mode 100755 new mode 100644 index eda0dc89b..11d902a50 --- a/Frameworks/GME/gme/M3u_Playlist.h +++ b/Frameworks/GME/gme/M3u_Playlist.h @@ -1,67 +1,87 @@ -// M3U playlist file parser, with support for subtrack information - -// Game_Music_Emu 0.5.2 -#ifndef M3U_PLAYLIST_H -#define M3U_PLAYLIST_H - -#include "blargg_common.h" -#include "Data_Reader.h" - -class M3u_Playlist { -public: - // Load playlist data - blargg_err_t load( const char* path ); - blargg_err_t load( Data_Reader& in ); - blargg_err_t load( void const* data, long size ); - - // Line number of first parse error, 0 if no error. Any lines with parse - // errors are ignored. - int first_error() const { return first_error_; } - - struct info_t - { - const char* title; - const char* composer; - const char* engineer; - const char* ripping; - const char* tagging; - }; - info_t const& info() const { return info_; } - - struct entry_t - { - const char* file; // filename without stupid ::TYPE suffix - const char* type; // if filename has ::TYPE suffix, this will be "TYPE". "" if none. - const char* name; - bool decimal_track; // true if track was specified in hex - // integers are -1 if not present - int track; // 1-based - int length; // seconds - int intro; - int loop; - int fade; - int repeat; // count - }; - entry_t const& operator [] ( int i ) const { return entries [i]; } - int size() const { return entries.size(); } - - void clear(); - -private: - blargg_vector entries; - blargg_vector data; - int first_error_; - info_t info_; - - blargg_err_t parse(); - blargg_err_t parse_(); -}; - -inline void M3u_Playlist::clear() -{ - first_error_ = 0; - entries.clear(); - data.clear(); -} - -#endif +// M3U playlist file parser, with support for subtrack information + +// Game_Music_Emu $vers +#ifndef M3U_PLAYLIST_H +#define M3U_PLAYLIST_H + +#include "blargg_common.h" +#include "Data_Reader.h" + +class M3u_Playlist { +public: + // Load playlist data + blargg_err_t load( const char* path ); + blargg_err_t load( Data_Reader& in ); + blargg_err_t load( void const* data, long size ); + + // Line number of first parse error, 0 if no error. Any lines with parse + // errors are ignored. + int first_error() const { return first_error_; } + + // All string pointers point to valid string, or "" if not available + struct info_t + { + const char* title; + const char* artist; + const char* date; + const char* composer; + const char* sequencer; + const char* engineer; + const char* ripping; + const char* tagging; + const char* copyright; + }; + info_t const& info() const { return info_; } + + struct entry_t + { + const char* file; // filename without stupid ::TYPE suffix + const char* type; // if filename has ::TYPE suffix, this is "TYPE", otherwise "" + const char* name; + bool decimal_track; // true if track was specified in decimal + // integers are -1 if not present + int track; + int length; // milliseconds + int intro; + int loop; + int fade; + int repeat; // count + }; + entry_t const& operator [] ( int i ) const { return entries [i]; } + int size() const { return entries.size(); } + + void clear(); + +private: + blargg_vector entries; + blargg_vector data; + int first_error_; + info_t info_; + + blargg_err_t parse(); + blargg_err_t parse_(); + void clear_(); +}; + +inline void M3u_Playlist::clear_() +{ + info_.title = ""; + info_.artist = ""; + info_.date = ""; + info_.composer = ""; + info_.sequencer = ""; + info_.engineer = ""; + info_.ripping = ""; + info_.tagging = ""; + info_.copyright = ""; + entries.clear(); + data.clear(); +} + +inline void M3u_Playlist::clear() +{ + first_error_ = 0; + clear_(); +} + +#endif diff --git a/Frameworks/GME/gme/Multi_Buffer.cpp b/Frameworks/GME/gme/Multi_Buffer.cpp old mode 100755 new mode 100644 index ecd8f8add..b1d3c5fd0 --- a/Frameworks/GME/gme/Multi_Buffer.cpp +++ b/Frameworks/GME/gme/Multi_Buffer.cpp @@ -1,232 +1,290 @@ -// Blip_Buffer 0.4.1. http://www.slack.net/~ant/ - -#include "Multi_Buffer.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) -{ - length_ = 0; - sample_rate_ = 0; - channels_changed_count_ = 1; -} - -blargg_err_t Multi_Buffer::set_channel_count( int ) { return 0; } - -// Silent_Buffer - -Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse -{ - // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? - chan.left = 0; - chan.center = 0; - chan.right = 0; -} - -// Mono_Buffer - -Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) -{ - chan.center = &buf; - chan.left = &buf; - chan.right = &buf; -} - -Mono_Buffer::~Mono_Buffer() { } - -blargg_err_t Mono_Buffer::set_sample_rate( long rate, int msec ) -{ - RETURN_ERR( buf.set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); -} - -// Stereo_Buffer - -Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) -{ - chan.center = &bufs [0]; - chan.left = &bufs [1]; - chan.right = &bufs [2]; -} - -Stereo_Buffer::~Stereo_Buffer() { } - -blargg_err_t Stereo_Buffer::set_sample_rate( long rate, int msec ) -{ - for ( int i = 0; i < buf_count; i++ ) - RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); - return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); -} - -void Stereo_Buffer::clock_rate( long rate ) -{ - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clock_rate( rate ); -} - -void Stereo_Buffer::bass_freq( int bass ) -{ - for ( unsigned i = 0; i < buf_count; i++ ) - bufs [i].bass_freq( bass ); -} - -void Stereo_Buffer::clear() -{ - stereo_added = 0; - was_stereo = false; - for ( int i = 0; i < buf_count; i++ ) - bufs [i].clear(); -} - -void Stereo_Buffer::end_frame( blip_time_t clock_count ) -{ - stereo_added = 0; - for ( unsigned i = 0; i < buf_count; i++ ) - { - stereo_added |= bufs [i].clear_modified() << i; - bufs [i].end_frame( clock_count ); - } -} - -long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) -{ - require( !(count & 1) ); // count must be even - count = (unsigned) count / 2; - - long avail = bufs [0].samples_avail(); - if ( count > avail ) - count = avail; - if ( count ) - { - int bufs_used = stereo_added | was_stereo; - //dprintf( "%X\n", bufs_used ); - if ( bufs_used <= 1 ) - { - mix_mono( out, count ); - bufs [0].remove_samples( count ); - bufs [1].remove_silence( count ); - bufs [2].remove_silence( count ); - } - else if ( bufs_used & 1 ) - { - mix_stereo( out, count ); - bufs [0].remove_samples( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - else - { - mix_stereo_no_center( out, count ); - bufs [0].remove_silence( count ); - bufs [1].remove_samples( count ); - bufs [2].remove_samples( count ); - } - - // to do: this might miss opportunities for optimization - if ( !bufs [0].samples_avail() ) - { - was_stereo = stereo_added; - stereo_added = 0; - } - } - - return count * 2; -} - -void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [1] ); - BLIP_READER_BEGIN( left, bufs [1] ); - BLIP_READER_BEGIN( right, bufs [2] ); - BLIP_READER_BEGIN( center, bufs [0] ); - - for ( ; count; --count ) - { - int c = BLIP_READER_READ( center ); - blargg_long l = c + BLIP_READER_READ( left ); - blargg_long r = c + BLIP_READER_READ( right ); - if ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - BLIP_READER_NEXT( center, bass ); - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - BLIP_READER_NEXT( left, bass ); - BLIP_READER_NEXT( right, bass ); - - out [0] = l; - out [1] = r; - out += 2; - } - - BLIP_READER_END( center, bufs [0] ); - BLIP_READER_END( right, bufs [2] ); - BLIP_READER_END( left, bufs [1] ); -} - -void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [1] ); - BLIP_READER_BEGIN( left, bufs [1] ); - BLIP_READER_BEGIN( right, bufs [2] ); - - for ( ; count; --count ) - { - blargg_long l = BLIP_READER_READ( left ); - if ( (BOOST::int16_t) l != l ) - l = 0x7FFF - (l >> 24); - - blargg_long r = BLIP_READER_READ( right ); - if ( (BOOST::int16_t) r != r ) - r = 0x7FFF - (r >> 24); - - BLIP_READER_NEXT( left, bass ); - BLIP_READER_NEXT( right, bass ); - - out [0] = l; - out [1] = r; - out += 2; - } - - BLIP_READER_END( right, bufs [2] ); - BLIP_READER_END( left, bufs [1] ); -} - -void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) -{ - blip_sample_t* BLIP_RESTRICT out = out_; - int const bass = BLIP_READER_BASS( bufs [0] ); - BLIP_READER_BEGIN( center, bufs [0] ); - - for ( ; count; --count ) - { - blargg_long s = BLIP_READER_READ( center ); - if ( (BOOST::int16_t) s != s ) - s = 0x7FFF - (s >> 24); - - BLIP_READER_NEXT( center, bass ); - out [0] = s; - out [1] = s; - out += 2; - } - - BLIP_READER_END( center, bufs [0] ); -} +// Blip_Buffer $vers. http://www.slack.net/~ant/ + +#include "Multi_Buffer.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf ) +{ + length_ = 0; + sample_rate_ = 0; + channels_changed_count_ = 1; + channel_types_ = NULL; + channel_count_ = 0; + immediate_removal_ = true; +} + +Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ ) +{ + channel_t ch; + ch.center = ch.left = ch.right = NULL; + return ch; +} + +// Silent_Buffer + +Silent_Buffer::Silent_Buffer() : Multi_Buffer( 1 ) // 0 channels would probably confuse +{ + // TODO: better to use empty Blip_Buffer so caller never has to check for NULL? + chan.left = NULL; + chan.center = NULL; + chan.right = NULL; +} + +// Mono_Buffer + +Mono_Buffer::Mono_Buffer() : Multi_Buffer( 1 ) +{ + chan.center = &buf; + chan.left = &buf; + chan.right = &buf; +} + +Mono_Buffer::~Mono_Buffer() { } + +blargg_err_t Mono_Buffer::set_sample_rate( int rate, int msec ) +{ + RETURN_ERR( buf.set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() ); +} + + +// Tracked_Blip_Buffer + +int const blip_buffer_extra = 32; // TODO: explain why this value + +Tracked_Blip_Buffer::Tracked_Blip_Buffer() +{ + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::clear() +{ + last_non_silence = 0; + Blip_Buffer::clear(); +} + +void Tracked_Blip_Buffer::end_frame( blip_time_t t ) +{ + Blip_Buffer::end_frame( t ); + if ( modified() ) + { + clear_modified(); + last_non_silence = samples_avail() + blip_buffer_extra; + } +} + +unsigned Tracked_Blip_Buffer::non_silent() const +{ + return last_non_silence | unsettled(); +} + +inline void Tracked_Blip_Buffer::remove_( int n ) +{ + if ( (last_non_silence -= n) < 0 ) + last_non_silence = 0; +} + +void Tracked_Blip_Buffer::remove_silence( int n ) +{ + remove_( n ); + Blip_Buffer::remove_silence( n ); +} + +void Tracked_Blip_Buffer::remove_samples( int n ) +{ + remove_( n ); + Blip_Buffer::remove_samples( n ); +} + +void Tracked_Blip_Buffer::remove_all_samples() +{ + int avail = samples_avail(); + if ( !non_silent() ) + remove_silence( avail ); + else + remove_samples( avail ); +} + +int Tracked_Blip_Buffer::read_samples( blip_sample_t out [], int count ) +{ + count = Blip_Buffer::read_samples( out, count ); + remove_( count ); + return count; +} + +// Stereo_Buffer + +int const stereo = 2; + +Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 ) +{ + chan.center = mixer.bufs [2] = &bufs [2]; + chan.left = mixer.bufs [0] = &bufs [0]; + chan.right = mixer.bufs [1] = &bufs [1]; + mixer.samples_read = 0; +} + +Stereo_Buffer::~Stereo_Buffer() { } + +blargg_err_t Stereo_Buffer::set_sample_rate( int rate, int msec ) +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); + return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); +} + +void Stereo_Buffer::clock_rate( int rate ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clock_rate( rate ); +} + +void Stereo_Buffer::bass_freq( int bass ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].bass_freq( bass ); +} + +void Stereo_Buffer::clear() +{ + mixer.samples_read = 0; + for ( int i = bufs_size; --i >= 0; ) + bufs [i].clear(); +} + +void Stereo_Buffer::end_frame( blip_time_t time ) +{ + for ( int i = bufs_size; --i >= 0; ) + bufs [i].end_frame( time ); +} + +int Stereo_Buffer::read_samples( blip_sample_t out [], int out_size ) +{ + require( (out_size & 1) == 0 ); // must read an even number of samples + out_size = min( out_size, samples_avail() ); + + int pair_count = int (out_size >> 1); + if ( pair_count ) + { + mixer.read_pairs( out, pair_count ); + + if ( samples_avail() <= 0 || immediate_removal() ) + { + for ( int i = bufs_size; --i >= 0; ) + { + buf_t& b = bufs [i]; + // TODO: might miss non-silence settling since it checks END of last read + if ( !b.non_silent() ) + b.remove_silence( mixer.samples_read ); + else + b.remove_samples( mixer.samples_read ); + } + mixer.samples_read = 0; + } + } + return out_size; +} + + +// Stereo_Mixer + +// mixers use a single index value to improve performance on register-challenged processors +// offset goes from negative to zero + +void Stereo_Mixer::read_pairs( blip_sample_t out [], int count ) +{ + // TODO: if caller never marks buffers as modified, uses mono + // except that buffer isn't cleared, so caller can encounter + // subtle problems and not realize the cause. + samples_read += count; + if ( bufs [0]->non_silent() | bufs [1]->non_silent() ) + mix_stereo( out, count ); + else + mix_mono( out, count ); +} + +void Stereo_Mixer::mix_mono( blip_sample_t out_ [], int count ) +{ + int const bass = bufs [2]->highpass_shift(); + Blip_Buffer::delta_t const* center = bufs [2]->read_pos() + samples_read; + int center_sum = bufs [2]->integrator(); + + typedef blip_sample_t stereo_blip_sample_t [stereo]; + stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_ + count; + int offset = -count; + do + { + int s = center_sum >> bufs [2]->delta_bits; + + center_sum -= center_sum >> bass; + center_sum += center [offset]; + + BLIP_CLAMP( s, s ); + + out [offset] [0] = (blip_sample_t) s; + out [offset] [1] = (blip_sample_t) s; + } + while ( ++offset ); + + bufs [2]->set_integrator( center_sum ); +} + +void Stereo_Mixer::mix_stereo( blip_sample_t out_ [], int count ) +{ + blip_sample_t* BLARGG_RESTRICT out = out_ + count * stereo; + + // do left + center and right + center separately to reduce register load + Tracked_Blip_Buffer* const* buf = &bufs [2]; + while ( true ) // loop runs twice + { + --buf; + --out; + + int const bass = bufs [2]->highpass_shift(); + Blip_Buffer::delta_t const* side = (*buf)->read_pos() + samples_read; + Blip_Buffer::delta_t const* center = bufs [2]->read_pos() + samples_read; + + int side_sum = (*buf)->integrator(); + int center_sum = bufs [2]->integrator(); + + int offset = -count; + do + { + int s = (center_sum + side_sum) >> Blip_Buffer::delta_bits; + + side_sum -= side_sum >> bass; + center_sum -= center_sum >> bass; + + side_sum += side [offset]; + center_sum += center [offset]; + + BLIP_CLAMP( s, s ); + + ++offset; // before write since out is decremented to slightly before end + out [offset * stereo] = (blip_sample_t) s; + } + while ( offset ); + + (*buf)->set_integrator( side_sum ); + + if ( buf != bufs ) + continue; + + // only end center once + bufs [2]->set_integrator( center_sum ); + break; + } +} diff --git a/Frameworks/GME/gme/Multi_Buffer.h b/Frameworks/GME/gme/Multi_Buffer.h old mode 100755 new mode 100644 index a39cca1a5..677d80ddc --- a/Frameworks/GME/gme/Multi_Buffer.h +++ b/Frameworks/GME/gme/Multi_Buffer.h @@ -1,6 +1,6 @@ // Multi-channel sound buffer interface, and basic mono and stereo buffers -// Blip_Buffer 0.4.1 +// Blip_Buffer $vers #ifndef MULTI_BUFFER_H #define MULTI_BUFFER_H @@ -11,146 +11,209 @@ // consisting of left, center, and right buffers. class Multi_Buffer { public: + + // 1=mono, 2=stereo Multi_Buffer( int samples_per_frame ); - virtual ~Multi_Buffer() { } + virtual ~Multi_Buffer() { } - // Set the number of channels available - virtual blargg_err_t set_channel_count( int ); + // Sets the number of channels available and optionally their types + // (type information used by Effects_Buffer) + enum { type_index_mask = 0xFF }; + enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; + virtual blargg_err_t set_channel_count( int, int const types [] = NULL ); + int channel_count() const { return channel_count_; } - // Get indexed channel, from 0 to channel count - 1 + // Gets indexed channel, from 0 to channel_count()-1 struct channel_t { Blip_Buffer* center; Blip_Buffer* left; Blip_Buffer* right; }; - enum { type_index_mask = 0xFF }; - enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; - virtual channel_t channel( int index, int type ) = 0; - - // See Blip_Buffer.h - virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; - virtual void clock_rate( long ) = 0; - virtual void bass_freq( int ) = 0; - virtual void clear() = 0; - long sample_rate() const; - - // Length of buffer, in milliseconds - int length() const; - - // See Blip_Buffer.h - virtual void end_frame( blip_time_t ) = 0; + virtual channel_t channel( int index ) BLARGG_PURE( ; ) // Number of samples per output frame (1 = mono, 2 = stereo) int samples_per_frame() const; // Count of changes to channel configuration. Incremented whenever // a change is made to any of the Blip_Buffers for any channel. - unsigned channels_changed_count() { return channels_changed_count_; } + unsigned channels_changed_count() { return channels_changed_count_; } // See Blip_Buffer.h - virtual long read_samples( blip_sample_t*, long ) = 0; - virtual long samples_avail() const = 0; - -public: - BLARGG_DISABLE_NOTHROW -protected: - void channels_changed() { channels_changed_count_++; } + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ) BLARGG_PURE( ; ) + int sample_rate() const; + int length() const; + virtual void clock_rate( int ) BLARGG_PURE( ; ) + virtual void bass_freq( int ) BLARGG_PURE( ; ) + virtual void clear() BLARGG_PURE( ; ) + virtual void end_frame( blip_time_t ) BLARGG_PURE( ; ) + virtual int read_samples( blip_sample_t [], int ) BLARGG_PURE( ; ) + virtual int samples_avail() const BLARGG_PURE( ; ) + private: // noncopyable Multi_Buffer( const Multi_Buffer& ); Multi_Buffer& operator = ( const Multi_Buffer& ); - + +// Implementation +public: + BLARGG_DISABLE_NOTHROW + void disable_immediate_removal() { immediate_removal_ = false; } + +protected: + bool immediate_removal() const { return immediate_removal_; } + int const* channel_types() const { return channel_types_; } + void channels_changed() { channels_changed_count_++; } + +private: unsigned channels_changed_count_; - long sample_rate_; + int sample_rate_; int length_; + int channel_count_; int const samples_per_frame_; + int const* channel_types_; + bool immediate_removal_; }; + // Uses a single buffer and outputs mono samples. class Mono_Buffer : public Multi_Buffer { - Blip_Buffer buf; - channel_t chan; public: // Buffer used for all channels - Blip_Buffer* center() { return &buf; } + Blip_Buffer* center() { return &buf; } +// Implementation public: Mono_Buffer(); ~Mono_Buffer(); - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ); - void clock_rate( long rate ) { buf.clock_rate( rate ); } - void bass_freq( int freq ) { buf.bass_freq( freq ); } - void clear() { buf.clear(); } - long samples_avail() const { return buf.samples_avail(); } - long read_samples( blip_sample_t* p, long s ) { return buf.read_samples( p, s ); } - channel_t channel( int, int ) { return chan; } - void end_frame( blip_time_t t ) { buf.end_frame( t ); } + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); + virtual void clock_rate( int rate ) { buf.clock_rate( rate ); } + virtual void bass_freq( int freq ) { buf.bass_freq( freq ); } + virtual void clear() { buf.clear(); } + virtual int samples_avail() const { return buf.samples_avail(); } + virtual int read_samples( blip_sample_t p [], int s ) { return buf.read_samples( p, s ); } + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t t ) { buf.end_frame( t ); } + +private: + Blip_Buffer buf; + channel_t chan; }; + class Tracked_Blip_Buffer : public Blip_Buffer { + public: + // Non-zero if buffer still has non-silent samples in it. Requires that you call + // set_modified() appropriately. + unsigned non_silent() const; + + // remove_samples( samples_avail() ) + void remove_all_samples(); + + // Implementation + public: + BLARGG_DISABLE_NOTHROW + int read_samples( blip_sample_t [], int ); + void remove_silence( int ); + void remove_samples( int ); + Tracked_Blip_Buffer(); + void clear(); + void end_frame( blip_time_t ); + + private: + int last_non_silence; + + delta_t unsettled() const { return integrator() >> delta_bits; } + void remove_( int ); + }; + + class Stereo_Mixer { + public: + Tracked_Blip_Buffer* bufs [3]; + int samples_read; + + Stereo_Mixer() : samples_read( 0 ) { } + void read_pairs( blip_sample_t out [], int count ); + + private: + void mix_mono ( blip_sample_t out [], int pair_count ); + void mix_stereo( blip_sample_t out [], int pair_count ); + }; + + // Uses three buffers (one for center) and outputs stereo sample pairs. class Stereo_Buffer : public Multi_Buffer { public: // Buffers used for all channels - Blip_Buffer* center() { return &bufs [0]; } - Blip_Buffer* left() { return &bufs [1]; } - Blip_Buffer* right() { return &bufs [2]; } + Blip_Buffer* center() { return &bufs [2]; } + Blip_Buffer* left() { return &bufs [0]; } + Blip_Buffer* right() { return &bufs [1]; } +// Implementation public: Stereo_Buffer(); ~Stereo_Buffer(); - blargg_err_t set_sample_rate( long, int msec = blip_default_length ); - void clock_rate( long ); - void bass_freq( int ); - void clear(); - channel_t channel( int, int ) { return chan; } - void end_frame( blip_time_t ); - - long samples_avail() const { return bufs [0].samples_avail() * 2; } - long read_samples( blip_sample_t*, long ); + virtual blargg_err_t set_sample_rate( int, int msec = blip_default_length ); + virtual void clock_rate( int ); + virtual void bass_freq( int ); + virtual void clear(); + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t ); + virtual int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; } + virtual int read_samples( blip_sample_t [], int ); private: - enum { buf_count = 3 }; - Blip_Buffer bufs [buf_count]; + enum { bufs_size = 3 }; + typedef Tracked_Blip_Buffer buf_t; + buf_t bufs [bufs_size]; + Stereo_Mixer mixer; channel_t chan; - int stereo_added; - int was_stereo; - - void mix_stereo_no_center( blip_sample_t*, blargg_long ); - void mix_stereo( blip_sample_t*, blargg_long ); - void mix_mono( blip_sample_t*, blargg_long ); + int samples_avail_; }; + // Silent_Buffer generates no samples, useful where no sound is wanted class Silent_Buffer : public Multi_Buffer { channel_t chan; public: Silent_Buffer(); - blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) - { - return Multi_Buffer::set_sample_rate( rate, msec ); - } - void clock_rate( long ) { } - void bass_freq( int ) { } - void clear() { } - channel_t channel( int, int ) { return chan; } - void end_frame( blip_time_t ) { } - long samples_avail() const { return 0; } - long read_samples( blip_sample_t*, long ) { return 0; } + virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ); + virtual void clock_rate( int ) { } + virtual void bass_freq( int ) { } + virtual void clear() { } + virtual channel_t channel( int ) { return chan; } + virtual void end_frame( blip_time_t ) { } + virtual int samples_avail() const { return 0; } + virtual int read_samples( blip_sample_t [], int ) { return 0; } }; -inline blargg_err_t Multi_Buffer::set_sample_rate( long rate, int msec ) +inline blargg_err_t Multi_Buffer::set_sample_rate( int rate, int msec ) { sample_rate_ = rate; length_ = msec; - return 0; + return blargg_ok; } -inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } +inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; } +inline int Multi_Buffer::sample_rate() const { return sample_rate_; } +inline int Multi_Buffer::length() const { return length_; } +inline void Multi_Buffer::clock_rate( int ) { } +inline void Multi_Buffer::bass_freq( int ) { } +inline void Multi_Buffer::clear() { } +inline void Multi_Buffer::end_frame( blip_time_t ) { } +inline int Multi_Buffer::read_samples( blip_sample_t [], int ) { return 0; } +inline int Multi_Buffer::samples_avail() const { return 0; } -inline long Multi_Buffer::sample_rate() const { return sample_rate_; } +inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const types [] ) +{ + channel_count_ = n; + channel_types_ = types; + return blargg_ok; +} -inline int Multi_Buffer::length() const { return length_; } +inline blargg_err_t Silent_Buffer::set_sample_rate( int rate, int msec ) +{ + return Multi_Buffer::set_sample_rate( rate, msec ); +} #endif diff --git a/Frameworks/GME/gme/Music_Emu.cpp b/Frameworks/GME/gme/Music_Emu.cpp old mode 100755 new mode 100644 index 31c7233ca..410168f41 --- a/Frameworks/GME/gme/Music_Emu.cpp +++ b/Frameworks/GME/gme/Music_Emu.cpp @@ -1,410 +1,235 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Music_Emu.h" - -#include "Multi_Buffer.h" -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Music_Emu.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" int const stereo = 2; // number of channels for stereo -int const silence_max = 6; // seconds -int const silence_threshold = 0x10; -long const fade_block_size = 512; -int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) -Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 }; +Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180, 0,0,0,0,0,0,0,0 }; void Music_Emu::clear_track_vars() { - current_track_ = -1; - out_time = 0; - emu_time = 0; - emu_track_ended_ = true; - track_ended_ = true; - fade_start = LONG_MAX / 2 + 1; - fade_step = 1; - silence_time = 0; - silence_count = 0; - buf_remain = 0; - warning(); // clear warning -} - -void Music_Emu::unload() -{ - voice_count_ = 0; - clear_track_vars(); - Gme_File::unload(); -} - -Music_Emu::Music_Emu() -{ - effects_buffer = 0; - - sample_rate_ = 0; - mute_mask_ = 0; - tempo_ = 1.0; - gain_ = 1.0; - - // defaults - max_initial_silence = 2; - silence_lookahead = 3; - ignore_silence_ = false; - equalizer_.treble = -1.0; - equalizer_.bass = 60; - - static const char* const names [] = { - "Voice 1", "Voice 2", "Voice 3", "Voice 4", - "Voice 5", "Voice 6", "Voice 7", "Voice 8" - }; - set_voice_names( names ); - Music_Emu::unload(); // non-virtual -} - -Music_Emu::~Music_Emu() { delete effects_buffer; } - -blargg_err_t Music_Emu::set_sample_rate( long rate ) -{ - require( !sample_rate() ); // sample rate can't be changed once set - RETURN_ERR( set_sample_rate_( rate ) ); - RETURN_ERR( buf.resize( buf_size ) ); - sample_rate_ = rate; - return 0; -} - -void Music_Emu::pre_load() -{ - require( sample_rate() ); // set_sample_rate() must be called before loading a file - Gme_File::pre_load(); -} - -void Music_Emu::set_equalizer( equalizer_t const& eq ) -{ - equalizer_ = eq; - set_equalizer_( eq ); -} - -void Music_Emu::mute_voice( int index, bool mute ) -{ - require( (unsigned) index < (unsigned) voice_count() ); - int bit = 1 << index; - int mask = mute_mask_ | bit; - if ( !mute ) - mask ^= bit; - mute_voices( mask ); -} - -void Music_Emu::mute_voices( int mask ) -{ - require( sample_rate() ); // sample rate must be set first - mute_mask_ = mask; - mute_voices_( mask ); -} - -void Music_Emu::set_tempo( double t ) -{ - require( sample_rate() ); // sample rate must be set first - double const min = 0.02; - double const max = 4.00; - if ( t < min ) t = min; - if ( t > max ) t = max; - tempo_ = t; - set_tempo_( t ); -} - -void Music_Emu::post_load_() -{ - set_tempo( tempo_ ); - remute_voices(); -} - -blargg_err_t Music_Emu::start_track( int track ) -{ - clear_track_vars(); - - int remapped = track; - RETURN_ERR( remap_track_( &remapped ) ); - current_track_ = track; - RETURN_ERR( start_track_( remapped ) ); - - emu_track_ended_ = false; - track_ended_ = false; - - if ( !ignore_silence_ ) - { - // play until non-silence or end of track - for ( long end = max_initial_silence * stereo * sample_rate(); emu_time < end; ) - { - fill_buf(); - if ( buf_remain | (int) emu_track_ended_ ) - break; - } - - emu_time = buf_remain; - out_time = 0; - silence_time = 0; - silence_count = 0; - } - return track_ended() ? warning() : 0; -} - -void Music_Emu::end_track_if_error( blargg_err_t err ) -{ - if ( err ) - { - emu_track_ended_ = true; - set_warning( err ); - } -} - -// Tell/Seek - -blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const -{ - blargg_long sec = msec / 1000; - msec -= sec * 1000; - return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; -} - -long Music_Emu::tell() const -{ - blargg_long rate = sample_rate() * stereo; - blargg_long sec = out_time / rate; - return sec * 1000 + (out_time - sec * rate) * 1000 / rate; -} - -blargg_err_t Music_Emu::seek( long msec ) -{ - blargg_long time = msec_to_samples( msec ); - if ( time < out_time ) - RETURN_ERR( start_track( current_track_ ) ); - return skip( time - out_time ); -} - -blargg_err_t Music_Emu::skip( long count ) -{ - require( current_track() >= 0 ); // start_track() must have been called already - out_time += count; - - // remove from silence and buf first - { - long n = min( count, silence_count ); - silence_count -= n; - count -= n; - - n = min( count, buf_remain ); - buf_remain -= n; - count -= n; - } - - if ( count && !emu_track_ended_ ) - { - emu_time += count; - end_track_if_error( skip_( count ) ); - } - - if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended - track_ended_ |= emu_track_ended_; - - return 0; -} - -blargg_err_t Music_Emu::skip_( long count ) -{ - // for long skip, mute sound - const long threshold = 30000; - if ( count > threshold ) - { - int saved_mute = mute_mask_; - mute_voices( ~0 ); - - while ( count > threshold / 2 && !emu_track_ended_ ) - { - RETURN_ERR( play_( buf_size, buf.begin() ) ); - count -= buf_size; - } - - mute_voices( saved_mute ); - } - - while ( count && !emu_track_ended_ ) - { - long n = buf_size; - if ( n > count ) - n = count; - count -= n; - RETURN_ERR( play_( n, buf.begin() ) ); - } - return 0; -} - -// Fading - -void Music_Emu::set_fade( long start_msec, long length_msec ) -{ - fade_step = sample_rate() * length_msec / (fade_block_size * fade_shift * 1000 / stereo); - fade_start = msec_to_samples( start_msec ); -} - -// unit / pow( 2.0, (double) x / step ) -static int int_log( blargg_long x, int step, int unit ) -{ - int shift = x / step; - int fraction = (x - shift * step) * unit / step; - return ((unit - fraction) + (fraction >> 1)) >> shift; -} - -void Music_Emu::handle_fade( long out_count, sample_t* out ) -{ - for ( int i = 0; i < out_count; i += fade_block_size ) - { - int const shift = 14; - int const unit = 1 << shift; - int gain = int_log( (out_time + i - fade_start) / fade_block_size, - fade_step, unit ); - if ( gain < (unit >> fade_shift) ) - track_ended_ = emu_track_ended_ = true; - - sample_t* io = &out [i]; - for ( int count = min( fade_block_size, out_count - i ); count; --count ) - { - *io = sample_t ((*io * gain) >> shift); - ++io; - } - } -} - -// Silence detection - -void Music_Emu::emu_play( long count, sample_t* out ) -{ - check( current_track_ >= 0 ); - emu_time += count; - if ( current_track_ >= 0 && !emu_track_ended_ ) - end_track_if_error( play_( count, out ) ); - else - memset( out, 0, count * sizeof *out ); -} - -// number of consecutive silent samples at end -static long count_silence( Music_Emu::sample_t* begin, long size ) -{ - Music_Emu::sample_t first = *begin; - *begin = silence_threshold; // sentinel - Music_Emu::sample_t* p = begin + size; - while ( (unsigned) (*--p + silence_threshold / 2) <= (unsigned) silence_threshold ) { } - *begin = first; - return size - (p - begin); -} - -// fill internal buffer and check it for silence -void Music_Emu::fill_buf() -{ - assert( !buf_remain ); - if ( !emu_track_ended_ ) - { - emu_play( buf_size, buf.begin() ); - long silence = count_silence( buf.begin(), buf_size ); - if ( silence < buf_size ) - { - silence_time = emu_time - silence; - buf_remain = buf_size; - return; - } - } - silence_count += buf_size; -} - -blargg_err_t Music_Emu::play( long out_count, sample_t* out ) -{ - if ( track_ended_ ) - { - memset( out, 0, out_count * sizeof *out ); - } - else - { - require( current_track() >= 0 ); - require( out_count % stereo == 0 ); - - assert( emu_time >= out_time ); - - // prints nifty graph of how far ahead we are when searching for silence - //dprintf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); - - long pos = 0; - if ( silence_count ) - { - // during a run of silence, run emulator at >=2x speed so it gets ahead - long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time; - while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) ) - fill_buf(); - - // fill with silence - pos = min( silence_count, out_count ); - memset( out, 0, pos * sizeof *out ); - silence_count -= pos; - - if ( emu_time - silence_time > silence_max * stereo * sample_rate() ) - { - track_ended_ = emu_track_ended_ = true; - silence_count = 0; - buf_remain = 0; - } - } - - if ( buf_remain ) - { - // empty silence buf - long n = min( buf_remain, out_count - pos ); - memcpy( &out [pos], buf.begin() + (buf_size - buf_remain), n * sizeof *out ); - buf_remain -= n; - pos += n; - } - - // generate remaining samples normally - long remain = out_count - pos; - if ( remain ) - { - emu_play( remain, out + pos ); - track_ended_ |= emu_track_ended_; - - if ( !ignore_silence_ || out_time > fade_start ) - { - // check end for a new run of silence - long silence = count_silence( out + pos, remain ); - if ( silence < remain ) - silence_time = emu_time - silence; - - if ( emu_time - silence_time >= buf_size ) - fill_buf(); // cause silence detection on next play() - } - } - - if ( out_time > fade_start ) - handle_fade( out_count, out ); - } - out_time += out_count; - return 0; -} - -// Gme_Info_ - -blargg_err_t Gme_Info_::set_sample_rate_( long ) { return 0; } -void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu -void Gme_Info_::post_load_() { Gme_File::post_load_(); } // skip Music_Emu -void Gme_Info_::set_equalizer_( equalizer_t const& ){ check( false ); } -void Gme_Info_::mute_voices_( int ) { check( false ); } -void Gme_Info_::set_tempo_( double ) { } -blargg_err_t Gme_Info_::start_track_( int ) { return "Use full emulator for playback"; } -blargg_err_t Gme_Info_::play_( long, sample_t* ) { return "Use full emulator for playback"; } + current_track_ = -1; + warning(); // clear warning + track_filter.stop(); +} + +void Music_Emu::unload() +{ + voice_count_ = 0; + clear_track_vars(); + Gme_File::unload(); +} + +Music_Emu::gme_t() +{ + effects_buffer_ = NULL; + sample_rate_ = 0; + mute_mask_ = 0; + tempo_ = 1.0; + gain_ = 1.0; + + // defaults + tfilter = track_filter.setup(); + set_max_initial_silence( 15 ); + set_silence_lookahead( 3 ); + ignore_silence( false ); + + equalizer_.treble = -1.0; + equalizer_.bass = 60; + + static const char* const names [] = { + "Voice 1", "Voice 2", "Voice 3", "Voice 4", + "Voice 5", "Voice 6", "Voice 7", "Voice 8" + }; + set_voice_names( names ); + Music_Emu::unload(); // clears fields +} + +Music_Emu::~gme_t() +{ + assert( !effects_buffer_ ); +} + +blargg_err_t Music_Emu::set_sample_rate( int rate ) +{ + require( !sample_rate() ); // sample rate can't be changed once set + RETURN_ERR( set_sample_rate_( rate ) ); + RETURN_ERR( track_filter.init( this ) ); + sample_rate_ = rate; + tfilter.max_silence = 6 * stereo * sample_rate(); + return blargg_ok; +} + +void Music_Emu::pre_load() +{ + require( sample_rate() ); // set_sample_rate() must be called before loading a file + Gme_File::pre_load(); +} + +void Music_Emu::set_equalizer( equalizer_t const& eq ) +{ + // TODO: why is GCC generating memcpy call here? + // Without the 'if', valgrind flags it. + if ( &eq != &equalizer_ ) + equalizer_ = eq; + set_equalizer_( eq ); +} + +void Music_Emu::mute_voice( int index, bool mute ) +{ + require( (unsigned) index < (unsigned) voice_count() ); + int bit = 1 << index; + int mask = mute_mask_ | bit; + if ( !mute ) + mask ^= bit; + mute_voices( mask ); +} + +void Music_Emu::mute_voices( int mask ) +{ + require( sample_rate() ); // sample rate must be set first + mute_mask_ = mask; + mute_voices_( mask ); +} + +const char* Music_Emu::voice_name( int i ) const +{ + if ( (unsigned) i < (unsigned) voice_count_ ) + return voice_names_ [i]; + + //check( false ); // TODO: enable? + return ""; +} + +void Music_Emu::set_tempo( double t ) +{ + require( sample_rate() ); // sample rate must be set first + double const min = 0.02; + double const max = 4.00; + if ( t < min ) t = min; + if ( t > max ) t = max; + tempo_ = t; + set_tempo_( t ); +} + +blargg_err_t Music_Emu::post_load() +{ + set_tempo( tempo_ ); + remute_voices(); + return Gme_File::post_load(); +} + +// Tell/Seek + +int Music_Emu::msec_to_samples( int msec ) const +{ + int sec = msec / 1000; + msec -= sec * 1000; + return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo; +} + +int Music_Emu::tell() const +{ + int rate = sample_rate() * stereo; + int sec = track_filter.sample_count() / rate; + return sec * 1000 + (track_filter.sample_count() - sec * rate) * 1000 / rate; +} + +blargg_err_t Music_Emu::seek( int msec ) +{ + int time = msec_to_samples( msec ); + if ( time < track_filter.sample_count() ) + RETURN_ERR( start_track( current_track_ ) ); + return skip( time - track_filter.sample_count() ); +} + +blargg_err_t Music_Emu::skip( int count ) +{ + require( current_track() >= 0 ); // start_track() must have been called already + return track_filter.skip( count ); +} + +blargg_err_t Music_Emu::skip_( int count ) +{ + // for long skip, mute sound + const int threshold = 32768; + if ( count > threshold ) + { + int saved_mute = mute_mask_; + mute_voices( ~0 ); + + int n = count - threshold/2; + n &= ~(2048-1); // round to multiple of 2048 + count -= n; + RETURN_ERR( track_filter.skip_( n ) ); + + mute_voices( saved_mute ); + } + + return track_filter.skip_( count ); +} + +// Playback + +blargg_err_t Music_Emu::start_track( int track ) +{ + clear_track_vars(); + + int remapped = track; + RETURN_ERR( remap_track_( &remapped ) ); + current_track_ = track; + blargg_err_t err = start_track_( remapped ); + if ( err ) + { + current_track_ = -1; + return err; + } + + // convert filter times to samples + Track_Filter::setup_t s = tfilter; + s.max_initial *= sample_rate() * stereo; + #if GME_DISABLE_SILENCE_LOOKAHEAD + s.lookahead = 1; + #endif + track_filter.setup( s ); + + return track_filter.start_track(); +} + +void Music_Emu::set_fade( int start_msec, int length_msec ) +{ + track_filter.set_fade( msec_to_samples( start_msec ), + length_msec * sample_rate() / (1000 / stereo) ); +} + +blargg_err_t Music_Emu::play( int out_count, sample_t out [] ) +{ + require( current_track() >= 0 ); + require( out_count % stereo == 0 ); + + return track_filter.play( out_count, out ); +} + +// Gme_Info_ + +blargg_err_t Gme_Info_::set_sample_rate_( int ) { return blargg_ok; } +void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu +blargg_err_t Gme_Info_::post_load() { return Gme_File::post_load(); } // skip Music_Emu +void Gme_Info_::set_equalizer_( equalizer_t const& ){ check( false ); } +void Gme_Info_::mute_voices_( int ) { check( false ); } +void Gme_Info_::set_tempo_( double ) { } +blargg_err_t Gme_Info_::start_track_( int ) { return BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); } +blargg_err_t Gme_Info_::play_( int, sample_t [] ) { return BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); } diff --git a/Frameworks/GME/gme/Music_Emu.h b/Frameworks/GME/gme/Music_Emu.h old mode 100755 new mode 100644 index 573403ced..32e32a9c6 --- a/Frameworks/GME/gme/Music_Emu.h +++ b/Frameworks/GME/gme/Music_Emu.h @@ -1,211 +1,252 @@ -// Common interface to game music file emulators - -// Game_Music_Emu 0.5.2 -#ifndef MUSIC_EMU_H -#define MUSIC_EMU_H - -#include "Gme_File.h" -class Multi_Buffer; - -struct Music_Emu : public Gme_File { -public: -// Basic functionality (see Gme_File.h for file loading/track info functions) - - // Set output sample rate. Must be called only once before loading file. - blargg_err_t set_sample_rate( long sample_rate ); - - // Start a track, where 0 is the first track. Also clears warning string. - blargg_err_t start_track( int ); - - // Generate 'count' samples info 'buf'. Output is in stereo. Any emulation - // errors set warning string, and major errors also end track. - typedef short sample_t; - blargg_err_t play( long count, sample_t* buf ); - -// Informational - - // Sample rate sound is generated at - long sample_rate() const; - - // Index of current track or -1 if one hasn't been started - int current_track() const; - - // Number of voices used by currently loaded file - int voice_count() const; - - // Names of voices - const char** voice_names() const; - -// Track status/control - - // Number of milliseconds (1000 msec = 1 second) played since beginning of track - long tell() const; - - // Seek to new time in track. Seeking backwards or far forward can take a while. - blargg_err_t seek( long msec ); - - // Skip n samples - blargg_err_t skip( long n ); - - // True if a track has reached its end - bool track_ended() const; - - // Set start time and length of track fade out. Once fade ends track_ended() returns - // true. Fade time can be changed while track is playing. - void set_fade( long start_msec, long length_msec = 8000 ); - - // Disable automatic end-of-track detection and skipping of silence at beginning - void ignore_silence( bool disable = true ); - - // Info for current track - Gme_File::track_info; - blargg_err_t track_info( track_info_t* out ) const; - -// Sound customization - - // Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. - // Track length as returned by track_info() assumes a tempo of 1.0. - void set_tempo( double ); - - // Mute/unmute voice i, where voice 0 is first voice - void mute_voice( int index, bool mute = true ); - - // Set muting state of all voices at once using a bit mask, where -1 mutes them all, - // 0 unmutes them all, 0x01 mutes just the first voice, etc. - void mute_voices( int mask ); - - // Change overall output amplitude, where 1.0 results in minimal clamping. - // Must be called before set_sample_rate(). - void set_gain( double ); - - // Request use of custom multichannel buffer. Only supported by "classic" emulators; - // on others this has no effect. Should be called only once *before* set_sample_rate(). - virtual void set_buffer( Multi_Buffer* ) { } - -// Sound equalization (treble/bass) - - // Frequency equalizer parameters (see gme.txt) - // See gme.h for definition of struct gme_equalizer_t. - typedef gme_equalizer_t equalizer_t; - - // Current frequency equalizater parameters - equalizer_t const& equalizer() const; - - // Set frequency equalizer parameters - void set_equalizer( equalizer_t const& ); - - // Equalizer settings for TV speaker - static equalizer_t const tv_eq; - -public: - Music_Emu(); - ~Music_Emu(); -protected: - void set_max_initial_silence( int n ) { max_initial_silence = n; } - void set_silence_lookahead( int n ) { silence_lookahead = n; } - void set_voice_count( int n ) { voice_count_ = n; } - void set_voice_names( const char* const* names ); - void set_track_ended() { emu_track_ended_ = true; } - double gain() const { return gain_; } - double tempo() const { return tempo_; } - void remute_voices(); - - virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; - virtual void set_equalizer_( equalizer_t const& ) { }; - virtual void mute_voices_( int mask ) = 0; - virtual void set_tempo_( double ) = 0; - virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this - virtual blargg_err_t play_( long count, sample_t* out ) = 0; - virtual blargg_err_t skip_( long count ); -protected: - virtual void unload(); - virtual void pre_load(); - virtual void post_load_(); -private: - // general - equalizer_t equalizer_; - int max_initial_silence; - const char** voice_names_; - int voice_count_; - int mute_mask_; - double tempo_; - double gain_; - - long sample_rate_; - blargg_long msec_to_samples( blargg_long msec ) const; - - // track-specific - int current_track_; - blargg_long out_time; // number of samples played since start of track - blargg_long emu_time; // number of samples emulator has generated since start of track - bool emu_track_ended_; // emulator has reached end of track - volatile bool track_ended_; - void clear_track_vars(); - void end_track_if_error( blargg_err_t ); - - // fading - blargg_long fade_start; - int fade_step; - void handle_fade( long count, sample_t* out ); - - // silence detection - int silence_lookahead; // speed to run emulator when looking ahead for silence - bool ignore_silence_; - long silence_time; // number of samples where most recent silence began - long silence_count; // number of samples of silence to play before using buf - long buf_remain; // number of samples left in silence buffer - enum { buf_size = 2048 }; - blargg_vector buf; - void fill_buf(); - void emu_play( long count, sample_t* out ); - - Multi_Buffer* effects_buffer; - friend Music_Emu* gme_new_emu( gme_type_t, long ); - friend void gme_set_stereo_depth( Music_Emu*, double ); -}; - -// base class for info-only derivations -struct Gme_Info_ : Music_Emu -{ - virtual blargg_err_t set_sample_rate_( long sample_rate ); - virtual void set_equalizer_( equalizer_t const& ); - virtual void mute_voices_( int mask ); - virtual void set_tempo_( double ); - virtual blargg_err_t start_track_( int ); - virtual blargg_err_t play_( long count, sample_t* out ); - virtual void pre_load(); - virtual void post_load_(); -}; - -inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const -{ - return track_info( out, current_track_ ); -} - -inline long Music_Emu::sample_rate() const { return sample_rate_; } -inline const char** Music_Emu::voice_names() const { return voice_names_; } -inline int Music_Emu::voice_count() const { return voice_count_; } -inline int Music_Emu::current_track() const { return current_track_; } -inline bool Music_Emu::track_ended() const { return track_ended_; } -inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } - -inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; } -inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } -inline void Music_Emu::ignore_silence( bool b ) { ignore_silence_ = b; } -inline blargg_err_t Music_Emu::start_track_( int ) { return 0; } - -inline void Music_Emu::set_voice_names( const char* const* names ) -{ - // Intentional removal of const, so users don't have to remember obscure const in middle - voice_names_ = (const char**) names; -} - -inline void Music_Emu::mute_voices_( int ) { } - -inline void Music_Emu::set_gain( double g ) -{ - assert( !sample_rate() ); // you must set gain before setting sample rate - gain_ = g; -} - -#endif +// Common interface to game music file emulators + +// Game_Music_Emu $vers +#ifndef MUSIC_EMU_H +#define MUSIC_EMU_H + +#include "Gme_File.h" +#include "Track_Filter.h" +#include "blargg_errors.h" +class Multi_Buffer; + +struct gme_t : public Gme_File, private Track_Filter::callbacks_t { +public: + // Sets output sample rate. Must be called only once before loading file. + blargg_err_t set_sample_rate( int sample_rate ); + + // Sample rate sound is generated at + int sample_rate() const; + +// File loading + + // See Gme_Loader.h + +// Basic playback + + // Starts a track, where 0 is the first track. Also clears warning string. + blargg_err_t start_track( int ); + + // Generates 'count' samples info 'buf'. Output is in stereo. Any emulation + // errors set warning string, and major errors also end track. + typedef short sample_t; + blargg_err_t play( int count, sample_t* buf ); + +// Track information + + // See Gme_File.h + + // Index of current track or -1 if one hasn't been started + int current_track() const; + + // Info for currently playing track + using Gme_File::track_info; + blargg_err_t track_info( track_info_t* out ) const; + + struct Hash_Function + { + virtual void hash_( byte const* data, size_t size ) BLARGG_PURE( ; ) + }; + virtual blargg_err_t hash_( Hash_Function& ) const BLARGG_PURE( ; ) + +// Track status/control + + // Number of milliseconds played since beginning of track (1000 per second) + int tell() const; + + // Seeks to new time in track. Seeking backwards or far forward can take a while. + blargg_err_t seek( int msec ); + + // Skips n samples + blargg_err_t skip( int n ); + + // True if a track has reached its end + bool track_ended() const; + + // Sets start time and length of track fade out. Once fade ends track_ended() returns + // true. Fade time must be set after track has been started, and can be changed + // at any time. + void set_fade( int start_msec, int length_msec = 8000 ); + + // Disables automatic end-of-track detection and skipping of silence at beginning + void ignore_silence( bool disable = true ); + +// Voices + + // Number of voices used by currently loaded file + int voice_count() const; + + // Name of voice i, from 0 to voice_count()-1 + const char* voice_name( int i ) const; + + // Mutes/unmutes voice i, where voice 0 is first voice + void mute_voice( int index, bool mute = true ); + + // Sets muting state of all voices at once using a bit mask, where -1 mutes them all, + // 0 unmutes them all, 0x01 mutes just the first voice, etc. + void mute_voices( int mask ); + +// Sound customization + + // Adjusts song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. + // Track length as returned by track_info() assumes a tempo of 1.0. + void set_tempo( double ); + + // Changes overall output amplitude, where 1.0 results in minimal clamping. + // Must be called before set_sample_rate(). + void set_gain( double ); + + // Requests use of custom multichannel buffer. Only supported by "classic" emulators; + // on others this has no effect. Should be called only once *before* set_sample_rate(). + virtual void set_buffer( class Multi_Buffer* ) { } + +// Sound equalization (treble/bass) + + // Frequency equalizer parameters (see gme.txt) + // See gme.h for definition of struct gme_equalizer_t. + typedef gme_equalizer_t equalizer_t; + + // Current frequency equalizater parameters + equalizer_t const& equalizer() const; + + // Sets frequency equalizer parameters + void set_equalizer( equalizer_t const& ); + + // Equalizer preset for a TV speaker + static equalizer_t const tv_eq; + +// Derived interface +protected: + // Cause any further generated samples to be silence, instead of calling play_() + void set_track_ended() { track_filter.set_track_ended(); } + + // If more than secs of silence are encountered, track is ended + void set_max_initial_silence( int secs ) { tfilter.max_initial = secs; } + + // Sets rate emulator is run at when scanning ahead for silence. 1=100%, 2=200% etc. + void set_silence_lookahead( int rate ) { tfilter.lookahead = rate; } + + // Sets number of voices + void set_voice_count( int n ) { voice_count_ = n; } + + // Sets names of voices + void set_voice_names( const char* const names [] ); + + // Current gain + double gain() const { return gain_; } + + // Current tempo + double tempo() const { return tempo_; } + + // Re-applies muting mask using mute_voices_() + void remute_voices(); + +// Overrides should do the indicated task + + // Set sample rate as close as possible to sample_rate, then call + // Music_Emu::set_sample_rate_() with the actual rate used. + virtual blargg_err_t set_sample_rate_( int sample_rate ) BLARGG_PURE( ; ) + + // Set equalizer parameters + virtual void set_equalizer_( equalizer_t const& ) { } + + // Mute voices based on mask + virtual void mute_voices_( int mask ) BLARGG_PURE( ; ) + + // Set tempo to t, which is constrained to the range 0.02 to 4.0. + virtual void set_tempo_( double t ) BLARGG_PURE( ; ) + + // Start track t, where 0 is the first track + virtual blargg_err_t start_track_( int t ) BLARGG_PURE( ; ) // tempo is set before this + + // Generate count samples into *out. Count will always be even. + virtual blargg_err_t play_( int count, sample_t out [] ) BLARGG_PURE( ; ) + + // Skip count samples. Count will always be even. + virtual blargg_err_t skip_( int count ); + + +// Implementation +public: + gme_t(); + ~gme_t(); + BLARGG_DEPRECATED( const char** voice_names() const { return CONST_CAST(const char**,voice_names_); } ) + +protected: + virtual void unload(); + virtual void pre_load(); + virtual blargg_err_t post_load(); + +private: + Track_Filter::setup_t tfilter; + Track_Filter track_filter; + equalizer_t equalizer_; + const char* const* voice_names_; + int voice_count_; + int mute_mask_; + double tempo_; + double gain_; + int sample_rate_; + int current_track_; + + void clear_track_vars(); + int msec_to_samples( int msec ) const; + + friend Music_Emu* gme_new_emu( gme_type_t, int ); + friend void gme_effects( Music_Emu const*, gme_effects_t* ); + friend void gme_set_effects( Music_Emu*, gme_effects_t const* ); + friend void gme_set_stereo_depth( Music_Emu*, double ); + friend const char** gme_voice_names ( Music_Emu const* ); + +protected: + Multi_Buffer* effects_buffer_; +}; + +// base class for info-only derivations +struct Gme_Info_ : Music_Emu +{ + virtual blargg_err_t set_sample_rate_( int sample_rate ); + virtual void set_equalizer_( equalizer_t const& ); + virtual void mute_voices_( int mask ); + virtual void set_tempo_( double ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int count, sample_t out [] ); + virtual void pre_load(); + virtual blargg_err_t post_load(); +}; + +inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const +{ + return track_info( out, current_track_ ); +} + +inline int Music_Emu::sample_rate() const { return sample_rate_; } +inline int Music_Emu::voice_count() const { return voice_count_; } +inline int Music_Emu::current_track() const { return current_track_; } +inline bool Music_Emu::track_ended() const { return track_filter.track_ended(); } +inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; } + +inline void Music_Emu::ignore_silence( bool b ) { track_filter.ignore_silence( b ); } +inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; } +inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } + +inline void Music_Emu::set_voice_names( const char* const p [] ) { voice_names_ = p; } + +inline void Music_Emu::mute_voices_( int ) { } + +inline void Music_Emu::set_gain( double g ) +{ + assert( !sample_rate() ); // you must set gain before setting sample rate + gain_ = g; +} + +inline blargg_err_t Music_Emu::start_track_( int ) { return blargg_ok; } + +inline blargg_err_t Music_Emu::set_sample_rate_( int ) { return blargg_ok; } + +inline blargg_err_t Music_Emu::play_( int, sample_t [] ) { return blargg_ok; } + +inline blargg_err_t Music_Emu::hash_( Hash_Function& ) const { return BLARGG_ERR( BLARGG_ERR_CALLER, "no hashing function defined" ); } + +inline void Music_Emu::Hash_Function::hash_( byte const*, size_t ) { } + +#endif diff --git a/Frameworks/GME/gme/Nes_Apu.cpp b/Frameworks/GME/gme/Nes_Apu.cpp old mode 100755 new mode 100644 index 8daf5d0e1..1862846ea --- a/Frameworks/GME/gme/Nes_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Apu.cpp @@ -1,8 +1,8 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Nes_Apu.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -23,8 +23,6 @@ Nes_Apu::Nes_Apu() : { tempo_ = 1.0; dmc.apu = this; - dmc.prg_reader = NULL; - irq_notifier_ = NULL; oscs [0] = &square1; oscs [1] = &square2; @@ -32,28 +30,28 @@ Nes_Apu::Nes_Apu() : oscs [3] = &noise; oscs [4] = &dmc; - output( NULL ); + set_output( NULL ); + dmc.nonlinear = false; volume( 1.0 ); reset( false ); } void Nes_Apu::treble_eq( const blip_eq_t& eq ) { - square_synth.treble_eq( eq ); + square_synth .treble_eq( eq ); triangle.synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); - dmc.synth.treble_eq( eq ); + noise .synth.treble_eq( eq ); + dmc .synth.treble_eq( eq ); } -void Nes_Apu::enable_nonlinear( double v ) +void Nes_Apu::enable_nonlinear_( double sq, double tnd ) { dmc.nonlinear = true; - square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); + square_synth.volume( sq ); - const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); - triangle.synth.volume( 3.0 * tnd ); - noise.synth.volume( 2.0 * tnd ); - dmc.synth.volume( tnd ); + triangle.synth.volume( tnd * 2.752 ); + noise .synth.volume( tnd * 1.849 ); + dmc .synth.volume( tnd ); square1 .last_amp = 0; square2 .last_amp = 0; @@ -64,17 +62,20 @@ void Nes_Apu::enable_nonlinear( double v ) void Nes_Apu::volume( double v ) { - dmc.nonlinear = false; - square_synth.volume( 0.1128 / amp_range * v ); - triangle.synth.volume( 0.12765 / amp_range * v ); - noise.synth.volume( 0.0741 / amp_range * v ); - dmc.synth.volume( 0.42545 / 127 * v ); + if ( !dmc.nonlinear ) + { + v *= 1.0 / 1.11; // TODO: merge into values below + square_synth .volume( 0.125 / amp_range * v ); // was 0.1128 1.108 + triangle.synth.volume( 0.150 / amp_range * v ); // was 0.12765 1.175 + noise .synth.volume( 0.095 / amp_range * v ); // was 0.0741 1.282 + dmc .synth.volume( 0.450 / 2048 * v ); // was 0.42545 1.058 + } } -void Nes_Apu::output( Blip_Buffer* buffer ) +void Nes_Apu::set_output( Blip_Buffer* buffer ) { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buffer ); + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buffer ); } void Nes_Apu::set_tempo( double t ) @@ -100,12 +101,13 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) last_dmc_time = 0; osc_enables = 0; irq_flag = false; + enable_w4011 = true; earliest_irq_ = no_irq; frame_delay = 1; write_register( 0, 0x4017, 0x00 ); write_register( 0, 0x4015, 0x00 ); - for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) + for ( int addr = io_addr; addr <= 0x4013; addr++ ) write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); dmc.dac = initial_dmc_dac; @@ -117,7 +119,7 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) void Nes_Apu::irq_changed() { - nes_time_t new_irq = dmc.next_irq; + blip_time_t new_irq = dmc.next_irq; if ( dmc.irq_flag | irq_flag ) { new_irq = 0; } @@ -127,25 +129,25 @@ void Nes_Apu::irq_changed() if ( new_irq != earliest_irq_ ) { earliest_irq_ = new_irq; - if ( irq_notifier_ ) - irq_notifier_( irq_data ); + if ( irq_notifier.f ) + irq_notifier.f( irq_notifier.data ); } } // frames -void Nes_Apu::run_until( nes_time_t end_time ) +void Nes_Apu::run_until( blip_time_t end_time ) { require( end_time >= last_dmc_time ); if ( end_time > next_dmc_read_time() ) { - nes_time_t start = last_dmc_time; + blip_time_t start = last_dmc_time; last_dmc_time = end_time; dmc.run( start, end_time ); } } -void Nes_Apu::run_until_( nes_time_t end_time ) +void Nes_Apu::run_until_( blip_time_t end_time ) { require( end_time >= last_time ); @@ -154,7 +156,7 @@ void Nes_Apu::run_until_( nes_time_t end_time ) if ( last_dmc_time < end_time ) { - nes_time_t start = last_dmc_time; + blip_time_t start = last_dmc_time; last_dmc_time = end_time; dmc.run( start, end_time ); } @@ -162,7 +164,7 @@ void Nes_Apu::run_until_( nes_time_t end_time ) while ( true ) { // earlier of next frame time or end time - nes_time_t time = last_time + frame_delay; + blip_time_t time = last_time + frame_delay; if ( time > end_time ) time = end_time; frame_delay -= time - last_time; @@ -226,7 +228,7 @@ void Nes_Apu::run_until_( nes_time_t end_time ) } template -inline void zero_apu_osc( T* osc, nes_time_t time ) +inline void zero_apu_osc( T* osc, blip_time_t time ) { Blip_Buffer* output = osc->output; int last_amp = osc->last_amp; @@ -235,7 +237,7 @@ inline void zero_apu_osc( T* osc, nes_time_t time ) osc->synth.offset( time, -last_amp, output ); } -void Nes_Apu::end_frame( nes_time_t end_time ) +void Nes_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until_( end_time ); @@ -280,13 +282,13 @@ static const unsigned char length_table [0x20] = { 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E }; -void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) +void Nes_Apu::write_register( blip_time_t time, int addr, int data ) { require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) require( (unsigned) data <= 0xFF ); // Ignore addresses outside range - if ( unsigned (addr - start_addr) > end_addr - start_addr ) + if ( unsigned (addr - io_addr) >= io_size ) return; run_until_( time ); @@ -294,7 +296,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) if ( addr < 0x4014 ) { // Write to channel - int osc_index = (addr - start_addr) >> 2; + int osc_index = (addr - io_addr) >> 2; Nes_Osc* osc = oscs [osc_index]; int reg = addr & 3; @@ -304,7 +306,8 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) if ( osc_index == 4 ) { // handle DMC specially - dmc.write_register( reg, data ); + if ( enable_w4011 || reg != 1 ) + dmc.write_register( reg, data ); } else if ( reg == 3 ) { @@ -366,7 +369,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) } } -int Nes_Apu::read_status( nes_time_t time ) +int Nes_Apu::read_status( blip_time_t time ) { run_until_( time - 1 ); diff --git a/Frameworks/GME/gme/Nes_Apu.h b/Frameworks/GME/gme/Nes_Apu.h old mode 100755 new mode 100644 index dbd8484c0..ed6374685 --- a/Frameworks/GME/gme/Nes_Apu.h +++ b/Frameworks/GME/gme/Nes_Apu.h @@ -1,14 +1,10 @@ // NES 2A03 APU sound chip emulator -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_APU_H #define NES_APU_H #include "blargg_common.h" - -typedef blargg_long nes_time_t; // CPU clock cycle count -typedef unsigned nes_addr_t; // 16-bit memory address - #include "Nes_Oscs.h" struct apu_state_t; @@ -16,73 +12,76 @@ class Nes_Buffer; class Nes_Apu { public: - // Set buffer to generate all sound into, or disable sound if NULL - void output( Blip_Buffer* ); +// Basics + + typedef int nes_time_t; // NES CPU clock cycle count - // Set memory reader callback used by DMC oscillator to fetch samples. + // Sets memory reader callback used by DMC oscillator to fetch samples. // When callback is invoked, 'user_data' is passed unchanged as the // first parameter. - void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); + //void dmc_reader( int (*callback)( void* user_data, int addr ), void* user_data = NULL ); + + // Sets buffer to generate sound into, or 0 to mute output (reduces + // emulation accuracy). + void set_output( Blip_Buffer* ); // All time values are the number of CPU clock cycles relative to the // beginning of the current time frame. Before resetting the CPU clock // count, call end_frame( last_cpu_time ). - // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; - void write_register( nes_time_t, nes_addr_t, int data ); + // Writes to register (0x4000-0x4013, and 0x4015 and 0x4017) + enum { io_addr = 0x4000 }; + enum { io_size = 0x18 }; + void write_register( nes_time_t, int addr, int data ); - // Read from status register at 0x4015 + // Reads from status register (0x4015) enum { status_addr = 0x4015 }; int read_status( nes_time_t ); - // Run all oscillators up to specified time, end current time frame, then - // start a new time frame at time 0. Time frames have no effect on emulation + // Runs all oscillators up to specified time, ends current time frame, then + // starts a new time frame at time 0. Time frames have no effect on emulation // and each can be whatever length is convenient. void end_frame( nes_time_t ); -// Additional optional features (can be ignored without any problem) +// Optional - // Reset internal frame counter, registers, and all oscillators. - // Use PAL timing if pal_timing is true, otherwise use NTSC timing. - // Set the DMC oscillator's initial DAC value to initial_dmc_dac without + // Resets internal frame counter, registers, and all oscillators. + // Uses PAL timing if pal_timing is true, otherwise use NTSC timing. + // Sets the DMC oscillator's initial DAC value to initial_dmc_dac without // any audible click. void reset( bool pal_mode = false, int initial_dmc_dac = 0 ); - // Adjust frame period + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Triangle, 3: Noise, 4: DMC + enum { osc_count = 5 }; + void set_output( int chan, Blip_Buffer* buf ); + + // Adjusts frame period void set_tempo( double ); - // Save/load exact emulation state + // Saves/loads exact emulation state void save_state( apu_state_t* out ) const; void load_state( apu_state_t const& ); - // Set overall volume (default is 1.0) + // Sets overall volume (default is 1.0) void volume( double ); - // Set treble equalization (see notes.txt) + // Sets treble equalization (see notes.txt) void treble_eq( const blip_eq_t& ); - // Set sound output of specific oscillator to buffer. If buffer is NULL, - // the specified oscillator is muted and emulation accuracy is reduced. - // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, - // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; - void osc_output( int index, Blip_Buffer* buffer ); - - // Set IRQ time callback that is invoked when the time of earliest IRQ + // Sets IRQ time callback that is invoked when the time of earliest IRQ // may have changed, or NULL to disable. When callback is invoked, // 'user_data' is passed unchanged as the first parameter. - void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); + //void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - // Get time that APU-generated IRQ will occur if no further register reads + // Gets time that APU-generated IRQ will occur if no further register reads // or writes occur. If IRQ is already pending, returns irq_waiting. If no // IRQ will occur, returns no_irq. - enum { no_irq = LONG_MAX / 2 + 1 }; + enum { no_irq = INT_MAX/2 + 1 }; enum { irq_waiting = 0 }; nes_time_t earliest_irq( nes_time_t ) const; - // Count number of DMC reads that would occur if 'run_until( t )' were executed. + // Counts number of DMC reads that would occur if 'run_until( t )' were executed. // If last_read is not NULL, set *last_read to the earliest time that // 'count_dmc_reads( time )' would result in the same result. int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; @@ -90,17 +89,30 @@ public: // Time when next DMC memory read will occur nes_time_t next_dmc_read_time() const; - // Run DMC until specified time, so that any DMC memory reads can be + // Runs DMC until specified time, so that any DMC memory reads can be // accounted for (i.e. inserting CPU wait states). void run_until( nes_time_t ); + +// Implementation public: Nes_Apu(); BLARGG_DISABLE_NOTHROW -private: - friend class Nes_Nonlinearizer; - void enable_nonlinear( double volume ); - static double nonlinear_tnd_gain() { return 0.75; } + // Use set_output() in place of these + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ); ) + + BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4000 }; ) + BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4017 }; ) + + blargg_callback dmc_reader; + blargg_callback irq_notifier; + + void enable_nonlinear_( double sq, double tnd ); + static float tnd_total_() { return 196.015f; } + + void enable_w4011_( bool enable = true ) { enable_w4011 = enable; } + private: friend struct Nes_Dmc; @@ -126,8 +138,7 @@ private: int osc_enables; int frame_mode; bool irq_flag; - void (*irq_notifier_)( void* user_data ); - void* irq_data; + bool enable_w4011; Nes_Square::Synth square_synth; // shared by squares void irq_changed(); @@ -138,42 +149,36 @@ private: friend class Nes_Core; }; -inline void Nes_Apu::osc_output( int osc, Blip_Buffer* buf ) +inline void Nes_Apu::set_output( int osc, Blip_Buffer* buf ) { assert( (unsigned) osc < osc_count ); oscs [osc]->output = buf; } -inline nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const +inline Nes_Apu::nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const { return earliest_irq_; } -inline void Nes_Apu::dmc_reader( int (*func)( void*, nes_addr_t ), void* user_data ) -{ - dmc.prg_reader_data = user_data; - dmc.prg_reader = func; -} - -inline void Nes_Apu::irq_notifier( void (*func)( void* user_data ), void* user_data ) -{ - irq_notifier_ = func; - irq_data = user_data; -} - inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const { return dmc.count_reads( time, last_read ); } -inline nes_time_t Nes_Dmc::next_read_time() const +inline Nes_Apu::nes_time_t Nes_Dmc::next_read_time() const { if ( length_counter == 0 ) return Nes_Apu::no_irq; // not reading - return apu->last_dmc_time + delay + long (bits_remain - 1) * period; + return apu->last_dmc_time + delay + (bits_remain - 1) * period; } -inline nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } +inline Nes_Apu::nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); } + +BLARGG_DEPRECATED( typedef int nes_time_t; ) // use your own typedef +BLARGG_DEPRECATED( typedef unsigned nes_addr_t; ) // use your own typedef + +BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::output ( Blip_Buffer* c ) { set_output( c ); } ) +BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::osc_output( int i, Blip_Buffer* c ) { set_output( i, c ); } ) #endif diff --git a/Frameworks/GME/gme/Nes_Cpu.cpp b/Frameworks/GME/gme/Nes_Cpu.cpp old mode 100755 new mode 100644 index 480b4aa48..96d594177 --- a/Frameworks/GME/gme/Nes_Cpu.cpp +++ b/Frameworks/GME/gme/Nes_Cpu.cpp @@ -1,13 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Nes_Cpu.h" #include "blargg_endian.h" -#include -#define BLARGG_CPU_X86 1 - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -18,1067 +15,48 @@ details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -#define FLUSH_TIME() (void) (s.time = s_time) -#define CACHE_TIME() (void) (s_time = s.time) - -#include "nes_cpu_io.h" - #include "blargg_source.h" -#ifndef CPU_DONE - #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } -#endif - -#ifndef CPU_READ_PPU - #define CPU_READ_PPU( cpu, addr, out, time )\ - {\ - FLUSH_TIME();\ - out = CPU_READ( cpu, addr, time );\ - CACHE_TIME();\ - } -#endif - -#if BLARGG_NONPORTABLE - #define PAGE_OFFSET( addr ) (addr) -#else - #define PAGE_OFFSET( addr ) ((addr) & (page_size - 1)) -#endif - inline void Nes_Cpu::set_code_page( int i, void const* p ) { - state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size ); + byte const* p2 = STATIC_CAST(byte const*,p) - NES_CPU_OFFSET( i * page_size ); + cpu_state->code_map [i] = p2; + cpu_state_.code_map [i] = p2; } -int const st_n = 0x80; -int const st_v = 0x40; -int const st_r = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; +void Nes_Cpu::map_code( addr_t start, int size, void const* data, int mirror_size ) +{ + // address range must begin and end on page boundaries + require( start % page_size == 0 ); + require( size % page_size == 0 ); + require( start + size <= 0x10000 ); + require( mirror_size % page_size == 0 ); + + for ( int offset = 0; offset < size; offset += page_size ) + set_code_page( NES_CPU_PAGE( start + offset ), + STATIC_CAST(char const*,data) + (offset & ((unsigned) mirror_size - 1)) ); +} void Nes_Cpu::reset( void const* unmapped_page ) { - check( state == &state_ ); - state = &state_; - r.status = st_i; + check( cpu_state == &cpu_state_ ); + cpu_state = &cpu_state_; + + r.flags = irq_inhibit_mask; r.sp = 0xFF; r.pc = 0; r.a = 0; r.x = 0; r.y = 0; - state_.time = 0; - state_.base = 0; - irq_time_ = future_nes_time; - end_time_ = future_nes_time; + + cpu_state_.time = 0; + cpu_state_.base = 0; + irq_time_ = future_time; + end_time_ = future_time; error_count_ = 0; - assert( page_size == 0x800 ); // assumes this set_code_page( page_count, unmapped_page ); - map_code( 0x2000, 0xE000, unmapped_page, true ); - map_code( 0x0000, 0x2000, low_mem, true ); + map_code( 0, 0x10000, unmapped_page, page_size ); blargg_verify_byte_order(); } - -void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool mirror ) -{ - // address range must begin and end on page boundaries - require( start % page_size == 0 ); - require( size % page_size == 0 ); - require( start + size <= 0x10000 ); - - unsigned page = start / page_size; - for ( unsigned n = size / page_size; n; --n ) - { - set_code_page( page++, data ); - if ( !mirror ) - data = (char const*) data + page_size; - } -} - -#define TIME (s_time + s.base) -#define READ_LIKELY_PPU( addr, out ) {CPU_READ_PPU( this, (addr), out, TIME );} -#define READ( addr ) CPU_READ( this, (addr), TIME ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} -#define READ_LOW( addr ) (low_mem [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) -#define READ_PROG( addr ) (s.code_map [(addr) >> page_bits] [PAGE_OFFSET( addr )]) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; - -bool Nes_Cpu::run( nes_time_t end_time ) -{ - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - // even on x86, using s.time in place of s_time was slower - fint16 s_time = s.time; - - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; - SET_SP( r.sp ); - - // status flags - #define IS_NEG (nz & 0x8080) - - #define CALC_STATUS( out ) do {\ - out = status & (st_v | st_d | st_i);\ - out |= ((nz >> 8) | nz) & st_n;\ - out |= c >> 8 & st_c;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & (st_v | st_d | st_i);\ - nz = in << 8;\ - c = nz;\ - nz |= ~in & st_z;\ - } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 - { - fuint8 temp = r.status; - SET_STATUS( temp ); - } - - goto loop; -dec_clock_loop: - s_time--; -loop: - - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) pc < 0x10000 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - check( -32768 <= s_time && s_time < 32767 ); - - uint8_t const* instr = s.code_map [pc >> page_bits]; - fuint8 opcode; - - // TODO: eliminate this special case - #if BLARGG_NONPORTABLE - opcode = instr [pc]; - pc++; - instr += pc; - #else - instr += PAGE_OFFSET( pc ); - opcode = *instr++; - pc++; - #endif - - static uint8_t const clock_table [256] = - {// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 - 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 - 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 - 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 - 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A - 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E - 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F - }; // 0x00 was 7 and 0xF2 was 2 - - fuint16 data; - -#if !BLARGG_CPU_X86 - if ( s_time >= 0 ) - goto out_of_time; - s_time += clock_table [opcode]; - - data = *instr; - - switch ( opcode ) - { -#else - - data = clock_table [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = *instr; - - switch ( opcode ) - { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; -#endif - -// Macros - -#define GET_MSB() (instr [1]) -#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) -#define GET_ADDR() GET_LE16( instr ) - -#define NO_PAGE_CROSSING( lsb ) -#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; - -#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - -#define IND_Y( cross, out ) {\ - fuint16 temp = READ_LOW( data ) + y;\ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - cross( temp );\ - } - -#define IND_X( out ) {\ - fuint16 temp = data + x;\ - out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ - } - -#define ARITH_ADDR_MODES( op )\ -case op - 0x04: /* (ind,x) */\ - IND_X( data )\ - goto ptr##op;\ -case op + 0x0C: /* (ind),y */\ - IND_Y( HANDLE_PAGE_CROSSING, data )\ - goto ptr##op;\ -case op + 0x10: /* zp,X */\ - data = uint8_t (data + x);\ -case op + 0x00: /* zp */\ - data = READ_LOW( data );\ - goto imm##op;\ -case op + 0x14: /* abs,Y */\ - data += y;\ - goto ind##op;\ -case op + 0x18: /* abs,X */\ - data += x;\ -ind##op:\ - HANDLE_PAGE_CROSSING( data );\ -case op + 0x08: /* abs */\ - ADD_PAGE();\ -ptr##op:\ - FLUSH_TIME();\ - data = READ( data );\ - CACHE_TIME();\ -case op + 0x04: /* imm */\ -imm##op: - -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ -{\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop;\ - pc = BOOST::uint16_t (pc + offset);\ - s_time += extra_clock >> 8 & 1;\ - goto loop;\ -} - -// Often-Used - - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (data + x) ); - pc++; - goto loop; - - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - fuint16 temp = pc + 1; - pc = GET_ADDR(); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x4C: // JMP abs - pc = GET_ADDR(); - goto loop; - - case 0xE8: // INX - INC_DEC_XY( x, 1 ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ); - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xC8: // INY - INC_DEC_XY( y, 1 ) - - case 0xA8: // TAY - y = a; - nz = a; - goto loop; - - case 0x98: // TYA - a = y; - nz = y; - goto loop; - - case 0xAD:{// LDA abs - unsigned addr = GET_ADDR(); - pc += 2; - READ_LIKELY_PPU( addr, nz ); - a = nz; - goto loop; - } - - case 0x60: // RTS - pc = 1 + READ_LOW( sp ); - pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); - sp = (sp - 0xFE) | 0x100; - goto loop; - - { - fuint16 addr; - - case 0x99: // STA abs,Y - addr = y + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x8D: // STA abs - addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x9D: // STA abs,X (slightly more common than STA abs) - addr = x + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - sta_ptr: - FLUSH_TIME(); - WRITE( addr, a ); - CACHE_TIME(); - goto loop; - - case 0x91: // STA (ind),Y - IND_Y( NO_PAGE_CROSSING, addr ) - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - IND_X( addr ) - pc++; - goto sta_ptr; - - } - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - - // common read instructions - { - fuint16 addr; - - case 0xA1: // LDA (ind,X) - IND_X( addr ) - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - HANDLE_PAGE_CROSSING( addr ); - addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); - pc++; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - HANDLE_PAGE_CROSSING( data + y ); - addr = GET_ADDR() + y; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xBD: // LDA abs,X - HANDLE_PAGE_CROSSING( data + x ); - addr = GET_ADDR() + x; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - a_nz_read_addr: - FLUSH_TIME(); - a = nz = READ( addr ); - CACHE_TIME(); - goto loop; - - } - -// Branch - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - -// Load/store - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (data + y); - case 0xA6: // LDX zp - data = READ_LOW( data ); - case 0xA2: // LDX #imm - pc++; - x = data; - nz = data; - goto loop; - - case 0xB4: // LDY zp,x - data = uint8_t (data + x); - case 0xA4: // LDY zp - data = READ_LOW( data ); - case 0xA0: // LDY #imm - pc++; - y = data; - nz = data; - goto loop; - - case 0xBC: // LDY abs,X - data += x; - HANDLE_PAGE_CROSSING( data ); - case 0xAC:{// LDY abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xBE: // LDX abs,y - data += y; - HANDLE_PAGE_CROSSING( data ); - case 0xAE:{// LDX abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - x = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - unsigned addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, temp ); - goto loop; - } - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( addr ); - CACHE_TIME(); - goto cpx_data; - } - - case 0xE4: // CPX zp - data = READ_LOW( data ); - case 0xE0: // CPX #imm - cpx_data: - nz = x - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( addr ); - CACHE_TIME(); - goto cpy_data; - } - - case 0xC4: // CPY zp - data = READ_LOW( data ); - case 0xC0: // CPY #imm - cpy_data: - nz = y - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - -// Logical - - ARITH_ADDR_MODES( 0x25 ) // AND - nz = (a &= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x45 ) // EOR - nz = (a ^= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x05 ) // ORA - nz = (a |= data); - pc++; - goto loop; - - case 0x2C:{// BIT abs - unsigned addr = GET_ADDR(); - pc += 2; - status &= ~st_v; - READ_LIKELY_PPU( addr, nz ); - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - } - - case 0x24: // BIT zp - nz = READ_LOW( data ); - pc++; - status &= ~st_v; - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - case 0xEB: // unofficial equivalent - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= ov >> 2 & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) nz; - goto loop; - } - -// Shift/rotate - - case 0x4A: // LSR A - c = 0; - case 0x6A: // ROR A - nz = c >> 1 & 0x80; - c = a << 8; - nz |= a >> 1; - a = nz; - goto loop; - - case 0x0A: // ASL A - nz = a << 1; - c = nz; - a = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - ADD_PAGE(); - FLUSH_TIME(); - int temp = READ( data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - goto rotate_common; - } - - case 0x3E: // ROL abs,X - data += x; - goto rol_abs; - - case 0x1E: // ASL abs,X - data += x; - case 0x0E: // ASL abs - c = 0; - case 0x2E: // ROL abs - rol_abs: - ADD_PAGE(); - nz = c >> 8 & 1; - FLUSH_TIME(); - nz |= (c = READ( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (data + x); - case 0x46: // LSR zp - c = 0; - case 0x66: // ROR zp - ror_zp: { - int temp = READ_LOW( data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - goto write_nz_zp; - } - - case 0x36: // ROL zp,x - data = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (data + x); - case 0x06: // ASL zp - c = 0; - case 0x26: // ROL zp - rol_zp: - nz = c >> 8 & 1; - nz |= (c = READ_LOW( data ) << 1); - goto write_nz_zp; - -// Increment/decrement - - case 0xCA: // DEX - INC_DEC_XY( x, -1 ) - - case 0x88: // DEY - INC_DEC_XY( y, -1 ) - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = (unsigned) -1; - add_nz_zp: - nz += READ_LOW( data ); - write_nz_zp: - pc++; - WRITE_LOW( data, nz ); - goto loop; - - case 0xFE: // INC abs,x - data = x + GET_ADDR(); - goto inc_ptr; - - case 0xEE: // INC abs - data = GET_ADDR(); - inc_ptr: - nz = 1; - goto inc_common; - - case 0xDE: // DEC abs,x - data = x + GET_ADDR(); - goto dec_ptr; - - case 0xCE: // DEC abs - data = GET_ADDR(); - dec_ptr: - nz = (unsigned) -1; - inc_common: - FLUSH_TIME(); - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xAA: // TAX - x = a; - nz = a; - goto loop; - - case 0x8A: // TXA - a = x; - nz = x; - goto loop; - - case 0x9A: // TXS - SET_SP( x ); // verified (no flag change) - goto loop; - - case 0xBA: // TSX - x = nz = GET_SP(); - goto loop; - -// Stack - - case 0x48: // PHA - PUSH( a ); // verified - goto loop; - - case 0x68: // PLA - a = nz = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - goto loop; - - case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) goto loop; - if ( status & st_i ) goto loop; - s_time += delta; - s.base = irq_time_; - goto loop; - } - - case 0x28:{// PLP - fuint8 temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - fuint8 changed = status ^ temp; - SET_STATUS( temp ); - if ( !(changed & st_i) ) - goto loop; // I flag didn't change - if ( status & st_i ) - goto handle_sei; - goto handle_cli; - } - - case 0x08: { // PHP - fuint8 temp; - CALC_STATUS( temp ); - PUSH( temp | (st_b | st_r) ); - goto loop; - } - - case 0x6C:{// JMP (ind) - data = GET_ADDR(); - check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space - uint8_t const* page = s.code_map [data >> page_bits]; - pc = page [PAGE_OFFSET( data )]; - data = (data & 0xFF00) | ((data + 1) & 0xFF); - pc |= page [PAGE_OFFSET( data )] << 8; - goto loop; - } - - case 0x00: // BRK - goto handle_brk; - -// Flags - - case 0x38: // SEC - c = (unsigned) ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: { - //dprintf( "CLI at %d\n", TIME ); - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) - { - if ( TIME < irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = irq_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - if ( delta >= s_time + 1 ) - { - s.base += s_time + 1; - s_time = -1; - goto loop; - } - - // TODO: implement - delayed_cli: - dprintf( "Delayed CLI not emulated\n" ); - goto loop; - } - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - dprintf( "Delayed SEI not emulated\n" ); - goto loop; - } - -// Unofficial - - // SKW - Skip word - case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: - HANDLE_PAGE_CROSSING( data + x ); - case 0x0C: - pc++; - // SKB - Skip byte - case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: - case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: - pc++; - goto loop; - - // NOP - case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: - goto loop; - - case bad_opcode: // HLT - pc--; - if ( pc > 0xFFFF ) - { - // handle wrap-around (assumes caller has put page of HLT at 0x10000) - pc &= 0xFFFF; - goto loop; - } - case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: - case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: - goto stop; - -// Unimplemented - - case 0xFF: // force 256-entry jump table for optimization purposes - c |= 1; - default: - check( (unsigned) opcode <= 0xFF ); - // skip over proper number of bytes - static unsigned char const illop_lens [8] = { - 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0 - }; - fuint8 opcode = instr [-1]; - fint16 len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3; - if ( opcode == 0x9C ) - len = 2; - pc += len; - error_count_++; - - if ( (opcode >> 4) == 0x0B ) - { - if ( opcode == 0xB3 ) - data = READ_LOW( data ); - if ( opcode != 0xB7 ) - HANDLE_PAGE_CROSSING( data + y ); - } - goto loop; - } - assert( false ); - - int result_; -handle_brk: - pc++; - result_ = 4; - -interrupt: - { - s_time += 7; - - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); - - sp = (sp - 3) | 0x100; - fuint8 temp; - CALC_STATUS( temp ); - temp |= st_r; - if ( result_ ) - temp |= st_b; // TODO: incorrectly sets B flag for IRQ - WRITE_LOW( sp, temp ); - - this->r.status = status |= st_i; - blargg_long delta = s.base - end_time_; - if ( delta >= 0 ) goto loop; - s_time += delta; - s.base = end_time_; - goto loop; - } - -out_of_time: - pc--; - FLUSH_TIME(); - CPU_DONE( this, TIME, result_ ); - CACHE_TIME(); - if ( result_ >= 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - -stop: - - s.time = s_time; - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return s_time < 0; -} - diff --git a/Frameworks/GME/gme/Nes_Cpu.h b/Frameworks/GME/gme/Nes_Cpu.h old mode 100755 new mode 100644 index d303b57c7..8fa09f2e3 --- a/Frameworks/GME/gme/Nes_Cpu.h +++ b/Frameworks/GME/gme/Nes_Cpu.h @@ -1,114 +1,131 @@ -// NES 6502 CPU emulator +// NES CPU emulator -// Game_Music_Emu 0.5.2 +// $package #ifndef NES_CPU_H #define NES_CPU_H #include "blargg_common.h" -typedef blargg_long nes_time_t; // clock cycle count -typedef unsigned nes_addr_t; // 16-bit address -enum { future_nes_time = LONG_MAX / 2 + 1 }; - class Nes_Cpu { public: - typedef BOOST::uint8_t uint8_t; + typedef BOOST::uint8_t byte; + typedef int time_t; + typedef int addr_t; + enum { future_time = INT_MAX/2 + 1 }; - // Clear registers, map low memory and its three mirrors to address 0, - // and mirror unmapped_page in remaining memory - void reset( void const* unmapped_page = 0 ); + // Clears registers and maps all pages to unmapped_page + void reset( void const* unmapped_page = NULL ); - // Map code memory (memory accessed via the program counter). Start and size - // must be multiple of page_size. If mirror is true, repeats code page - // throughout address range. - enum { page_size = 0x800 }; - void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); + // Maps code memory (memory accessed via the program counter). Start and size + // must be multiple of page_size. If mirror_size is non-zero, the first + // mirror_size bytes are repeated over the range. mirror_size must be a + // multiple of page_size. + enum { page_bits = 11 }; + enum { page_size = 1 << page_bits }; + void map_code( addr_t start, int size, void const* code, int mirror_size = 0 ); - // Access emulated memory as CPU does - uint8_t const* get_code( nes_addr_t ); + // Accesses emulated memory as CPU does + byte const* get_code( addr_t ) const; - // 2KB of RAM at address 0 - uint8_t low_mem [0x800]; - - // NES 6502 registers. Not kept updated during a call to run(). + // NES 6502 registers. NOT kept updated during emulation. struct registers_t { BOOST::uint16_t pc; - BOOST::uint8_t a; - BOOST::uint8_t x; - BOOST::uint8_t y; - BOOST::uint8_t status; - BOOST::uint8_t sp; + byte a; + byte x; + byte y; + byte flags; + byte sp; }; registers_t r; - // Set end_time and run CPU from current time. Returns true if execution - // stopped due to encountering bad_opcode. - bool run( nes_time_t end_time ); - // Time of beginning of next instruction to be executed - nes_time_t time() const { return state->time + state->base; } - void set_time( nes_time_t t ) { state->time = t - state->base; } - void adjust_time( int delta ) { state->time += delta; } + time_t time() const { return cpu_state->time + cpu_state->base; } + void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; } + void adjust_time( int delta ) { cpu_state->time += delta; } - nes_time_t irq_time() const { return irq_time_; } - void set_irq_time( nes_time_t ); + // Clocks past end (negative if before) + int time_past_end() const { return cpu_state->time; } - nes_time_t end_time() const { return end_time_; } - void set_end_time( nes_time_t ); + // Time of next IRQ + time_t irq_time() const { return irq_time_; } + void set_irq_time( time_t ); - // Number of undefined instructions encountered and skipped - void clear_error_count() { error_count_ = 0; } - unsigned long error_count() const { return error_count_; } + // Emulation stops once time >= end_time + time_t end_time() const { return end_time_; } + void set_end_time( time_t ); - // CPU invokes bad opcode handler if it encounters this - enum { bad_opcode = 0xF2 }; + // Number of unimplemented instructions encountered and skipped + void clear_error_count() { error_count_ = 0; } + unsigned error_count() const { return error_count_; } + void count_error() { error_count_++; } -public: - Nes_Cpu() { state = &state_; } - enum { page_bits = 11 }; - enum { page_count = 0x10000 >> page_bits }; - enum { irq_inhibit = 0x04 }; + // Unmapped page should be filled with this + enum { halt_opcode = 0x22 }; + + enum { irq_inhibit_mask = 0x04 }; + + // Can read this many bytes past end of a page + enum { cpu_padding = 8 }; + private: - struct state_t { - uint8_t const* code_map [page_count + 1]; - nes_time_t base; + // noncopyable + Nes_Cpu( const Nes_Cpu& ); + Nes_Cpu& operator = ( const Nes_Cpu& ); + + +// Implementation +public: + Nes_Cpu() { cpu_state = &cpu_state_; } + enum { page_count = 0x10000 >> page_bits }; + + struct cpu_state_t { + byte const* code_map [page_count + 1]; + time_t base; int time; }; - state_t* state; // points to state_ or a local copy within run() - state_t state_; - nes_time_t irq_time_; - nes_time_t end_time_; - unsigned long error_count_; + cpu_state_t* cpu_state; // points to cpu_state_ or a local copy + cpu_state_t cpu_state_; + time_t irq_time_; + time_t end_time_; + unsigned error_count_; +private: void set_code_page( int, void const* ); - inline int update_end_time( nes_time_t end, nes_time_t irq ); + inline void update_end_time( time_t end, time_t irq ); }; -inline BOOST::uint8_t const* Nes_Cpu::get_code( nes_addr_t addr ) +#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits) + +#if BLARGG_NONPORTABLE + #define NES_CPU_OFFSET( addr ) (addr) +#else + #define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1)) +#endif + +inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const { - return state->code_map [addr >> page_bits] + addr - #if !BLARGG_NONPORTABLE - % (unsigned) page_size - #endif - ; + return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr ); } -inline int Nes_Cpu::update_end_time( nes_time_t t, nes_time_t irq ) +inline void Nes_Cpu::update_end_time( time_t end, time_t irq ) { - if ( irq < t && !(r.status & irq_inhibit) ) t = irq; - int delta = state->base - t; - state->base = t; - return delta; + if ( end > irq && !(r.flags & irq_inhibit_mask) ) + end = irq; + + cpu_state->time += cpu_state->base - end; + cpu_state->base = end; } -inline void Nes_Cpu::set_irq_time( nes_time_t t ) +inline void Nes_Cpu::set_irq_time( time_t t ) { - state->time += update_end_time( end_time_, (irq_time_ = t) ); + irq_time_ = t; + update_end_time( end_time_, t ); } -inline void Nes_Cpu::set_end_time( nes_time_t t ) +inline void Nes_Cpu::set_end_time( time_t t ) { - state->time += update_end_time( (end_time_ = t), irq_time_ ); -} + end_time_ = t; + update_end_time( t, irq_time_ ); +} #endif diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp old mode 100755 new mode 100644 index c058f6b1f..b3ab6d093 --- a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp @@ -1,9 +1,7 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ #include "Nes_Fme7_Apu.h" -#include - /* Copyright (C) 2003-2006 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -51,7 +49,6 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) Blip_Buffer* const osc_output = oscs [index].output; if ( !osc_output ) continue; - osc_output->set_modified(); // check for unsupported mode #ifndef NDEBUG @@ -78,11 +75,13 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) int amp = volume; if ( !phases [index] ) amp = 0; + { int delta = amp - oscs [index].last_amp; if ( delta ) { oscs [index].last_amp = amp; + osc_output->set_modified(); synth.offset( last_time, delta, osc_output ); } } @@ -91,6 +90,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) if ( time < end_time ) { int delta = amp * 2 - volume; + osc_output->set_modified(); if ( volume ) { do @@ -109,7 +109,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) // maintain phase when silent int count = (end_time - time + period - 1) / period; phases [index] ^= count & 1; - time += (blargg_long) count * period; + time += count * period; } } diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.h b/Frameworks/GME/gme/Nes_Fme7_Apu.h old mode 100755 new mode 100644 index eb60af038..e7272637a --- a/Frameworks/GME/gme/Nes_Fme7_Apu.h +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.h @@ -1,6 +1,6 @@ // Sunsoft FME-7 sound emulator -// Game_Music_Emu 0.5.2 +// $package #ifndef NES_FME7_APU_H #define NES_FME7_APU_H @@ -22,9 +22,9 @@ public: void reset(); void volume( double ); void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); + void set_output( Blip_Buffer* ); enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); void end_frame( blip_time_t ); void save_state( fme7_apu_state_t* ) const; void load_state( fme7_apu_state_t const& ); @@ -57,7 +57,7 @@ private: blip_time_t last_time; enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff - Blip_Synth synth; + Blip_Synth_Norm synth; void run_until( blip_time_t ); }; @@ -72,21 +72,21 @@ inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq ) synth.treble_eq( eq ); } -inline void Nes_Fme7_Apu::osc_output( int i, Blip_Buffer* buf ) +inline void Nes_Fme7_Apu::set_output( int i, Blip_Buffer* buf ) { assert( (unsigned) i < osc_count ); oscs [i].output = buf; } -inline void Nes_Fme7_Apu::output( Blip_Buffer* buf ) +inline void Nes_Fme7_Apu::set_output( Blip_Buffer* buf ) { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); } inline Nes_Fme7_Apu::Nes_Fme7_Apu() { - output( NULL ); + set_output( NULL ); volume( 1.0 ); reset(); } diff --git a/Frameworks/GME/gme/Nes_Namco_Apu.cpp b/Frameworks/GME/gme/Nes_Namco_Apu.cpp old mode 100755 new mode 100644 index f3235b383..cba754a14 --- a/Frameworks/GME/gme/Nes_Namco_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Namco_Apu.cpp @@ -1,145 +1,152 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ - -#include "Nes_Namco_Apu.h" - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Nes_Namco_Apu::Nes_Namco_Apu() -{ - output( NULL ); - volume( 1.0 ); - reset(); -} - -void Nes_Namco_Apu::reset() -{ - last_time = 0; - addr_reg = 0; - - int i; - for ( i = 0; i < reg_count; i++ ) - reg [i] = 0; - - for ( i = 0; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - osc.delay = 0; - osc.last_amp = 0; - osc.wave_pos = 0; - } -} - -void Nes_Namco_Apu::output( Blip_Buffer* buf ) -{ - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); -} - -/* -void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) -{ - reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); - - static const char hex [17] = "0123456789ABCDEF"; - int i; - for ( i = 0; i < reg_count; i++ ) - reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); - - for ( i = 0; i < osc_count; i++ ) - { - reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); - reflect_int16( data, BLARGG_4CHAR('P','O','S','0') + i, &oscs [i].wave_pos ); - } -} -*/ - -void Nes_Namco_Apu::end_frame( blip_time_t time ) -{ - if ( time > last_time ) - run_until( time ); - - assert( last_time >= time ); - last_time -= time; -} - -void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) -{ - int active_oscs = (reg [0x7F] >> 4 & 7) + 1; - for ( int i = osc_count - active_oscs; i < osc_count; i++ ) - { - Namco_Osc& osc = oscs [i]; - Blip_Buffer* output = osc.output; - if ( !output ) - continue; - output->set_modified(); - - blip_resampled_time_t time = - output->resampled_time( last_time ) + osc.delay; - blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); - osc.delay = 0; - if ( time < end_time ) - { - const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; - if ( !(osc_reg [4] & 0xE0) ) - continue; - - int volume = osc_reg [7] & 15; - if ( !volume ) - continue; - - blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; - if ( freq < 64 * active_oscs ) - continue; // prevent low frequencies from excessively delaying freq changes - blip_resampled_time_t period = - output->resampled_duration( 983040 ) / freq * active_oscs; - - int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; - if ( !wave_size ) - continue; - - int last_amp = osc.last_amp; - int wave_pos = osc.wave_pos; - - do - { - // read wave sample - int addr = wave_pos + osc_reg [6]; - int sample = reg [addr >> 1] >> (addr << 2 & 4); - wave_pos++; - sample = (sample & 15) * volume; - - // output impulse if amplitude changed - int delta = sample - last_amp; - if ( delta ) - { - last_amp = sample; - synth.offset_resampled( time, delta, output ); - } - - // next sample - time += period; - if ( wave_pos >= wave_size ) - wave_pos = 0; - } - while ( time < end_time ); - - osc.wave_pos = wave_pos; - osc.last_amp = last_amp; - } - osc.delay = time - end_time; - } - - last_time = nes_end_time; -} - +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ + +#include "Nes_Namco_Apu.h" + +/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Nes_Namco_Apu::Nes_Namco_Apu() +{ + set_output( NULL ); + volume( 1.0 ); + reset(); +} + +void Nes_Namco_Apu::reset() +{ + last_time = 0; + addr_reg = 0; + + int i; + for ( i = 0; i < reg_count; i++ ) + reg [i] = 0; + + for ( i = 0; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + osc.delay = 0; + osc.last_amp = 0; + osc.wave_pos = 0; + } +} + +void Nes_Namco_Apu::set_output( Blip_Buffer* buf ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); +} + +/* +void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) +{ + reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); + + static const char hex [17] = "0123456789ABCDEF"; + int i; + for ( i = 0; i < reg_count; i++ ) + reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); + + for ( i = 0; i < osc_count; i++ ) + { + reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); + reflect_int16( data, BLARGG_4CHAR('P','O','S','0') + i, &oscs [i].wave_pos ); + } +} +*/ + +void Nes_Namco_Apu::end_frame( blip_time_t time ) +{ + if ( time > last_time ) + run_until( time ); + + assert( last_time >= time ); + last_time -= time; +} + +void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) +{ + int active_oscs = (reg [0x7F] >> 4 & 7) + 1; + for ( int i = osc_count - active_oscs; i < osc_count; i++ ) + { + Namco_Osc& osc = oscs [i]; + Blip_Buffer* output = osc.output; + if ( !output ) + continue; + + blip_resampled_time_t time = + output->resampled_time( last_time ) + osc.delay; + blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); + osc.delay = 0; + if ( time < end_time ) + { + const BOOST::uint8_t* osc_reg = ® [i * 8 + 0x40]; + if ( !(osc_reg [4] & 0xE0) ) + continue; + + int volume = osc_reg [7] & 15; + if ( !volume ) + continue; + + int freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100 + osc_reg [0]; + if ( freq < 64 * active_oscs ) + continue; // prevent low frequencies from excessively delaying freq changes + + int const master_clock_divider = 12; // NES time derived via divider of master clock + int const n106_divider = 45; // N106 then divides master clock by this + int const max_freq = 0x3FFFF; + int const lowest_freq_period = (max_freq + 1) * n106_divider / master_clock_divider; + // divide by 8 to avoid overflow + blip_resampled_time_t period = + output->resampled_duration( lowest_freq_period / 8 ) / freq * 8 * active_oscs; + + int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; + if ( !wave_size ) + continue; + + int last_amp = osc.last_amp; + int wave_pos = osc.wave_pos; + + output->set_modified(); + + do + { + // read wave sample + int addr = wave_pos + osc_reg [6]; + int sample = reg [addr >> 1] >> (addr << 2 & 4); + wave_pos++; + sample = (sample & 15) * volume; + + // output impulse if amplitude changed + int delta = sample - last_amp; + if ( delta ) + { + last_amp = sample; + synth.offset_resampled( time, delta, output ); + } + + // next sample + time += period; + if ( wave_pos >= wave_size ) + wave_pos = 0; + } + while ( time < end_time ); + + osc.wave_pos = wave_pos; + osc.last_amp = last_amp; + } + osc.delay = time - end_time; + } + + last_time = nes_end_time; +} + diff --git a/Frameworks/GME/gme/Nes_Namco_Apu.h b/Frameworks/GME/gme/Nes_Namco_Apu.h old mode 100755 new mode 100644 index db5fea4bf..2a067d9e9 --- a/Frameworks/GME/gme/Nes_Namco_Apu.h +++ b/Frameworks/GME/gme/Nes_Namco_Apu.h @@ -1,6 +1,6 @@ // Namco 106 sound chip emulator -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_NAMCO_APU_H #define NES_NAMCO_APU_H @@ -14,9 +14,9 @@ public: // See Nes_Apu.h for reference. void volume( double ); void treble_eq( const blip_eq_t& ); - void output( Blip_Buffer* ); + void set_output( Blip_Buffer* ); enum { osc_count = 8 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); void reset(); void end_frame( blip_time_t ); @@ -42,7 +42,7 @@ private: Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); struct Namco_Osc { - blargg_long delay; + int delay; Blip_Buffer* output; short last_amp; short wave_pos; @@ -55,7 +55,7 @@ private: enum { reg_count = 0x80 }; BOOST::uint8_t reg [reg_count]; - Blip_Synth synth; + Blip_Synth_Norm synth; BOOST::uint8_t& access(); void run_until( blip_time_t ); @@ -79,7 +79,7 @@ inline BOOST::uint8_t& Nes_Namco_Apu::access() return reg [addr]; } -inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count * v ); } +inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count / 15 * v ); } inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); } @@ -87,7 +87,7 @@ inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; } inline int Nes_Namco_Apu::read_data() { return access(); } -inline void Nes_Namco_Apu::osc_output( int i, Blip_Buffer* buf ) +inline void Nes_Namco_Apu::set_output( int i, Blip_Buffer* buf ) { assert( (unsigned) i < osc_count ); oscs [i].output = buf; diff --git a/Frameworks/GME/gme/Nes_Oscs.cpp b/Frameworks/GME/gme/Nes_Oscs.cpp old mode 100755 new mode 100644 index 1ad3f59c0..367744a7a --- a/Frameworks/GME/gme/Nes_Oscs.cpp +++ b/Frameworks/GME/gme/Nes_Oscs.cpp @@ -1,4 +1,4 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Nes_Apu.h" @@ -26,12 +26,14 @@ void Nes_Osc::clock_length( int halt_mask ) void Nes_Envelope::clock_envelope() { int period = regs [0] & 15; - if ( reg_written [3] ) { + if ( reg_written [3] ) + { reg_written [3] = false; env_delay = period; envelope = 15; } - else if ( --env_delay < 0 ) { + else if ( --env_delay < 0 ) + { env_delay = period; if ( envelope | (regs [0] & 0x20) ) envelope = (envelope - 1) & 15; @@ -72,14 +74,15 @@ void Nes_Square::clock_sweep( int negative_adjust ) } } - if ( reg_written [1] ) { + if ( reg_written [1] ) + { reg_written [1] = false; sweep_delay = (sweep >> 4) & 7; } } // TODO: clean up -inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time, +inline Nes_Square::nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time, nes_time_t timer_period ) { nes_time_t remain = end_time - time; @@ -87,7 +90,7 @@ inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_ti { int count = (remain + timer_period - 1) / timer_period; phase = (phase + count) & (phase_range - 1); - time += (blargg_long) count * timer_period; + time += count * timer_period; } return time; } @@ -103,8 +106,6 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) return; } - output->set_modified(); - int offset = period >> (regs [1] & shift_mask); if ( regs [1] & negate_flag ) offset = 0; @@ -112,7 +113,9 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) const int volume = this->volume(); if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) { - if ( last_amp ) { + if ( last_amp ) + { + output->set_modified(); synth.offset( time, -last_amp, output ); last_amp = 0; } @@ -126,13 +129,15 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) int duty_select = (regs [0] >> 6) & 3; int duty = 1 << duty_select; // 1, 2, 4, 2 int amp = 0; - if ( duty_select == 3 ) { + if ( duty_select == 3 ) + { duty = 2; // negated 25% amp = volume; } if ( phase < duty ) amp ^= volume; + output->set_modified(); { int delta = update_amp( amp ); if ( delta ) @@ -147,9 +152,11 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) int delta = amp * 2 - volume; int phase = this->phase; - do { + do + { phase = (phase + 1) & (phase_range - 1); - if ( phase == 0 || phase == duty ) { + if ( phase == 0 || phase == duty ) + { delta = -delta; synth.offset_inline( time, delta, output ); } @@ -187,7 +194,7 @@ inline int Nes_Triangle::calc_amp() const } // TODO: clean up -inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, +inline Nes_Square::nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time, nes_time_t timer_period ) { nes_time_t remain = end_time - time; @@ -196,7 +203,7 @@ inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_ int count = (remain + timer_period - 1) / timer_period; phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); phase++; - time += (blargg_long) count * timer_period; + time += count * timer_period; } return time; } @@ -213,14 +220,15 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) return; } - output->set_modified(); - // to do: track phase when period < 3 // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. int delta = update_amp( calc_amp() ); if ( delta ) + { + output->set_modified(); synth.offset( time, delta, output ); + } time += delay; if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) @@ -233,17 +241,22 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) int phase = this->phase; int volume = 1; - if ( phase > phase_range ) { + if ( phase > phase_range ) + { phase -= phase_range; volume = -volume; } + output->set_modified(); - do { - if ( --phase == 0 ) { + do + { + if ( --phase == 0 ) + { phase = phase_range; volume = -volume; } - else { + else + { synth.offset_inline( time, volume, output ); } @@ -284,7 +297,8 @@ void Nes_Dmc::recalc_irq() if ( irq_enabled && length_counter ) irq = apu->last_dmc_time + delay + ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1; - if ( irq != next_irq ) { + if ( irq != next_irq ) + { next_irq = irq; apu->irq_changed(); } @@ -332,18 +346,27 @@ inline void Nes_Dmc::reload_sample() length_counter = regs [3] * 0x10 + 1; } -static byte const dac_table [128] = +static int const dmc_table [128] = { - 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9,10,11,12,13,14, - 15,15,16,17,18,19,20,20,21,22,23,24,24,25,26,27, - 27,28,29,30,31,31,32,33,33,34,35,36,36,37,38,38, - 39,40,41,41,42,43,43,44,45,45,46,47,47,48,48,49, - 50,50,51,52,52,53,53,54,55,55,56,56,57,58,58,59, - 59,60,60,61,61,62,63,63,64,64,65,65,66,66,67,67, - 68,68,69,70,70,71,71,72,72,73,73,74,74,75,75,75, - 76,76,77,77,78,78,79,79,80,80,81,81,82,82,82,83, + 0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340, + 361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664, + 683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955, + 972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217, +1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454, +1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670, +1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867, +1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048, }; +inline int Nes_Dmc::update_amp_nonlinear( int in ) +{ + if ( !nonlinear ) + in = dmc_table [in]; + int delta = in - last_amp; + last_amp = in; + return delta; +} + void Nes_Dmc::write_register( int addr, int data ) { if ( addr == 0 ) @@ -355,14 +378,7 @@ void Nes_Dmc::write_register( int addr, int data ) } else if ( addr == 1 ) { - int old_dac = dac; dac = data & 0x7F; - - // adjust last_amp so that "pop" amplitude will be properly non-linear - // with respect to change in dac - int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); - if ( !nonlinear ) - last_amp = faked_nonlinear; } } @@ -377,16 +393,18 @@ void Nes_Dmc::fill_buffer() { if ( !buf_full && length_counter ) { - require( prg_reader ); // prg_reader must be set - buf = prg_reader( prg_reader_data, 0x8000u + address ); + require( apu->dmc_reader.f ); // dmc_reader must be set + buf = apu->dmc_reader.f( apu->dmc_reader.data, 0x8000u + address ); address = (address + 1) & 0x7FFF; buf_full = true; if ( --length_counter == 0 ) { - if ( regs [0] & loop_flag ) { + if ( regs [0] & loop_flag ) + { reload_sample(); } - else { + else + { apu->osc_enables &= ~0x10; irq_flag = irq_enabled; next_irq = Nes_Apu::no_irq; @@ -398,16 +416,15 @@ void Nes_Dmc::fill_buffer() void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) { - int delta = update_amp( dac ); + int delta = update_amp_nonlinear( dac ); if ( !output ) { silence = true; } - else + else if ( delta ) { output->set_modified(); - if ( delta ) - synth.offset( time, delta, output ); + synth.offset( time, delta, output ); } time += delay; @@ -426,6 +443,8 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) const int period = this->period; int bits = this->bits; int dac = this->dac; + if ( output ) + output->set_modified(); do { @@ -433,9 +452,10 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) { int step = (bits & 1) * 4 - 2; bits >>= 1; - if ( unsigned (dac + step) <= 0x7F ) { + if ( unsigned (dac + step) <= 0x7F ) + { dac += step; - synth.offset_inline( time, step, output ); + synth.offset_inline( time, update_amp_nonlinear( dac ), output ); } } @@ -444,10 +464,12 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) if ( --bits_remain == 0 ) { bits_remain = 8; - if ( !buf_full ) { + if ( !buf_full ) + { silence = true; } - else { + else + { silence = false; bits = buf; buf_full = false; @@ -460,7 +482,6 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) while ( time < end_time ); this->dac = dac; - this->last_amp = dac; this->bits = bits; } this->bits_remain = bits_remain; @@ -487,14 +508,16 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) return; } - output->set_modified(); const int volume = this->volume(); int amp = (noise & 1) ? volume : 0; { int delta = update_amp( amp ); if ( delta ) + { + output->set_modified(); synth.offset( time, delta, output ); + } } time += delay; @@ -509,7 +532,8 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) // approximate noise cycling while muted, by shuffling up noise register // to do: precise muted noise cycling? - if ( !(regs [2] & mode_flag) ) { + if ( !(regs [2] & mode_flag) ) + { int feedback = (noise << 13) ^ (noise << 14); noise = (feedback & 0x4000) | (noise >> 1); } @@ -525,12 +549,15 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) int noise = this->noise; int delta = amp * 2 - volume; const int tap = (regs [2] & mode_flag ? 8 : 13); + output->set_modified(); - do { + do + { int feedback = (noise << tap) ^ (noise << 14); time += period; - if ( (noise + 1) & 2 ) { + if ( (noise + 1) & 2 ) + { // bits 0 and 1 of noise differ delta = -delta; synth.offset_resampled( rtime, delta, output ); diff --git a/Frameworks/GME/gme/Nes_Oscs.h b/Frameworks/GME/gme/Nes_Oscs.h old mode 100755 new mode 100644 index b675bfb47..0bf922a82 --- a/Frameworks/GME/gme/Nes_Oscs.h +++ b/Frameworks/GME/gme/Nes_Oscs.h @@ -1,6 +1,6 @@ // Private oscillators used by Nes_Apu -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_OSCS_H #define NES_OSCS_H @@ -11,6 +11,8 @@ class Nes_Apu; struct Nes_Osc { + typedef int nes_time_t; + unsigned char regs [4]; bool reg_written [4]; Blip_Buffer* output; @@ -56,7 +58,7 @@ struct Nes_Square : Nes_Envelope int phase; int sweep_delay; - typedef Blip_Synth Synth; + typedef Blip_Synth_Norm Synth; Synth const& synth; // shared between squares Nes_Square( Synth const* s ) : synth( *s ) { } @@ -77,7 +79,7 @@ struct Nes_Triangle : Nes_Osc enum { phase_range = 16 }; int phase; int linear_counter; - Blip_Synth synth; + Blip_Synth_Fast synth; int calc_amp() const; void run( nes_time_t, nes_time_t ); @@ -95,7 +97,7 @@ struct Nes_Triangle : Nes_Osc struct Nes_Noise : Nes_Envelope { int noise; - Blip_Synth synth; + Blip_Synth_Fast synth; void run( nes_time_t, nes_time_t ); void reset() { @@ -126,13 +128,11 @@ struct Nes_Dmc : Nes_Osc bool pal_mode; bool nonlinear; - int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function - void* prg_reader_data; - Nes_Apu* apu; - Blip_Synth synth; + Blip_Synth_Fast synth; + int update_amp_nonlinear( int dac_in ); void start(); void write_register( int, int ); void run( nes_time_t, nes_time_t ); diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp old mode 100755 new mode 100644 index d178407c3..990e60640 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp @@ -1,4 +1,4 @@ -// Nes_Snd_Emu 0.1.8. http://www.slack.net/~ant/ +// Nes_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Nes_Vrc6_Apu.h" @@ -15,11 +15,10 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -Nes_Vrc6_Apu::Nes_Vrc6_Apu() +void Nes_Vrc6_Apu::set_output( Blip_Buffer* buf ) { - output( NULL ); - volume( 1.0 ); - reset(); + for ( int i = 0; i < osc_count; ++i ) + set_output( i, buf ); } void Nes_Vrc6_Apu::reset() @@ -37,10 +36,11 @@ void Nes_Vrc6_Apu::reset() } } -void Nes_Vrc6_Apu::output( Blip_Buffer* buf ) +Nes_Vrc6_Apu::Nes_Vrc6_Apu() { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, buf ); + set_output( NULL ); + volume( 1.0 ); + reset(); } void Nes_Vrc6_Apu::run_until( blip_time_t time ) @@ -107,7 +107,6 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) Blip_Buffer* output = osc.output; if ( !output ) return; - output->set_modified(); int volume = osc.regs [0] & 15; if ( !(osc.regs [2] & 0x80) ) @@ -120,6 +119,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) if ( delta ) { osc.last_amp += delta; + output->set_modified(); square_synth.offset( time, delta, output ); } @@ -131,6 +131,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) if ( time < end_time ) { int phase = osc.phase; + output->set_modified(); do { diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.h b/Frameworks/GME/gme/Nes_Vrc6_Apu.h old mode 100755 new mode 100644 index 18722233f..56af076f4 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.h +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.h @@ -1,6 +1,6 @@ // Konami VRC6 sound chip emulator -// Nes_Snd_Emu 0.1.8 +// Nes_Snd_Emu $vers #ifndef NES_VRC6_APU_H #define NES_VRC6_APU_H @@ -15,9 +15,9 @@ public: void reset(); void volume( double ); void treble_eq( blip_eq_t const& ); - void output( Blip_Buffer* ); + void set_output( Blip_Buffer* ); enum { osc_count = 3 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); void end_frame( blip_time_t ); void save_state( vrc6_apu_state_t* ) const; void load_state( vrc6_apu_state_t const& ); @@ -49,15 +49,15 @@ private: int period() const { - return (regs [2] & 0x0F) * 0x100L + regs [1] + 1; + return (regs [2] & 0x0F) * 0x100 + regs [1] + 1; } }; Vrc6_Osc oscs [osc_count]; blip_time_t last_time; - Blip_Synth saw_synth; - Blip_Synth square_synth; + Blip_Synth_Fast saw_synth; + Blip_Synth_Norm square_synth; void run_until( blip_time_t ); void run_square( Vrc6_Osc& osc, blip_time_t ); @@ -73,7 +73,7 @@ struct vrc6_apu_state_t BOOST::uint8_t unused; }; -inline void Nes_Vrc6_Apu::osc_output( int i, Blip_Buffer* buf ) +inline void Nes_Vrc6_Apu::set_output( int i, Blip_Buffer* buf ) { assert( (unsigned) i < osc_count ); oscs [i].output = buf; diff --git a/Frameworks/GME/gme/Nsf_Emu.cpp b/Frameworks/GME/gme/Nsf_Emu.cpp old mode 100755 new mode 100644 index 3670d1dbd..449d43a6d --- a/Frameworks/GME/gme/Nsf_Emu.cpp +++ b/Frameworks/GME/gme/Nsf_Emu.cpp @@ -1,557 +1,342 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Nsf_Emu.h" - -#include "blargg_endian.h" -#include -#include - -#if !NSF_EMU_APU_ONLY - #include "Nes_Namco_Apu.h" - #include "Nes_Vrc6_Apu.h" - #include "Nes_Fme7_Apu.h" -#endif - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nsf_Emu.h" + +#if !NSF_EMU_APU_ONLY + #include "Nes_Namco_Apu.h" + #include "Nes_Vrc6_Apu.h" + #include "Nes_Fme7_Apu.h" + #include "Nes_Fds_Apu.h" + #include "Nes_Mmc5_Apu.h" + #include "Nes_Vrc7_Apu.h" +#endif + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const vrc6_flag = 0x01; -int const namco_flag = 0x10; -int const fme7_flag = 0x20; - -long const clock_divisor = 12; - -Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80 }; -Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80 }; - -int Nsf_Emu::pcm_read( void* emu, nes_addr_t addr ) -{ - return *((Nsf_Emu*) emu)->cpu::get_code( addr ); -} +Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80, 0,0,0,0,0,0,0,0 }; +Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80, 0,0,0,0,0,0,0,0 }; Nsf_Emu::Nsf_Emu() { - vrc6 = 0; - namco = 0; - fme7 = 0; - - set_type( gme_nsf_type ); - set_silence_lookahead( 6 ); - apu.dmc_reader( pcm_read, this ); - Music_Emu::set_equalizer( nes_eq ); - set_gain( 1.4 ); - memset( unmapped_code, Nes_Cpu::bad_opcode, sizeof unmapped_code ); -} - -Nsf_Emu::~Nsf_Emu() { unload(); } - -void Nsf_Emu::unload() -{ - #if !NSF_EMU_APU_ONLY - { - delete vrc6; - vrc6 = 0; - - delete namco; - namco = 0; - - delete fme7; - fme7 = 0; - } - #endif - - rom.clear(); - Music_Emu::unload(); -} - -// Track info - -static void copy_nsf_fields( Nsf_Emu::header_t const& h, track_info_t* out ) -{ - GME_COPY_FIELD( h, out, game ); - GME_COPY_FIELD( h, out, author ); - GME_COPY_FIELD( h, out, copyright ); - if ( h.chip_flags ) - Gme_File::copy_field_( out->system, "Famicom" ); -} - -blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const -{ - copy_nsf_fields( header_, out ); - return 0; -} - -static blargg_err_t check_nsf_header( void const* header ) -{ - if ( memcmp( header, "NESM\x1A", 5 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Nsf_File : Gme_Info_ -{ - Nsf_Emu::header_t h; - - Nsf_File() { set_type( gme_nsf_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - blargg_err_t err = in.read( &h, Nsf_Emu::header_size ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - - if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) - set_warning( "Uses unsupported audio expansion hardware" ); - - set_track_count( h.track_count ); - return check_nsf_header( &h ); - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_nsf_fields( h, out ); - return 0; - } -}; - -static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } -static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } - -gme_type_t_ const gme_nsf_type [1] = { "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }; - -// Setup - -void Nsf_Emu::set_tempo_( double t ) -{ - unsigned playback_rate = get_le16( header_.ntsc_speed ); - unsigned standard_rate = 0x411A; - clock_rate_ = 1789772.72727; - play_period = 262 * 341L * 4 - 2; // two fewer PPU clocks every four frames - - if ( pal_only ) - { - play_period = 33247 * clock_divisor; - clock_rate_ = 1662607.125; - standard_rate = 0x4E20; - playback_rate = get_le16( header_.pal_speed ); - } - - if ( !playback_rate ) - playback_rate = standard_rate; - - if ( playback_rate != standard_rate || t != 1.0 ) - play_period = long (playback_rate * clock_rate_ / (1000000.0 / clock_divisor * t)); - - apu.set_tempo( t ); -} - -blargg_err_t Nsf_Emu::init_sound() -{ - if ( header_.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag) ) - set_warning( "Uses unsupported audio expansion hardware" ); - - { - #define APU_NAMES "Square 1", "Square 2", "Triangle", "Noise", "DMC" - - int const count = Nes_Apu::osc_count; - static const char* const apu_names [count] = { APU_NAMES }; - set_voice_count( count ); - set_voice_names( apu_names ); - - } - - static int const types [] = { - wave_type | 1, wave_type | 2, wave_type | 0, - noise_type | 0, mixed_type | 1, - wave_type | 3, wave_type | 4, wave_type | 5, - wave_type | 6, wave_type | 7, wave_type | 8, wave_type | 9, - wave_type |10, wave_type |11, wave_type |12, wave_type |13 - }; - set_voice_types( types ); // common to all sound chip configurations - - double adjusted_gain = gain(); - - #if NSF_EMU_APU_ONLY - { - if ( header_.chip_flags ) - set_warning( "Uses unsupported audio expansion hardware" ); - } - #else - { - if ( header_.chip_flags & (namco_flag | vrc6_flag | fme7_flag) ) - set_voice_count( Nes_Apu::osc_count + 3 ); - - if ( header_.chip_flags & namco_flag ) - { - namco = BLARGG_NEW Nes_Namco_Apu; - CHECK_ALLOC( namco ); - adjusted_gain *= 0.75; - - int const count = Nes_Apu::osc_count + Nes_Namco_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Wave 1", "Wave 2", "Wave 3", "Wave 4", - "Wave 5", "Wave 6", "Wave 7", "Wave 8" - }; - set_voice_count( count ); - set_voice_names( names ); - } - - if ( header_.chip_flags & vrc6_flag ) - { - vrc6 = BLARGG_NEW Nes_Vrc6_Apu; - CHECK_ALLOC( vrc6 ); - adjusted_gain *= 0.75; - - { - int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Saw Wave", "Square 3", "Square 4" - }; - set_voice_count( count ); - set_voice_names( names ); - } - - if ( header_.chip_flags & namco_flag ) - { - int const count = Nes_Apu::osc_count + Nes_Vrc6_Apu::osc_count + - Nes_Namco_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Saw Wave", "Square 3", "Square 4", - "Wave 1", "Wave 2", "Wave 3", "Wave 4", - "Wave 5", "Wave 6", "Wave 7", "Wave 8" - }; - set_voice_count( count ); - set_voice_names( names ); - } - } - - if ( header_.chip_flags & fme7_flag ) - { - fme7 = BLARGG_NEW Nes_Fme7_Apu; - CHECK_ALLOC( fme7 ); - adjusted_gain *= 0.75; - - int const count = Nes_Apu::osc_count + Nes_Fme7_Apu::osc_count; - static const char* const names [count] = { - APU_NAMES, - "Square 3", "Square 4", "Square 5" - }; - set_voice_count( count ); - set_voice_names( names ); - } - - if ( namco ) namco->volume( adjusted_gain ); - if ( vrc6 ) vrc6 ->volume( adjusted_gain ); - if ( fme7 ) fme7 ->volume( adjusted_gain ); - } - #endif - - apu.volume( adjusted_gain ); - - return 0; -} - -blargg_err_t Nsf_Emu::load_( Data_Reader& in ) -{ - assert( offsetof (header_t,unused [4]) == header_size ); - RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); - - set_track_count( header_.track_count ); - RETURN_ERR( check_nsf_header( &header_ ) ); - - if ( header_.vers != 1 ) - set_warning( "Unknown file version" ); - - // sound and memory - blargg_err_t err = init_sound(); - if ( err ) - return err; - - // set up data - nes_addr_t load_addr = get_le16( header_.load_addr ); - init_addr = get_le16( header_.init_addr ); - play_addr = get_le16( header_.play_addr ); - if ( !load_addr ) load_addr = rom_begin; - if ( !init_addr ) init_addr = rom_begin; - if ( !play_addr ) play_addr = rom_begin; - if ( load_addr < rom_begin || init_addr < rom_begin ) - { - const char* w = warning(); - if ( !w ) - w = "Corrupt file (invalid load/init/play address)"; - return w; - } - - rom.set_addr( load_addr % bank_size ); - int total_banks = rom.size() / bank_size; - - // bank switching - int first_bank = (load_addr - rom_begin) / bank_size; - for ( int i = 0; i < bank_count; i++ ) - { - unsigned bank = i - first_bank; - if ( bank >= (unsigned) total_banks ) - bank = 0; - initial_banks [i] = bank; - - if ( header_.banks [i] ) - { - // bank-switched - memcpy( initial_banks, header_.banks, sizeof initial_banks ); - break; - } - } - - pal_only = (header_.speed_flags & 3) == 1; - - #if !NSF_EMU_EXTRA_FLAGS - header_.speed_flags = 0; - #endif - - set_tempo( tempo() ); - - return setup_buffer( (long) (clock_rate_ + 0.5) ); -} - -void Nsf_Emu::update_eq( blip_eq_t const& eq ) -{ - apu.treble_eq( eq ); - - #if !NSF_EMU_APU_ONLY - { - if ( namco ) namco->treble_eq( eq ); - if ( vrc6 ) vrc6 ->treble_eq( eq ); - if ( fme7 ) fme7 ->treble_eq( eq ); - } - #endif -} - -void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) -{ - if ( i < Nes_Apu::osc_count ) - { - apu.osc_output( i, buf ); - return; - } - i -= Nes_Apu::osc_count; - - #if !NSF_EMU_APU_ONLY - { - if ( fme7 && i < Nes_Fme7_Apu::osc_count ) - { - fme7->osc_output( i, buf ); - return; - } - - if ( vrc6 ) - { - if ( i < Nes_Vrc6_Apu::osc_count ) - { - // put saw first - if ( --i < 0 ) - i = 2; - vrc6->osc_output( i, buf ); - return; - } - i -= Nes_Vrc6_Apu::osc_count; - } - - if ( namco && i < Nes_Namco_Apu::osc_count ) - { - namco->osc_output( i, buf ); - return; - } - } - #endif -} - -// Emulation - -// see nes_cpu_io.h for read/write functions - -void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) -{ - #if !NSF_EMU_APU_ONLY - { - if ( namco ) - { - switch ( addr ) - { - case Nes_Namco_Apu::data_reg_addr: - namco->write_data( time(), data ); - return; - - case Nes_Namco_Apu::addr_reg_addr: - namco->write_addr( data ); - return; - } - } - - if ( addr >= Nes_Fme7_Apu::latch_addr && fme7 ) - { - switch ( addr & Nes_Fme7_Apu::addr_mask ) - { - case Nes_Fme7_Apu::latch_addr: - fme7->write_latch( data ); - return; - - case Nes_Fme7_Apu::data_addr: - fme7->write_data( time(), data ); - return; - } - } - - if ( vrc6 ) - { - unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1); - unsigned osc = unsigned (addr - Nes_Vrc6_Apu::base_addr) / Nes_Vrc6_Apu::addr_step; - if ( osc < Nes_Vrc6_Apu::osc_count && reg < Nes_Vrc6_Apu::reg_count ) - { - vrc6->write_osc( time(), osc, reg, data ); - return; - } - } - } - #endif - - // unmapped write - - #ifndef NDEBUG - { - // some games write to $8000 and $8001 repeatedly - if ( addr == 0x8000 || addr == 0x8001 ) return; - - // probably namco sound mistakenly turned on in mck - if ( addr == 0x4800 || addr == 0xF800 ) return; - - // memory mapper? - if ( addr == 0xFFF8 ) return; - - dprintf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data ); - } - #endif -} - -blargg_err_t Nsf_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( low_mem, 0, sizeof low_mem ); - memset( sram, 0, sizeof sram ); - - cpu::reset( unmapped_code ); // also maps low_mem - cpu::map_code( sram_addr, sizeof sram, sram ); - for ( int i = 0; i < bank_count; ++i ) - cpu_write( bank_select_addr + i, initial_banks [i] ); - - apu.reset( pal_only, (header_.speed_flags & 0x20) ? 0x3F : 0 ); - apu.write_register( 0, 0x4015, 0x0F ); - apu.write_register( 0, 0x4017, (header_.speed_flags & 0x10) ? 0x80 : 0 ); - #if !NSF_EMU_APU_ONLY - { - if ( namco ) namco->reset(); - if ( vrc6 ) vrc6 ->reset(); - if ( fme7 ) fme7 ->reset(); - } - #endif - - play_ready = 4; - play_extra = 0; - next_play = play_period / clock_divisor; - - saved_state.pc = badop_addr; - low_mem [0x1FF] = (badop_addr - 1) >> 8; - low_mem [0x1FE] = (badop_addr - 1) & 0xFF; - r.sp = 0xFD; - r.pc = init_addr; - r.a = track; - r.x = pal_only; - - return 0; -} - -blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) -{ - set_time( 0 ); - while ( time() < duration ) - { - nes_time_t end = min( next_play, duration ); - end = min( end, time() + 32767 ); // allows CPU to use 16-bit time delta - if ( cpu::run( end ) ) - { - if ( r.pc != badop_addr ) - { - set_warning( "Emulation error (illegal instruction)" ); - r.pc++; - } - else - { - play_ready = 1; - if ( saved_state.pc != badop_addr ) - { - cpu::r = saved_state; - saved_state.pc = badop_addr; - } - else - { - set_time( end ); - } - } - } - - if ( time() >= next_play ) - { - nes_time_t period = (play_period + play_extra) / clock_divisor; - play_extra = play_period - period * clock_divisor; - next_play += period; - if ( play_ready && !--play_ready ) - { - check( saved_state.pc == badop_addr ); - if ( r.pc != badop_addr ) - saved_state = cpu::r; - - r.pc = play_addr; - low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8; - low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF; - GME_FRAME_HOOK( this ); - } - } - } - - if ( cpu::error_count() ) - { - cpu::clear_error_count(); - set_warning( "Emulation error (illegal instruction)" ); - } - - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - if ( next_play < 0 ) - next_play = 0; - - apu.end_frame( duration ); - - #if !NSF_EMU_APU_ONLY - { - if ( namco ) namco->end_frame( duration ); - if ( vrc6 ) vrc6 ->end_frame( duration ); - if ( fme7 ) fme7 ->end_frame( duration ); - } - #endif - - return 0; -} + set_type( gme_nsf_type ); + set_silence_lookahead( 6 ); + set_gain( 1.4 ); + set_equalizer( nes_eq ); +} + +Nsf_Emu::~Nsf_Emu() +{ + unload(); +} + +void Nsf_Emu::unload() +{ + core_.unload(); + Music_Emu::unload(); +} + +// Track info + +static void copy_nsf_fields( Nsf_Emu::header_t const& h, track_info_t* out ) +{ + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, author ); + GME_COPY_FIELD( h, out, copyright ); + if ( h.chip_flags ) + Music_Emu::copy_field_( out->system, "Famicom" ); +} + +void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.vers, sizeof(h.vers) ); + out.hash_( &h.track_count, sizeof(h.track_count) ); + out.hash_( &h.first_track, sizeof(h.first_track) ); + out.hash_( &h.load_addr[0], sizeof(h.load_addr) ); + out.hash_( &h.init_addr[0], sizeof(h.init_addr) ); + out.hash_( &h.play_addr[0], sizeof(h.play_addr) ); + out.hash_( &h.ntsc_speed[0], sizeof(h.ntsc_speed) ); + out.hash_( &h.banks[0], sizeof(h.banks) ); + out.hash_( &h.pal_speed[0], sizeof(h.pal_speed) ); + out.hash_( &h.speed_flags, sizeof(h.speed_flags) ); + out.hash_( &h.chip_flags, sizeof(h.chip_flags) ); + out.hash_( &h.unused[0], sizeof(h.unused) ); + + out.hash_( data, data_size ); +} + +blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const +{ + copy_nsf_fields( header(), out ); + return blargg_ok; +} + +static blargg_err_t check_nsf_header( Nsf_Emu::header_t const& h ) +{ + if ( !h.valid_tag() ) + return blargg_err_file_type; + return blargg_ok; +} + +struct Nsf_File : Gme_Info_ +{ + Nsf_Emu::header_t const* h; + + Nsf_File() { set_type( gme_nsf_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + h = ( Nsf_Emu::header_t const* ) begin; + + if ( h->vers != 1 ) + set_warning( "Unknown file version" ); + + int unsupported_chips = ~Nsf_Core::chips_mask; + #if NSF_EMU_NO_VRC7 + unsupported_chips |= Nsf_Emu::header_t::vrc7_mask; + #endif + if ( h->chip_flags & unsupported_chips ) + set_warning( "Uses unsupported audio expansion hardware" ); + + set_track_count( h->track_count ); + return check_nsf_header( *h ); + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_nsf_fields( *h, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_nsf_file( *h, file_begin() + h->size, file_end() - file_begin() - h->size, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_nsf_emu () { return BLARGG_NEW Nsf_Emu ; } +static Music_Emu* new_nsf_file() { return BLARGG_NEW Nsf_File; } + +gme_type_t_ const gme_nsf_type [1] = {{ "Nintendo NES", 0, &new_nsf_emu, &new_nsf_file, "NSF", 1 }}; + +// Setup + +void Nsf_Emu::set_tempo_( double t ) +{ + core_.set_tempo( t ); +} + +void Nsf_Emu::append_voices( const char* const names [], int const types [], int count ) +{ + assert( voice_count_ + count < max_voices ); + for ( int i = 0; i < count; i++ ) + { + voice_names_ [voice_count_ + i] = names [i]; + voice_types_ [voice_count_ + i] = types [i]; + } + voice_count_ += count; + set_voice_count( voice_count_ ); + set_voice_types( voice_types_ ); +} + +blargg_err_t Nsf_Emu::init_sound() +{ + voice_count_ = 0; + set_voice_names( voice_names_ ); + + { + int const count = Nes_Apu::osc_count; + static const char* const names [Nes_Apu::osc_count] = { + "Square 1", "Square 2", "Triangle", "Noise", "DMC" + }; + static int const types [count] = { + wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1 + }; + append_voices( names, types, count ); + } + + // Make adjusted_gain * 0.75 = 1.0 so usual APU and one sound chip uses 1.0 + double adjusted_gain = 1.0 / 0.75 * gain(); + +#if !NSF_EMU_APU_ONLY + // TODO: order of chips here must match that in set_voice() + + if ( core_.vrc6_apu() ) + { + int const count = Nes_Vrc6_Apu::osc_count; + static const char* const names [count] = { + "Square 3", "Square 4", "Saw Wave" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.fme7_apu() ) + { + int const count = Nes_Fme7_Apu::osc_count; + static const char* const names [count] = { + "Square 3", "Square 4", "Square 5" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.mmc5_apu() ) + { + int const count = Nes_Mmc5_Apu::osc_count; + static const char* const names [count] = { + "Square 3", "Square 4", "PCM" + }; + static int const types [count] = { + wave_type+3, wave_type+4, mixed_type+2 + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.fds_apu() ) + { + int const count = Nes_Fds_Apu::osc_count; + static const char* const names [count] = { + "FM" + }; + static int const types [count] = { + wave_type+0 + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.namco_apu() ) + { + int const count = Nes_Namco_Apu::osc_count; + static const char* const names [count] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, wave_type+ 6, + wave_type+7, wave_type+8, wave_type+9, wave_type+10, + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.vrc7_apu() ) + { + int const count = Nes_Vrc7_Apu::osc_count; + static const char* const names [count] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6" + }; + static int const types [count] = { + wave_type+3, wave_type+4, wave_type+5, wave_type+6, + wave_type+7, wave_type+8 + }; + append_voices( names, types, count ); + adjusted_gain *= 0.75; + } + + if ( core_.vrc7_apu() ) core_.vrc7_apu() ->volume( adjusted_gain ); + if ( core_.namco_apu() ) core_.namco_apu()->volume( adjusted_gain ); + if ( core_.vrc6_apu() ) core_.vrc6_apu() ->volume( adjusted_gain ); + if ( core_.fme7_apu() ) core_.fme7_apu() ->volume( adjusted_gain ); + if ( core_.mmc5_apu() ) core_.mmc5_apu() ->volume( adjusted_gain ); + if ( core_.fds_apu() ) core_.fds_apu() ->volume( adjusted_gain ); +#endif + + if ( adjusted_gain > gain() ) + adjusted_gain = gain(); // only occurs if no other sound chips + + core_.nes_apu()->volume( adjusted_gain ); + + return blargg_ok; +} + +blargg_err_t Nsf_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( core_.load( in ) ); + set_track_count( header().track_count ); + RETURN_ERR( check_nsf_header( header() ) ); + set_warning( core_.warning() ); + RETURN_ERR( init_sound() ); + set_tempo( tempo() ); + return setup_buffer( (int) (header().clock_rate() + 0.5) ); +} + +void Nsf_Emu::update_eq( blip_eq_t const& eq ) +{ + core_.nes_apu()->treble_eq( eq ); + + #if !NSF_EMU_APU_ONLY + { + if ( core_.namco_apu() ) core_.namco_apu()->treble_eq( eq ); + if ( core_.vrc6_apu() ) core_.vrc6_apu() ->treble_eq( eq ); + if ( core_.fme7_apu() ) core_.fme7_apu() ->treble_eq( eq ); + if ( core_.mmc5_apu() ) core_.mmc5_apu() ->treble_eq( eq ); + if ( core_.fds_apu() ) core_.fds_apu() ->treble_eq( eq ); + if ( core_.vrc7_apu() ) core_.vrc7_apu() ->treble_eq( eq ); + } + #endif +} + +void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) +{ + #define HANDLE_CHIP( chip ) \ + if ( chip && (i -= chip->osc_count) < 0 )\ + {\ + chip->set_output( i + chip->osc_count, buf );\ + return;\ + }\ + + HANDLE_CHIP( core_.nes_apu() ); + + #if !NSF_EMU_APU_ONLY + { + // TODO: order of chips here must match that in init_sound() + HANDLE_CHIP( core_.vrc6_apu() ); + HANDLE_CHIP( core_.fme7_apu() ); + HANDLE_CHIP( core_.mmc5_apu() ); + HANDLE_CHIP( core_.fds_apu() ); + HANDLE_CHIP( core_.namco_apu() ); + HANDLE_CHIP( core_.vrc7_apu() ); + } + #endif +} + +blargg_err_t Nsf_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + return core_.start_track( track ); +} + +blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) +{ + core_.end_frame( duration ); + const char* w = core_.warning(); + if ( w ) + set_warning( w ); + return blargg_ok; +} + +blargg_err_t Nsf_Emu::hash_( Hash_Function& out ) const +{ + hash_nsf_file( header(), core_.rom_().begin(), core_.rom_().file_size(), out ); + return blargg_ok; +} \ No newline at end of file diff --git a/Frameworks/GME/gme/Nsf_Emu.h b/Frameworks/GME/gme/Nsf_Emu.h old mode 100755 new mode 100644 index e06b91727..9599a2a25 --- a/Frameworks/GME/gme/Nsf_Emu.h +++ b/Frameworks/GME/gme/Nsf_Emu.h @@ -1,106 +1,55 @@ // Nintendo NES/Famicom NSF music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef NSF_EMU_H #define NSF_EMU_H #include "Classic_Emu.h" -#include "Nes_Apu.h" -#include "Nes_Cpu.h" +#include "Nsf_Core.h" -class Nsf_Emu : private Nes_Cpu, public Classic_Emu { - typedef Nes_Cpu cpu; +void hash_nsf_file( Nsf_Core::header_t const& h, unsigned char const* data, int data_size, Music_Emu::Hash_Function& out ); + +class Nsf_Emu : public Classic_Emu { public: // Equalizer profiles for US NES and Japanese Famicom static equalizer_t const nes_eq; static equalizer_t const famicom_eq; - // NSF file header - enum { header_size = 0x80 }; - struct header_t - { - char tag [5]; - byte vers; - byte track_count; - byte first_track; - byte load_addr [2]; - byte init_addr [2]; - byte play_addr [2]; - char game [32]; - char author [32]; - char copyright [32]; - byte ntsc_speed [2]; - byte banks [8]; - byte pal_speed [2]; - byte speed_flags; - byte chip_flags; - byte unused [4]; - }; + // NSF file header (see Nsf_Impl.h) + typedef Nsf_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return header_; } + header_t const& header() const { return core_.header(); } + + blargg_err_t hash_( Hash_Function& ) const; static gme_type_t static_type() { return gme_nsf_type; } -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - + Nsf_Core& core() { return core_; } + public: Nsf_Emu(); ~Nsf_Emu(); - Nes_Apu* apu_() { return &apu; } + virtual void unload(); + protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_( Data_Reader& ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); - void unload(); -protected: - enum { bank_count = 8 }; - byte initial_banks [bank_count]; - nes_addr_t init_addr; - nes_addr_t play_addr; - double clock_rate_; - bool pal_only; - - // timing - Nes_Cpu::registers_t saved_state; - nes_time_t next_play; - nes_time_t play_period; - int play_extra; - int play_ready; - - enum { rom_begin = 0x8000 }; - enum { bank_select_addr = 0x5FF8 }; - enum { bank_size = 0x1000 }; - Rom_Data rom; - -public: private: friend class Nes_Cpu; - void cpu_jsr( nes_addr_t ); - int cpu_read( nes_addr_t ); - void cpu_write( nes_addr_t, int ); - void cpu_write_misc( nes_addr_t, int ); - enum { badop_addr = bank_select_addr }; + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); private: - class Nes_Namco_Apu* namco; - class Nes_Vrc6_Apu* vrc6; - class Nes_Fme7_Apu* fme7; - Nes_Apu apu; - static int pcm_read( void*, nes_addr_t ); + enum { max_voices = 32 }; + const char* voice_names_ [32]; + int voice_types_ [32]; + int voice_count_; + Nsf_Core core_; + blargg_err_t init_sound(); - - header_t header_; - - enum { sram_addr = 0x6000 }; - byte sram [0x2000]; - byte unmapped_code [Nes_Cpu::page_size + 8]; + void append_voices( const char* const names [], int const types [], int count ); }; #endif diff --git a/Frameworks/GME/gme/Nsfe_Emu.cpp b/Frameworks/GME/gme/Nsfe_Emu.cpp old mode 100755 new mode 100644 index 0a785e609..af99fc24a --- a/Frameworks/GME/gme/Nsfe_Emu.cpp +++ b/Frameworks/GME/gme/Nsfe_Emu.cpp @@ -1,330 +1,329 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Nsfe_Emu.h" - -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2005-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Nsfe_Info::Nsfe_Info() { playlist_disabled = false; } - -Nsfe_Info::~Nsfe_Info() { } - -inline void Nsfe_Info::unload() -{ - track_name_data.clear(); - track_names.clear(); - playlist.clear(); - track_times.clear(); -} - -// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ? -void Nsfe_Info::disable_playlist( bool b ) -{ - playlist_disabled = b; - info.track_count = playlist.size(); - if ( !info.track_count || playlist_disabled ) - info.track_count = actual_track_count_; -} - -int Nsfe_Info::remap_track( int track ) const -{ - if ( !playlist_disabled && (unsigned) track < playlist.size() ) - track = playlist [track]; - return track; -} - -// Read multiple strings and separate into individual strings -static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector& chars, - blargg_vector& strs ) -{ - RETURN_ERR( chars.resize( size + 1 ) ); - chars [size] = 0; // in case last string doesn't have terminator - RETURN_ERR( in.read( &chars [0], size ) ); - - RETURN_ERR( strs.resize( 128 ) ); - int count = 0; - for ( int i = 0; i < size; i++ ) - { - if ( (int) strs.size() <= count ) - RETURN_ERR( strs.resize( count * 2 ) ); - strs [count++] = &chars [i]; - while ( i < size && chars [i] ) - i++; - } - - return strs.resize( count ); -} - -// Copy in to out, where out has out_max characters allocated. Truncate to -// out_max - 1 characters. -static void copy_str( const char* in, char* out, int out_max ) -{ - out [out_max - 1] = 0; - strncpy( out, in, out_max - 1 ); -} - -struct nsfe_info_t -{ - byte load_addr [2]; - byte init_addr [2]; - byte play_addr [2]; - byte speed_flags; - byte chip_flags; - byte track_count; - byte first_track; - byte unused [6]; -}; - -blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) -{ - int const nsfe_info_size = 16; - assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); - - // check header - byte signature [4]; - blargg_err_t err = in.read( signature, sizeof signature ); - if ( err ) - return (err == in.eof_error ? gme_wrong_file_type : err); - if ( memcmp( signature, "NSFE", 4 ) ) - return gme_wrong_file_type; - - // free previous info - track_name_data.clear(); - track_names.clear(); - playlist.clear(); - track_times.clear(); - - // default nsf header - static const Nsf_Emu::header_t base_header = - { - {'N','E','S','M','\x1A'},// tag - 1, // version - 1, 1, // track count, first track - {0,0},{0,0},{0,0}, // addresses - "","","", // strings - {0x1A, 0x41}, // NTSC rate - {0,0,0,0,0,0,0,0}, // banks - {0x20, 0x4E}, // PAL rate - 0, 0, // flags - {0,0,0,0} // unused - }; - Nsf_Emu::header_t& header = info; - header = base_header; - - // parse tags - int phase = 0; - while ( phase != 3 ) - { - // read size and tag - byte block_header [2] [4]; - RETURN_ERR( in.read( block_header, sizeof block_header ) ); - blargg_long size = get_le32( block_header [0] ); - blargg_long tag = get_le32( block_header [1] ); - - //dprintf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); - - switch ( tag ) - { - case BLARGG_4CHAR('O','F','N','I'): { - check( phase == 0 ); - if ( size < 8 ) - return "Corrupt file"; - - nsfe_info_t finfo; - finfo.track_count = 1; - finfo.first_track = 0; - - RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) ); - if ( size > nsfe_info_size ) - RETURN_ERR( in.skip( size - nsfe_info_size ) ); - phase = 1; - info.speed_flags = finfo.speed_flags; - info.chip_flags = finfo.chip_flags; - info.track_count = finfo.track_count; - this->actual_track_count_ = finfo.track_count; - info.first_track = finfo.first_track; - memcpy( info.load_addr, finfo.load_addr, 2 * 3 ); - break; - } - - case BLARGG_4CHAR('K','N','A','B'): - if ( size > (int) sizeof info.banks ) - return "Corrupt file"; - RETURN_ERR( in.read( info.banks, size ) ); - break; - - case BLARGG_4CHAR('h','t','u','a'): { - blargg_vector chars; - blargg_vector strs; - RETURN_ERR( read_strs( in, size, chars, strs ) ); - int n = strs.size(); - - if ( n > 3 ) - copy_str( strs [3], info.dumper, sizeof info.dumper ); - - if ( n > 2 ) - copy_str( strs [2], info.copyright, sizeof info.copyright ); - - if ( n > 1 ) - copy_str( strs [1], info.author, sizeof info.author ); - - if ( n > 0 ) - copy_str( strs [0], info.game, sizeof info.game ); - - break; - } - - case BLARGG_4CHAR('e','m','i','t'): - RETURN_ERR( track_times.resize( size / 4 ) ); - RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); - break; - - case BLARGG_4CHAR('l','b','l','t'): - RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); - break; - - case BLARGG_4CHAR('t','s','l','p'): - RETURN_ERR( playlist.resize( size ) ); - RETURN_ERR( in.read( &playlist [0], size ) ); - break; - - case BLARGG_4CHAR('A','T','A','D'): { - check( phase == 1 ); - phase = 2; - if ( !nsf_emu ) - { - RETURN_ERR( in.skip( size ) ); - } - else - { - Subset_Reader sub( &in, size ); // limit emu to nsf data - Remaining_Reader rem( &header, Nsf_Emu::header_size, &sub ); - RETURN_ERR( nsf_emu->load( rem ) ); - check( rem.remain() == 0 ); - } - break; - } - - case BLARGG_4CHAR('D','N','E','N'): - check( phase == 2 ); - phase = 3; - break; - - default: - // tags that can be skipped start with a lowercase character - check( islower( (tag >> 24) & 0xFF ) ); - RETURN_ERR( in.skip( size ) ); - break; - } - } - - return 0; -} - -blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const -{ - int remapped = remap_track( track ); - if ( (unsigned) remapped < track_times.size() ) - { - long length = (BOOST::int32_t) get_le32( track_times [remapped] ); - if ( length > 0 ) - out->length = length; - } - if ( (unsigned) remapped < track_names.size() ) - Gme_File::copy_field_( out->song, track_names [remapped] ); - - GME_COPY_FIELD( info, out, game ); - GME_COPY_FIELD( info, out, author ); - GME_COPY_FIELD( info, out, copyright ); - GME_COPY_FIELD( info, out, dumper ); - return 0; -} - -Nsfe_Emu::Nsfe_Emu() -{ - loading = false; - set_type( gme_nsfe_type ); -} - -Nsfe_Emu::~Nsfe_Emu() { } - -void Nsfe_Emu::unload() -{ - if ( !loading ) - info.unload(); // TODO: extremely hacky! - Nsf_Emu::unload(); -} - -blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const -{ - return info.track_info_( out, track ); -} - -struct Nsfe_File : Gme_Info_ -{ - Nsfe_Info info; - - Nsfe_File() { set_type( gme_nsfe_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - RETURN_ERR( info.load( in, 0 ) ); - info.disable_playlist( false ); - set_track_count( info.info.track_count ); - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int track ) const - { - return info.track_info_( out, track ); - } -}; - -static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } -static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } - -gme_type_t_ const gme_nsfe_type [1] = { "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }; - -blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) -{ - if ( loading ) - return Nsf_Emu::load_( in ); - - // TODO: this hacky recursion-avoidance could have subtle problems - loading = true; - blargg_err_t err = info.load( in, this ); - loading = false; - disable_playlist( false ); - return err; -} - -void Nsfe_Emu::disable_playlist( bool b ) -{ - info.disable_playlist( b ); - set_track_count( info.info.track_count ); -} - -void Nsfe_Emu::clear_playlist_() -{ - disable_playlist(); - Nsf_Emu::clear_playlist_(); -} - -blargg_err_t Nsfe_Emu::start_track_( int track ) -{ - return Nsf_Emu::start_track_( info.remap_track( track ) ); -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Nsfe_Emu.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2005-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Nsfe_Info::Nsfe_Info() { playlist_disabled = false; } + +Nsfe_Info::~Nsfe_Info() { } + +inline void Nsfe_Info::unload() +{ + data.clear(); + track_name_data.clear(); + track_names.clear(); + playlist.clear(); + track_times.clear(); +} + +// TODO: if no playlist, treat as if there is a playlist that is just 1,2,3,4,5... ? +void Nsfe_Info::disable_playlist( bool b ) +{ + playlist_disabled = b; + info.track_count = playlist.size(); + if ( !info.track_count || playlist_disabled ) + info.track_count = actual_track_count_; +} + +int Nsfe_Info::remap_track( int track ) const +{ + if ( !playlist_disabled && (unsigned) track < playlist.size() ) + track = playlist [track]; + return track; +} + +// Read multiple strings and separate into individual strings +static blargg_err_t read_strs( Data_Reader& in, int size, blargg_vector& chars, + blargg_vector& strs ) +{ + RETURN_ERR( chars.resize( size + 1 ) ); + chars [size] = 0; // in case last string doesn't have terminator + RETURN_ERR( in.read( &chars [0], size ) ); + + RETURN_ERR( strs.resize( 128 ) ); + int count = 0; + for ( int i = 0; i < size; i++ ) + { + if ( (int) strs.size() <= count ) + RETURN_ERR( strs.resize( count * 2 ) ); + strs [count++] = &chars [i]; + while ( i < size && chars [i] ) + i++; + } + + return strs.resize( count ); +} + +// Copy in to out, where out has out_max characters allocated. Truncate to +// out_max - 1 characters. +static void copy_str( const char in [], char out [], int out_max ) +{ + out [out_max - 1] = 0; + strncpy( out, in, out_max - 1 ); +} + +struct nsfe_info_t +{ + byte load_addr [2]; + byte init_addr [2]; + byte play_addr [2]; + byte speed_flags; + byte chip_flags; + byte track_count; + byte first_track; + byte unused [6]; +}; + +blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsfe_Emu* nsf_emu ) +{ + int const nsfe_info_size = 16; + assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); + + // check header + byte signature [4]; + blargg_err_t err = in.read( signature, sizeof signature ); + if ( err ) + return (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err); + if ( memcmp( signature, "NSFE", 4 ) ) + return blargg_err_file_type; + + // free previous info + track_name_data.clear(); + track_names.clear(); + playlist.clear(); + track_times.clear(); + + // default nsf header + static const Nsf_Emu::header_t base_header = + { + {'N','E','S','M','\x1A'},// tag + 1, // version + 1, 1, // track count, first track + {0,0},{0,0},{0,0}, // addresses + "","","", // strings + {0x1A, 0x41}, // NTSC rate + {0,0,0,0,0,0,0,0}, // banks + {0x20, 0x4E}, // PAL rate + 0, 0, // flags + {0,0,0,0} // unused + }; + Nsf_Emu::header_t& header = info; + header = base_header; + + // parse tags + int phase = 0; + while ( phase != 3 ) + { + // read size and tag + byte block_header [2] [4]; + RETURN_ERR( in.read( block_header, sizeof block_header ) ); + int size = get_le32( block_header [0] ); + int tag = get_le32( block_header [1] ); + + //dprintf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); + + switch ( tag ) + { + case BLARGG_4CHAR('O','F','N','I'): { + check( phase == 0 ); + if ( size < 8 ) + return blargg_err_file_corrupt; + + nsfe_info_t finfo; + finfo.track_count = 1; + finfo.first_track = 0; + + RETURN_ERR( in.read( &finfo, min( size, (int) nsfe_info_size ) ) ); + if ( size > nsfe_info_size ) + RETURN_ERR( in.skip( size - nsfe_info_size ) ); + phase = 1; + info.speed_flags = finfo.speed_flags; + info.chip_flags = finfo.chip_flags; + info.track_count = finfo.track_count; + this->actual_track_count_ = finfo.track_count; + info.first_track = finfo.first_track; + memcpy( info.load_addr, finfo.load_addr, 2 * 3 ); + break; + } + + case BLARGG_4CHAR('K','N','A','B'): + if ( size > (int) sizeof info.banks ) + return blargg_err_file_corrupt; + RETURN_ERR( in.read( info.banks, size ) ); + break; + + case BLARGG_4CHAR('h','t','u','a'): { + blargg_vector chars; + blargg_vector strs; + RETURN_ERR( read_strs( in, size, chars, strs ) ); + int n = strs.size(); + + if ( n > 3 ) + copy_str( strs [3], info.dumper, sizeof info.dumper ); + + if ( n > 2 ) + copy_str( strs [2], info.copyright, sizeof info.copyright ); + + if ( n > 1 ) + copy_str( strs [1], info.author, sizeof info.author ); + + if ( n > 0 ) + copy_str( strs [0], info.game, sizeof info.game ); + + break; + } + + case BLARGG_4CHAR('e','m','i','t'): + RETURN_ERR( track_times.resize( size / 4 ) ); + RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); + break; + + case BLARGG_4CHAR('l','b','l','t'): + RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); + break; + + case BLARGG_4CHAR('t','s','l','p'): + RETURN_ERR( playlist.resize( size ) ); + RETURN_ERR( in.read( &playlist [0], size ) ); + break; + + case BLARGG_4CHAR('A','T','A','D'): { + check( phase == 1 ); + phase = 2; + if ( !nsf_emu ) + { + RETURN_ERR( data.resize( size ) ); + RETURN_ERR( in.read( data.begin(), size ) ); + } + else + { + Subset_Reader sub( &in, size ); // limit emu to nsf data + Remaining_Reader rem( &header, header.size, &sub ); + RETURN_ERR( nsf_emu->Nsf_Emu::load_( rem ) ); + check( rem.remain() == 0 ); + } + break; + } + + case BLARGG_4CHAR('D','N','E','N'): + check( phase == 2 ); + phase = 3; + break; + + default: + // tags that can be skipped start with a lowercase character + check( islower( (tag >> 24) & 0xFF ) ); + RETURN_ERR( in.skip( size ) ); + break; + } + } + + return blargg_ok; +} + +blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const +{ + int remapped = remap_track( track ); + if ( (unsigned) remapped < track_times.size() ) + { + int length = (BOOST::int32_t) get_le32( track_times [remapped] ); + if ( length > 0 ) + out->length = length; + } + if ( (unsigned) remapped < track_names.size() ) + Gme_File::copy_field_( out->song, track_names [remapped] ); + + GME_COPY_FIELD( info, out, game ); + GME_COPY_FIELD( info, out, author ); + GME_COPY_FIELD( info, out, copyright ); + GME_COPY_FIELD( info, out, dumper ); + return blargg_ok; +} + +Nsfe_Emu::Nsfe_Emu() +{ + set_type( gme_nsfe_type ); +} + +Nsfe_Emu::~Nsfe_Emu() { } + +void Nsfe_Emu::unload() +{ + info.unload(); + Nsf_Emu::unload(); +} + +blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const +{ + return info.track_info_( out, track ); +} + +struct Nsfe_File : Gme_Info_ +{ + Nsfe_Info info; + + Nsfe_File() { set_type( gme_nsfe_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + RETURN_ERR( info.load( in, 0 ) ); + info.disable_playlist( false ); + set_track_count( info.info.track_count ); + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int track ) const + { + return info.track_info_( out, track ); + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_nsf_file( info.info, info.data.begin(), info.data.end() - info.data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_nsfe_emu () { return BLARGG_NEW Nsfe_Emu ; } +static Music_Emu* new_nsfe_file() { return BLARGG_NEW Nsfe_File; } + +gme_type_t_ const gme_nsfe_type [1] = {{ "Nintendo NES", 0, &new_nsfe_emu, &new_nsfe_file, "NSFE", 1 }}; + +blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) +{ + RETURN_ERR( info.load( in, this ) ); + disable_playlist_( false ); + return blargg_ok; +} + +void Nsfe_Emu::disable_playlist_( bool b ) +{ + info.disable_playlist( b ); + set_track_count( info.info.track_count ); +} + +void Nsfe_Emu::clear_playlist_() +{ + disable_playlist_( true ); + Nsf_Emu::clear_playlist_(); +} + +blargg_err_t Nsfe_Emu::start_track_( int track ) +{ + return Nsf_Emu::start_track_( info.remap_track( track ) ); +} diff --git a/Frameworks/GME/gme/Nsfe_Emu.h b/Frameworks/GME/gme/Nsfe_Emu.h old mode 100755 new mode 100644 index 561c3be0f..a166bfc66 --- a/Frameworks/GME/gme/Nsfe_Emu.h +++ b/Frameworks/GME/gme/Nsfe_Emu.h @@ -1,16 +1,17 @@ // Nintendo NES/Famicom NSFE music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef NSFE_EMU_H #define NSFE_EMU_H #include "blargg_common.h" #include "Nsf_Emu.h" +class Nsfe_Emu; // Allows reading info from NSFE file without creating emulator class Nsfe_Info { public: - blargg_err_t load( Data_Reader&, Nsf_Emu* ); + blargg_err_t load( Data_Reader&, Nsfe_Emu* ); struct info_t : Nsf_Emu::header_t { @@ -19,7 +20,9 @@ public: char copyright [256]; char dumper [256]; } info; - + + blargg_vector data; + void disable_playlist( bool = true ); blargg_err_t track_info_( track_info_t* out, int track ) const; @@ -28,8 +31,11 @@ public: void unload(); +// Implementation +public: Nsfe_Info(); ~Nsfe_Info(); + BLARGG_DISABLE_NOTHROW private: blargg_vector track_name_data; blargg_vector track_names; @@ -43,26 +49,26 @@ class Nsfe_Emu : public Nsf_Emu { public: static gme_type_t static_type() { return gme_nsfe_type; } -public: - // deprecated struct header_t { char tag [4]; }; - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - void disable_playlist( bool = true ); // use clear_playlist() + +// Implementation public: Nsfe_Emu(); ~Nsfe_Emu(); + virtual void unload(); + protected: - blargg_err_t load_( Data_Reader& ); - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t start_track_( int ); - void unload(); - void clear_playlist_(); + virtual blargg_err_t load_( Data_Reader& ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t start_track_( int ); + virtual void clear_playlist_(); + private: Nsfe_Info info; - bool loading; + + void disable_playlist_( bool b ); + friend class Nsfe_Info; }; #endif diff --git a/Frameworks/GME/gme/Sap_Apu.cpp b/Frameworks/GME/gme/Sap_Apu.cpp old mode 100755 new mode 100644 index 23fa90720..6c7a202a4 --- a/Frameworks/GME/gme/Sap_Apu.cpp +++ b/Frameworks/GME/gme/Sap_Apu.cpp @@ -1,10 +1,8 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ #include "Sap_Apu.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -19,9 +17,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ int const max_frequency = 12000; // pure waves above this frequency are silenced -static void gen_poly( blargg_ulong mask, int count, byte* out ) +static void gen_poly( unsigned mask, int count, byte out [] ) { - blargg_ulong n = 1; + unsigned n = 1; do { int bits = 0; @@ -30,7 +28,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) { // implemented using "Galios configuration" bits |= (n & 1) << b; - n = (n >> 1) ^ (mask & -(n & 1)); + n = (n >> 1) ^ (mask * (n & 1)); } while ( b++ < 7 ); *out++ = bits; @@ -40,16 +38,16 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) // poly5 int const poly5_len = (1 << 5) - 1; -blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; -blargg_ulong const poly5 = 0x167C6EA1; +unsigned const poly5_mask = (1U << poly5_len) - 1; +unsigned const poly5 = 0x167C6EA1; -inline blargg_ulong run_poly5( blargg_ulong in, int shift ) +inline unsigned run_poly5( unsigned in, int shift ) { return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); } #define POLY_MASK( width, tap1, tap2 ) \ - ((1UL << (width - 1 - tap1)) | (1UL << (width - 1 - tap2))) + ((1U << (width - 1 - tap1)) | (1U << (width - 1 - tap2))) Sap_Apu_Impl::Sap_Apu_Impl() { @@ -61,20 +59,25 @@ Sap_Apu_Impl::Sap_Apu_Impl() { byte poly5 [4]; gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 ); - blargg_ulong n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L + - poly5 [1] * 0x100L + poly5 [0]; - blargg_ulong rev = n & 1; + unsigned n = poly5 [3] * 0x1000000 + poly5 [2] * 0x10000 + + poly5 [1] * 0x100 + poly5 [0]; + unsigned rev = n & 1; for ( int i = 1; i < poly5_len; i++ ) rev |= (n >> i & 1) << (poly5_len - i); dprintf( "poly5: 0x%08lX\n", rev ); } } +void Sap_Apu::set_output( Blip_Buffer* b ) +{ + for ( int i = 0; i < osc_count; ++i ) + set_output( i, b ); +} + Sap_Apu::Sap_Apu() { - impl = 0; - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, 0 ); + impl = NULL; + set_output( NULL ); } void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) @@ -102,14 +105,14 @@ inline void Sap_Apu::calc_periods() osc_t* const osc = &oscs [i]; int const osc_reload = osc->regs [0]; // cache - blargg_long period = (osc_reload + 1) * divider; + int period = (osc_reload + 1) * divider; static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 }; if ( this->control & fast_bits [i] ) { period = osc_reload + 4; if ( i & 1 ) { - period = osc_reload * 0x100L + osc [-1].regs [0] + 7; + period = osc_reload * 0x100 + osc [-1].regs [0] + 7; if ( !(this->control & fast_bits [i - 1]) ) period = (period - 6) * divider; @@ -146,8 +149,6 @@ void Sap_Apu::run_until( blip_time_t end_time ) Blip_Buffer* output = osc->output; if ( output ) { - output->set_modified(); - int const osc_control = osc->regs [1]; // cache int volume = (osc_control & 0x0F) * 2; if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency @@ -160,6 +161,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) if ( delta ) { osc->last_amp = volume; + output->set_modified(); impl->synth.offset( last_time, delta, output ); } @@ -208,7 +210,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) poly_inc -= poly_len; // allows more optimized inner loop below // square/poly5 wave - blargg_ulong wave = poly5; + unsigned wave = poly5; check( poly5 & 1 ); // low bit is set for pure wave int poly5_inc = 0; if ( !(osc_control & 0x80) ) @@ -217,6 +219,8 @@ void Sap_Apu::run_until( blip_time_t end_time ) poly5_inc = period % poly5_len; } + output->set_modified(); + // Run wave and high pass interleved with each catching up to the other. // Disabled high pass has no performance effect since inner wave loop // makes no compromise for high pass, and only runs once in that case. @@ -247,7 +251,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) { if ( wave & 1 ) { - int amp = volume & -(poly [poly_pos >> 3] >> (poly_pos & 7) & 1); + int amp = volume * (poly [poly_pos >> 3] >> (poly_pos & 7) & 1); if ( (poly_pos += poly_inc) < 0 ) poly_pos += poly_len; int delta = amp - osc_last_amp; @@ -281,7 +285,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) blip_time_t remain = end_time - time; if ( remain > 0 ) { - blargg_long count = (remain + period - 1) / period; + int count = (remain + period - 1) / period; osc->phase ^= count; time += count * period; } @@ -296,11 +300,11 @@ void Sap_Apu::run_until( blip_time_t end_time ) polym_pos += duration; // will get %'d on next call } -void Sap_Apu::write_data( blip_time_t time, unsigned addr, int data ) +void Sap_Apu::write_data( blip_time_t time, int addr, int data ) { run_until( time ); - int i = (addr ^ 0xD200) >> 1; - if ( i < osc_count ) + int i = (addr - 0xD200) >> 1; + if ( (unsigned) i < osc_count ) { oscs [i].regs [addr & 1] = data; } @@ -331,4 +335,5 @@ void Sap_Apu::end_frame( blip_time_t end_time ) run_until( end_time ); last_time -= end_time; + assert( last_time >= 0 ); } diff --git a/Frameworks/GME/gme/Sap_Apu.h b/Frameworks/GME/gme/Sap_Apu.h old mode 100755 new mode 100644 index c71ce31ed..e53834332 --- a/Frameworks/GME/gme/Sap_Apu.h +++ b/Frameworks/GME/gme/Sap_Apu.h @@ -1,6 +1,6 @@ // Atari POKEY sound chip emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef SAP_APU_H #define SAP_APU_H @@ -11,19 +11,40 @@ class Sap_Apu_Impl; class Sap_Apu { public: +// Basics + + // Sets buffer to generate sound into, or 0 to mute + void set_output( Blip_Buffer* ); + + // Emulates to time t, then writes data to addr + void write_data( blip_time_t t, int addr, int data ); + + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Same as set_output(), but for a particular channel enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* ); + void set_output( int index, Blip_Buffer* ); - void reset( Sap_Apu_Impl* ); + // Resets sound chip and sets Sap_Apu_Impl + void reset( Sap_Apu_Impl* impl ); - enum { start_addr = 0xD200 }; - enum { end_addr = 0xD209 }; - void write_data( blip_time_t, unsigned addr, int data ); + // Registers are at io_addr to io_addr+io_size-1 + enum { io_addr = 0xD200 }; + enum { io_size = 0x0A }; - void end_frame( blip_time_t ); +private: + // noncopyable + Sap_Apu( const Sap_Apu& ); + Sap_Apu& operator = ( const Sap_Apu& ); +// Implementation public: Sap_Apu(); + private: struct osc_t { @@ -46,29 +67,34 @@ private: void calc_periods(); void run_until( blip_time_t ); - enum { poly4_len = (1L << 4) - 1 }; - enum { poly9_len = (1L << 9) - 1 }; - enum { poly17_len = (1L << 17) - 1 }; + enum { poly4_len = (1 << 4) - 1 }; + enum { poly9_len = (1 << 9) - 1 }; + enum { poly17_len = (1 << 17) - 1 }; friend class Sap_Apu_Impl; }; // Common tables and Blip_Synth that can be shared among multiple Sap_Apu objects class Sap_Apu_Impl { public: - Blip_Synth synth; + // Set treble with synth.treble_eq() + Blip_Synth_Norm synth; - Sap_Apu_Impl(); + // Sets overall volume, where 1.0is normal void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } + +// Implementation +public: + Sap_Apu_Impl(); + private: - typedef unsigned char byte; - byte poly4 [Sap_Apu::poly4_len / 8 + 1]; - byte poly9 [Sap_Apu::poly9_len / 8 + 1]; - byte poly17 [Sap_Apu::poly17_len / 8 + 1]; + BOOST::uint8_t poly4 [Sap_Apu::poly4_len /8 + 1]; + BOOST::uint8_t poly9 [Sap_Apu::poly9_len /8 + 1]; + BOOST::uint8_t poly17 [Sap_Apu::poly17_len/8 + 1]; friend class Sap_Apu; }; -inline void Sap_Apu::osc_output( int i, Blip_Buffer* b ) +inline void Sap_Apu::set_output( int i, Blip_Buffer* b ) { assert( (unsigned) i < osc_count ); oscs [i].output = b; diff --git a/Frameworks/GME/gme/Sap_Cpu.cpp b/Frameworks/GME/gme/Sap_Cpu.cpp old mode 100755 new mode 100644 index 10dc60618..24bd9b202 --- a/Frameworks/GME/gme/Sap_Cpu.cpp +++ b/Frameworks/GME/gme/Sap_Cpu.cpp @@ -1,13 +1,13 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// $package. http://www.slack.net/~ant/ -#include "Sap_Cpu.h" +#include "Sap_Core.h" -#include #include "blargg_endian.h" +//#define CPU_LOG_MAX 100000 //#include "nes_cpu_log.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -18,994 +18,79 @@ details. You should have received a copy of the GNU Lesser General Public License along with this module; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#define FLUSH_TIME() (void) (s.time = s_time) -#define CACHE_TIME() (void) (s_time = s.time) - -#include "sap_cpu_io.h" - -#ifndef CPU_DONE - #define CPU_DONE( cpu, time, result_out ) { result_out = -1; } -#endif - #include "blargg_source.h" -int const st_n = 0x80; -int const st_v = 0x40; -int const st_r = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; +// functions defined in same file as CPU emulator to help compiler's optimizer -void Sap_Cpu::reset( void* new_mem ) +int Sap_Core::read_d40b() { - check( state == &state_ ); - state = &state_; - mem = (uint8_t*) new_mem; - r.status = st_i; - r.sp = 0xFF; - r.pc = 0; - r.a = 0; - r.x = 0; - r.y = 0; - state_.time = 0; - state_.base = 0; - irq_time_ = future_sap_time; - end_time_ = future_sap_time; - - blargg_verify_byte_order(); + //dprintf( "D40B read\n" ); + check( cpu.time() >= frame_start ); + return ((unsigned) (cpu.time() - frame_start) / scanline_period % lines_per_frame) / 2; } -#define TIME (s_time + s.base) -#define READ( addr ) CPU_READ( this, (addr), TIME ) -#define WRITE( addr, data ) {CPU_WRITE( this, (addr), (data), TIME );} -#define READ_LOW( addr ) (mem [int (addr)]) -#define WRITE_LOW( addr, data ) (void) (READ_LOW( addr ) = (data)) -#define READ_PROG( addr ) (READ_LOW( addr )) - -#define SET_SP( v ) (sp = ((v) + 1) | 0x100) -#define GET_SP() ((sp - 1) & 0xFF) -#define PUSH( v ) ((sp = (sp - 1) | 0x100), WRITE_LOW( sp, v )) - -// even on x86, using short and unsigned char was slower -typedef int fint16; -typedef unsigned fuint16; -typedef unsigned fuint8; -typedef blargg_long fint32; - -bool Sap_Cpu::run( sap_time_t end_time ) +void Sap_Core::write_D2xx( int d2xx, int data ) { - bool illegal_encountered = false; - set_end_time( end_time ); - state_t s = this->state_; - this->state = &s; - fint32 s_time = s.time; - uint8_t* const mem = this->mem; // cache + addr_t const base = 0xD200; - // registers - fuint16 pc = r.pc; - fuint8 a = r.a; - fuint8 x = r.x; - fuint8 y = r.y; - fuint16 sp; - SET_SP( r.sp ); - - // status flags - #define IS_NEG (nz & 0x8080) - - #define CALC_STATUS( out ) do {\ - out = status & (st_v | st_d | st_i);\ - out |= ((nz >> 8) | nz) & st_n;\ - out |= c >> 8 & st_c;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & (st_v | st_d | st_i);\ - nz = in << 8;\ - c = nz;\ - nz |= ~in & st_z;\ - } while ( 0 ) - - fuint8 status; - fuint16 c; // carry set if (c & 0x100) != 0 - fuint16 nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 + if ( d2xx < apu_.io_size ) { - fuint8 temp = r.status; - SET_STATUS( temp ); + apu_.write_data( time(), d2xx + base, data ); + return; } - goto loop; -dec_clock_loop: - s_time--; -loop: - - #ifndef NDEBUG + if ( (unsigned) (d2xx - 0x10) < apu2_.io_size && info.stereo ) { - sap_time_t correct = end_time_; - if ( !(status & st_i) && correct > irq_time_ ) - correct = irq_time_; - check( s.base == correct ); + apu2_.write_data( time(), d2xx + (base - 0x10), data ); + return; } - #endif - check( (unsigned) GET_SP() < 0x100 ); - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - fuint8 opcode = mem [pc]; - pc++; - uint8_t const* instr = mem + pc; - - static uint8_t const clock_table [256] = - {// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1 - 6,6,2,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 3 - 6,6,2,8,3,3,5,5,3,2,2,2,3,4,6,6,// 4 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 5 - 6,6,2,8,3,3,5,5,4,2,2,2,5,4,6,6,// 6 - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 7 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// 8 - 3,6,2,6,4,4,4,4,2,5,2,5,5,5,5,5,// 9 - 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4,// A - 3,5,2,5,4,4,4,4,2,4,2,4,4,4,4,4,// B - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// C - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// D - 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E - 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F - }; // 0x00 was 7 - - fuint16 data; - data = clock_table [opcode]; - if ( (s_time += data) >= 0 ) - goto possibly_out_of_time; -almost_out_of_time: - - data = *instr; - - #ifdef NES_CPU_LOG_H - nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] ); - #endif - - switch ( opcode ) + if ( d2xx == 0xD40A - base ) { -possibly_out_of_time: - if ( s_time < (int) data ) - goto almost_out_of_time; - s_time -= data; - goto out_of_time; - -// Macros - -#define GET_MSB() (instr [1]) -#define ADD_PAGE() (pc++, data += 0x100 * GET_MSB()) -#define GET_ADDR() GET_LE16( instr ) - -#define NO_PAGE_CROSSING( lsb ) -#define HANDLE_PAGE_CROSSING( lsb ) s_time += (lsb) >> 8; - -#define INC_DEC_XY( reg, n ) reg = uint8_t (nz = reg + n); goto loop; - -#define IND_Y( cross, out ) {\ - fuint16 temp = READ_LOW( data ) + y;\ - out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ - cross( temp );\ + dprintf( "D40A write\n" ); + time_t t = cpu.time(); + time_t into_line = (t - frame_start) % scanline_period; + cpu.set_end_time( t - into_line + scanline_period ); + return; } -#define IND_X( out ) {\ - fuint16 temp = data + x;\ - out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ - } - -#define ARITH_ADDR_MODES( op )\ -case op - 0x04: /* (ind,x) */\ - IND_X( data )\ - goto ptr##op;\ -case op + 0x0C: /* (ind),y */\ - IND_Y( HANDLE_PAGE_CROSSING, data )\ - goto ptr##op;\ -case op + 0x10: /* zp,X */\ - data = uint8_t (data + x);\ -case op + 0x00: /* zp */\ - data = READ_LOW( data );\ - goto imm##op;\ -case op + 0x14: /* abs,Y */\ - data += y;\ - goto ind##op;\ -case op + 0x18: /* abs,X */\ - data += x;\ -ind##op:\ - HANDLE_PAGE_CROSSING( data );\ -case op + 0x08: /* abs */\ - ADD_PAGE();\ -ptr##op:\ - FLUSH_TIME();\ - data = READ( data );\ - CACHE_TIME();\ -case op + 0x04: /* imm */\ -imm##op: + if ( (d2xx & ~0x0010) != 0x0F || data != 0x03 ) + dprintf( "Unmapped write $%04X <- $%02X\n", d2xx + base, data ); +} -// TODO: more efficient way to handle negative branch that wraps PC around -#define BRANCH( cond )\ +inline int Sap_Core::read_mem( addr_t addr ) +{ + int result = mem.ram [addr]; + if ( addr == 0xD40B ) + result = read_d40b(); + else if ( (addr & 0xF900) == 0xD000 ) + dprintf( "Unmapped read $%04X\n", addr ); + return result; +} + + +#define READ_LOW( addr ) (ram [addr]) +#define WRITE_LOW( addr, data ) (ram [addr] = data) + +#define READ_MEM( addr ) read_mem( addr ) +#define WRITE_MEM( addr, data ) \ {\ - fint16 offset = (BOOST::int8_t) data;\ - fuint16 extra_clock = (++pc & 0xFF) + offset;\ - if ( !(cond) ) goto dec_clock_loop;\ - pc += offset;\ - s_time += extra_clock >> 8 & 1;\ - goto loop;\ + ram [addr] = data;\ + int d2xx = addr - 0xD200;\ + if ( (unsigned) d2xx < 0x100 )\ + write_D2xx( d2xx, data );\ } -// Often-Used +#define CPU cpu +#define FLAT_MEM ram - case 0xB5: // LDA zp,x - a = nz = READ_LOW( uint8_t (data + x) ); - pc++; - goto loop; +#define CPU_BEGIN \ +bool Sap_Core::run_cpu( time_t end )\ +{\ + CPU.set_end_time( end );\ + byte* const ram = this->mem.ram; /* cache */ - case 0xA5: // LDA zp - a = nz = READ_LOW( data ); - pc++; - goto loop; + #include "Nes_Cpu_run.h" - case 0xD0: // BNE - BRANCH( (uint8_t) nz ); - - case 0x20: { // JSR - fuint16 temp = pc + 1; - pc = GET_ADDR(); - WRITE_LOW( 0x100 | (sp - 1), temp >> 8 ); - sp = (sp - 2) | 0x100; - WRITE_LOW( sp, temp ); - goto loop; - } - - case 0x4C: // JMP abs - pc = GET_ADDR(); - goto loop; - - case 0xE8: // INX - INC_DEC_XY( x, 1 ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - ARITH_ADDR_MODES( 0xC5 ) // CMP - nz = a - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ); - - case 0x95: // STA zp,x - data = uint8_t (data + x); - case 0x85: // STA zp - pc++; - WRITE_LOW( data, a ); - goto loop; - - case 0xC8: // INY - INC_DEC_XY( y, 1 ) - - case 0xA8: // TAY - y = a; - nz = a; - goto loop; - - case 0x98: // TYA - a = y; - nz = y; - goto loop; - - case 0xAD:{// LDA abs - unsigned addr = GET_ADDR(); - pc += 2; - nz = READ( addr ); - a = nz; - goto loop; - } - - case 0x60: // RTS - pc = 1 + READ_LOW( sp ); - pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); - sp = (sp - 0xFE) | 0x100; - goto loop; - - { - fuint16 addr; - - case 0x99: // STA abs,Y - addr = y + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x8D: // STA abs - addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - goto sta_ptr; - - case 0x9D: // STA abs,X (slightly more common than STA abs) - addr = x + GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, a ); - goto loop; - } - sta_ptr: - FLUSH_TIME(); - WRITE( addr, a ); - CACHE_TIME(); - goto loop; - - case 0x91: // STA (ind),Y - IND_Y( NO_PAGE_CROSSING, addr ) - pc++; - goto sta_ptr; - - case 0x81: // STA (ind,X) - IND_X( addr ) - pc++; - goto sta_ptr; - - } - - case 0xA9: // LDA #imm - pc++; - a = data; - nz = data; - goto loop; - - // common read instructions - { - fuint16 addr; - - case 0xA1: // LDA (ind,X) - IND_X( addr ) - pc++; - goto a_nz_read_addr; - - case 0xB1:// LDA (ind),Y - addr = READ_LOW( data ) + y; - HANDLE_PAGE_CROSSING( addr ); - addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); - pc++; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xB9: // LDA abs,Y - HANDLE_PAGE_CROSSING( data + y ); - addr = GET_ADDR() + y; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - goto a_nz_read_addr; - - case 0xBD: // LDA abs,X - HANDLE_PAGE_CROSSING( data + x ); - addr = GET_ADDR() + x; - pc += 2; - a = nz = READ_PROG( addr ); - if ( (addr ^ 0x8000) <= 0x9FFF ) - goto loop; - a_nz_read_addr: - FLUSH_TIME(); - a = nz = READ( addr ); - CACHE_TIME(); - goto loop; - - } - -// Branch - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - -// Load/store - - case 0x94: // STY zp,x - data = uint8_t (data + x); - case 0x84: // STY zp - pc++; - WRITE_LOW( data, y ); - goto loop; - - case 0x96: // STX zp,y - data = uint8_t (data + y); - case 0x86: // STX zp - pc++; - WRITE_LOW( data, x ); - goto loop; - - case 0xB6: // LDX zp,y - data = uint8_t (data + y); - case 0xA6: // LDX zp - data = READ_LOW( data ); - case 0xA2: // LDX #imm - pc++; - x = data; - nz = data; - goto loop; - - case 0xB4: // LDY zp,x - data = uint8_t (data + x); - case 0xA4: // LDY zp - data = READ_LOW( data ); - case 0xA0: // LDY #imm - pc++; - y = data; - nz = data; - goto loop; - - case 0xBC: // LDY abs,X - data += x; - HANDLE_PAGE_CROSSING( data ); - case 0xAC:{// LDY abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - y = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - case 0xBE: // LDX abs,y - data += y; - HANDLE_PAGE_CROSSING( data ); - case 0xAE:{// LDX abs - unsigned addr = data + 0x100 * GET_MSB(); - pc += 2; - FLUSH_TIME(); - x = nz = READ( addr ); - CACHE_TIME(); - goto loop; - } - - { - fuint8 temp; - case 0x8C: // STY abs - temp = y; - goto store_abs; - - case 0x8E: // STX abs - temp = x; - store_abs: - unsigned addr = GET_ADDR(); - pc += 2; - if ( addr <= 0x7FF ) - { - WRITE_LOW( addr, temp ); - goto loop; - } - FLUSH_TIME(); - WRITE( addr, temp ); - CACHE_TIME(); - goto loop; - } - -// Compare - - case 0xEC:{// CPX abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( addr ); - CACHE_TIME(); - goto cpx_data; - } - - case 0xE4: // CPX zp - data = READ_LOW( data ); - case 0xE0: // CPX #imm - cpx_data: - nz = x - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0xCC:{// CPY abs - unsigned addr = GET_ADDR(); - pc++; - FLUSH_TIME(); - data = READ( addr ); - CACHE_TIME(); - goto cpy_data; - } - - case 0xC4: // CPY zp - data = READ_LOW( data ); - case 0xC0: // CPY #imm - cpy_data: - nz = y - data; - pc++; - c = ~nz; - nz &= 0xFF; - goto loop; - -// Logical - - ARITH_ADDR_MODES( 0x25 ) // AND - nz = (a &= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x45 ) // EOR - nz = (a ^= data); - pc++; - goto loop; - - ARITH_ADDR_MODES( 0x05 ) // ORA - nz = (a |= data); - pc++; - goto loop; - - case 0x2C:{// BIT abs - unsigned addr = GET_ADDR(); - pc += 2; - status &= ~st_v; - nz = READ( addr ); - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - } - - case 0x24: // BIT zp - nz = READ_LOW( data ); - pc++; - status &= ~st_v; - status |= nz & st_v; - if ( a & nz ) - goto loop; - nz <<= 8; // result must be zero, even if N bit is set - goto loop; - -// Add/subtract - - ARITH_ADDR_MODES( 0xE5 ) // SBC - case 0xEB: // unofficial equivalent - data ^= 0xFF; - goto adc_imm; - - ARITH_ADDR_MODES( 0x65 ) // ADC - adc_imm: { - check( !(status & st_d) ); - fint16 carry = c >> 8 & 1; - fint16 ov = (a ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - status &= ~st_v; - status |= ov >> 2 & 0x40; - c = nz = a + data + carry; - pc++; - a = (uint8_t) nz; - goto loop; - } - -// Shift/rotate - - case 0x4A: // LSR A - c = 0; - case 0x6A: // ROR A - nz = c >> 1 & 0x80; - c = a << 8; - nz |= a >> 1; - a = nz; - goto loop; - - case 0x0A: // ASL A - nz = a << 1; - c = nz; - a = (uint8_t) nz; - goto loop; - - case 0x2A: { // ROL A - nz = a << 1; - fint16 temp = c >> 8 & 1; - c = nz; - nz |= temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x5E: // LSR abs,X - data += x; - case 0x4E: // LSR abs - c = 0; - case 0x6E: // ROR abs - ror_abs: { - ADD_PAGE(); - FLUSH_TIME(); - int temp = READ( data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - goto rotate_common; - } - - case 0x3E: // ROL abs,X - data += x; - goto rol_abs; - - case 0x1E: // ASL abs,X - data += x; - case 0x0E: // ASL abs - c = 0; - case 0x2E: // ROL abs - rol_abs: - ADD_PAGE(); - nz = c >> 8 & 1; - FLUSH_TIME(); - nz |= (c = READ( data ) << 1); - rotate_common: - pc++; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - - case 0x7E: // ROR abs,X - data += x; - goto ror_abs; - - case 0x76: // ROR zp,x - data = uint8_t (data + x); - goto ror_zp; - - case 0x56: // LSR zp,x - data = uint8_t (data + x); - case 0x46: // LSR zp - c = 0; - case 0x66: // ROR zp - ror_zp: { - int temp = READ_LOW( data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - goto write_nz_zp; - } - - case 0x36: // ROL zp,x - data = uint8_t (data + x); - goto rol_zp; - - case 0x16: // ASL zp,x - data = uint8_t (data + x); - case 0x06: // ASL zp - c = 0; - case 0x26: // ROL zp - rol_zp: - nz = c >> 8 & 1; - nz |= (c = READ_LOW( data ) << 1); - goto write_nz_zp; - -// Increment/decrement - - case 0xCA: // DEX - INC_DEC_XY( x, -1 ) - - case 0x88: // DEY - INC_DEC_XY( y, -1 ) - - case 0xF6: // INC zp,x - data = uint8_t (data + x); - case 0xE6: // INC zp - nz = 1; - goto add_nz_zp; - - case 0xD6: // DEC zp,x - data = uint8_t (data + x); - case 0xC6: // DEC zp - nz = (unsigned) -1; - add_nz_zp: - nz += READ_LOW( data ); - write_nz_zp: - pc++; - WRITE_LOW( data, nz ); - goto loop; - - case 0xFE: // INC abs,x - data = x + GET_ADDR(); - goto inc_ptr; - - case 0xEE: // INC abs - data = GET_ADDR(); - inc_ptr: - nz = 1; - goto inc_common; - - case 0xDE: // DEC abs,x - data = x + GET_ADDR(); - goto dec_ptr; - - case 0xCE: // DEC abs - data = GET_ADDR(); - dec_ptr: - nz = (unsigned) -1; - inc_common: - FLUSH_TIME(); - nz += READ( data ); - pc += 2; - WRITE( data, (uint8_t) nz ); - CACHE_TIME(); - goto loop; - -// Transfer - - case 0xAA: // TAX - x = a; - nz = a; - goto loop; - - case 0x8A: // TXA - a = x; - nz = x; - goto loop; - - case 0x9A: // TXS - SET_SP( x ); // verified (no flag change) - goto loop; - - case 0xBA: // TSX - x = nz = GET_SP(); - goto loop; - -// Stack - - case 0x48: // PHA - PUSH( a ); // verified - goto loop; - - case 0x68: // PLA - a = nz = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - goto loop; - - case 0x40:{// RTI - fuint8 temp = READ_LOW( sp ); - pc = READ_LOW( 0x100 | (sp - 0xFF) ); - pc |= READ_LOW( 0x100 | (sp - 0xFE) ) * 0x100; - sp = (sp - 0xFD) | 0x100; - data = status; - SET_STATUS( temp ); - this->r.status = status; // update externally-visible I flag - if ( (data ^ status) & st_i ) - { - sap_time_t new_time = end_time_; - if ( !(status & st_i) && new_time > irq_time_ ) - new_time = irq_time_; - blargg_long delta = s.base - new_time; - s.base = new_time; - s_time += delta; - } - goto loop; - } - - case 0x28:{// PLP - fuint8 temp = READ_LOW( sp ); - sp = (sp - 0xFF) | 0x100; - fuint8 changed = status ^ temp; - SET_STATUS( temp ); - if ( !(changed & st_i) ) - goto loop; // I flag didn't change - if ( status & st_i ) - goto handle_sei; - goto handle_cli; - } - - case 0x08: { // PHP - fuint8 temp; - CALC_STATUS( temp ); - PUSH( temp | (st_b | st_r) ); - goto loop; - } - - case 0x6C:{// JMP (ind) - data = GET_ADDR(); - pc = READ_PROG( data ); - data = (data & 0xFF00) | ((data + 1) & 0xFF); - pc |= 0x100 * READ_PROG( data ); - goto loop; - } - - case 0x00: // BRK - goto handle_brk; - -// Flags - - case 0x38: // SEC - c = (unsigned) ~0; - goto loop; - - case 0x18: // CLC - c = 0; - goto loop; - - case 0xB8: // CLV - status &= ~st_v; - goto loop; - - case 0xD8: // CLD - status &= ~st_d; - goto loop; - - case 0xF8: // SED - status |= st_d; - goto loop; - - case 0x58: // CLI - if ( !(status & st_i) ) - goto loop; - status &= ~st_i; - handle_cli: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; - if ( delta <= 0 ) - { - if ( TIME < irq_time_ ) - goto loop; - goto delayed_cli; - } - s.base = irq_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - - if ( delta >= s_time + 1 ) - { - // delayed irq until after next instruction - s.base += s_time + 1; - s_time = -1; - irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop - goto loop; - } - delayed_cli: - dprintf( "Delayed CLI not emulated\n" ); - goto loop; - } - - case 0x78: // SEI - if ( status & st_i ) - goto loop; - status |= st_i; - handle_sei: { - this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - if ( s_time < 0 ) - goto loop; - dprintf( "Delayed SEI not emulated\n" ); - goto loop; - } - -// Unofficial - - // SKW - Skip word - case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: - HANDLE_PAGE_CROSSING( data + x ); - case 0x0C: - pc++; - // SKB - Skip byte - case 0x74: case 0x04: case 0x14: case 0x34: case 0x44: case 0x54: case 0x64: - case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: - pc++; - goto loop; - - // NOP - case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: - goto loop; - -// Unimplemented - - // halt - //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: - //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: - - default: - assert( (unsigned) opcode <= 0xFF ); - illegal_encountered = true; - pc--; - goto stop; - } - assert( false ); - - int result_; -handle_brk: - if ( (pc - 1) >= idle_addr ) - goto idle_done; - pc++; - result_ = 4; - dprintf( "BRK executed\n" ); - -interrupt: - { - s_time += 7; - - WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); - WRITE_LOW( 0x100 | (sp - 2), pc ); - pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); - - sp = (sp - 3) | 0x100; - fuint8 temp; - CALC_STATUS( temp ); - temp |= st_r; - if ( result_ ) - temp |= st_b; // TODO: incorrectly sets B flag for IRQ - WRITE_LOW( sp, temp ); - - status &= ~st_d; - status |= st_i; - this->r.status = status; // update externally-visible I flag - - blargg_long delta = s.base - end_time_; - s.base = end_time_; - s_time += delta; - goto loop; - } - -idle_done: - //s_time = 0; - pc--; - goto stop; -out_of_time: - pc--; - FLUSH_TIME(); - CPU_DONE( this, TIME, result_ ); - CACHE_TIME(); - if ( result_ >= 0 ) - goto interrupt; - if ( s_time < 0 ) - goto loop; - -stop: - - s.time = s_time; - - r.pc = pc; - r.sp = GET_SP(); - r.a = a; - r.x = x; - r.y = y; - - { - fuint8 temp; - CALC_STATUS( temp ); - r.status = temp; - } - - this->state_ = s; - this->state = &this->state_; - - return illegal_encountered; + return cpu.time_past_end() < 0; } - diff --git a/Frameworks/GME/gme/Sap_Emu.cpp b/Frameworks/GME/gme/Sap_Emu.cpp old mode 100755 new mode 100644 index 8314fd6e8..c8471890b --- a/Frameworks/GME/gme/Sap_Emu.cpp +++ b/Frameworks/GME/gme/Sap_Emu.cpp @@ -1,442 +1,410 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Sap_Emu.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -long const base_scanline_period = 114; - -Sap_Emu::Sap_Emu() -{ - set_type( gme_sap_type ); - - static const char* const names [Sap_Apu::osc_count * 2] = { - "Wave 1", "Wave 2", "Wave 3", "Wave 4", - "Wave 5", "Wave 6", "Wave 7", "Wave 8", - }; - set_voice_names( names ); - - static int const types [Sap_Apu::osc_count * 2] = { - wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, - wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4, - }; - set_voice_types( types ); - set_silence_lookahead( 6 ); -} - -Sap_Emu::~Sap_Emu() { } - -// Track info - -// Returns 16 or greater if not hex -inline int from_hex_char( int h ) -{ - h -= 0x30; - if ( (unsigned) h > 9 ) - h = ((h - 0x11) & 0xDF) + 10; - return h; -} - -static long from_hex( byte const* in ) -{ - unsigned result = 0; - for ( int n = 4; n--; ) - { - int h = from_hex_char( *in++ ); - if ( h > 15 ) - return -1; - result = result * 0x10 + h; - } - return result; -} - -static int from_dec( byte const* in, byte const* end ) -{ - if ( in >= end ) - return -1; - - int n = 0; - while ( in < end ) - { - int dig = *in++ - '0'; - if ( (unsigned) dig > 9 ) - return -1; - n = n * 10 + dig; - } - return n; -} - -static void parse_string( byte const* in, byte const* end, int len, char* out ) -{ - byte const* start = in; - if ( *in++ == '\"' ) - { - start++; - while ( in < end && *in != '\"' ) - in++; - } - else - { - in = end; - } - len = min( len - 1, int (in - start) ); - out [len] = 0; - memcpy( out, start, len ); -} - -static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out ) -{ - out->track_count = 1; - out->author [0] = 0; - out->name [0] = 0; - out->copyright [0] = 0; - - if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) - return gme_wrong_file_type; - - byte const* file_end = in + size - 5; - in += 5; - while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) - { - byte const* line_end = in; - while ( line_end < file_end && *line_end != 0x0D ) - line_end++; - - char const* tag = (char const*) in; - while ( in < line_end && *in > ' ' ) - in++; - int tag_len = (char const*) in - tag; - - while ( in < line_end && *in <= ' ' ) in++; - - if ( tag_len <= 0 ) - { - // skip line - } - else if ( !strncmp( "INIT", tag, tag_len ) ) - { - out->init_addr = from_hex( in ); - if ( (unsigned long) out->init_addr > 0xFFFF ) - return "Invalid init address"; - } - else if ( !strncmp( "PLAYER", tag, tag_len ) ) - { - out->play_addr = from_hex( in ); - if ( (unsigned long) out->play_addr > 0xFFFF ) - return "Invalid play address"; - } - else if ( !strncmp( "MUSIC", tag, tag_len ) ) - { - out->music_addr = from_hex( in ); - if ( (unsigned long) out->music_addr > 0xFFFF ) - return "Invalid music address"; - } - else if ( !strncmp( "SONGS", tag, tag_len ) ) - { - out->track_count = from_dec( in, line_end ); - if ( out->track_count <= 0 ) - return "Invalid track count"; - } - else if ( !strncmp( "TYPE", tag, tag_len ) ) - { - switch ( out->type = *in ) - { - case 'C': - case 'B': - break; - - case 'D': - return "Digimusic not supported"; - - default: - return "Unsupported player type"; - } - } - else if ( !strncmp( "STEREO", tag, tag_len ) ) - { - out->stereo = true; - } - else if ( !strncmp( "FASTPLAY", tag, tag_len ) ) - { - out->fastplay = from_dec( in, line_end ); - if ( out->fastplay <= 0 ) - return "Invalid fastplay value"; - } - else if ( !strncmp( "AUTHOR", tag, tag_len ) ) - { - parse_string( in, line_end, sizeof out->author, out->author ); - } - else if ( !strncmp( "NAME", tag, tag_len ) ) - { - parse_string( in, line_end, sizeof out->name, out->name ); - } - else if ( !strncmp( "DATE", tag, tag_len ) ) - { - parse_string( in, line_end, sizeof out->copyright, out->copyright ); - } - - in = line_end + 2; - } - - if ( in [0] != 0xFF || in [1] != 0xFF ) - return "ROM data missing"; - out->rom_data = in + 2; - - return 0; -} - -static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out ) -{ - Gme_File::copy_field_( out->game, in.name ); - Gme_File::copy_field_( out->author, in.author ); - Gme_File::copy_field_( out->copyright, in.copyright ); -} - -blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const -{ - copy_sap_fields( info, out ); - return 0; -} - -struct Sap_File : Gme_Info_ -{ - Sap_Emu::info_t info; - - Sap_File() { set_type( gme_sap_type ); } - - blargg_err_t load_mem_( byte const* begin, long size ) - { - RETURN_ERR( parse_info( begin, size, &info ) ); - set_track_count( info.track_count ); - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - copy_sap_fields( info, out ); - return 0; - } -}; - -static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } -static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } - -gme_type_t_ const gme_sap_type [1] = { "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }; - -// Setup - -blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) -{ - file_end = in + size; - - info.warning = 0; - info.type = 'B'; - info.stereo = false; - info.init_addr = -1; - info.play_addr = -1; - info.music_addr = -1; - info.fastplay = 312; - RETURN_ERR( parse_info( in, size, &info ) ); - - set_warning( info.warning ); - set_track_count( info.track_count ); - set_voice_count( Sap_Apu::osc_count << info.stereo ); - apu_impl.volume( gain() ); - - return setup_buffer( 1773447 ); -} - -void Sap_Emu::update_eq( blip_eq_t const& eq ) -{ - apu_impl.synth.treble_eq( eq ); -} - -void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) -{ - int i2 = i - Sap_Apu::osc_count; - if ( i2 >= 0 ) - apu2.osc_output( i2, right ); - else - apu.osc_output( i, (info.stereo ? left : center) ); -} - -// Emulation - -void Sap_Emu::set_tempo_( double t ) -{ - scanline_period = sap_time_t (base_scanline_period / t); -} - -inline sap_time_t Sap_Emu::play_period() const { return info.fastplay * scanline_period; } - -void Sap_Emu::cpu_jsr( sap_addr_t addr ) -{ - check( r.sp >= 0xFE ); // catch anything trying to leave data on stack - r.pc = addr; - int high_byte = (idle_addr - 1) >> 8; - if ( r.sp == 0xFE && mem.ram [0x1FF] == high_byte ) - r.sp = 0xFF; // pop extra byte off - mem.ram [0x100 + r.sp--] = high_byte; // some routines use RTI to return - mem.ram [0x100 + r.sp--] = high_byte; - mem.ram [0x100 + r.sp--] = (idle_addr - 1) & 0xFF; -} - -void Sap_Emu::run_routine( sap_addr_t addr ) -{ - cpu_jsr( addr ); - cpu::run( 312 * base_scanline_period * 60 ); - check( r.pc == idle_addr ); -} - -inline void Sap_Emu::call_init( int track ) -{ - switch ( info.type ) - { - case 'B': - r.a = track; - run_routine( info.init_addr ); - break; - - case 'C': - r.a = 0x70; - r.x = info.music_addr&0xFF; - r.y = info.music_addr >> 8; - run_routine( info.play_addr + 3 ); - r.a = 0; - r.x = track; - run_routine( info.play_addr + 3 ); - break; - } -} - -blargg_err_t Sap_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - - memset( &mem, 0, sizeof mem ); - - byte const* in = info.rom_data; - while ( file_end - in >= 5 ) - { - unsigned start = get_le16( in ); - unsigned end = get_le16( in + 2 ); - //dprintf( "Block $%04X-$%04X\n", start, end ); - in += 4; - if ( end < start ) - { - set_warning( "Invalid file data block" ); - break; - } - long len = end - start + 1; - if ( len > file_end - in ) - { - set_warning( "Invalid file data block" ); - break; - } - - memcpy( mem.ram + start, in, len ); - in += len; - if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) - in += 2; - } - - apu.reset( &apu_impl ); - apu2.reset( &apu_impl ); - cpu::reset( mem.ram ); - time_mask = 0; // disables sound during init - call_init( track ); - time_mask = -1; - - next_play = play_period(); - - return 0; -} - -// Emulation - -// see sap_cpu_io.h for read/write functions - -void Sap_Emu::cpu_write_( sap_addr_t addr, int data ) -{ - if ( (addr ^ Sap_Apu::start_addr) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) ) - { - GME_APU_HOOK( this, addr - Sap_Apu::start_addr, data ); - apu.write_data( time() & time_mask, addr, data ); - return; - } - - if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && - info.stereo ) - { - GME_APU_HOOK( this, addr - 0x10 - Sap_Apu::start_addr + 10, data ); - apu2.write_data( time() & time_mask, addr ^ 0x10, data ); - return; - } - - if ( (addr & ~0x0010) != 0xD20F || data != 0x03 ) - dprintf( "Unmapped write $%04X <- $%02X\n", addr, data ); -} - -inline void Sap_Emu::call_play() -{ - switch ( info.type ) - { - case 'B': - cpu_jsr( info.play_addr ); - break; - - case 'C': - cpu_jsr( info.play_addr + 6 ); - break; - } -} - -blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) -{ - set_time( 0 ); - while ( time() < duration ) - { - if ( cpu::run( duration ) || r.pc > idle_addr ) - return "Emulation error (illegal instruction)"; - - if ( r.pc == idle_addr ) - { - if ( next_play <= duration ) - { - set_time( next_play ); - next_play += play_period(); - call_play(); - GME_FRAME_HOOK( this ); - } - else - { - set_time( duration ); - } - } - } - - duration = time(); - next_play -= duration; - check( next_play >= 0 ); - if ( next_play < 0 ) - next_play = 0; - apu.end_frame( duration ); - if ( info.stereo ) - apu2.end_frame( duration ); - - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Sap_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2006-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +Sap_Emu::Sap_Emu() +{ + set_type( gme_sap_type ); + set_silence_lookahead( 6 ); +} + +Sap_Emu::~Sap_Emu() { } + +// Track info + +// Returns 16 or greater if not hex. Handles uppercase and lowercase. +// Thoroughly tested and rejects ALL non-hex characters. +inline int from_hex_char( int h ) +{ + h -= 0x30; + if ( (unsigned) h > 9 ) + h = ((h - 0x11) & 0xDF) + 10; + return h; +} + +static int from_hex( byte const in [] ) +{ + int result = 0; + for ( int n = 4; n--; ) + { + int h = from_hex_char( *in++ ); + if ( h > 15 ) + return -1; + result = result * 0x10 + h; + } + return result; +} + +static int parse_int( byte const* io [], byte const* end ) +{ + byte const* in = *io; + int n = 0; + while ( in < end ) + { + int dig = *in - '0'; + if ( (unsigned) dig > 9 ) + break; + ++in; + n = n * 10 + dig; + } + if ( in == *io ) + n = -1; // no numeric characters + *io = in; + return n; +} + +static int from_dec( byte const in [], byte const* end ) +{ + int n = parse_int( &in, end ); + if ( in < end ) + n = -1; + return n; +} + +static void parse_string( byte const in [], byte const* end, int len, char out [] ) +{ + byte const* start = in; + if ( *in++ == '\"' ) + { + start++; + while ( in < end && *in != '\"' ) + in++; + } + else + { + in = end; + } + len = min( len - 1, int (in - start) ); + out [len] = 0; + memcpy( out, start, len ); +} + +static int parse_time( byte const in [], byte const* end ) +{ + int minutes = parse_int( &in, end ); + if ( minutes < 0 || *in != ':' ) + return 0; + + ++in; + int seconds = parse_int( &in, end ); + if ( seconds < 0 ) + return 0; + + int time = minutes * 60000 + seconds * 1000; + if ( *in == '.' ) + { + byte const* start = ++in; + int msec = parse_int( &in, end ); + if ( msec >= 0 ) + { + // allow 1-3 digits + for ( int n = in - start; n < 3; n++ ) + msec *= 10; + time += msec; + } + } + + while ( in < end && *in <= ' ' ) + ++in; + + if ( end - in >= 4 && !memcmp( in, "LOOP", 4 ) ) + time = -time; + + return time; +} + +static blargg_err_t parse_info( byte const in [], int size, Sap_Emu::info_t* out ) +{ + out->track_count = 1; + out->author [0] = 0; + out->name [0] = 0; + out->copyright [0] = 0; + + for ( int i = 0; i < Sap_Emu::max_tracks; i++ ) + out->track_times [i] = 0; + + if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) + return blargg_err_file_type; + + int time_count = 0; + byte const* file_end = in + size - 5; + in += 5; + while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) + { + byte const* line_end = in; + while ( line_end < file_end && *line_end != 0x0D ) + line_end++; + + char const* tag = (char const*) in; + while ( in < line_end && *in > ' ' ) + in++; + int tag_len = (char const*) in - tag; + + while ( in < line_end && *in <= ' ' ) in++; + + if ( tag_len <= 0 ) + { + // skip line + } + else if ( !strncmp( "TIME", tag, tag_len ) && time_count < Sap_Emu::max_tracks ) + { + out->track_times [time_count++] = parse_time( in, line_end ); + } + else if ( !strncmp( "INIT", tag, tag_len ) ) + { + out->init_addr = from_hex( in ); + if ( (unsigned) out->init_addr >= 0x10000 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "init address" ); + } + else if ( !strncmp( "PLAYER", tag, tag_len ) ) + { + out->play_addr = from_hex( in ); + if ( (unsigned) out->play_addr >= 0x10000 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "play address" ); + } + else if ( !strncmp( "MUSIC", tag, tag_len ) ) + { + out->music_addr = from_hex( in ); + if ( (unsigned) out->music_addr >= 0x10000 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "music address" ); + } + else if ( !strncmp( "SONGS", tag, tag_len ) ) + { + out->track_count = from_dec( in, line_end ); + if ( out->track_count <= 0 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "track count" ); + } + else if ( !strncmp( "TYPE", tag, tag_len ) ) + { + switch ( out->type = *in ) + { + case 'S': + out->type = 'C'; + case 'B': + case 'C': + case 'D': + break; + + default: + return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "player type" ); + } + } + else if ( !strncmp( "STEREO", tag, tag_len ) ) + { + out->stereo = true; + } + else if ( !strncmp( "FASTPLAY", tag, tag_len ) ) + { + out->fastplay = from_dec( in, line_end ); + if ( out->fastplay <= 0 ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "fastplay value" ); + } + else if ( !strncmp( "AUTHOR", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->author, out->author ); + } + else if ( !strncmp( "NAME", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->name, out->name ); + } + else if ( !strncmp( "DATE", tag, tag_len ) ) + { + parse_string( in, line_end, sizeof out->copyright, out->copyright ); + } + + in = line_end + 2; + } + + if ( in [0] != 0xFF || in [1] != 0xFF ) + return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "ROM data missing" ); + out->rom_data = in + 2; + + return blargg_ok; +} + +static void copy_sap_fields( Sap_Emu::info_t const& in, track_info_t* out ) +{ + Gme_File::copy_field_( out->game, in.name ); + Gme_File::copy_field_( out->author, in.author ); + Gme_File::copy_field_( out->copyright, in.copyright ); +} + +static void hash_sap_file( Sap_Emu::info_t const& i, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + unsigned char temp[4]; + set_le32( &temp[0], i.init_addr ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.play_addr ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.music_addr ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.type ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.fastplay ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.stereo ); out.hash_( &temp[0], sizeof(temp) ); + set_le32( &temp[0], i.track_count ); out.hash_( &temp[0], sizeof(temp) ); + out.hash_( data, data_size ); +} + +blargg_err_t Sap_Emu::track_info_( track_info_t* out, int track ) const +{ + copy_sap_fields( info_, out ); + + if ( track < max_tracks ) + { + int time = info_.track_times [track]; + if ( time ) + { + if ( time > 0 ) + { + out->loop_length = 0; + } + else + { + time = -time; + out->loop_length = time; + } + out->length = time; + } + } + return blargg_ok; +} + +struct Sap_File : Gme_Info_ +{ + Sap_Emu::info_t info; + + Sap_File() { set_type( gme_sap_type ); } + + blargg_err_t load_mem_( byte const begin [], int size ) + { + RETURN_ERR( parse_info( begin, size, &info ) ); + set_track_count( info.track_count ); + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + copy_sap_fields( info, out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_sap_file( info, info.rom_data, file_end() - info.rom_data, out ); + return blargg_ok; + } +}; + +static Music_Emu* new_sap_emu () { return BLARGG_NEW Sap_Emu ; } +static Music_Emu* new_sap_file() { return BLARGG_NEW Sap_File; } + +gme_type_t_ const gme_sap_type [1] = {{ "Atari XL", 0, &new_sap_emu, &new_sap_file, "SAP", 1 }}; + +// Setup + +blargg_err_t Sap_Emu::load_mem_( byte const in [], int size ) +{ + file_end = in + size; + + info_.warning = NULL; + info_.type = 'B'; + info_.stereo = false; + info_.init_addr = -1; + info_.play_addr = -1; + info_.music_addr = -1; + info_.fastplay = 312; + RETURN_ERR( parse_info( in, size, &info_ ) ); + + set_warning( info_.warning ); + set_track_count( info_.track_count ); + set_voice_count( Sap_Apu::osc_count << info_.stereo ); + core.apu_impl().volume( gain() ); + + static const char* const names [Sap_Apu::osc_count * 2] = { + "Wave 1", "Wave 2", "Wave 3", "Wave 4", + "Wave 5", "Wave 6", "Wave 7", "Wave 8", + }; + set_voice_names( names ); + + static int const types [Sap_Apu::osc_count * 2] = { + wave_type+1, wave_type+2, wave_type+3, wave_type+0, + wave_type+5, wave_type+6, wave_type+7, wave_type+4, + }; + set_voice_types( types ); + + return setup_buffer( 1773447 ); +} + +void Sap_Emu::update_eq( blip_eq_t const& eq ) +{ + core.apu_impl().synth.treble_eq( eq ); +} + +void Sap_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +{ + int i2 = i - Sap_Apu::osc_count; + if ( i2 >= 0 ) + core.apu2().set_output( i2, right ); + else + core.apu().set_output( i, (info_.stereo ? left : center) ); +} + +// Emulation + +void Sap_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Sap_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + core.setup_ram(); + + // Copy file data to RAM + byte const* in = info_.rom_data; + while ( file_end - in >= 5 ) + { + int start = get_le16( in ); + int end = get_le16( in + 2 ); + //dprintf( "Block $%04X-$%04X\n", start, end ); + in += 4; + int len = end - start + 1; + if ( (unsigned) len > (unsigned) (file_end - in) ) + { + set_warning( "Invalid file data block" ); + break; + } + + memcpy( core.ram() + start, in, len ); + in += len; + if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) + in += 2; + } + + return core.start_track( track, info_ ); +} + +blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) +{ + return core.end_frame( duration ); +} + +blargg_err_t Sap_Emu::hash_( Hash_Function& out ) const +{ + hash_sap_file( info(), info().rom_data, file_end - info().rom_data, out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Sap_Emu.h b/Frameworks/GME/gme/Sap_Emu.h old mode 100755 new mode 100644 index 4878faa66..aac1acc5f --- a/Frameworks/GME/gme/Sap_Emu.h +++ b/Frameworks/GME/gme/Sap_Emu.h @@ -1,69 +1,53 @@ // Atari XL/XE SAP music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef SAP_EMU_H #define SAP_EMU_H #include "Classic_Emu.h" #include "Sap_Apu.h" -#include "Sap_Cpu.h" +#include "Sap_Core.h" -class Sap_Emu : private Sap_Cpu, public Classic_Emu { - typedef Sap_Cpu cpu; +class Sap_Emu : public Classic_Emu { public: - static gme_type_t static_type() { return gme_sap_type; } -public: - Sap_Emu(); - ~Sap_Emu(); - struct info_t { + enum { max_tracks = 32 }; // TODO: no fixed limit + + // SAP file info (see Sap_Core.h for more) + struct info_t : Sap_Core::info_t { byte const* rom_data; const char* warning; - long init_addr; - long play_addr; - long music_addr; - int type; int track_count; - int fastplay; - bool stereo; + int track_times [max_tracks]; char author [256]; char name [256]; char copyright [ 32]; }; + + // Info for currently loaded file + info_t const& info() const { return info_; } + + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_sap_type; } + +// Implementation +public: + Sap_Emu(); + ~Sap_Emu(); + protected: - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t start_track_( int ); - blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); -public: private: friend class Sap_Cpu; - int cpu_read( sap_addr_t ); - void cpu_write( sap_addr_t, int ); - void cpu_write_( sap_addr_t, int ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t run_clocks( blip_time_t&, int ); + virtual void set_tempo_( double ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + private: - info_t info; - + info_t info_; byte const* file_end; - sap_time_t scanline_period; - sap_time_t next_play; - sap_time_t time_mask; - Sap_Apu apu; - Sap_Apu apu2; - - // large items - struct { - byte padding1 [0x100]; - byte ram [0x10000]; - byte padding2 [0x100]; - } mem; - Sap_Apu_Impl apu_impl; - - sap_time_t play_period() const; - void call_play(); - void cpu_jsr( sap_addr_t ); - void call_init( int track ); - void run_routine( sap_addr_t ); + Sap_Core core; }; #endif diff --git a/Frameworks/GME/gme/Sms_Apu.cpp b/Frameworks/GME/gme/Sms_Apu.cpp old mode 100755 new mode 100644 index b41fdec41..3a50434cd --- a/Frameworks/GME/gme/Sms_Apu.cpp +++ b/Frameworks/GME/gme/Sms_Apu.cpp @@ -1,8 +1,8 @@ -// Sms_Snd_Emu 0.1.4. http://www.slack.net/~ant/ +// Sms_Snd_Emu $vers. http://www.slack.net/~ant/ #include "Sms_Apu.h" -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -15,253 +15,222 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// Sms_Osc - -Sms_Osc::Sms_Osc() -{ - output = 0; - outputs [0] = 0; // always stays NULL - outputs [1] = 0; - outputs [2] = 0; - outputs [3] = 0; -} - -void Sms_Osc::reset() -{ - delay = 0; - last_amp = 0; - volume = 0; - output_select = 3; - output = outputs [3]; -} - -// Sms_Square - -inline void Sms_Square::reset() -{ - period = 0; - phase = 0; - Sms_Osc::reset(); -} - -void Sms_Square::run( blip_time_t time, blip_time_t end_time ) -{ - if ( !volume || period <= 128 ) - { - // ignore 16kHz and higher - if ( last_amp ) - { - synth->offset( time, -last_amp, output ); - last_amp = 0; - } - time += delay; - if ( !period ) - { - time = end_time; - } - else if ( time < end_time ) - { - // keep calculating phase - int count = (end_time - time + period - 1) / period; - phase = (phase + count) & 1; - time += count * period; - } - } - else - { - int amp = phase ? volume : -volume; - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth->offset( time, delta, output ); - } - } - - time += delay; - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - int delta = amp * 2; - do - { - delta = -delta; - synth->offset_inline( time, delta, output ); - time += period; - phase ^= 1; - } - while ( time < end_time ); - this->last_amp = phase ? volume : -volume; - } - } - delay = time - end_time; -} - -// Sms_Noise - -static int const noise_periods [3] = { 0x100, 0x200, 0x400 }; - -inline void Sms_Noise::reset() -{ - period = &noise_periods [0]; - shifter = 0x8000; - feedback = 0x9000; - Sms_Osc::reset(); -} - -void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) -{ - int amp = volume; - if ( shifter & 1 ) - amp = -amp; - - { - int delta = amp - last_amp; - if ( delta ) - { - last_amp = amp; - synth.offset( time, delta, output ); - } - } - - time += delay; - if ( !volume ) - time = end_time; - - if ( time < end_time ) - { - Blip_Buffer* const output = this->output; - unsigned shifter = this->shifter; - int delta = amp * 2; - int period = *this->period * 2; - if ( !period ) - period = 16; - - do - { - int changed = shifter + 1; - shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1); - if ( changed & 2 ) // true if bits 0 and 1 differ - { - delta = -delta; - synth.offset_inline( time, delta, output ); - } - time += period; - } - while ( time < end_time ); - - this->shifter = shifter; - this->last_amp = delta >> 1; - } - delay = time - end_time; -} - -// Sms_Apu - -Sms_Apu::Sms_Apu() -{ - for ( int i = 0; i < 3; i++ ) - { - squares [i].synth = &square_synth; - oscs [i] = &squares [i]; - } - oscs [3] = &noise; - - volume( 1.0 ); - reset(); -} - -Sms_Apu::~Sms_Apu() -{ -} +int const noise_osc = 3; void Sms_Apu::volume( double vol ) { - vol *= 0.85 / (osc_count * 64 * 2); - square_synth.volume( vol ); - noise.synth.volume( vol ); + vol *= 0.85 / osc_count / 64; + norm_synth.volume( vol ); + fast_synth.volume( vol ); } -void Sms_Apu::treble_eq( const blip_eq_t& eq ) +void Sms_Apu::treble_eq( blip_eq_t const& eq ) { - square_synth.treble_eq( eq ); - noise.synth.treble_eq( eq ); + norm_synth.treble_eq( eq ); + fast_synth.treble_eq( eq ); } -void Sms_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +inline int Sms_Apu::calc_output( int i ) const { - require( (unsigned) index < osc_count ); - require( (center && left && right) || (!center && !left && !right) ); - Sms_Osc& osc = *oscs [index]; - osc.outputs [1] = right; - osc.outputs [2] = left; - osc.outputs [3] = center; - osc.output = osc.outputs [osc.output_select]; + int flags = ggstereo >> i; + return (flags >> 3 & 2) | (flags & 1); } -void Sms_Apu::output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) +void Sms_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) { - for ( int i = 0; i < osc_count; i++ ) - osc_output( i, center, left, right ); + // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL) + require( !center || (center && !left && !right) || (center && left && right) ); + require( (unsigned) i < osc_count ); // fails if you pass invalid osc index + + if ( center ) + { + unsigned const divisor = 16384 * 16 * 2; + min_tone_period = ((unsigned) center->clock_rate() + divisor/2) / divisor; + } + + if ( !center || !left || !right ) + { + left = center; + right = center; + } + + Osc& o = oscs [i]; + o.outputs [0] = NULL; + o.outputs [1] = right; + o.outputs [2] = left; + o.outputs [3] = center; + o.output = o.outputs [calc_output( i )]; +} + +void Sms_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + for ( int i = osc_count; --i >= 0; ) + set_output( i, c, l, r ); +} + +static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width ) +{ + unsigned galois = 0; + while ( --width >= 0 ) + { + galois = (galois << 1) | (fibonacci & 1); + fibonacci >>= 1; + } + return galois; } void Sms_Apu::reset( unsigned feedback, int noise_width ) { last_time = 0; - latch = 0; + latch = 0; + ggstereo = 0; + // Calculate noise feedback values if ( !feedback || !noise_width ) { - feedback = 0x0009; + feedback = 0x0009; noise_width = 16; } - // convert to "Galios configuration" looped_feedback = 1 << (noise_width - 1); - noise_feedback = 0; - while ( noise_width-- ) + noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width ); + + // Reset oscs + for ( int i = osc_count; --i >= 0; ) { - noise_feedback = (noise_feedback << 1) | (feedback & 1); - feedback >>= 1; + Osc& o = oscs [i]; + o.output = NULL; + o.last_amp = 0; + o.delay = 0; + o.phase = 0; + o.period = 0; + o.volume = 15; // silent } - squares [0].reset(); - squares [1].reset(); - squares [2].reset(); - noise.reset(); + oscs [noise_osc].phase = 0x8000; + write_ggstereo( 0, 0xFF ); +} + +Sms_Apu::Sms_Apu() +{ + min_tone_period = 7; + + // Clear outputs to NULL FIRST + ggstereo = 0; + set_output( NULL ); + + volume( 1.0 ); + reset(); } void Sms_Apu::run_until( blip_time_t end_time ) { - require( end_time >= last_time ); // end_time must not be before previous time + require( end_time >= last_time ); + if ( end_time <= last_time ) + return; - if ( end_time > last_time ) + // Synthesize each oscillator + for ( int idx = osc_count; --idx >= 0; ) { - // run oscillators - for ( int i = 0; i < osc_count; ++i ) + Osc& osc = oscs [idx]; + int vol = 0; + int amp = 0; + + // Determine what will be generated + Blip_Buffer* const out = osc.output; + if ( out ) { - Sms_Osc& osc = *oscs [i]; - if ( osc.output ) + // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) + static unsigned char const volumes [16] = { + 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0 + }; + + vol = volumes [osc.volume]; + amp = (osc.phase & 1) * vol; + + // Square freq above 16 kHz yields constant amplitude at half volume + if ( idx != noise_osc && osc.period < min_tone_period ) { - osc.output->set_modified(); - if ( i < 3 ) - squares [i].run( last_time, end_time ); - else - noise.run( last_time, end_time ); + amp = vol >> 1; + vol = 0; + } + + // Update amplitude + int delta = amp - osc.last_amp; + if ( delta ) + { + osc.last_amp = amp; + norm_synth.offset( last_time, delta, out ); + out->set_modified(); } } - last_time = end_time; + // Generate wave + blip_time_t time = last_time + osc.delay; + if ( time < end_time ) + { + // Calculate actual period + int period = osc.period; + if ( idx == noise_osc ) + { + period = 0x20 << (period & 3); + if ( period == 0x100 ) + period = oscs [2].period * 2; + } + period *= 0x10; + if ( !period ) + period = 0x10; + + // Maintain phase when silent + int phase = osc.phase; + if ( !vol ) + { + int count = (end_time - time + period - 1) / period; + time += count * period; + if ( idx != noise_osc ) // TODO: maintain noise LFSR phase? + phase ^= count & 1; + } + else + { + int delta = amp * 2 - vol; + + if ( idx != noise_osc ) + { + // Square + do + { + delta = -delta; + norm_synth.offset( time, delta, out ); + time += period; + } + while ( time < end_time ); + phase = (delta >= 0); + } + else + { + // Noise + unsigned const feedback = (osc.period & 4 ? noise_feedback : looped_feedback); + do + { + unsigned changed = phase + 1; + phase = ((phase & 1) * feedback) ^ (phase >> 1); + if ( changed & 2 ) // true if bits 0 and 1 differ + { + delta = -delta; + fast_synth.offset_inline( time, delta, out ); + } + time += period; + } + while ( time < end_time ); + check( phase ); + } + osc.last_amp = (phase & 1) * vol; + out->set_modified(); + } + osc.phase = phase; + } + osc.delay = time - end_time; } -} - -void Sms_Apu::end_frame( blip_time_t end_time ) -{ - if ( end_time > last_time ) - run_until( end_time ); - - assert( last_time >= end_time ); - last_time -= end_time; + last_time = end_time; } void Sms_Apu::write_ggstereo( blip_time_t time, int data ) @@ -269,31 +238,30 @@ void Sms_Apu::write_ggstereo( blip_time_t time, int data ) require( (unsigned) data <= 0xFF ); run_until( time ); + ggstereo = data; - for ( int i = 0; i < osc_count; i++ ) + for ( int i = osc_count; --i >= 0; ) { - Sms_Osc& osc = *oscs [i]; - int flags = data >> i; - Blip_Buffer* old_output = osc.output; - osc.output_select = (flags >> 3 & 2) | (flags & 1); - osc.output = osc.outputs [osc.output_select]; - if ( osc.output != old_output && osc.last_amp ) + Osc& osc = oscs [i]; + + Blip_Buffer* old = osc.output; + osc.output = osc.outputs [calc_output( i )]; + if ( osc.output != old ) { - if ( old_output ) + int delta = -osc.last_amp; + if ( delta ) { - old_output->set_modified(); - square_synth.offset( time, -osc.last_amp, old_output ); + osc.last_amp = 0; + if ( old ) + { + old->set_modified(); + fast_synth.offset( last_time, delta, old ); + } } - osc.last_amp = 0; } } } -// volumes [i] = 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 ) -static unsigned char const volumes [16] = { - 64, 50, 39, 31, 24, 19, 15, 12, 9, 7, 5, 4, 3, 2, 1, 0 -}; - void Sms_Apu::write_data( blip_time_t time, int data ) { require( (unsigned) data <= 0xFF ); @@ -303,28 +271,101 @@ void Sms_Apu::write_data( blip_time_t time, int data ) if ( data & 0x80 ) latch = data; - int index = (latch >> 5) & 3; + // We want the raw values written so our save state format can be + // as close to hardware as possible and unspecific to any emulator. + int idx = latch >> 5 & 3; + Osc& osc = oscs [idx]; if ( latch & 0x10 ) { - oscs [index]->volume = volumes [data & 15]; - } - else if ( index < 3 ) - { - Sms_Square& sq = squares [index]; - if ( data & 0x80 ) - sq.period = (sq.period & 0xFF00) | (data << 4 & 0x00FF); - else - sq.period = (sq.period & 0x00FF) | (data << 8 & 0x3F00); + osc.volume = data & 0x0F; } else { - int select = data & 3; - if ( select < 3 ) - noise.period = &noise_periods [select]; - else - noise.period = &squares [2].period; + if ( idx == noise_osc ) + osc.phase = 0x8000; // reset noise LFSR - noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; - noise.shifter = 0x8000; + // Replace high 6 bits/low 4 bits of register with data + int lo = osc.period; + int hi = data << 4; + if ( idx == noise_osc || (data & 0x80) ) + { + hi = lo; + lo = data; + } + osc.period = (hi & 0x3F0) | (lo & 0x00F); } } + +void Sms_Apu::end_frame( blip_time_t end_time ) +{ + if ( end_time > last_time ) + run_until( end_time ); + + last_time -= end_time; + assert( last_time >= 0 ); +} + +#if SMS_APU_CUSTOM_STATE + #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) ) +#else + #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y ))) + + static unsigned get_val( byte const p [] ) + { + return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0]; + } + + static void set_val( byte p [], unsigned n ) + { + p [0] = (byte) (n ); + p [1] = (byte) (n >> 8); + p [2] = (byte) (n >> 16); + p [3] = (byte) (n >> 24); + } +#endif + +inline const char* Sms_Apu::save_load( sms_apu_state_t* io, bool save ) +{ + #if !SMS_APU_CUSTOM_STATE + assert( sizeof (sms_apu_state_t) == 128 ); + #endif + + // Format of data, where later format is incompatible with earlier + int format = io->format0; + REFLECT( format, format ); + if ( format != io->format0 ) + return "Unsupported sound save state format"; + + // Version of data, where later versions just add fields to the end + int version = 0; + REFLECT( version, version ); + + REFLECT( latch, latch ); + REFLECT( ggstereo, ggstereo ); + + for ( int i = osc_count; --i >= 0; ) + { + Osc& osc = oscs [i]; + REFLECT( osc.period, periods [i] ); + REFLECT( osc.volume, volumes [i] ); + REFLECT( osc.delay, delays [i] ); + REFLECT( osc.phase, phases [i] ); + } + + return 0; +} + +void Sms_Apu::save_state( sms_apu_state_t* out ) +{ + save_load( out, true ); + #if !SMS_APU_CUSTOM_STATE + memset( out->unused, 0, sizeof out->unused ); + #endif +} + +blargg_err_t Sms_Apu::load_state( sms_apu_state_t const& in ) +{ + RETURN_ERR( save_load( CONST_CAST(sms_apu_state_t*,&in), false ) ); + write_ggstereo( 0, ggstereo ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Sms_Apu.h b/Frameworks/GME/gme/Sms_Apu.h old mode 100755 new mode 100644 index 3c11a9c3c..b93e47bc8 --- a/Frameworks/GME/gme/Sms_Apu.h +++ b/Frameworks/GME/gme/Sms_Apu.h @@ -1,75 +1,128 @@ // Sega Master System SN76489 PSG sound chip emulator -// Sms_Snd_Emu 0.1.4 +// Sms_Snd_Emu $vers #ifndef SMS_APU_H #define SMS_APU_H -#include "Sms_Oscs.h" +#include "blargg_common.h" +#include "Blip_Buffer.h" + +struct sms_apu_state_t; class Sms_Apu { public: - // Set overall volume of all oscillators, where 1.0 is full volume - void volume( double ); +// Basics + + // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0, + // output is mono. + void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); + + // Emulates to time t, then writes data to Game Gear left/right assignment byte + void write_ggstereo( blip_time_t t, int data ); - // Set treble equalization - void treble_eq( const blip_eq_t& ); + // Emulates to time t, then writes data + void write_data( blip_time_t t, int data ); - // Outputs can be assigned to a single buffer for mono output, or to three - // buffers for stereo output (using Stereo_Buffer to do the mixing). - - // Assign all oscillator outputs to specified buffer(s). If buffer - // is NULL, silences all oscillators. - void output( Blip_Buffer* mono ); - void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, - // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL, - // silences oscillator. - enum { osc_count = 4 }; - void osc_output( int index, Blip_Buffer* mono ); - void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - - // Reset oscillators and internal state + // Emulates to time t, then subtracts t from the current time. + // OK if previous write call had time slightly after t. + void end_frame( blip_time_t t ); + +// More features + + // Resets sound chip and sets noise feedback bits and width void reset( unsigned noise_feedback = 0, int noise_width = 0 ); - // Write GameGear left/right assignment byte - void write_ggstereo( blip_time_t, int ); + // Same as set_output(), but for a particular channel + // 0: Square 1, 1: Square 2, 2: Square 3, 3: Noise + enum { osc_count = 4 }; // 0 <= chan < osc_count + void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL ); - // Write to data port - void write_data( blip_time_t, int ); + // Sets overall volume, where 1.0 is normal + void volume( double ); - // Run all oscillators up to specified time, end current frame, then - // start a new frame at time 0. - void end_frame( blip_time_t ); + // Sets treble equalization + void treble_eq( blip_eq_t const& ); + + // Saves full emulation state to state_out. Data format is portable and + // includes some extra space to avoid expansion in case more state needs + // to be stored in the future. + void save_state( sms_apu_state_t* state_out ); + + // Loads state. You should call reset() BEFORE this. + blargg_err_t load_state( sms_apu_state_t const& in ); -public: - Sms_Apu(); - ~Sms_Apu(); private: // noncopyable Sms_Apu( const Sms_Apu& ); Sms_Apu& operator = ( const Sms_Apu& ); + +// Implementation +public: + Sms_Apu(); + ~Sms_Apu() { } + BLARGG_DISABLE_NOTHROW + + // Use set_output() instead + BLARGG_DEPRECATED( void output ( Blip_Buffer* c ) { set_output( c, c, c ); } ) + BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } ) + BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } ) + +private: + struct Osc + { + Blip_Buffer* outputs [4]; // NULL, right, left, center + Blip_Buffer* output; + int last_amp; + + int volume; + int period; + int delay; + unsigned phase; + }; + + Osc oscs [osc_count]; + int ggstereo; + int latch; - Sms_Osc* oscs [osc_count]; - Sms_Square squares [3]; - Sms_Square::Synth square_synth; // used by squares blip_time_t last_time; - int latch; - Sms_Noise noise; + int min_tone_period; unsigned noise_feedback; unsigned looped_feedback; + Blip_Synth_Fast fast_synth; + Blip_Synth_Norm norm_synth; + int calc_output( int i ) const; void run_until( blip_time_t ); + const char* save_load( sms_apu_state_t*, bool save ); + friend class Sms_Apu_Tester; }; struct sms_apu_state_t { - unsigned char regs [8] [2]; - unsigned char latch; + // If SMS_APU_CUSTOM_STATE is 1, values are stored as normal integers, + // so your code can then save and load them however it likes. Otherwise, + // they are 4-byte arrays in little-endian format, making entire + // structure suitable for direct storage on disk. + +#if SMS_APU_CUSTOM_STATE + typedef int val_t; +#else + typedef unsigned char val_t [4]; +#endif + + enum { format0 = 0x50414D53 }; + + val_t format; + val_t version; + val_t latch; + val_t ggstereo; + val_t periods [4]; + val_t volumes [4]; + val_t delays [4]; + val_t phases [4]; + + val_t unused [12]; // for future expansion }; -inline void Sms_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - -inline void Sms_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } - #endif diff --git a/Frameworks/GME/gme/Snes_Spc.cpp b/Frameworks/GME/gme/Snes_Spc.cpp old mode 100755 new mode 100644 index e909ea186..2efa1fd2f --- a/Frameworks/GME/gme/Snes_Spc.cpp +++ b/Frameworks/GME/gme/Snes_Spc.cpp @@ -1,10 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// SPC emulation support: init, sample buffering, reset, SPC loading + +// snes_spc $vers. http://www.slack.net/~ant/ #include "Snes_Spc.h" -#include - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -17,473 +17,359 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// always in the future (CPU time can go over 0, but not by this much) -int const timer_disabled_time = 127; +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) -Snes_Spc::Snes_Spc() : dsp( mem.ram ), cpu( this, mem.ram ) +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) + + +//// Init + +blargg_err_t Snes_Spc::init() { - set_tempo( 1.0 ); + memset( &m, 0, sizeof m ); + dsp.init( RAM ); + + set_sfm_queue( 0, 0 ); - // Put STOP instruction around memory to catch PC underflow/overflow. - memset( mem.padding1, 0xFF, sizeof mem.padding1 ); - memset( mem.padding2, 0xFF, sizeof mem.padding2 ); + m.tempo = tempo_unit; - // A few tracks read from the last four bytes of IPL ROM - boot_rom [sizeof boot_rom - 2] = 0xC0; - boot_rom [sizeof boot_rom - 1] = 0xFF; - memset( boot_rom, 0, sizeof boot_rom - 2 ); -} - -void Snes_Spc::set_tempo( double t ) -{ - int unit = (int) (16.0 / t + 0.5); + // Most SPC music doesn't need ROM, and almost all the rest only rely + // on these two bytes + m.rom [0x3E] = 0xFF; + m.rom [0x3F] = 0xC0; - timer [0].divisor = unit * 8; // 8 kHz - timer [1].divisor = unit * 8; // 8 kHz - timer [2].divisor = unit; // 64 kHz -} - -// Load - -void Snes_Spc::set_ipl_rom( void const* in ) -{ - memcpy( boot_rom, in, sizeof boot_rom ); -} - -blargg_err_t Snes_Spc::load_spc( const void* data, long size ) -{ - struct spc_file_t { - char signature [27]; - char unused [10]; - uint8_t pc [2]; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - char unused2 [212]; - uint8_t ram [0x10000]; - uint8_t dsp [128]; - uint8_t ipl_rom [128]; + static unsigned char const cycle_table [128] = + {// 01 23 45 67 89 AB CD EF + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1 + 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2 + 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4 + 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5 + 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7 + 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8 + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9 + 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A + 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B + 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C + 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D + 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E + 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F }; - assert( offsetof (spc_file_t,ipl_rom) == spc_file_size ); - const spc_file_t* spc = (spc_file_t const*) data; - - if ( size < spc_file_size ) - return "Not an SPC file"; - - if ( strncmp( spc->signature, "SNES-SPC700 Sound File Data", 27 ) != 0 ) - return "Not an SPC file"; - - registers_t regs; - regs.pc = spc->pc [1] * 0x100 + spc->pc [0]; - regs.a = spc->a; - regs.x = spc->x; - regs.y = spc->y; - regs.status = spc->status; - regs.sp = spc->sp; - - if ( (unsigned long) size >= sizeof *spc ) - set_ipl_rom( spc->ipl_rom ); - - const char* error = load_state( regs, spc->ram, spc->dsp ); - - echo_accessed = false; - - return error; -} - -void Snes_Spc::clear_echo() -{ - if ( !(dsp.read( 0x6C ) & 0x20) ) + // unpack cycle table + for ( int i = 0; i < 128; i++ ) { - unsigned addr = 0x100 * dsp.read( 0x6D ); - size_t size = 0x800 * dsp.read( 0x7D ); - memset( mem.ram + addr, 0xFF, min( size, sizeof mem.ram - addr ) ); + int n = cycle_table [i]; + m.cycle_table [i * 2 + 0] = n >> 4; + m.cycle_table [i * 2 + 1] = n & 0x0F; } + + #if SPC_LESS_ACCURATE + memcpy( reg_times, reg_times_, sizeof reg_times ); + #endif + + reset(); + return blargg_ok; } -// Handle other file formats (emulator save states) in user code, not here. - -blargg_err_t Snes_Spc::load_state( const registers_t& cpu_state, const void* new_ram, - const void* dsp_state ) +void Snes_Spc::init_rom( uint8_t const in [rom_size] ) { - // cpu - cpu.r = cpu_state; + memcpy( m.rom, in, sizeof m.rom ); +} + +void Snes_Spc::set_tempo( int t ) +{ + m.tempo = t; + int const timer2_shift = 4; // 64 kHz + int const other_shift = 3; // 8 kHz - // Allow DSP to generate one sample before code starts - // (Tengai Makyo Zero, Tenjin's Table Toss first notes are lost since it - // clears KON 31 cycles from starting execution. It works on the SNES - // since the SPC player adds a few extra cycles delay after restoring - // KON from the DSP registers at the end of an SPC file). - extra_cycles = 32; - - // ram - memcpy( mem.ram, new_ram, sizeof mem.ram ); - memcpy( extra_ram, mem.ram + rom_addr, sizeof extra_ram ); - - // boot rom (have to force enable_rom() to update it) - rom_enabled = !(mem.ram [0xF1] & 0x80); - enable_rom( !rom_enabled ); - - // dsp - dsp.reset(); + if ( !t ) + t = 1; + int const timer2_rate = 1 << timer2_shift; + int rate = (timer2_rate * tempo_unit + (t >> 1)) / t; + if ( rate < timer2_rate / 4 ) + rate = timer2_rate / 4; // max 4x tempo + m.timers [2].prescaler = rate; + m.timers [1].prescaler = rate << other_shift; + m.timers [0].prescaler = rate << other_shift; +} + +// Timer registers have been loaded. Applies these to the timers. Does not +// reset timer prescalers or dividers. +void Snes_Spc::timers_loaded() +{ int i; - for ( i = 0; i < Spc_Dsp::register_count; i++ ) - dsp.write( i, ((uint8_t const*) dsp_state) [i] ); - - // timers for ( i = 0; i < timer_count; i++ ) { - Timer& t = timer [i]; - - t.next_tick = 0; - t.enabled = (mem.ram [0xF1] >> i) & 1; - if ( !t.enabled ) - t.next_tick = timer_disabled_time; - t.count = 0; - t.counter = mem.ram [0xFD + i] & 15; - - int p = mem.ram [0xFA + i]; - t.period = p ? p : 0x100; + Timer* t = &m.timers [i]; + t->period = IF_0_THEN_256( REGS [r_t0target + i] ); + t->enabled = REGS [r_control] >> i & 1; + t->counter = REGS_IN [r_t0out + i] & 0x0F; } - // Handle registers which already give 0 when read by setting RAM and not changing it. - // Put STOP instruction in registers which can be read, to catch attempted CPU execution. - mem.ram [0xF0] = 0; - mem.ram [0xF1] = 0; - mem.ram [0xF3] = 0xFF; - mem.ram [0xFA] = 0; - mem.ram [0xFB] = 0; - mem.ram [0xFC] = 0; - mem.ram [0xFD] = 0xFF; - mem.ram [0xFE] = 0xFF; - mem.ram [0xFF] = 0xFF; - - return 0; // success + set_tempo( m.tempo ); } -// Hardware - -// Current time starts negative and ends at 0 -inline spc_time_t Snes_Spc::time() const +// Loads registers from unified 16-byte format +void Snes_Spc::load_regs( uint8_t const in [reg_count] ) { - return -cpu.remain(); + memcpy( REGS, in, reg_count ); + memcpy( REGS_IN, REGS, reg_count ); + + // These always read back as 0 + REGS_IN [r_test ] = 0; + REGS_IN [r_control ] = 0; + REGS_IN [r_t0target] = 0; + REGS_IN [r_t1target] = 0; + REGS_IN [r_t2target] = 0; } -// Keep track of next time to run and avoid a function call if it hasn't been reached. - -// Timers - -void Snes_Spc::Timer::run_until_( spc_time_t time ) +// RAM was just loaded from SPC, with $F0-$FF containing SMP registers +// and timer counts. Copies these to proper registers. +void Snes_Spc::ram_loaded() { - if ( !enabled ) - dprintf( "next_tick: %ld, time: %ld", (long) next_tick, (long) time ); - assert( enabled ); // when disabled, next_tick should always be in the future + m.rom_enabled = 0; + load_regs( &RAM [0xF0] ); - int elapsed = ((time - next_tick) / divisor) + 1; - next_tick += elapsed * divisor; - - elapsed += count; - if ( elapsed >= period ) // avoid unnecessary division - { - int n = elapsed / period; - elapsed -= n * period; - counter = (counter + n) & 15; - } - count = elapsed; + // Put STOP instruction around memory to catch PC underflow/overflow + memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); + memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 ); } -// DSP - -const int clocks_per_sample = 32; // 1.024 MHz CPU clock / 32000 samples per second - -void Snes_Spc::run_dsp_( spc_time_t time ) +// Registers were just loaded. Applies these new values. +void Snes_Spc::regs_loaded() { - int count = ((time - next_dsp) >> 5) + 1; // divide by clocks_per_sample - sample_t* buf = sample_buf; - if ( buf ) { - sample_buf = buf + count * 2; // stereo - assert( sample_buf <= buf_end ); - } - next_dsp += count * clocks_per_sample; - dsp.run( count, buf ); + enable_rom( REGS [r_control] & 0x80 ); + timers_loaded(); } -inline void Snes_Spc::run_dsp( spc_time_t time ) +void Snes_Spc::reset_time_regs() { - if ( time >= next_dsp ) - run_dsp_( time ); -} - -// Debug-only check for read/write within echo buffer, since this might result in -// inaccurate emulation due to the DSP not being caught up to the present. -inline void Snes_Spc::check_for_echo_access( spc_addr_t addr ) -{ - if ( !echo_accessed && !(dsp.read( 0x6C ) & 0x20) ) - { - // ** If echo accesses are found that require running the DSP, cache - // the start and end address on DSP writes to speed up checking. - - unsigned start = 0x100 * dsp.read( 0x6D ); - unsigned end = start + 0x800 * dsp.read( 0x7D ); - if ( start <= addr && addr < end ) { - echo_accessed = true; - dprintf( "Read/write at $%04X within echo buffer\n", (unsigned) addr ); - } - } -} - -// Read - -int Snes_Spc::read( spc_addr_t addr ) -{ - int result = mem.ram [addr]; + m.cpu_error = NULL; + m.echo_accessed = 0; + m.spc_time = 0; + m.dsp_time = 0; + #if SPC_LESS_ACCURATE + m.dsp_time = clocks_per_sample + 1; + #endif - if ( (rom_addr <= addr && addr < 0xFFFC || addr >= 0xFFFE) && rom_enabled ) - dprintf( "Read from ROM: %04X -> %02X\n", addr, result ); - - if ( unsigned (addr - 0xF0) < 0x10 ) - { - assert( 0xF0 <= addr && addr <= 0xFF ); - - // counters - int i = addr - 0xFD; - if ( i >= 0 ) - { - Timer& t = timer [i]; - t.run_until( time() ); - int old = t.counter; - t.counter = 0; - return old; - } - - // dsp - if ( addr == 0xF3 ) - { - run_dsp( time() ); - if ( mem.ram [0xF2] >= Spc_Dsp::register_count ) - dprintf( "DSP read from $%02X\n", (int) mem.ram [0xF2] ); - return dsp.read( mem.ram [0xF2] & 0x7F ); - } - - if ( addr == 0xF0 || addr == 0xF1 || addr == 0xF8 || - addr == 0xF9 || addr == 0xFA ) - dprintf( "Read from register $%02X\n", (int) addr ); - - // Registers which always read as 0 are handled by setting mem.ram [reg] to 0 - // at startup then never changing that value. - - check(( check_for_echo_access( addr ), true )); - } - - return result; -} - - -// Write - -void Snes_Spc::enable_rom( bool enable ) -{ - if ( rom_enabled != enable ) - { - rom_enabled = enable; - memcpy( mem.ram + rom_addr, (enable ? boot_rom : extra_ram), rom_size ); - // TODO: ROM can still get overwritten when DSP writes to echo buffer - } -} - -void Snes_Spc::write( spc_addr_t addr, int data ) -{ - // first page is very common - if ( addr < 0xF0 ) { - mem.ram [addr] = (uint8_t) data; - } - else switch ( addr ) - { - // RAM - default: - check(( check_for_echo_access( addr ), true )); - if ( addr < rom_addr ) { - mem.ram [addr] = (uint8_t) data; - } - else { - extra_ram [addr - rom_addr] = (uint8_t) data; - if ( !rom_enabled ) - mem.ram [addr] = (uint8_t) data; - } - break; - - // DSP - //case 0xF2: // mapped to RAM - case 0xF3: { - run_dsp( time() ); - int reg = mem.ram [0xF2]; - if ( next_dsp > 0 ) { - // skip mode - - // key press - if ( reg == 0x4C ) - keys_pressed |= data & ~dsp.read( 0x5C ); - - // key release - if ( reg == 0x5C ) { - keys_released |= data; - keys_pressed &= ~data; - } - } - if ( reg < Spc_Dsp::register_count ) { - dsp.write( reg, data ); - } - else { - dprintf( "DSP write to $%02X\n", (int) reg ); - } - break; - } - - case 0xF0: // Test register - dprintf( "Wrote $%02X to $F0\n", (int) data ); - break; - - // Config - case 0xF1: - { - // timers - for ( int i = 0; i < timer_count; i++ ) - { - Timer& t = timer [i]; - if ( !(data & (1 << i)) ) { - t.enabled = 0; - t.next_tick = timer_disabled_time; - } - else if ( !t.enabled ) { - // just enabled - t.enabled = 1; - t.counter = 0; - t.count = 0; - t.next_tick = time(); - } - } - - // port clears - if ( data & 0x10 ) { - mem.ram [0xF4] = 0; - mem.ram [0xF5] = 0; - } - if ( data & 0x20 ) { - mem.ram [0xF6] = 0; - mem.ram [0xF7] = 0; - } - - enable_rom( (data & 0x80) != 0 ); - - break; - } - - // Ports - case 0xF4: - case 0xF5: - case 0xF6: - case 0xF7: - // to do: handle output ports - break; - - //case 0xF8: // verified on SNES that these are read/write (RAM) - //case 0xF9: - - // Timers - case 0xFA: - case 0xFB: - case 0xFC: { - Timer& t = timer [addr - 0xFA]; - if ( (t.period & 0xFF) != data ) { - t.run_until( time() ); - t.period = data ? data : 0x100; - } - break; - } - - // Counters (cleared on write) - case 0xFD: - case 0xFE: - case 0xFF: - dprintf( "Wrote to counter $%02X\n", (int) addr ); - timer [addr - 0xFD].counter = 0; - break; - } -} - -// Play - -blargg_err_t Snes_Spc::skip( long count ) -{ - if ( count > 4 * 32000L ) - { - // don't run DSP for long durations (2-3 times faster) - - const long sync_count = 32000L * 2; - - // keep track of any keys pressed/released (and not subsequently released) - keys_pressed = 0; - keys_released = 0; - // sentinel tells play to ignore DSP - RETURN_ERR( play( count - sync_count, skip_sentinel ) ); - - // press/release keys now - dsp.write( 0x5C, keys_released & ~keys_pressed ); - dsp.write( 0x4C, keys_pressed ); - - clear_echo(); - - // play the last few seconds normally to help synchronize DSP - count = sync_count; - } - - return play( count ); -} - -blargg_err_t Snes_Spc::play( long count, sample_t* out ) -{ - require( count % 2 == 0 ); // output is always in pairs of samples - - // CPU time() runs from -duration to 0 - spc_time_t duration = (count / 2) * clocks_per_sample; - - // DSP output is made on-the-fly when the CPU reads/writes DSP registers - sample_buf = out; - buf_end = out + (out && out != skip_sentinel ? count : 0); - next_dsp = (out == skip_sentinel) ? clocks_per_sample : -duration + clocks_per_sample; - - // Localize timer next_tick times and run them to the present to prevent a running - // but ignored timer's next_tick from getting too far behind and overflowing. for ( int i = 0; i < timer_count; i++ ) { - Timer& t = timer [i]; - if ( t.enabled ) - { - t.next_tick -= duration; - t.run_until( -duration ); - } + Timer* t = &m.timers [i]; + t->next_time = 1; + t->divider = 0; } - // Run CPU for duration, reduced by any extra cycles from previous run - int elapsed = cpu.run( duration - extra_cycles ); - if ( elapsed > 0 ) - { - dprintf( "Unhandled instruction $%02X, pc = $%04X\n", - (int) cpu.read( cpu.r.pc ), (unsigned) cpu.r.pc ); - return "Emulation error (illegal/unsupported instruction)"; - } - extra_cycles = -elapsed; + regs_loaded(); - // Catch DSP up to present. - run_dsp( 0 ); - if ( out ) { - assert( next_dsp == clocks_per_sample ); - assert( out == skip_sentinel || sample_buf - out == count ); - } - buf_end = 0; - - return 0; + m.extra_clocks = 0; + reset_buf(); +} + +void Snes_Spc::reset_common( int timer_counter_init ) +{ + int i; + for ( i = 0; i < timer_count; i++ ) + REGS_IN [r_t0out + i] = timer_counter_init; + + // Run IPL ROM + memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); + m.cpu_regs.pc = rom_addr; + + REGS [r_test ] = 0x0A; + REGS [r_control] = 0xB0; // ROM enabled, clear ports + for ( i = 0; i < port_count; i++ ) + REGS_IN [r_cpuio0 + i] = 0; + + reset_time_regs(); +} + +void Snes_Spc::soft_reset() +{ + reset_common( 0 ); + dsp.soft_reset(); +} + +void Snes_Spc::reset() +{ + memset( RAM, 0xFF, 0x10000 ); + ram_loaded(); + reset_common( 0x0F ); + dsp.reset(); +} + +char const Snes_Spc::signature [signature_size + 1] = + "SNES-SPC700 Sound File Data v0.30\x1A\x1A"; + +blargg_err_t Snes_Spc::load_spc( void const* data, long size ) +{ + spc_file_t const* const spc = (spc_file_t const*) data; + + // be sure compiler didn't insert any padding into fle_t + assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 ); + + // Check signature and file size + if ( size < signature_size || memcmp( spc, signature, 27 ) ) + return "Not an SPC file"; + + if ( size < spc_min_file_size ) + return "Corrupt SPC file"; + + // CPU registers + m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; + m.cpu_regs.a = spc->a; + m.cpu_regs.x = spc->x; + m.cpu_regs.y = spc->y; + m.cpu_regs.psw = spc->psw; + m.cpu_regs.sp = spc->sp; + + // RAM and registers + memcpy( RAM, spc->ram, 0x10000 ); + ram_loaded(); + + // DSP registers + dsp.load( spc->dsp ); + + reset_time_regs(); + + return blargg_ok; +} + +void Snes_Spc::clear_echo(bool force) +{ + if ( ( force || !m.echo_cleared ) && !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) + { + int addr = 0x100 * dsp.read( Spc_Dsp::r_esa ); + int end = addr + 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F); + if ( end > 0x10000 ) + end = 0x10000; + memset( &RAM [addr], 0xFF, end - addr ); + m.echo_cleared = true; + } +} + + +//// Sample output + +void Snes_Spc::reset_buf() +{ + // Start with half extra buffer of silence + sample_t* out = m.extra_buf; + while ( out < &m.extra_buf [extra_size / 2] ) + *out++ = 0; + + m.extra_pos = out; + m.buf_begin = NULL; + + dsp.set_output( NULL, 0 ); +} + +void Snes_Spc::set_output( sample_t out [], int size ) +{ + require( (size & 1) == 0 ); // size must be even + + m.extra_clocks &= clocks_per_sample - 1; + if ( out ) + { + sample_t const* out_end = out + size; + m.buf_begin = out; + m.buf_end = out_end; + + // Copy extra to output + sample_t const* in = m.extra_buf; + while ( in < m.extra_pos && out < out_end ) + *out++ = *in++; + + // Handle output being full already + if ( out >= out_end ) + { + // Have DSP write to remaining extra space + out = dsp.extra(); + out_end = &dsp.extra() [extra_size]; + + // Copy any remaining extra samples as if DSP wrote them + while ( in < m.extra_pos ) + *out++ = *in++; + assert( out <= out_end ); + } + + dsp.set_output( out, out_end - out ); + } + else + { + reset_buf(); + } +} + +void Snes_Spc::save_extra() +{ + // Get end pointers + sample_t const* main_end = m.buf_end; // end of data written to buf + sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra() + if ( m.buf_begin <= dsp_end && dsp_end <= main_end ) + { + main_end = dsp_end; + dsp_end = dsp.extra(); // nothing in DSP's extra + } + + // Copy any extra samples at these ends into extra_buf + sample_t* out = m.extra_buf; + sample_t const* in; + for ( in = m.buf_begin + sample_count(); in < main_end; in++ ) + *out++ = *in; + for ( in = dsp.extra(); in < dsp_end ; in++ ) + *out++ = *in; + + m.extra_pos = out; + assert( out <= &m.extra_buf [extra_size] ); +} + +blargg_err_t Snes_Spc::play( int count, sample_t out [] ) +{ + require( (count & 1) == 0 ); // must be even + if ( count ) + { + set_output( out, count ); + end_frame( count * (clocks_per_sample / 2) ); + } + + const char* err = m.cpu_error; + m.cpu_error = NULL; + return err; +} + +blargg_err_t Snes_Spc::skip( int count ) +{ + #if SPC_LESS_ACCURATE + if ( count > 2 * sample_rate * 2 ) + { + set_output( NULL, 0 ); + + // Skip a multiple of 4 samples + time_t end = count; + count = (count & 3) + 1 * sample_rate * 2; + end = (end - count) * (clocks_per_sample / 2); + + m.skipped_kon = 0; + m.skipped_koff = 0; + + // Preserve DSP and timer synchronization + // TODO: verify that this really preserves it + int old_dsp_time = m.dsp_time + m.spc_time; + m.dsp_time = end - m.spc_time + skipping_time; + end_frame( end ); + m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; + + dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon ); + dsp.write( Spc_Dsp::r_kon , m.skipped_kon ); + clear_echo(); + } + #endif + + return play( count, NULL ); } diff --git a/Frameworks/GME/gme/Snes_Spc.h b/Frameworks/GME/gme/Snes_Spc.h old mode 100755 new mode 100644 index b558fb71d..12679a038 --- a/Frameworks/GME/gme/Snes_Spc.h +++ b/Frameworks/GME/gme/Snes_Spc.h @@ -1,121 +1,311 @@ -// Super Nintendo (SNES) SPC-700 APU Emulator +// SNES SPC-700 APU emulator -// Game_Music_Emu 0.5.2 +// snes_spc $vers #ifndef SNES_SPC_H #define SNES_SPC_H -#include "blargg_common.h" -#include "Spc_Cpu.h" #include "Spc_Dsp.h" +#include "blargg_endian.h" + +class Sfm_Emu; + +struct Snes_Spc { + friend class Sfm_Emu; -class Snes_Spc { public: + typedef BOOST::uint8_t uint8_t; - // Load copy of SPC data into emulator. Clear echo buffer if 'clear_echo' is true. - enum { spc_file_size = 0x10180 }; - blargg_err_t load_spc( const void* spc, long spc_size ); + // Must be called once before using + blargg_err_t init(); - // Generate 'count' samples and optionally write to 'buf'. Count must be even. - // Sample output is 16-bit 32kHz, signed stereo pairs with the left channel first. + // Sample pairs generated per second + enum { sample_rate = 32000 }; + +// Emulator use + + // Sets IPL ROM data. Library does not include ROM data. Most SPC music files + // don't need ROM, but a full emulator must provide this. + enum { rom_size = 0x40 }; + void init_rom( uint8_t const rom [rom_size] ); + + // Sets destination for output samples typedef short sample_t; - blargg_err_t play( long count, sample_t* buf = NULL ); + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since last set + int sample_count() const; + + // Resets SPC to power-on state. This resets your output buffer, so you must + // call set_output() after this. + void reset(); + + // Emulates pressing reset switch on SNES. This resets your output buffer, so + // you must call set_output() after this. + void soft_reset(); + + // 1024000 SPC clocks per second, sample pair every 32 clocks + typedef int time_t; + enum { clock_rate = 1024000 }; + enum { clocks_per_sample = 32 }; -// Optional functionality + // Emulated port read/write at specified time + enum { port_count = 4 }; + int read_port ( time_t, int port ); + void write_port( time_t, int port, int data ); + + // Runs SPC to end_time and starts a new time frame at 0 + void end_frame( time_t end_time ); - // Load copy of state into emulator. - typedef Spc_Cpu::registers_t registers_t; - blargg_err_t load_state( const registers_t& cpu_state, const void* ram_64k, - const void* dsp_regs_128 ); +// Sound control - // Clear echo buffer, useful because many tracks have junk in the buffer. - void clear_echo(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { voice_count = Spc_Dsp::voice_count }; + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; void mute_voices( int mask ); - // Skip forward by the specified number of samples (64000 samples = 1 second) - blargg_err_t skip( long count ); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped the - // 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated + // If true, prevents channels and global volumes from being phase-negated. void disable_surround( bool disable = true ); + + // If true, enables cubic interpolation + void interpolation_level( int level = 0 ); - // Set 128 bytes to use for IPL boot ROM. Makes copy. Default is zero filled, - // to avoid including copyrighted code from the SPC-700. - void set_ipl_rom( const void* ); + // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. + enum { tempo_unit = 0x100 }; + void set_tempo( int ); + +// SPC music files + + // Loads SPC data into emulator + enum { spc_min_file_size = 0x10180 }; + enum { spc_file_size = 0x10200 }; + blargg_err_t load_spc( void const* in, long size ); - void set_tempo( double ); + // Clears echo region. Useful after loading an SPC as many have garbage in echo. + void clear_echo(bool force = false); + + // Plays for count samples and write samples to out. Discards samples if out + // is NULL. Count must be a multiple of 2 since output is stereo. + blargg_err_t play( int count, sample_t out [] ); + // Skips count samples. Several times faster than play() when using fast DSP. + blargg_err_t skip( int count ); + + // blah + Spc_Dsp const* get_dsp() const; + Spc_Dsp * get_dsp(); + + // SFM Queue + void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end); + +// State save/load (only available with accurate DSP) + +#if !SPC_NO_COPY_STATE_FUNCS + // Saves/loads state + enum { state_size = 67 * 1024 }; // maximum space needed when saving + typedef Spc_Dsp::copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Writes minimal header to spc_out + static void init_header( void* spc_out ); + + // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. + // Does not set up SPC header; use init_header() for that. + void save_spc( void* spc_out ); + + // Returns true if new key-on events occurred since last check. Useful for + // trimming silence while saving an SPC. + bool check_kon(); +#endif + public: - Snes_Spc(); - typedef BOOST::uint8_t uint8_t; -private: - // timers + // TODO: document + struct regs_t + { + int pc; + int a; + int x; + int y; + int psw; + int sp; + }; + regs_t& smp_regs() { return m.cpu_regs; } + + uint8_t* smp_ram() { return m.ram.ram; } + + void run_until( time_t t ) { run_until_( t ); } +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::uint16_t uint16_t; + + // Time relative to m_spc_time. Speeds up code a bit by eliminating need to + // constantly add m_spc_time to time from CPU. CPU uses time that ends at + // 0 to eliminate reloading end time every instruction. It pays off. + typedef int rel_time_t; + struct Timer { - spc_time_t next_tick; + rel_time_t next_time; // time of next event + int prescaler; int period; - int count; - int divisor; + int divider; int enabled; int counter; - - void run_until_( spc_time_t ); - void run_until( spc_time_t time ) - { - if ( time >= next_tick ) - run_until_( time ); - } }; + enum { reg_count = 0x10 }; enum { timer_count = 3 }; - Timer timer [timer_count]; - - // hardware - int extra_cycles; - spc_time_t time() const; - int read( spc_addr_t ); - void write( spc_addr_t, int ); - friend class Spc_Cpu; + enum { extra_size = Spc_Dsp::extra_size }; - // dsp - sample_t* sample_buf; - sample_t* buf_end; // to do: remove this once possible bug resolved - spc_time_t next_dsp; + enum { signature_size = 35 }; + +private: Spc_Dsp dsp; - int keys_pressed; - int keys_released; - sample_t skip_sentinel [1]; // special value for play() passed by skip() - void run_dsp( spc_time_t ); - void run_dsp_( spc_time_t ); - bool echo_accessed; - void check_for_echo_access( spc_addr_t ); - // boot rom - enum { rom_size = 64 }; + #if SPC_LESS_ACCURATE + static signed char const reg_times_ [256]; + signed char reg_times [256]; + #endif + + struct state_t + { + Timer timers [timer_count]; + + uint8_t smp_regs [2] [reg_count]; + + regs_t cpu_regs; + + rel_time_t dsp_time; + time_t spc_time; + bool echo_accessed; + bool echo_cleared; + + int tempo; + int skipped_kon; + int skipped_koff; + const char* cpu_error; + + int extra_clocks; + sample_t* buf_begin; + sample_t const* buf_end; + sample_t* extra_pos; + sample_t extra_buf [extra_size]; + + int rom_enabled; + uint8_t rom [rom_size]; + uint8_t hi_ram [rom_size]; + + uint8_t const* sfm_queue; + uint8_t const* sfm_queue_end; + + unsigned char cycle_table [256]; + + struct + { + // padding to neutralize address overflow + union { + uint8_t padding1 [0x100]; + uint16_t align; // makes compiler align data for 16-bit access + } padding1 [1]; + uint8_t ram [0x10000]; + uint8_t padding2 [0x100]; + } ram; + }; + state_t m; + enum { rom_addr = 0xFFC0 }; - bool rom_enabled; - void enable_rom( bool ); - // CPU and RAM (at end because it's large) - Spc_Cpu cpu; - uint8_t extra_ram [rom_size]; - struct { - // padding to catch jumps before beginning or past end - uint8_t padding1 [0x100]; + enum { skipping_time = 127 }; + + // Value that padding should be filled with + enum { cpu_pad_fill = 0xFF }; + + enum { + r_test = 0x0, r_control = 0x1, + r_dspaddr = 0x2, r_dspdata = 0x3, + r_cpuio0 = 0x4, r_cpuio1 = 0x5, + r_cpuio2 = 0x6, r_cpuio3 = 0x7, + r_f8 = 0x8, r_f9 = 0x9, + r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, + r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF + }; + + void timers_loaded(); + void enable_rom( int enable ); + void reset_buf(); + void save_extra(); + void load_regs( uint8_t const in [reg_count] ); + void ram_loaded(); + void regs_loaded(); + void reset_time_regs(); + void reset_common( int timer_counter_init ); + + Timer* run_timer_ ( Timer* t, rel_time_t ); + Timer* run_timer ( Timer* t, rel_time_t ); + int dsp_read ( rel_time_t ); + void dsp_write ( int data, rel_time_t ); + void cpu_write_smp_reg_( int data, rel_time_t, int addr ); + void cpu_write_smp_reg ( int data, rel_time_t, int addr ); + void cpu_write_high ( int data, int i, rel_time_t ); + void cpu_write ( int data, int addr, rel_time_t ); + int cpu_read_smp_reg ( int i, rel_time_t ); + int cpu_read ( int addr, rel_time_t ); + unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); + + bool check_echo_access ( int addr ); + uint8_t* run_until_( time_t end_time ); + + struct spc_file_t + { + char signature [signature_size]; + uint8_t has_id666; + uint8_t version; + uint8_t pcl, pch; + uint8_t a; + uint8_t x; + uint8_t y; + uint8_t psw; + uint8_t sp; + char text [212]; uint8_t ram [0x10000]; - uint8_t padding2 [0x100]; - } mem; - uint8_t boot_rom [rom_size]; + uint8_t dsp [128]; + uint8_t unused [0x40]; + uint8_t ipl_rom [0x40]; + }; + + static char const signature [signature_size + 1]; + + void save_regs( uint8_t out [reg_count] ); }; -inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } +#include + +inline int Snes_Spc::sample_count() const { return (m.extra_clocks >> 5) * 2; } + +inline int Snes_Spc::read_port( time_t t, int port ) +{ + assert( (unsigned) port < port_count ); + return run_until_( t ) [port]; +} + +inline void Snes_Spc::write_port( time_t t, int port, int data ) +{ + assert( (unsigned) port < port_count ); + run_until_( t ) [0x10 + port] = data; +} inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } + +inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } -inline void Snes_Spc::set_gain( double v ) { dsp.set_gain( v ); } +inline void Snes_Spc::interpolation_level( int level ) { dsp.interpolation_level( level ); } + +inline Spc_Dsp const* Snes_Spc::get_dsp() const { return &dsp; } +inline Spc_Dsp * Snes_Spc::get_dsp() { return &dsp; } + +inline void Snes_Spc::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { m.sfm_queue = queue; m.sfm_queue_end = queue_end; } + +#if !SPC_NO_COPY_STATE_FUNCS +inline bool Snes_Spc::check_kon() { return dsp.check_kon(); } +#endif #endif diff --git a/Frameworks/GME/gme/Spc_Cpu.cpp b/Frameworks/GME/gme/Spc_Cpu.cpp old mode 100755 new mode 100644 index fb9983b83..8622af2ce --- a/Frameworks/GME/gme/Spc_Cpu.cpp +++ b/Frameworks/GME/gme/Spc_Cpu.cpp @@ -1,11 +1,10 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Core SPC emulation: CPU, timers, SMP registers, memory -#include "Spc_Cpu.h" +// snes_spc $vers. http://www.slack.net/~ant/ -#include "blargg_endian.h" #include "Snes_Spc.h" -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This @@ -18,1045 +17,558 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -// Several instructions are commented out (or not even implemented). These aren't -// used by the SPC files tested. +#define RAM (m.ram.ram) +#define REGS (m.smp_regs [0]) +#define REGS_IN (m.smp_regs [1]) -// Optimize performance for the most common instructions, and size for the rest: -// -// 15% 0xF0 BEQ rel -// 8% 0xE4 MOV A,dp -// 4% 0xF5 MOV A,abs+X -// 4% 0xD0 BNE rel -// 4% 0x6F RET -// 4% 0x3F CALL addr -// 4% 0xF4 MOV A,dp+X -// 3% 0xC4 MOV dp,A -// 2% 0xEB MOV Y,dp -// 2% 0x3D INC X -// 2% 0xF6 MOV A,abs+Y -// (1% and below not shown) +// (n ? n : 256) +#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) -Spc_Cpu::Spc_Cpu( Snes_Spc* e, uint8_t* ram_in ) : ram( ram_in ), emu( *e ) +// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which +// do crazy echo buffer accesses. +#ifndef SPC_MORE_ACCURACY + #define SPC_MORE_ACCURACY 0 +#endif + + +//// Timers + +#define TIMER_DIV( t, n ) ((n) / t->prescaler) +#define TIMER_MUL( t, n ) ((n) * t->prescaler) + +Snes_Spc::Timer* Snes_Spc::run_timer_( Timer* t, rel_time_t time ) { - remain_ = 0; - assert( INT_MAX >= 0x7FFFFFFF ); // requires 32-bit int - blargg_verify_byte_order(); -} - -#define READ( addr ) (emu.read( addr )) -#define WRITE( addr, value ) (emu.write( addr, value )) - -#define READ_DP( addr ) READ( (addr) + dp ) -#define WRITE_DP( addr, value ) WRITE( (addr) + dp, value ) - -#define READ_PROG( addr ) (ram [addr]) -#define READ_PROG16( addr ) GET_LE16( &READ_PROG( addr ) ) - -int Spc_Cpu::read( spc_addr_t addr ) -{ - return READ( addr ); -} - -void Spc_Cpu::write( spc_addr_t addr, int data ) -{ - WRITE( addr, data ); -} - -// Cycle table derived from text copy of SPC-700 manual (using regular expressions) -static unsigned char const cycle_table [0x100] = { -// 0 1 2 3 4 5 6 7 8 9 A B C D E F - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,6,8, // 0 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,4,6, // 1 - 2,8,4,5,3,4,3,6,2,6,5,4,5,4,5,4, // 2 - 2,8,4,5,4,5,5,6,5,5,6,5,2,2,3,8, // 3 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,6,6, // 4 - 2,8,4,5,4,5,5,6,5,5,4,5,2,2,4,3, // 5 - 2,8,4,5,3,4,3,6,2,6,4,4,5,4,5,5, // 6 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,6, // 7 - 2,8,4,5,3,4,3,6,2,6,5,4,5,2,4,5, // 8 - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,12,5,// 9 - 3,8,4,5,3,4,3,6,2,6,4,4,5,2,4,4, // A - 2,8,4,5,4,5,5,6,5,5,5,5,2,2,3,4, // B - 3,8,4,5,4,5,4,7,2,5,6,4,5,2,4,9, // C - 2,8,4,5,5,6,6,7,4,5,4,5,2,2,6,3, // D - 2,8,4,5,3,4,3,6,2,4,5,3,4,3,4,3, // E - 2,8,4,5,4,5,5,6,3,4,5,4,2,2,4,3 // F -}; - -// The C,mem instructions are hardly used, so a non-inline function is used for -// the common access code. -unsigned Spc_Cpu::mem_bit( spc_addr_t pc ) -{ - unsigned addr = READ_PROG16( pc ); - unsigned t = READ( addr & 0x1FFF ) >> (addr >> 13); - return (t << 8) & 0x100; -} - -spc_time_t Spc_Cpu::run( spc_time_t cycle_count ) -{ - remain_ = cycle_count; + int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; + t->next_time += TIMER_MUL( t, elapsed ); - uint8_t* const ram = this->ram; // cache - - // Stack pointer is kept one greater than usual SPC stack pointer to allow - // common pre-decrement and post-increment memory instructions that some - // processors have. Address wrap-around isn't supported. - #define PUSH( v ) (*--sp = uint8_t (v)) - #define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) - #define POP() (*sp++) - #define SET_SP( v ) (sp = ram + 0x101 + (v)) - #define GET_SP() (sp - 0x101 - ram) - - uint8_t* sp; - SET_SP( r.sp ); - - // registers - unsigned pc = (unsigned) r.pc; - int a = r.a; - int x = r.x; - int y = r.y; - - // status flags - - const int st_n = 0x80; - const int st_v = 0x40; - const int st_p = 0x20; - const int st_b = 0x10; - const int st_h = 0x08; - const int st_i = 0x04; - const int st_z = 0x02; - const int st_c = 0x01; - - #define IS_NEG (nz & 0x880) - - #define CALC_STATUS( out ) do {\ - out = status & ~(st_n | st_z | st_c);\ - out |= (c >> 8) & st_c;\ - out |= (dp >> 3) & st_p;\ - if ( IS_NEG ) out |= st_n;\ - if ( !(nz & 0xFF) ) out |= st_z;\ - } while ( 0 ) - - #define SET_STATUS( in ) do {\ - status = in & ~(st_n | st_z | st_c | st_p);\ - c = in << 8;\ - nz = (in << 4) & 0x800;\ - nz |= ~in & st_z;\ - dp = (in << 3) & 0x100;\ - } while ( 0 ) - - int status; - int c; // store C as 'c' & 0x100. - int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x880) != 0 - unsigned dp; // direct page base + if ( t->enabled ) { - int temp = r.status; - SET_STATUS( temp ); - } - - goto loop; - - unsigned data; // first operand of instruction and temporary across function calls - - // Common endings for instructions -cbranch_taken_loop: // compare and branch - pc += (BOOST::int8_t) READ_PROG( pc ); - remain_ -= 2; -inc_pc_loop: // end of instruction with an operand - pc++; -loop: - - check( (unsigned) pc < 0x10000 ); - check( (unsigned) GET_SP() < 0x100 ); - - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - unsigned opcode = READ_PROG( pc ); - pc++; - // to do: if pc is at end of memory, this will get wrong byte - data = READ_PROG( pc ); - - if ( remain_ <= 0 ) - goto stop; - - remain_ -= cycle_table [opcode]; - - // Use 'data' for temporaries whose lifetime crosses read/write calls, otherwise - // use a local temporary. - switch ( opcode ) - { - - #define BRANCH( cond ) {\ - pc++;\ - int offset = (BOOST::int8_t) data;\ - if ( cond ) {\ - pc += offset;\ - remain_ -= 2;\ - }\ - goto loop;\ - } - -// Most-Common - - case 0xF0: // BEQ (most common) - BRANCH( !(uint8_t) nz ) - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ) - - case 0x3F: // CALL - PUSH16( pc + 2 ); - pc = READ_PROG16( pc ); - goto loop; - - case 0x6F: // RET - pc = POP(); - pc += POP() * 0x100; - goto loop; - -#define CASE( n ) case n: - -// Define common address modes based on opcode for immediate mode. Execution -// ends with data set to the address of the operand. -#define ADDR_MODES( op )\ - CASE( op - 0x02 ) /* (X) */\ - data = x + dp;\ - pc--;\ - goto end_##op;\ - CASE( op + 0x0F ) /* (dp)+Y */\ - data = READ_PROG16( data + dp ) + y;\ - goto end_##op;\ - CASE( op - 0x01 ) /* (dp+X) */\ - data = READ_PROG16( uint8_t (data + x) + dp );\ - goto end_##op;\ - CASE( op + 0x0E ) /* abs+Y */\ - data += y;\ - goto abs_##op;\ - CASE( op + 0x0D ) /* abs+X */\ - data += x;\ - CASE( op - 0x03 ) /* abs */\ - abs_##op:\ - pc++;\ - data += 0x100 * READ_PROG( pc );\ - goto end_##op;\ - CASE( op + 0x0C ) /* dp+X */\ - data = uint8_t (data + x);\ - CASE( op - 0x04 ) /* dp */\ - data += dp;\ - end_##op: - -// 1. 8-bit Data Transmission Commands. Group I - - ADDR_MODES( 0xE8 ) // MOV A,addr - // case 0xE4: // MOV a,dp (most common) - mov_a_addr: - a = nz = READ( data ); - goto inc_pc_loop; - case 0xBF: // MOV A,(X)+ - data = x + dp; - x = uint8_t (x + 1); - pc--; - goto mov_a_addr; - - case 0xE8: // MOV A,imm - a = data; - nz = data; - goto inc_pc_loop; - - case 0xF9: // MOV X,dp+Y - data = uint8_t (data + y); - case 0xF8: // MOV X,dp - data += dp; - goto mov_x_addr; - case 0xE9: // MOV X,abs - data = READ_PROG16( pc ); - pc++; - mov_x_addr: - data = READ( data ); - case 0xCD: // MOV X,imm - x = data; - nz = data; - goto inc_pc_loop; - - case 0xFB: // MOV Y,dp+X - data = uint8_t (data + x); - case 0xEB: // MOV Y,dp - data += dp; - goto mov_y_addr; - case 0xEC: // MOV Y,abs - data = READ_PROG16( pc ); - pc++; - mov_y_addr: - data = READ( data ); - case 0x8D: // MOV Y,imm - y = data; - nz = data; - goto inc_pc_loop; - -// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 - - ADDR_MODES( 0xC8 ) // MOV addr,A - WRITE( data, a ); - goto inc_pc_loop; - - { - int temp; - case 0xCC: // MOV abs,Y - temp = y; - goto mov_abs_temp; - case 0xC9: // MOV abs,X - temp = x; - mov_abs_temp: - WRITE( READ_PROG16( pc ), temp ); - pc += 2; - goto loop; - } - - case 0xD9: // MOV dp+Y,X - data = uint8_t (data + y); - case 0xD8: // MOV dp,X - WRITE( data + dp, x ); - goto inc_pc_loop; - - case 0xDB: // MOV dp+X,Y - data = uint8_t (data + x); - case 0xCB: // MOV dp,Y - WRITE( data + dp, y ); - goto inc_pc_loop; - - case 0xFA: // MOV dp,dp - data = READ( data + dp ); - case 0x8F: // MOV dp,#imm - pc++; - WRITE_DP( READ_PROG( pc ), data ); - goto inc_pc_loop; - -// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. - - case 0x7D: // MOV A,X - a = x; - nz = x; - goto loop; - - case 0xDD: // MOV A,Y - a = y; - nz = y; - goto loop; - - case 0x5D: // MOV X,A - x = a; - nz = a; - goto loop; - - case 0xFD: // MOV Y,A - y = a; - nz = a; - goto loop; - - case 0x9D: // MOV X,SP - x = nz = GET_SP(); - goto loop; - - case 0xBD: // MOV SP,X - SET_SP( x ); - goto loop; - - //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) - - case 0xAF: // MOV (X)+,A - WRITE_DP( x, a ); - x++; - goto loop; - -// 5. 8-BIT LOGIC OPERATION COMMANDS - -#define LOGICAL_OP( op, func )\ - ADDR_MODES( op ) /* addr */\ - data = READ( data );\ - case op: /* imm */\ - nz = a func##= data;\ - goto inc_pc_loop;\ - { unsigned addr;\ - case op + 0x11: /* X,Y */\ - data = READ_DP( y );\ - addr = x + dp;\ - pc--;\ - goto addr_##op;\ - case op + 0x01: /* dp,dp */\ - data = READ_DP( data );\ - case op + 0x10: /*dp,imm*/\ - pc++;\ - addr = READ_PROG( pc ) + dp;\ - addr_##op:\ - nz = data func READ( addr );\ - WRITE( addr, nz );\ - goto inc_pc_loop;\ - } - - LOGICAL_OP( 0x28, & ); // AND - - LOGICAL_OP( 0x08, | ); // OR - - LOGICAL_OP( 0x48, ^ ); // EOR - -// 4. 8-BIT ARITHMETIC OPERATION COMMANDS - - ADDR_MODES( 0x68 ) // CMP addr - data = READ( data ); - case 0x68: // CMP imm - nz = a - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x79: // CMP (X),(Y) - data = READ_DP( x ); - nz = data - READ_DP( y ); - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x69: // CMP (dp),(dp) - data = READ_DP( data ); - case 0x78: // CMP dp,imm - pc++; - nz = READ_DP( READ_PROG( pc ) ) - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x3E: // CMP X,dp - data += dp; - goto cmp_x_addr; - case 0x1E: // CMP X,abs - data = READ_PROG16( pc ); - pc++; - cmp_x_addr: - data = READ( data ); - case 0xC8: // CMP X,imm - nz = x - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x7E: // CMP Y,dp - data += dp; - goto cmp_y_addr; - case 0x5E: // CMP Y,abs - data = READ_PROG16( pc ); - pc++; - cmp_y_addr: - data = READ( data ); - case 0xAD: // CMP Y,imm - nz = y - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - { - int addr; - case 0xB9: // SBC (x),(y) - case 0x99: // ADC (x),(y) - pc--; // compensate for inc later - data = READ_DP( x ); - addr = y + dp; - goto adc_addr; - case 0xA9: // SBC dp,dp - case 0x89: // ADC dp,dp - data = READ_DP( data ); - case 0xB8: // SBC dp,imm - case 0x98: // ADC dp,imm - pc++; - addr = READ_PROG( pc ) + dp; - adc_addr: - nz = READ( addr ); - goto adc_data; - -// catch ADC and SBC together, then decode later based on operand -#undef CASE -#define CASE( n ) case n: case (n) + 0x20: - ADDR_MODES( 0x88 ) // ADC/SBC addr - data = READ( data ); - case 0xA8: // SBC imm - case 0x88: // ADC imm - addr = -1; // A - nz = a; - adc_data: { - if ( opcode & 0x20 ) - data ^= 0xFF; // SBC - int carry = (c >> 8) & 1; - int ov = (nz ^ 0x80) + carry + (BOOST::int8_t) data; // sign-extend - int hc = (nz & 15) + carry; - c = nz += data + carry; - hc = (nz & 15) - hc; - status = (status & ~(st_v | st_h)) | ((ov >> 2) & st_v) | ((hc >> 1) & st_h); - if ( addr < 0 ) { - a = (uint8_t) nz; - goto inc_pc_loop; - } - WRITE( addr, (uint8_t) nz ); - goto inc_pc_loop; - } - - } - -// 6. ADDITION & SUBTRACTION COMMANDS - -#define INC_DEC_REG( reg, n )\ - nz = reg + n;\ - reg = (uint8_t) nz;\ - goto loop; - - case 0xBC: INC_DEC_REG( a, 1 ) // INC A - case 0x3D: INC_DEC_REG( x, 1 ) // INC X - case 0xFC: INC_DEC_REG( y, 1 ) // INC Y - - case 0x9C: INC_DEC_REG( a, -1 ) // DEC A - case 0x1D: INC_DEC_REG( x, -1 ) // DEC X - case 0xDC: INC_DEC_REG( y, -1 ) // DEC Y - - case 0x9B: // DEC dp+X - case 0xBB: // INC dp+X - data = uint8_t (data + x); - case 0x8B: // DEC dp - case 0xAB: // INC dp - data += dp; - goto inc_abs; - case 0x8C: // DEC abs - case 0xAC: // INC abs - data = READ_PROG16( pc ); - pc++; - inc_abs: - nz = ((opcode >> 4) & 2) - 1; - nz += READ( data ); - WRITE( data, (uint8_t) nz ); - goto inc_pc_loop; - -// 7. SHIFT, ROTATION COMMANDS - - case 0x5C: // LSR A - c = 0; - case 0x7C:{// ROR A - nz = ((c >> 1) & 0x80) | (a >> 1); - c = a << 8; - a = nz; - goto loop; - } - - case 0x1C: // ASL A - c = 0; - case 0x3C:{// ROL A - int temp = (c >> 8) & 1; - c = a << 1; - nz = c | temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x0B: // ASL dp - c = 0; - data += dp; - goto rol_mem; - case 0x1B: // ASL dp+X - c = 0; - case 0x3B: // ROL dp+X - data = uint8_t (data + x); - case 0x2B: // ROL dp - data += dp; - goto rol_mem; - case 0x0C: // ASL abs - c = 0; - case 0x2C: // ROL abs - data = READ_PROG16( pc ); - pc++; - rol_mem: - nz = (c >> 8) & 1; - nz |= (c = READ( data ) << 1); - WRITE( data, (uint8_t) nz ); - goto inc_pc_loop; - - case 0x4B: // LSR dp - c = 0; - data += dp; - goto ror_mem; - case 0x5B: // LSR dp+X - c = 0; - case 0x7B: // ROR dp+X - data = uint8_t (data + x); - case 0x6B: // ROR dp - data += dp; - goto ror_mem; - case 0x4C: // LSR abs - c = 0; - case 0x6C: // ROR abs - data = READ_PROG16( pc ); - pc++; - ror_mem: { - int temp = READ( data ); - nz = ((c >> 1) & 0x80) | (temp >> 1); - c = temp << 8; - WRITE( data, nz ); - goto inc_pc_loop; - } - - case 0x9F: // XCN - nz = a = (a >> 4) | uint8_t (a << 4); - goto loop; - -// 8. 16-BIT TRANSMISION COMMANDS - - case 0xBA: // MOVW YA,dp - a = READ_DP( data ); - nz = (a & 0x7F) | (a >> 1); - y = READ_DP( uint8_t (data + 1) ); - nz |= y; - goto inc_pc_loop; - - case 0xDA: // MOVW dp,YA - WRITE_DP( data, a ); - WRITE_DP( uint8_t (data + 1), y ); - goto inc_pc_loop; - -// 9. 16-BIT OPERATION COMMANDS - - case 0x3A: // INCW dp - case 0x1A:{// DECW dp - data += dp; - - // low byte - int temp = READ( data ); - temp += ((opcode >> 4) & 2) - 1; // +1 for INCW, -1 for DECW - nz = ((temp >> 1) | temp) & 0x7F; - WRITE( data, (uint8_t) temp ); - - // high byte - data = uint8_t (data + 1) + dp; - temp >>= 8; - temp = uint8_t (temp + READ( data )); - nz |= temp; - WRITE( data, temp ); - - goto inc_pc_loop; - } - - case 0x9A: // SUBW YA,dp - case 0x7A: // ADDW YA,dp - { - // read 16-bit addend - int temp = READ_DP( data ); - int sign = READ_DP( uint8_t (data + 1) ); - temp += 0x100 * sign; - status &= ~(st_v | st_h); - - // to do: fix half-carry for SUBW (it's probably wrong) - - // for SUBW, negate and truncate to 16 bits - if ( opcode & 0x80 ) { - temp = (temp ^ 0xFFFF) + 1; - sign = temp >> 8; - } - - // add low byte (A) - temp += a; - a = (uint8_t) temp; - nz = (temp | (temp >> 1)) & 0x7F; - - // add high byte (Y) - temp >>= 8; - c = y + temp; - nz = (nz | c) & 0xFF; - - // half-carry (temporary avoids CodeWarrior optimizer bug) - unsigned hc = (c & 15) - (y & 15); - status |= (hc >> 4) & st_h; - - // overflow if sign of YA changed when previous sign and addend sign were same - status |= (((c ^ y) & ~(y ^ sign)) >> 1) & st_v; - - y = (uint8_t) c; - - goto inc_pc_loop; - } - - case 0x5A: { // CMPW YA,dp - int temp = a - READ_DP( data ); - nz = ((temp >> 1) | temp) & 0x7F; - temp = y + (temp >> 8); - temp -= READ_DP( uint8_t (data + 1) ); - nz |= temp; - c = ~temp; - nz &= 0xFF; - goto inc_pc_loop; - } - -// 10. MULTIPLICATION & DIVISON COMMANDS - - case 0xCF: { // MUL YA - unsigned temp = y * a; - a = (uint8_t) temp; - nz = ((temp >> 1) | temp) & 0x7F; - y = temp >> 8; - nz |= y; - goto loop; - } - - case 0x9E: // DIV YA,X - { - // behavior based on SPC CPU tests - - status &= ~(st_h | st_v); - - if ( (y & 15) >= (x & 15) ) - status |= st_h; - - if ( y >= x ) - status |= st_v; - - unsigned ya = y * 0x100 + a; - if ( y < x * 2 ) + int remain = IF_0_THEN_256( t->period - t->divider ); + int divider = t->divider + elapsed; + int over = elapsed - remain; + if ( over >= 0 ) { - a = ya / x; - y = ya - a * x; + int n = over / t->period; + t->counter = (t->counter + 1 + n) & 0x0F; + divider = over - n * t->period; } + t->divider = (uint8_t) divider; + } + return t; +} + +inline Snes_Spc::Timer* Snes_Spc::run_timer( Timer* t, rel_time_t time ) +{ + if ( time >= t->next_time ) + t = run_timer_( t, time ); + return t; +} + + +//// ROM + +void Snes_Spc::enable_rom( int enable ) +{ + if ( m.rom_enabled != enable ) + { + m.rom_enabled = enable; + if ( enable ) + memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram ); + memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size ); + // TODO: ROM can still get overwritten when DSP writes to echo buffer + } +} + + +//// DSP + +#if SPC_LESS_ACCURATE + int const max_reg_time = 29; + + /* Fast DSP only runs every 32nd clock. By adjusting the end time based + on which register is being accessed, in most cases the register access + is emulated at the precise time. */ + signed char const Snes_Spc::reg_times_ [256] = + { + -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, + 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23, + 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23, + 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24, + 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24, + 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24, + 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25, + 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25, + + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + }; + + #define RUN_DSP( time, offset ) \ + int count = (time) - (offset) - m.dsp_time;\ + if ( count >= 0 )\ + {\ + int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\ + m.dsp_time += clock_count;\ + dsp.run( clock_count );\ + } +#else + #define RUN_DSP( time, offset ) \ + {\ + int count = (time) - m.dsp_time;\ + if ( !SPC_MORE_ACCURACY || count )\ + {\ + assert( count > 0 );\ + m.dsp_time = (time);\ + dsp.run( count );\ + }\ + } +#endif + +int Snes_Spc::dsp_read( rel_time_t time ) +{ + RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); + + int result = dsp.read( REGS [r_dspaddr] & 0x7F ); + + #ifdef SPC_DSP_READ_HOOK + SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); + #endif + + return result; +} + +inline void Snes_Spc::dsp_write( int data, rel_time_t time ) +{ + RUN_DSP( time, reg_times [REGS [r_dspaddr]] ) + #if SPC_LESS_ACCURATE + else if ( m.dsp_time == skipping_time ) + { + int r = REGS [r_dspaddr]; + if ( r == Spc_Dsp::r_kon ) + m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff ); + + if ( r == Spc_Dsp::r_koff ) + { + m.skipped_koff |= data; + m.skipped_kon &= ~data; + } + } + #endif + + #ifdef SPC_DSP_WRITE_HOOK + SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); + #endif + + if ( REGS [r_dspaddr] <= 0x7F ) + { + if ( REGS [r_dspaddr] != Spc_Dsp::r_flg ) + dsp.write( REGS [r_dspaddr], data ); else { - a = 255 - (ya - x * 0x200) / (256 - x); - y = x + (ya - x * 0x200) % (256 - x); + int prev = dsp.read( Spc_Dsp::r_flg ); + dsp.write( Spc_Dsp::r_flg, data ); + if ( ( data & 0x20 ) == ( ( data ^ prev ) & 0x20 ) ) clear_echo(); + } + } + else if ( !SPC_MORE_ACCURACY ) + dprintf( "SPC wrote to DSP register > $7F\n" ); +} + + +//// Memory access extras + +#if SPC_MORE_ACCURACY + #define MEM_ACCESS( time, addr ) \ + {\ + if ( time >= m.dsp_time )\ + {\ + RUN_DSP( time, max_reg_time );\ + }\ + } +#elif !defined (NDEBUG) + // Debug-only check for read/write within echo buffer, since this might result in + // inaccurate emulation due to the DSP not being caught up to the present. + + bool Snes_Spc::check_echo_access( int addr ) + { + if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) + { + int start = 0x100 * dsp.read( Spc_Dsp::r_esa ); + int size = 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F); + int end = start + (size ? size : 4); + if ( start <= addr && addr < end ) + { + if ( !m.echo_accessed ) + { + m.echo_accessed = 1; + return true; + } + } + } + return false; + } + + #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); +#else + #define MEM_ACCESS( time, addr ) +#endif + + +//// CPU write + +#if SPC_MORE_ACCURACY +static unsigned char const glitch_probs [3] [256] = +{ + 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, + 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, + 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, + 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, + 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, + 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, + 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, + 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, + 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, + 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, + 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, + 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, + 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, + 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, + 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, + 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, + + 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, + 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, + 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, + 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, + 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, + 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, + 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, + 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, + 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, + 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, + 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, + 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, + 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, + 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, + 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, + 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01, + + 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, + 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, + 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, + 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, + 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, + 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, + 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, + 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, + 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, + 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, + 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, + 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, + 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, + 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, + 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, + 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, +}; +#endif + +// Read/write handlers are divided into multiple functions to keep rarely-used +// functionality separate so often-used functionality can be optimized better +// by compiler. + +// If write isn't preceded by read, data has this added to it +int const no_read_before_write = 0x2000; + +void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) +{ + switch ( addr ) + { + case r_t0target: + case r_t1target: + case r_t2target: { + Timer* t = &m.timers [addr - r_t0target]; + int period = IF_0_THEN_256( data ); + if ( t->period != period ) + { + t = run_timer( t, time ); + #if SPC_MORE_ACCURACY + // Insane behavior when target is written just after counter is + // clocked and counter matches new period and new period isn't 1, 2, 4, or 8 + if ( t->divider == (period & 0xFF) && + t->next_time == time + TIMER_MUL( t, 1 ) && + ((period - 1) | ~0x0F) & period ) + { + //dprintf( "SPC pathological timer target write\n" ); + + // If the period is 3, 5, or 9, there's a probability this behavior won't occur, + // based on the previous period + int prob = 0xFF; + int old_period = t->period & 0xFF; + if ( period == 3 ) prob = glitch_probs [0] [old_period]; + if ( period == 5 ) prob = glitch_probs [1] [old_period]; + if ( period == 9 ) prob = glitch_probs [2] [old_period]; + + // The glitch suppresses incrementing of one of the counter bits, based on + // the lowest set bit in the new period + int b = 1; + while ( !(period & b) ) + b <<= 1; + + if ( (rand() >> 4 & 0xFF) <= prob ) + t->divider = (t->divider - b) & 0xFF; + } + #endif + t->period = period; + } + break; + } + + case r_t0out: + case r_t1out: + case r_t2out: + if ( !SPC_MORE_ACCURACY ) + dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); + + if ( data < no_read_before_write / 2 ) + run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; + break; + + // Registers that act like RAM + case 0x8: + case 0x9: + REGS_IN [addr] = (uint8_t) data; + break; + + case r_test: + if ( (uint8_t) data != 0x0A ) + dprintf( "SPC wrote to test register\n" ); + break; + + case r_control: + // port clears + if ( data & 0x10 ) + { + REGS_IN [r_cpuio0] = 0; + REGS_IN [r_cpuio1] = 0; + } + if ( data & 0x20 ) + { + REGS_IN [r_cpuio2] = 0; + REGS_IN [r_cpuio3] = 0; } - nz = (uint8_t) a; - a = (uint8_t) a; - - goto loop; + // timers + { + for ( int i = 0; i < timer_count; i++ ) + { + Timer* t = &m.timers [i]; + int enabled = data >> i & 1; + if ( t->enabled != enabled ) + { + t = run_timer( t, time ); + t->enabled = enabled; + if ( enabled ) + { + t->divider = 0; + t->counter = 0; + } + } + } + } + enable_rom( data & 0x80 ); + break; } - -// 11. DECIMAL COMPENSATION COMMANDS - - // seem unused - // case 0xDF: // DAA - // case 0xBE: // DAS - -// 12. BRANCHING COMMANDS - - case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; - goto inc_pc_loop; - - case 0x30: // BMI - BRANCH( IS_NEG ) - - case 0x10: // BPL - BRANCH( !IS_NEG ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x70: // BVS - BRANCH( status & st_v ) - - case 0x50: // BVC - BRANCH( !(status & st_v) ) - - case 0x03: // BBS dp.bit,rel - case 0x23: - case 0x43: - case 0x63: - case 0x83: - case 0xA3: - case 0xC3: - case 0xE3: - pc++; - if ( (READ_DP( data ) >> (opcode >> 5)) & 1 ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0x13: // BBC dp.bit,rel - case 0x33: - case 0x53: - case 0x73: - case 0x93: - case 0xB3: - case 0xD3: - case 0xF3: - pc++; - if ( !((READ_DP( data ) >> (opcode >> 5)) & 1) ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xDE: // CBNE dp+X,rel - data = uint8_t (data + x); - // fall through - case 0x2E: // CBNE dp,rel - pc++; - if ( READ_DP( data ) != a ) - goto cbranch_taken_loop; - goto inc_pc_loop; - - case 0xFE: // DBNZ Y,rel - y = uint8_t (y - 1); - BRANCH( y ) - - case 0x6E: { // DBNZ dp,rel - pc++; - unsigned temp = READ_DP( data ) - 1; - WRITE_DP( (uint8_t) data, (uint8_t) temp ); - if ( temp ) - goto cbranch_taken_loop; - goto inc_pc_loop; - } - - case 0x1F: // JMP (abs+X) - pc = READ_PROG16( pc ) + x; - // fall through - case 0x5F: // JMP abs - pc = READ_PROG16( pc ); - goto loop; - -// 13. SUB-ROUTINE CALL RETURN COMMANDS - - case 0x0F:{// BRK - check( false ); // untested - PUSH16( pc + 1 ); - pc = READ_PROG16( 0xFFDE ); // vector address verified - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - status = (status | st_b) & ~st_i; - goto loop; - } - - case 0x4F: // PCALL offset - pc++; - PUSH16( pc ); - pc = 0xFF00 + data; - goto loop; - - case 0x01: // TCALL n - case 0x11: - case 0x21: - case 0x31: - case 0x41: - case 0x51: - case 0x61: - case 0x71: - case 0x81: - case 0x91: - case 0xA1: - case 0xB1: - case 0xC1: - case 0xD1: - case 0xE1: - case 0xF1: - PUSH16( pc ); - pc = READ_PROG16( 0xFFDE - (opcode >> 3) ); - goto loop; - -// 14. STACK OPERATION COMMANDS - - { - int temp; - case 0x7F: // RET1 - temp = POP(); - pc = POP(); - pc |= POP() << 8; - goto set_status; - case 0x8E: // POP PSW - temp = POP(); - set_status: - SET_STATUS( temp ); - goto loop; - } - - case 0x0D: { // PUSH PSW - int temp; - CALC_STATUS( temp ); - PUSH( temp ); - goto loop; - } - - case 0x2D: // PUSH A - PUSH( a ); - goto loop; - - case 0x4D: // PUSH X - PUSH( x ); - goto loop; - - case 0x6D: // PUSH Y - PUSH( y ); - goto loop; - - case 0xAE: // POP A - a = POP(); - goto loop; - - case 0xCE: // POP X - x = POP(); - goto loop; - - case 0xEE: // POP Y - y = POP(); - goto loop; - -// 15. BIT OPERATION COMMANDS - - case 0x02: // SET1 - case 0x22: - case 0x42: - case 0x62: - case 0x82: - case 0xA2: - case 0xC2: - case 0xE2: - case 0x12: // CLR1 - case 0x32: - case 0x52: - case 0x72: - case 0x92: - case 0xB2: - case 0xD2: - case 0xF2: { - data += dp; - int bit = 1 << (opcode >> 5); - int mask = ~bit; - if ( opcode & 0x10 ) - bit = 0; - WRITE( data, (READ( data ) & mask) | bit ); - goto inc_pc_loop; - } - - case 0x0E: // TSET1 abs - case 0x4E:{// TCLR1 abs - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data ); - nz = temp & a; - temp &= ~a; - if ( !(opcode & 0x40) ) - temp |= a; - WRITE( data, temp ); - goto loop; - } - - case 0x4A: // AND1 C,mem.bit - c &= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x6A: // AND1 C,/mem.bit - check( false ); // untested - c &= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x0A: // OR1 C,mem.bit - check( false ); // untested - c |= mem_bit( pc ); - pc += 2; - goto loop; - - case 0x2A: // OR1 C,/mem.bit - check( false ); // untested - c |= ~mem_bit( pc ); - pc += 2; - goto loop; - - case 0x8A: // EOR1 C,mem.bit - c ^= mem_bit( pc ); - pc += 2; - goto loop; - - case 0xEA: { // NOT1 mem.bit - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1FFF ); - temp ^= 1 << (data >> 13); - WRITE( data & 0x1FFF, temp ); - goto loop; - } - - case 0xCA: { // MOV1 mem.bit,C - data = READ_PROG16( pc ); - pc += 2; - unsigned temp = READ( data & 0x1FFF ); - unsigned bit = data >> 13; - temp = (temp & ~(1 << bit)) | (((c >> 8) & 1) << bit); - WRITE( data & 0x1FFF, temp ); - goto loop; - } - - case 0xAA: // MOV1 C,mem.bit - c = mem_bit( pc ); - pc += 2; - goto loop; - -// 16. PROGRAM STATUS FLAG OPERATION COMMANDS - - case 0x60: // CLRC - c = 0; - goto loop; - - case 0x80: // SETC - c = ~0; - goto loop; - - case 0xED: // NOTC - c ^= 0x100; - goto loop; - - case 0xE0: // CLRV - status &= ~(st_v | st_h); - goto loop; - - case 0x20: // CLRP - dp = 0; - goto loop; - - case 0x40: // SETP - dp = 0x100; - goto loop; - - case 0xA0: // EI - check( false ); // untested - status |= st_i; - goto loop; - - case 0xC0: // DI - check( false ); // untested - status &= ~st_i; - goto loop; - -// 17. OTHER COMMANDS - - case 0x00: // NOP - goto loop; - - //case 0xEF: // SLEEP - //case 0xFF: // STOP - - } // switch - - // unhandled instructions fall out of switch so emulator can catch them - -stop: - pc--; - - { - int temp; - CALC_STATUS( temp ); - r.status = (uint8_t) temp; - } - - r.pc = pc; - r.sp = (uint8_t) GET_SP(); - r.a = (uint8_t) a; - r.x = (uint8_t) x; - r.y = (uint8_t) y; - - return remain_; } + +void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, int addr ) +{ + if ( addr == r_dspdata ) // 99% + dsp_write( data, time ); + else + cpu_write_smp_reg_( data, time, addr ); +} + +void Snes_Spc::cpu_write_high( int data, int i, rel_time_t time ) +{ + if ( i < rom_size ) + { + m.hi_ram [i] = (uint8_t) data; + if ( m.rom_enabled ) + RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM + } + else + { + assert( RAM [i + rom_addr] == (uint8_t) data ); + RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding + cpu_write( data, i + rom_addr - 0x10000, time ); + } +} + +int const bits_in_int = CHAR_BIT * sizeof (int); + +void Snes_Spc::cpu_write( int data, int addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + RAM [addr] = (uint8_t) data; + int reg = addr - 0xF0; + if ( reg >= 0 ) // 64% + { + // $F0-$FF + if ( reg < reg_count ) // 87% + { + REGS [reg] = (uint8_t) data; + + // Ports + #ifdef SPC_PORT_WRITE_HOOK + if ( (unsigned) (reg - r_cpuio0) < port_count ) + SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), + (uint8_t) data, ®S [r_cpuio0] ); + #endif + + // Registers other than $F2 and $F4-$F7 + //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 ) + // TODO: this is a bit on the fragile side + if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% + cpu_write_smp_reg( data, time, reg ); + } + // High mem/address wrap-around + else + { + reg -= rom_addr - 0xF0; + if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around + cpu_write_high( data, reg, time ); + } + } +} + + +//// CPU read + +inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time ) +{ + int result = REGS_IN [reg]; + reg -= r_dspaddr; + // DSP addr and data + if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3 + { + result = REGS [r_dspaddr]; + if ( (unsigned) reg == 1 ) + result = dsp_read( time ); // 0xF3 + } + reg -= r_cpuio0 - r_dspaddr; + if ( (unsigned) reg <= 3 ) + { + if ( m.sfm_queue && m.sfm_queue < m.sfm_queue_end ) + { + result = *m.sfm_queue++; + } + } + return result; +} + +int Snes_Spc::cpu_read( int addr, rel_time_t time ) +{ + MEM_ACCESS( time, addr ) + + // RAM + int result = RAM [addr]; + int reg = addr - 0xF0; + if ( reg >= 0 ) // 40% + { + reg -= 0x10; + if ( (unsigned) reg >= 0xFF00 ) // 21% + { + reg += 0x10 - r_t0out; + + // Timers + if ( (unsigned) reg < timer_count ) // 90% + { + Timer* t = &m.timers [reg]; + if ( time >= t->next_time ) + t = run_timer_( t, time ); + result = t->counter; + t->counter = 0; + } + // Other registers + else if ( reg < 0 ) // 10% + { + result = cpu_read_smp_reg( reg + r_t0out, time ); + } + else // 1% + { + assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 ); + result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time ); + } + } + } + + return result; +} + + +//// Run + +// Prefix and suffix for CPU emulator function +#define SPC_CPU_RUN_FUNC \ +BOOST::uint8_t* Snes_Spc::run_until_( time_t end_time )\ +{\ + rel_time_t rel_time = m.spc_time - end_time;\ + assert( rel_time <= 0 );\ + m.spc_time = end_time;\ + m.dsp_time += rel_time;\ + m.timers [0].next_time += rel_time;\ + m.timers [1].next_time += rel_time;\ + m.timers [2].next_time += rel_time; + +#define SPC_CPU_RUN_FUNC_END \ + m.spc_time += rel_time;\ + m.dsp_time -= rel_time;\ + m.timers [0].next_time -= rel_time;\ + m.timers [1].next_time -= rel_time;\ + m.timers [2].next_time -= rel_time;\ + assert( m.spc_time <= end_time );\ + return ®S [r_cpuio0];\ +} + +int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks + +void Snes_Spc::end_frame( time_t end_time ) +{ + // Catch CPU up to as close to end as possible. If final instruction + // would exceed end, does NOT execute it and leaves m.spc_time < end. + if ( end_time > m.spc_time ) + run_until_( end_time ); + + m.spc_time -= end_time; + m.extra_clocks += end_time; + + // Greatest number of clocks early that emulation can stop early due to + // not being able to execute current instruction without going over + // allowed time. + assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); + + // Catch timers up to CPU + for ( int i = 0; i < timer_count; i++ ) + run_timer( &m.timers [i], 0 ); + + // Catch DSP up to CPU + if ( m.dsp_time < 0 ) + { + RUN_DSP( 0, max_reg_time ); + } + + // Save any extra samples beyond what should be generated + if ( m.buf_begin ) + save_extra(); +} + +// Inclusion here allows static memory access functions and better optimization +#include "Spc_Cpu.h" diff --git a/Frameworks/GME/gme/Spc_Cpu.h b/Frameworks/GME/gme/Spc_Cpu.h old mode 100755 new mode 100644 index 2252663bd..dbd1bc2f8 --- a/Frameworks/GME/gme/Spc_Cpu.h +++ b/Frameworks/GME/gme/Spc_Cpu.h @@ -1,57 +1,1225 @@ -// Super Nintendo (SNES) SPC-700 CPU emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_CPU_H -#define SPC_CPU_H - -#include "blargg_common.h" - -typedef unsigned spc_addr_t; -typedef blargg_long spc_time_t; - -class Snes_Spc; - -class Spc_Cpu { - typedef BOOST::uint8_t uint8_t; - uint8_t* const ram; -public: - // Keeps pointer to 64K RAM - Spc_Cpu( Snes_Spc* spc, uint8_t* ram ); - - // SPC-700 registers. *Not* kept updated during a call to run(). - struct registers_t { - long pc; // more than 16 bits to allow overflow detection - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t status; - uint8_t sp; - } r; - - // Run CPU for at least 'count' cycles. Return the number of cycles remaining - // when emulation stopped (negative if extra cycles were emulated). Emulation - // stops when there are no more remaining cycles or an unhandled instruction - // is encountered (STOP, SLEEP, and any others not yet implemented). In the - // latter case, the return value is greater than zero. - spc_time_t run( spc_time_t count ); - - // Number of clock cycles remaining for current run() call - spc_time_t remain() const; - - // Access memory as the emulated CPU does - int read ( spc_addr_t ); - void write( spc_addr_t, int ); - -private: - // noncopyable - Spc_Cpu( const Spc_Cpu& ); - Spc_Cpu& operator = ( const Spc_Cpu& ); - unsigned mem_bit( spc_addr_t ); - - spc_time_t remain_; - Snes_Spc& emu; -}; - -inline spc_time_t Spc_Cpu::remain() const { return remain_; } - -#endif +// snes_spc $vers. http://www.slack.net/~ant/ + +/* Copyright (C) 2004-2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +//// Memory access + +#if SPC_MORE_ACCURACY + #define SUSPICIOUS_OPCODE( name ) ((void) 0) +#else + #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) +#endif + +#define CPU_READ( time, offset, addr )\ + cpu_read( addr, time + offset ) + +#define CPU_WRITE( time, offset, addr, data )\ + cpu_write( data, addr, time + offset ) + +#if SPC_MORE_ACCURACY + #define CPU_READ_TIMER( time, offset, addr, out )\ + { out = CPU_READ( time, offset, addr ); } + +#else + // timers are by far the most common thing read from dp + #define CPU_READ_TIMER( time, offset, addr_, out )\ + {\ + rel_time_t adj_time = time + offset;\ + int dp_addr = addr_;\ + int ti = dp_addr - (r_t0out + 0xF0);\ + if ( (unsigned) ti < timer_count )\ + {\ + Timer* t = &m.timers [ti];\ + if ( adj_time >= t->next_time )\ + t = run_timer_( t, adj_time );\ + out = t->counter;\ + t->counter = 0;\ + }\ + else\ + {\ + out = ram [dp_addr];\ + int i = dp_addr - 0xF0;\ + if ( (unsigned) i < 0x10 )\ + out = cpu_read_smp_reg( i, adj_time );\ + }\ + } +#endif + +#define TIME_ADJ( n ) (n) + +#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) +#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) +#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) + +#define DP_ADDR( addr ) (dp + (addr)) + +#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) +#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) +#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) + +#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) + +#define SET_PC( n ) (pc = ram + (n)) +#define GET_PC() (pc - ram) +#define READ_PC( pc ) (*(pc)) +#define READ_PC16( pc ) GET_LE16( pc ) + +// TODO: remove non-wrapping versions? +#define SPC_NO_SP_WRAPAROUND 0 + +#define SET_SP( v ) (sp = ram + 0x101 + (v)) +#define GET_SP() (sp - 0x101 - ram) + +#if SPC_NO_SP_WRAPAROUND +#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) +#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) +#define POP( out ) (void) ((out) = *sp++) + +#else +#define PUSH16( data )\ +{\ + int addr = (sp -= 2) - ram;\ + if ( addr > 0x100 )\ + {\ + SET_LE16( sp, data );\ + }\ + else\ + {\ + ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ + sp [1] = (uint8_t) (data >> 8);\ + sp += 0x100;\ + }\ +} + +#define PUSH( data )\ +{\ + *--sp = (uint8_t) (data);\ + if ( sp - ram == 0x100 )\ + sp += 0x100;\ +} + +#define POP( out )\ +{\ + out = *sp++;\ + if ( sp - ram == 0x201 )\ + {\ + out = sp [-0x101];\ + sp -= 0x100;\ + }\ +} + +#endif + +#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) + +unsigned Snes_Spc::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) +{ + unsigned addr = READ_PC16( pc ); + unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); + return t << 8 & 0x100; +} + +//// Status flag handling + +// Flags with hex value for clarity when used as mask. +// Stored in indicated variable during emulation. +int const n80 = 0x80; // nz +int const v40 = 0x40; // psw +int const p20 = 0x20; // dp +int const b10 = 0x10; // psw +int const h08 = 0x08; // psw +int const i04 = 0x04; // psw +int const z02 = 0x02; // nz +int const c01 = 0x01; // c + +int const nz_neg_mask = 0x880; // either bit set indicates N flag set + +#define GET_PSW( out )\ +{\ + out = psw & ~(n80 | p20 | z02 | c01);\ + out |= c >> 8 & c01;\ + out |= dp >> 3 & p20;\ + out |= ((nz >> 4) | nz) & n80;\ + if ( !(uint8_t) nz ) out |= z02;\ +} + +#define SET_PSW( in )\ +{\ + psw = in;\ + c = in << 8;\ + dp = in << 3 & 0x100;\ + nz = (in << 4 & 0x800) | (~in & z02);\ +} + +SPC_CPU_RUN_FUNC +{ + uint8_t* const ram = RAM; + int a = m.cpu_regs.a; + int x = m.cpu_regs.x; + int y = m.cpu_regs.y; + uint8_t const* pc; + uint8_t* sp; + int psw; + int c; + int nz; + int dp; + + SET_PC( m.cpu_regs.pc ); + SET_SP( m.cpu_regs.sp ); + SET_PSW( m.cpu_regs.psw ); + + goto loop; + + + // Main loop + +cbranch_taken_loop: + pc += *(BOOST::int8_t const*) pc; +inc_pc_loop: + pc++; +loop: +{ + unsigned opcode; + unsigned data; + + check( (unsigned) a < 0x100 ); + check( (unsigned) x < 0x100 ); + check( (unsigned) y < 0x100 ); + + opcode = *pc; + if ( (rel_time += m.cycle_table [opcode]) > 0 ) + goto out_of_time; + + #ifdef SPC_CPU_OPCODE_HOOK + SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); + #endif + + #ifdef CPU_INSTR_HOOK + CPU_INSTR_HOOK( GET_PC(), pc, a, x, y, GET_SP(), rel_time ); + #endif + + /* + //SUB_CASE_COUNTER( 1 ); + #define PROFILE_TIMER_LOOP( op, addr, len )\ + if ( opcode == op )\ + {\ + int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ + pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ + SUB_CASE_COUNTER( op && cond );\ + } + + PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); + PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); + PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); + */ + + // TODO: if PC is at end of memory, this will get wrong operand (very obscure) + data = *++pc; + switch ( opcode ) + { + +// Common instructions + +#define BRANCH( cond )\ +{\ + pc++;\ + pc += (BOOST::int8_t) data;\ + if ( cond )\ + goto loop;\ + pc -= (BOOST::int8_t) data;\ + rel_time -= 2;\ + goto loop;\ +} + + case 0xF0: // BEQ + BRANCH( !(uint8_t) nz ) // 89% taken + + case 0xD0: // BNE + BRANCH( (uint8_t) nz ) + + case 0x3F:{// CALL + int old_addr = GET_PC() + 2; + SET_PC( READ_PC16( pc ) ); + PUSH16( old_addr ); + goto loop; + } + + case 0x6F:// RET + #if SPC_NO_SP_WRAPAROUND + { + SET_PC( GET_LE16( sp ) ); + sp += 2; + } + #else + { + int addr = sp - ram; + SET_PC( GET_LE16( sp ) ); + sp += 2; + if ( addr < 0x1FF ) + goto loop; + + SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); + sp -= 0x100; + } + #endif + goto loop; + + case 0xE4: // MOV a,dp + ++pc; + // 80% from timer + READ_DP_TIMER( 0, data, a = nz ); + goto loop; + + case 0xFA:{// MOV dp,dp + int temp; + READ_DP_TIMER( -2, data, temp ); + data = temp + no_read_before_write ; + } + // fall through + case 0x8F:{// MOV dp,#imm + int temp = READ_PC( pc + 1 ); + pc += 2; + + #if !SPC_MORE_ACCURACY + { + int i = dp + temp; + ram [i] = (uint8_t) data; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 76% + { + REGS [i] = (uint8_t) data; + + // Registers other than $F2 and $F4-$F7 + //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) + if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% + cpu_write_smp_reg( data, rel_time, i ); + } + } + #else + WRITE_DP( 0, temp, data ); + #endif + goto loop; + } + + case 0xC4: // MOV dp,a + ++pc; + #if !SPC_MORE_ACCURACY + { + int i = dp + data; + ram [i] = (uint8_t) a; + i -= 0xF0; + if ( (unsigned) i < 0x10 ) // 39% + { + unsigned sel = i - 2; + REGS [i] = (uint8_t) a; + + if ( sel == 1 ) // 51% $F3 + dsp_write( a, rel_time ); + else if ( sel > 1 ) // 1% not $F2 or $F3 + cpu_write_smp_reg_( a, rel_time, i ); + } + } + #else + WRITE_DP( 0, data, a ); + #endif + goto loop; + +#define CASE( n ) case n: + +// Define common address modes based on opcode for immediate mode. Execution +// ends with data set to the address of the operand. +#define ADDR_MODES_( op )\ + CASE( op - 0x02 ) /* (X) */\ + data = x + dp;\ + pc--;\ + goto end_##op;\ + CASE( op + 0x0F ) /* (dp)+Y */\ + data = READ_PROG16( data + dp ) + y;\ + goto end_##op;\ + CASE( op - 0x01 ) /* (dp+X) */\ + data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ + goto end_##op;\ + CASE( op + 0x0E ) /* abs+Y */\ + data += y;\ + goto abs_##op;\ + CASE( op + 0x0D ) /* abs+X */\ + data += x;\ + CASE( op - 0x03 ) /* abs */\ + abs_##op:\ + data += 0x100 * READ_PC( ++pc );\ + goto end_##op;\ + CASE( op + 0x0C ) /* dp+X */\ + data = (uint8_t) (data + x); + +#define ADDR_MODES_NO_DP( op )\ + ADDR_MODES_( op )\ + data += dp;\ + end_##op: + +#define ADDR_MODES( op )\ + ADDR_MODES_( op )\ + CASE( op - 0x04 ) /* dp */\ + data += dp;\ + end_##op: + +// 1. 8-bit Data Transmission Commands. Group I + + ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr + a = nz = READ( 0, data ); + goto inc_pc_loop; + + case 0xBF:{// MOV A,(X)+ + int temp = x + dp; + x = (uint8_t) (x + 1); + a = nz = READ( -1, temp ); + goto loop; + } + + case 0xE8: // MOV A,imm + a = data; + nz = data; + goto inc_pc_loop; + + case 0xF9: // MOV X,dp+Y + data = (uint8_t) (data + y); + case 0xF8: // MOV X,dp + READ_DP_TIMER( 0, data, x = nz ); + goto inc_pc_loop; + + case 0xE9: // MOV X,abs + data = READ_PC16( pc ); + ++pc; + data = READ( 0, data ); + case 0xCD: // MOV X,imm + x = data; + nz = data; + goto inc_pc_loop; + + case 0xFB: // MOV Y,dp+X + data = (uint8_t) (data + x); + case 0xEB: // MOV Y,dp + // 70% from timer + pc++; + READ_DP_TIMER( 0, data, y = nz ); + goto loop; + + case 0xEC:{// MOV Y,abs + int temp = READ_PC16( pc ); + pc += 2; + READ_TIMER( 0, temp, y = nz ); + //y = nz = READ( 0, temp ); + goto loop; + } + + case 0x8D: // MOV Y,imm + y = data; + nz = data; + goto inc_pc_loop; + +// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 + + ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A + WRITE( 0, data, a ); + goto inc_pc_loop; + + { + int temp; + case 0xCC: // MOV abs,Y + temp = y; + goto mov_abs_temp; + case 0xC9: // MOV abs,X + temp = x; + mov_abs_temp: + WRITE( 0, READ_PC16( pc ), temp ); + pc += 2; + goto loop; + } + + case 0xD9: // MOV dp+Y,X + data = (uint8_t) (data + y); + case 0xD8: // MOV dp,X + WRITE( 0, data + dp, x ); + goto inc_pc_loop; + + case 0xDB: // MOV dp+X,Y + data = (uint8_t) (data + x); + case 0xCB: // MOV dp,Y + WRITE( 0, data + dp, y ); + goto inc_pc_loop; + +// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. + + case 0x7D: // MOV A,X + a = x; + nz = x; + goto loop; + + case 0xDD: // MOV A,Y + a = y; + nz = y; + goto loop; + + case 0x5D: // MOV X,A + x = a; + nz = a; + goto loop; + + case 0xFD: // MOV Y,A + y = a; + nz = a; + goto loop; + + case 0x9D: // MOV X,SP + x = nz = GET_SP(); + goto loop; + + case 0xBD: // MOV SP,X + SET_SP( x ); + goto loop; + + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) + + case 0xAF: // MOV (X)+,A + WRITE_DP( 0, x, a + no_read_before_write ); + x++; + goto loop; + +// 5. 8-BIT LOGIC OPERATION COMMANDS + +#define LOGICAL_OP( op, func )\ + ADDR_MODES( op ) /* addr */\ + data = READ( 0, data );\ + case op: /* imm */\ + nz = a func##= data;\ + goto inc_pc_loop;\ + { unsigned addr;\ + case op + 0x11: /* X,Y */\ + data = READ_DP( -2, y );\ + addr = x + dp;\ + goto addr_##op;\ + case op + 0x01: /* dp,dp */\ + data = READ_DP( -3, data );\ + case op + 0x10:{/*dp,imm*/\ + uint8_t const* addr2 = pc + 1;\ + pc += 2;\ + addr = READ_PC( addr2 ) + dp;\ + }\ + addr_##op:\ + nz = data func READ( -1, addr );\ + WRITE( 0, addr, nz );\ + goto loop;\ + } + + LOGICAL_OP( 0x28, & ); // AND + + LOGICAL_OP( 0x08, | ); // OR + + LOGICAL_OP( 0x48, ^ ); // EOR + +// 4. 8-BIT ARITHMETIC OPERATION COMMANDS + + ADDR_MODES( 0x68 ) // CMP addr + data = READ( 0, data ); + case 0x68: // CMP imm + nz = a - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x79: // CMP (X),(Y) + data = READ_DP( -2, y ); + nz = READ_DP( -1, x ) - data; + c = ~nz; + nz &= 0xFF; + goto loop; + + case 0x69: // CMP dp,dp + data = READ_DP( -3, data ); + case 0x78: // CMP dp,imm + nz = READ_DP( -1, READ_PC( ++pc ) ) - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x3E: // CMP X,dp + data += dp; + goto cmp_x_addr; + case 0x1E: // CMP X,abs + data = READ_PC16( pc ); + pc++; + cmp_x_addr: + data = READ( 0, data ); + case 0xC8: // CMP X,imm + nz = x - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + case 0x7E: // CMP Y,dp + data += dp; + goto cmp_y_addr; + case 0x5E: // CMP Y,abs + data = READ_PC16( pc ); + pc++; + cmp_y_addr: + data = READ( 0, data ); + case 0xAD: // CMP Y,imm + nz = y - data; + c = ~nz; + nz &= 0xFF; + goto inc_pc_loop; + + { + int addr; + case 0xB9: // SBC (x),(y) + case 0x99: // ADC (x),(y) + pc--; // compensate for inc later + data = READ_DP( -2, y ); + addr = x + dp; + goto adc_addr; + case 0xA9: // SBC dp,dp + case 0x89: // ADC dp,dp + data = READ_DP( -3, data ); + case 0xB8: // SBC dp,imm + case 0x98: // ADC dp,imm + addr = READ_PC( ++pc ) + dp; + adc_addr: + nz = READ( -1, addr ); + goto adc_data; + +// catch ADC and SBC together, then decode later based on operand +#undef CASE +#define CASE( n ) case n: case (n) + 0x20: + ADDR_MODES( 0x88 ) // ADC/SBC addr + data = READ( 0, data ); + case 0xA8: // SBC imm + case 0x88: // ADC imm + addr = -1; // A + nz = a; + adc_data: { + int flags; + if ( opcode >= 0xA0 ) // SBC + data ^= 0xFF; + + flags = data ^ nz; + nz += data + (c >> 8 & 1); + flags ^= nz; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = nz; + if ( addr < 0 ) + { + a = (uint8_t) nz; + goto inc_pc_loop; + } + WRITE( 0, addr, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + } + + } + +// 6. ADDITION & SUBTRACTION COMMANDS + +#define INC_DEC_REG( reg, op )\ + nz = reg op;\ + reg = (uint8_t) nz;\ + goto loop; + + case 0xBC: INC_DEC_REG( a, + 1 ) // INC A + case 0x3D: INC_DEC_REG( x, + 1 ) // INC X + case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y + + case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A + case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X + case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y + + case 0x9B: // DEC dp+X + case 0xBB: // INC dp+X + data = (uint8_t) (data + x); + case 0x8B: // DEC dp + case 0xAB: // INC dp + data += dp; + goto inc_abs; + case 0x8C: // DEC abs + case 0xAC: // INC abs + data = READ_PC16( pc ); + pc++; + inc_abs: + nz = (opcode >> 4 & 2) - 1; + nz += READ( -1, data ); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + +// 7. SHIFT, ROTATION COMMANDS + + case 0x5C: // LSR A + c = 0; + case 0x7C:{// ROR A + nz = (c >> 1 & 0x80) | (a >> 1); + c = a << 8; + a = nz; + goto loop; + } + + case 0x1C: // ASL A + c = 0; + case 0x3C:{// ROL A + int temp = c >> 8 & 1; + c = a << 1; + nz = c | temp; + a = (uint8_t) nz; + goto loop; + } + + case 0x0B: // ASL dp + c = 0; + data += dp; + goto rol_mem; + case 0x1B: // ASL dp+X + c = 0; + case 0x3B: // ROL dp+X + data = (uint8_t) (data + x); + case 0x2B: // ROL dp + data += dp; + goto rol_mem; + case 0x0C: // ASL abs + c = 0; + case 0x2C: // ROL abs + data = READ_PC16( pc ); + pc++; + rol_mem: + nz = c >> 8 & 1; + nz |= (c = READ( -1, data ) << 1); + WRITE( 0, data, /*(uint8_t)*/ nz ); + goto inc_pc_loop; + + case 0x4B: // LSR dp + c = 0; + data += dp; + goto ror_mem; + case 0x5B: // LSR dp+X + c = 0; + case 0x7B: // ROR dp+X + data = (uint8_t) (data + x); + case 0x6B: // ROR dp + data += dp; + goto ror_mem; + case 0x4C: // LSR abs + c = 0; + case 0x6C: // ROR abs + data = READ_PC16( pc ); + pc++; + ror_mem: { + int temp = READ( -1, data ); + nz = (c >> 1 & 0x80) | (temp >> 1); + c = temp << 8; + WRITE( 0, data, nz ); + goto inc_pc_loop; + } + + case 0x9F: // XCN + nz = a = (a >> 4) | (uint8_t) (a << 4); + goto loop; + +// 8. 16-BIT TRANSMISION COMMANDS + + case 0xBA: // MOVW YA,dp + a = READ_DP( -2, data ); + nz = (a & 0x7F) | (a >> 1); + y = READ_DP( 0, (uint8_t) (data + 1) ); + nz |= y; + goto inc_pc_loop; + + case 0xDA: // MOVW dp,YA + WRITE_DP( -1, data, a ); + WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); + goto inc_pc_loop; + +// 9. 16-BIT OPERATION COMMANDS + + case 0x3A: // INCW dp + case 0x1A:{// DECW dp + int temp; + // low byte + data += dp; + temp = READ( -3, data ); + temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW + nz = ((temp >> 1) | temp) & 0x7F; + WRITE( -2, data, /*(uint8_t)*/ temp ); + + // high byte + data = (uint8_t) (data + 1) + dp; + temp = (uint8_t) ((temp >> 8) + READ( -1, data )); + nz |= temp; + WRITE( 0, data, temp ); + + goto inc_pc_loop; + } + + case 0x7A: // ADDW YA,dp + case 0x9A:{// SUBW YA,dp + int lo = READ_DP( -2, data ); + int hi = READ_DP( 0, (uint8_t) (data + 1) ); + int result; + int flags; + + if ( opcode == 0x9A ) // SUBW + { + lo = (lo ^ 0xFF) + 1; + hi ^= 0xFF; + } + + lo += a; + result = y + hi + (lo >> 8); + flags = hi ^ y ^ result; + + psw = (psw & ~(v40 | h08)) | + (flags >> 1 & h08) | + ((flags + 0x80) >> 2 & v40); + c = result; + a = (uint8_t) lo; + result = (uint8_t) result; + y = result; + nz = (((lo >> 1) | lo) & 0x7F) | result; + + goto inc_pc_loop; + } + + case 0x5A: { // CMPW YA,dp + int temp = a - READ_DP( -1, data ); + nz = ((temp >> 1) | temp) & 0x7F; + temp = y + (temp >> 8); + temp -= READ_DP( 0, (uint8_t) (data + 1) ); + nz |= temp; + c = ~temp; + nz &= 0xFF; + goto inc_pc_loop; + } + +// 10. MULTIPLICATION & DIVISON COMMANDS + + case 0xCF: { // MUL YA + unsigned temp = y * a; + a = (uint8_t) temp; + nz = ((temp >> 1) | temp) & 0x7F; + y = temp >> 8; + nz |= y; + goto loop; + } + + case 0x9E: // DIV YA,X + { + unsigned ya = y * 0x100 + a; + + psw &= ~(h08 | v40); + + if ( y >= x ) + psw |= v40; + + if ( (y & 15) >= (x & 15) ) + psw |= h08; + + if ( y < x * 2 ) + { + a = ya / x; + y = ya - a * x; + } + else + { + a = 255 - (ya - x * 0x200) / (256 - x); + y = x + (ya - x * 0x200) % (256 - x); + } + + nz = (uint8_t) a; + a = (uint8_t) a; + + goto loop; + } + +// 11. DECIMAL COMPENSATION COMMANDS + + case 0xDF: // DAA + SUSPICIOUS_OPCODE( "DAA" ); + if ( a > 0x99 || c & 0x100 ) + { + a += 0x60; + c = 0x100; + } + + if ( (a & 0x0F) > 9 || psw & h08 ) + a += 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + + case 0xBE: // DAS + SUSPICIOUS_OPCODE( "DAS" ); + if ( a > 0x99 || !(c & 0x100) ) + { + a -= 0x60; + c = 0; + } + + if ( (a & 0x0F) > 9 || !(psw & h08) ) + a -= 0x06; + + nz = a; + a = (uint8_t) a; + goto loop; + +// 12. BRANCHING COMMANDS + + case 0x2F: // BRA rel + pc += (BOOST::int8_t) data; + goto inc_pc_loop; + + case 0x30: // BMI + BRANCH( (nz & nz_neg_mask) ) + + case 0x10: // BPL + BRANCH( !(nz & nz_neg_mask) ) + + case 0xB0: // BCS + BRANCH( c & 0x100 ) + + case 0x90: // BCC + BRANCH( !(c & 0x100) ) + + case 0x70: // BVS + BRANCH( psw & v40 ) + + case 0x50: // BVC + BRANCH( !(psw & v40) ) + + #define CBRANCH( cond )\ + {\ + pc++;\ + if ( cond )\ + goto cbranch_taken_loop;\ + rel_time -= 2;\ + goto inc_pc_loop;\ + } + + case 0x03: // BBS dp.bit,rel + case 0x23: + case 0x43: + case 0x63: + case 0x83: + case 0xA3: + case 0xC3: + case 0xE3: + CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) + + case 0x13: // BBC dp.bit,rel + case 0x33: + case 0x53: + case 0x73: + case 0x93: + case 0xB3: + case 0xD3: + case 0xF3: + CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) + + case 0xDE: // CBNE dp+X,rel + data = (uint8_t) (data + x); + // fall through + case 0x2E:{// CBNE dp,rel + int temp; + // 61% from timer + READ_DP_TIMER( -4, data, temp ); + CBRANCH( temp != a ) + } + + case 0x6E: { // DBNZ dp,rel + unsigned temp = READ_DP( -4, data ) - 1; + WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); + CBRANCH( temp ) + } + + case 0xFE: // DBNZ Y,rel + y = (uint8_t) (y - 1); + BRANCH( y ) + + case 0x1F: // JMP [abs+X] + SET_PC( READ_PC16( pc ) + x ); + // fall through + case 0x5F: // JMP abs + SET_PC( READ_PC16( pc ) ); + goto loop; + +// 13. SUB-ROUTINE CALL RETURN COMMANDS + + case 0x0F:{// BRK + int temp; + int ret_addr = GET_PC(); + SUSPICIOUS_OPCODE( "BRK" ); + SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified + PUSH16( ret_addr ); + GET_PSW( temp ); + psw = (psw | b10) & ~i04; + PUSH( temp ); + goto loop; + } + + case 0x4F:{// PCALL offset + int ret_addr = GET_PC() + 1; + SET_PC( 0xFF00 | data ); + PUSH16( ret_addr ); + goto loop; + } + + case 0x01: // TCALL n + case 0x11: + case 0x21: + case 0x31: + case 0x41: + case 0x51: + case 0x61: + case 0x71: + case 0x81: + case 0x91: + case 0xA1: + case 0xB1: + case 0xC1: + case 0xD1: + case 0xE1: + case 0xF1: { + int ret_addr = GET_PC(); + SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); + PUSH16( ret_addr ); + goto loop; + } + +// 14. STACK OPERATION COMMANDS + + { + int temp; + case 0x7F: // RET1 + temp = *sp; + SET_PC( GET_LE16( sp + 1 ) ); + sp += 3; + goto set_psw; + case 0x8E: // POP PSW + POP( temp ); + set_psw: + SET_PSW( temp ); + goto loop; + } + + case 0x0D: { // PUSH PSW + int temp; + GET_PSW( temp ); + PUSH( temp ); + goto loop; + } + + case 0x2D: // PUSH A + PUSH( a ); + goto loop; + + case 0x4D: // PUSH X + PUSH( x ); + goto loop; + + case 0x6D: // PUSH Y + PUSH( y ); + goto loop; + + case 0xAE: // POP A + POP( a ); + goto loop; + + case 0xCE: // POP X + POP( x ); + goto loop; + + case 0xEE: // POP Y + POP( y ); + goto loop; + +// 15. BIT OPERATION COMMANDS + + case 0x02: // SET1 + case 0x22: + case 0x42: + case 0x62: + case 0x82: + case 0xA2: + case 0xC2: + case 0xE2: + case 0x12: // CLR1 + case 0x32: + case 0x52: + case 0x72: + case 0x92: + case 0xB2: + case 0xD2: + case 0xF2: { + int bit = 1 << (opcode >> 5); + int mask = ~bit; + if ( opcode & 0x10 ) + bit = 0; + data += dp; + WRITE( 0, data, (READ( -1, data ) & mask) | bit ); + goto inc_pc_loop; + } + + case 0x0E: // TSET1 abs + case 0x4E: // TCLR1 abs + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data ); + nz = (uint8_t) (a - temp); + temp &= ~a; + if ( opcode == 0x0E ) + temp |= a; + WRITE( 0, data, temp ); + } + goto loop; + + case 0x4A: // AND1 C,mem.bit + c &= MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x6A: // AND1 C,/mem.bit + c &= ~MEM_BIT( 0 ); + pc += 2; + goto loop; + + case 0x0A: // OR1 C,mem.bit + c |= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x2A: // OR1 C,/mem.bit + c |= ~MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0x8A: // EOR1 C,mem.bit + c ^= MEM_BIT( -1 ); + pc += 2; + goto loop; + + case 0xEA: // NOT1 mem.bit + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -1, data & 0x1FFF ); + temp ^= 1 << (data >> 13); + WRITE( 0, data & 0x1FFF, temp ); + } + goto loop; + + case 0xCA: // MOV1 mem.bit,C + data = READ_PC16( pc ); + pc += 2; + { + unsigned temp = READ( -2, data & 0x1FFF ); + unsigned bit = data >> 13; + temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); + WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); + } + goto loop; + + case 0xAA: // MOV1 C,mem.bit + c = MEM_BIT( 0 ); + pc += 2; + goto loop; + +// 16. PROGRAM PSW FLAG OPERATION COMMANDS + + case 0x60: // CLRC + c = 0; + goto loop; + + case 0x80: // SETC + c = ~0; + goto loop; + + case 0xED: // NOTC + c ^= 0x100; + goto loop; + + case 0xE0: // CLRV + psw &= ~(v40 | h08); + goto loop; + + case 0x20: // CLRP + dp = 0; + goto loop; + + case 0x40: // SETP + dp = 0x100; + goto loop; + + case 0xA0: // EI + SUSPICIOUS_OPCODE( "EI" ); + psw |= i04; + goto loop; + + case 0xC0: // DI + SUSPICIOUS_OPCODE( "DI" ); + psw &= ~i04; + goto loop; + +// 17. OTHER COMMANDS + + case 0x00: // NOP + goto loop; + + case 0xFF:{// STOP + // handle PC wrap-around + unsigned addr = GET_PC() - 1; + if ( addr >= 0x10000 ) + { + addr &= 0xFFFF; + SET_PC( addr ); + dprintf( "SPC: PC wrapped around\n" ); + goto loop; + } + } + // fall through + case 0xEF: // SLEEP + SUSPICIOUS_OPCODE( "STOP/SLEEP" ); + --pc; + rel_time = 0; + m.cpu_error = "SPC emulation error"; + goto stop; + } // switch + + assert( 0 ); // catch any unhandled instructions +} +out_of_time: + rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode +stop: + + // Uncache registers + if ( GET_PC() >= 0x10000 ) + dprintf( "SPC: PC wrapped around\n" ); + m.cpu_regs.pc = (uint16_t) GET_PC(); + m.cpu_regs.sp = ( uint8_t) GET_SP(); + m.cpu_regs.a = ( uint8_t) a; + m.cpu_regs.x = ( uint8_t) x; + m.cpu_regs.y = ( uint8_t) y; + { + int temp; + GET_PSW( temp ); + m.cpu_regs.psw = (uint8_t) temp; + } +} +SPC_CPU_RUN_FUNC_END diff --git a/Frameworks/GME/gme/Spc_Dsp.cpp b/Frameworks/GME/gme/Spc_Dsp.cpp old mode 100755 new mode 100644 index 3d934f637..5a6f98d88 --- a/Frameworks/GME/gme/Spc_Dsp.cpp +++ b/Frameworks/GME/gme/Spc_Dsp.cpp @@ -1,666 +1,1419 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -// Based on Brad Martin's OpenSPC DSP emulator - -#include "Spc_Dsp.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2002 Brad Martin */ -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -Spc_Dsp::Spc_Dsp( uint8_t* ram_ ) : ram( ram_ ) -{ - set_gain( 1.0 ); - mute_voices( 0 ); - disable_surround( false ); - - assert( offsetof (globals_t,unused9 [2]) == register_count ); - assert( sizeof (voice) == register_count ); - blargg_verify_byte_order(); -} - -void Spc_Dsp::mute_voices( int mask ) -{ - for ( int i = 0; i < voice_count; i++ ) - voice_state [i].enabled = (mask >> i & 1) ? 31 : 7; -} - -void Spc_Dsp::reset() -{ - keys = 0; - echo_ptr = 0; - noise_count = 0; - noise = 1; - fir_offset = 0; - - g.flags = 0xE0; // reset, mute, echo off - g.key_ons = 0; - - for ( int i = 0; i < voice_count; i++ ) - { - voice_t& v = voice_state [i]; - v.on_cnt = 0; - v.volume [0] = 0; - v.volume [1] = 0; - v.envstate = state_release; - } - - memset( fir_buf, 0, sizeof fir_buf ); -} - -void Spc_Dsp::write( int i, int data ) -{ - require( (unsigned) i < register_count ); - - reg [i] = data; - int high = i >> 4; - switch ( i & 0x0F ) - { - // voice volume - case 0: - case 1: { - short* volume = voice_state [high].volume; - int left = (int8_t) reg [i & ~1]; - int right = (int8_t) reg [i | 1]; - volume [0] = left; - volume [1] = right; - // kill surround only if enabled and signs of volumes differ - if ( left * right < surround_threshold ) - { - if ( left < 0 ) - volume [0] = -left; - else - volume [1] = -right; - } - break; - } - - // fir coefficients - case 0x0F: - fir_coeff [high] = (int8_t) data; // sign-extend - break; - } -} - -// This table is for envelope timing. It represents the number of counts -// that should be subtracted from the counter each sample period (32kHz). -// The counter starts at 30720 (0x7800). Each count divides exactly into -// 0x7800 without remainder. -const int env_rate_init = 0x7800; -static short const env_rates [0x20] = -{ - 0x0000, 0x000F, 0x0014, 0x0018, 0x001E, 0x0028, 0x0030, 0x003C, - 0x0050, 0x0060, 0x0078, 0x00A0, 0x00C0, 0x00F0, 0x0140, 0x0180, - 0x01E0, 0x0280, 0x0300, 0x03C0, 0x0500, 0x0600, 0x0780, 0x0A00, - 0x0C00, 0x0F00, 0x1400, 0x1800, 0x1E00, 0x2800, 0x3C00, 0x7800 -}; - -const int env_range = 0x800; - -inline int Spc_Dsp::clock_envelope( int v ) -{ /* Return value is current - * ENVX */ - raw_voice_t& raw_voice = this->voice [v]; - voice_t& voice = voice_state [v]; - - int envx = voice.envx; - if ( voice.envstate == state_release ) - { - /* - * Docs: "When in the state of "key off". the "click" sound is - * prevented by the addition of the fixed value 1/256" WTF??? - * Alright, I'm going to choose to interpret that this way: - * When a note is keyed off, start the RELEASE state, which - * subtracts 1/256th each sample period (32kHz). Note there's - * no need for a count because it always happens every update. - */ - envx -= env_range / 256; - if ( envx <= 0 ) - { - envx = 0; - keys &= ~(1 << v); - return -1; - } - voice.envx = envx; - raw_voice.envx = envx >> 8; - return envx; - } - - int cnt = voice.envcnt; - int adsr1 = raw_voice.adsr [0]; - if ( adsr1 & 0x80 ) - { - switch ( voice.envstate ) - { - case state_attack: { - // increase envelope by 1/64 each step - int t = adsr1 & 15; - if ( t == 15 ) - { - envx += env_range / 2; - } - else - { - cnt -= env_rates [t * 2 + 1]; - if ( cnt > 0 ) - break; - envx += env_range / 64; - cnt = env_rate_init; - } - if ( envx >= env_range ) - { - envx = env_range - 1; - voice.envstate = state_decay; - } - voice.envx = envx; - break; - } - - case state_decay: { - // Docs: "DR... [is multiplied] by the fixed value - // 1-1/256." Well, at least that makes some sense. - // Multiplying ENVX by 255/256 every time DECAY is - // updated. - cnt -= env_rates [((adsr1 >> 3) & 0xE) + 0x10]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - int sustain_level = raw_voice.adsr [1] >> 5; - - if ( envx <= (sustain_level + 1) * 0x100 ) - voice.envstate = state_sustain; - break; - } - - case state_sustain: - // Docs: "SR [is multiplied] by the fixed value 1-1/256." - // Multiplying ENVX by 255/256 every time SUSTAIN is - // updated. - cnt -= env_rates [raw_voice.adsr [1] & 0x1F]; - if ( cnt <= 0 ) - { - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - voice.envx = envx; - } - break; - - case state_release: - // handled above - break; - } - } - else - { /* GAIN mode is set */ - /* - * Note: if the game switches between ADSR and GAIN modes - * partway through, should the count be reset, or should it - * continue from where it was? Does the DSP actually watch for - * that bit to change, or does it just go along with whatever - * it sees when it performs the update? I'm going to assume - * the latter and not update the count, unless I see a game - * that obviously wants the other behavior. The effect would - * be pretty subtle, in any case. - */ - int t = raw_voice.gain; - if (t < 0x80) - { - envx = voice.envx = t << 4; - } - else switch (t >> 5) - { - case 4: /* Docs: "Decrease (linear): Subtraction - * of the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= env_range / 64; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 5: /* Docs: "Drecrease (exponential): - * Multiplication by the fixed value - * 1-1/256." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx -= ((envx - 1) >> 8) + 1; - if ( envx < 0 ) - { - envx = 0; - if ( voice.envstate == state_attack ) - voice.envstate = state_decay; - } - voice.envx = envx; - break; - case 6: /* Docs: "Increase (linear): Addition of - * the fixed value 1/64." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - envx += env_range / 64; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - case 7: /* Docs: "Increase (bent line): Addition - * of the constant 1/64 up to .75 of the - * constaint 1/256 from .75 to 1." */ - cnt -= env_rates [t & 0x1F]; - if (cnt > 0) - break; - cnt = env_rate_init; - if ( envx < env_range * 3 / 4 ) - envx += env_range / 64; - else - envx += env_range / 256; - if ( envx >= env_range ) - envx = env_range - 1; - voice.envx = envx; - break; - } - } - voice.envcnt = cnt; - raw_voice.envx = envx >> 4; - return envx; -} - -// Clamp n into range -32768 <= n <= 32767 -inline int clamp_16( int n ) -{ - if ( (BOOST::int16_t) n != n ) - n = BOOST::int16_t (0x7FFF - (n >> 31)); - return n; -} - -void Spc_Dsp::run( long count, short* out_buf ) -{ - // to do: make clock_envelope() inline so that this becomes a leaf function? - - // Should we just fill the buffer with silence? Flags won't be cleared - // during this run so it seems it should keep resetting every sample. - if ( g.flags & 0x80 ) - reset(); - - struct src_dir { - char start [2]; - char loop [2]; - }; - - const src_dir* const sd = (src_dir*) &ram [g.wave_page * 0x100]; - - int left_volume = g.left_volume; - int right_volume = g.right_volume; - if ( left_volume * right_volume < surround_threshold ) - right_volume = -right_volume; // kill global surround - left_volume *= emu_gain; - right_volume *= emu_gain; - - while ( --count >= 0 ) - { - // Here we check for keys on/off. Docs say that successive writes - // to KON/KOF must be separated by at least 2 Ts periods or risk - // being neglected. Therefore DSP only looks at these during an - // update, and not at the time of the write. Only need to do this - // once however, since the regs haven't changed over the whole - // period we need to catch up with. - - g.wave_ended &= ~g.key_ons; // Keying on a voice resets that bit in ENDX. - - if ( g.noise_enables ) - { - noise_count -= env_rates [g.flags & 0x1F]; - if ( noise_count <= 0 ) - { - noise_count = env_rate_init; - - noise_amp = BOOST::int16_t (noise * 2); - - // TODO: switch to Galios style - int feedback = (noise << 13) ^ (noise << 14); - noise = (feedback & 0x4000) | (noise >> 1); - } - } - - // What is the expected behavior when pitch modulation is enabled on - // voice 0? Jurassic Park 2 does this. Assume 0 for now. - blargg_long prev_outx = 0; - - int echol = 0; - int echor = 0; - int left = 0; - int right = 0; - for ( int vidx = 0; vidx < voice_count; vidx++ ) - { - const int vbit = 1 << vidx; - raw_voice_t& raw_voice = voice [vidx]; - voice_t& voice = voice_state [vidx]; - - if ( voice.on_cnt && !--voice.on_cnt ) - { - // key on - keys |= vbit; - voice.addr = GET_LE16( sd [raw_voice.waveform].start ); - voice.block_remain = 1; - voice.envx = 0; - voice.block_header = 0; - voice.fraction = 0x3FFF; // decode three samples immediately - voice.interp0 = 0; // BRR decoder filter uses previous two samples - voice.interp1 = 0; - - // NOTE: Real SNES does *not* appear to initialize the - // envelope counter to anything in particular. The first - // cycle always seems to come at a random time sooner than - // expected; as yet, I have been unable to find any - // pattern. I doubt it will matter though, so we'll go - // ahead and do the full time for now. - voice.envcnt = env_rate_init; - voice.envstate = state_attack; - } - - if ( g.key_ons & vbit & ~g.key_offs ) - { - // voice doesn't come on if key off is set - g.key_ons &= ~vbit; - voice.on_cnt = 8; - } - - if ( keys & g.key_offs & vbit ) - { - // key off - voice.envstate = state_release; - voice.on_cnt = 0; - } - - int envx; - if ( !(keys & vbit) || (envx = clock_envelope( vidx )) < 0 ) - { - raw_voice.envx = 0; - raw_voice.outx = 0; - prev_outx = 0; - continue; - } - - // Decode samples when fraction >= 1.0 (0x1000) - for ( int n = voice.fraction >> 12; --n >= 0; ) - { - if ( !--voice.block_remain ) - { - if ( voice.block_header & 1 ) - { - g.wave_ended |= vbit; - - if ( voice.block_header & 2 ) - { - // verified (played endless looping sample and ENDX was set) - voice.addr = GET_LE16( sd [raw_voice.waveform].loop ); - } - else - { - // first block was end block; don't play anything (verified) - goto sample_ended; // to do: find alternative to goto - } - } - - voice.block_header = ram [voice.addr++]; - voice.block_remain = 16; // nybbles - } - - // if next block has end flag set, *this* block ends *early* (verified) - if ( voice.block_remain == 9 && (ram [voice.addr + 5] & 3) == 1 && - (voice.block_header & 3) != 3 ) - { - sample_ended: - g.wave_ended |= vbit; - keys &= ~vbit; - raw_voice.envx = 0; - voice.envx = 0; - // add silence samples to interpolation buffer - do - { - voice.interp3 = voice.interp2; - voice.interp2 = voice.interp1; - voice.interp1 = voice.interp0; - voice.interp0 = 0; - } - while ( --n >= 0 ); - break; - } - - int delta = ram [voice.addr]; - if ( voice.block_remain & 1 ) - { - delta <<= 4; // use lower nybble - voice.addr++; - } - - // Use sign-extended upper nybble - delta = int8_t (delta) >> 4; - - // For invalid ranges (D,E,F): if the nybble is negative, - // the result is F000. If positive, 0000. Nothing else - // like previous range, etc seems to have any effect. If - // range is valid, do the shift normally. Note these are - // both shifted right once to do the filters properly, but - // the output will be shifted back again at the end. - int shift = voice.block_header >> 4; - delta = (delta << shift) >> 1; - if ( shift > 0x0C ) - delta = (delta >> 14) & ~0x7FF; - - // One, two and three point IIR filters - int smp1 = voice.interp0; - int smp2 = voice.interp1; - if ( voice.block_header & 8 ) - { - delta += smp1; - delta -= smp2 >> 1; - if ( !(voice.block_header & 4) ) - { - delta += (-smp1 - (smp1 >> 1)) >> 5; - delta += smp2 >> 5; - } - else - { - delta += (-smp1 * 13) >> 7; - delta += (smp2 + (smp2 >> 1)) >> 4; - } - } - else if ( voice.block_header & 4 ) - { - delta += smp1 >> 1; - delta += (-smp1) >> 5; - } - - voice.interp3 = voice.interp2; - voice.interp2 = smp2; - voice.interp1 = smp1; - voice.interp0 = BOOST::int16_t (clamp_16( delta ) * 2); // sign-extend - } - - // rate (with possible modulation) - int rate = GET_LE16( raw_voice.rate ) & 0x3FFF; - if ( g.pitch_mods & vbit ) - rate = (rate * (prev_outx + 32768)) >> 15; - - // Gaussian interpolation using most recent 4 samples - int index = voice.fraction >> 2 & 0x3FC; - voice.fraction = (voice.fraction & 0x0FFF) + rate; - const BOOST::int16_t* table = (BOOST::int16_t const*) ((char const*) gauss + index); - const BOOST::int16_t* table2 = (BOOST::int16_t const*) ((char const*) gauss + (255*4 - index)); - int s = ((table [0] * voice.interp3) >> 12) + - ((table [1] * voice.interp2) >> 12) + - ((table2 [1] * voice.interp1) >> 12); - s = (BOOST::int16_t) (s * 2); - s += (table2 [0] * voice.interp0) >> 11 & ~1; - int output = clamp_16( s ); - if ( g.noise_enables & vbit ) - output = noise_amp; - - // scale output and set outx values - output = (output * envx) >> 11 & ~1; - - // output and apply muting (by setting voice.enabled to 31) - // if voice is externally disabled (not a SNES feature) - int l = (voice.volume [0] * output) >> voice.enabled; - int r = (voice.volume [1] * output) >> voice.enabled; - prev_outx = output; - raw_voice.outx = int8_t (output >> 8); - if ( g.echo_ons & vbit ) - { - echol += l; - echor += r; - } - left += l; - right += r; - } - // end of channel loop - - // main volume control - left = (left * left_volume ) >> (7 + emu_gain_bits); - right = (right * right_volume) >> (7 + emu_gain_bits); - - // Echo FIR filter - - // read feedback from echo buffer - int echo_ptr = this->echo_ptr; - uint8_t* echo_buf = &ram [(g.echo_page * 0x100 + echo_ptr) & 0xFFFF]; - echo_ptr += 4; - if ( echo_ptr >= (g.echo_delay & 15) * 0x800 ) - echo_ptr = 0; - int fb_left = (BOOST::int16_t) GET_LE16( echo_buf ); // sign-extend - int fb_right = (BOOST::int16_t) GET_LE16( echo_buf + 2 ); // sign-extend - this->echo_ptr = echo_ptr; - - // put samples in history ring buffer - const int fir_offset = this->fir_offset; - short (*fir_pos) [2] = &fir_buf [fir_offset]; - this->fir_offset = (fir_offset + 7) & 7; // move backwards one step - fir_pos [0] [0] = (short) fb_left; - fir_pos [0] [1] = (short) fb_right; - fir_pos [8] [0] = (short) fb_left; // duplicate at +8 eliminates wrap checking below - fir_pos [8] [1] = (short) fb_right; - - // FIR - fb_left = fb_left * fir_coeff [7] + - fir_pos [1] [0] * fir_coeff [6] + - fir_pos [2] [0] * fir_coeff [5] + - fir_pos [3] [0] * fir_coeff [4] + - fir_pos [4] [0] * fir_coeff [3] + - fir_pos [5] [0] * fir_coeff [2] + - fir_pos [6] [0] * fir_coeff [1] + - fir_pos [7] [0] * fir_coeff [0]; - - fb_right = fb_right * fir_coeff [7] + - fir_pos [1] [1] * fir_coeff [6] + - fir_pos [2] [1] * fir_coeff [5] + - fir_pos [3] [1] * fir_coeff [4] + - fir_pos [4] [1] * fir_coeff [3] + - fir_pos [5] [1] * fir_coeff [2] + - fir_pos [6] [1] * fir_coeff [1] + - fir_pos [7] [1] * fir_coeff [0]; - - left += (fb_left * g.left_echo_volume ) >> 14; - right += (fb_right * g.right_echo_volume) >> 14; - - // echo buffer feedback - if ( !(g.flags & 0x20) ) - { - echol += (fb_left * g.echo_feedback) >> 14; - echor += (fb_right * g.echo_feedback) >> 14; - SET_LE16( echo_buf , clamp_16( echol ) ); - SET_LE16( echo_buf + 2, clamp_16( echor ) ); - } - - if ( out_buf ) - { - // write final samples - - left = clamp_16( left ); - right = clamp_16( right ); - - int mute = g.flags & 0x40; - - out_buf [0] = (short) left; - out_buf [1] = (short) right; - out_buf += 2; - - // muting - if ( mute ) - { - out_buf [-2] = 0; - out_buf [-1] = 0; - } - } - } -} - -// Base normal_gauss table is almost exactly (with an error of 0 or -1 for each entry): -// int normal_gauss [512]; -// normal_gauss [i] = exp((i-511)*(i-511)*-9.975e-6)*pow(sin(0.00307096*i),1.7358)*1304.45 - -// Interleved gauss table (to improve cache coherency). -// gauss [i * 2 + j] = normal_gauss [(1 - j) * 256 + i] -const BOOST::int16_t Spc_Dsp::gauss [512] = -{ - 370,1305, 366,1305, 362,1304, 358,1304, 354,1304, 351,1304, 347,1304, 343,1303, - 339,1303, 336,1303, 332,1302, 328,1302, 325,1301, 321,1300, 318,1300, 314,1299, - 311,1298, 307,1297, 304,1297, 300,1296, 297,1295, 293,1294, 290,1293, 286,1292, - 283,1291, 280,1290, 276,1288, 273,1287, 270,1286, 267,1284, 263,1283, 260,1282, - 257,1280, 254,1279, 251,1277, 248,1275, 245,1274, 242,1272, 239,1270, 236,1269, - 233,1267, 230,1265, 227,1263, 224,1261, 221,1259, 218,1257, 215,1255, 212,1253, - 210,1251, 207,1248, 204,1246, 201,1244, 199,1241, 196,1239, 193,1237, 191,1234, - 188,1232, 186,1229, 183,1227, 180,1224, 178,1221, 175,1219, 173,1216, 171,1213, - 168,1210, 166,1207, 163,1205, 161,1202, 159,1199, 156,1196, 154,1193, 152,1190, - 150,1186, 147,1183, 145,1180, 143,1177, 141,1174, 139,1170, 137,1167, 134,1164, - 132,1160, 130,1157, 128,1153, 126,1150, 124,1146, 122,1143, 120,1139, 118,1136, - 117,1132, 115,1128, 113,1125, 111,1121, 109,1117, 107,1113, 106,1109, 104,1106, - 102,1102, 100,1098, 99,1094, 97,1090, 95,1086, 94,1082, 92,1078, 90,1074, - 89,1070, 87,1066, 86,1061, 84,1057, 83,1053, 81,1049, 80,1045, 78,1040, - 77,1036, 76,1032, 74,1027, 73,1023, 71,1019, 70,1014, 69,1010, 67,1005, - 66,1001, 65, 997, 64, 992, 62, 988, 61, 983, 60, 978, 59, 974, 58, 969, - 56, 965, 55, 960, 54, 955, 53, 951, 52, 946, 51, 941, 50, 937, 49, 932, - 48, 927, 47, 923, 46, 918, 45, 913, 44, 908, 43, 904, 42, 899, 41, 894, - 40, 889, 39, 884, 38, 880, 37, 875, 36, 870, 36, 865, 35, 860, 34, 855, - 33, 851, 32, 846, 32, 841, 31, 836, 30, 831, 29, 826, 29, 821, 28, 816, - 27, 811, 27, 806, 26, 802, 25, 797, 24, 792, 24, 787, 23, 782, 23, 777, - 22, 772, 21, 767, 21, 762, 20, 757, 20, 752, 19, 747, 19, 742, 18, 737, - 17, 732, 17, 728, 16, 723, 16, 718, 15, 713, 15, 708, 15, 703, 14, 698, - 14, 693, 13, 688, 13, 683, 12, 678, 12, 674, 11, 669, 11, 664, 11, 659, - 10, 654, 10, 649, 10, 644, 9, 640, 9, 635, 9, 630, 8, 625, 8, 620, - 8, 615, 7, 611, 7, 606, 7, 601, 6, 596, 6, 592, 6, 587, 6, 582, - 5, 577, 5, 573, 5, 568, 5, 563, 4, 559, 4, 554, 4, 550, 4, 545, - 4, 540, 3, 536, 3, 531, 3, 527, 3, 522, 3, 517, 2, 513, 2, 508, - 2, 504, 2, 499, 2, 495, 2, 491, 2, 486, 1, 482, 1, 477, 1, 473, - 1, 469, 1, 464, 1, 460, 1, 456, 1, 451, 1, 447, 1, 443, 1, 439, - 0, 434, 0, 430, 0, 426, 0, 422, 0, 418, 0, 414, 0, 410, 0, 405, - 0, 401, 0, 397, 0, 393, 0, 389, 0, 385, 0, 381, 0, 378, 0, 374, -}; +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "Spc_Dsp.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [Spc_Dsp::register_count] = +{ + 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, + 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, + 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, + 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, + 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, + 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, + 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, + 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void Spc_Dsp::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Gaussian interpolation + +static short const gauss [512] = +{ + 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, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, +1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, +1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, +1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, +1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, +1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, +1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, +1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, +}; + +static short const cubic [514] = +{ + 0, -4, -8, -12, -16, -20, -23, -27, -30, -34, -37, -41, -44, -47, -50, -53, + -56, -59, -62, -65, -68, -71, -73, -76, -78, -81, -84, -87, -89, -91, -93, -95, + -98,-100,-102,-104,-106,-109,-110,-112,-113,-116,-117,-119,-121,-122,-123,-125, +-126,-128,-129,-131,-132,-134,-134,-136,-136,-138,-138,-140,-141,-141,-142,-143, +-144,-144,-145,-146,-147,-148,-147,-148,-148,-149,-149,-150,-150,-150,-150,-151, +-151,-151,-151,-151,-152,-152,-151,-152,-151,-152,-151,-151,-151,-151,-150,-150, +-150,-149,-149,-149,-149,-148,-147,-147,-146,-146,-145,-145,-144,-144,-143,-142, +-141,-141,-140,-139,-139,-138,-137,-136,-135,-135,-133,-133,-132,-131,-130,-129, +-128,-127,-126,-125,-124,-123,-121,-121,-119,-118,-117,-116,-115,-114,-112,-111, +-110,-109,-107,-106,-105,-104,-102,-102,-100, -99, -97, -97, -95, -94, -92, -91, + -90, -88, -87, -86, -85, -84, -82, -81, -79, -78, -76, -76, -74, -73, -71, -70, + -68, -67, -66, -65, -63, -62, -60, -60, -58, -57, -55, -55, -53, -52, -50, -49, + -48, -46, -45, -44, -43, -42, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30, + -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -19, -17, -16, -15, -14, + -14, -13, -12, -11, -11, -10, -9, -9, -8, -8, -7, -7, -6, -5, -4, -4, + -3, -3, -3, -2, -2, -2, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0, + 0, +2048,2048,2048,2048,2047,2047,2046,2045,2043,2042,2041,2039,2037,2035,2033,2031, +2028,2026,2024,2021,2018,2015,2012,2009,2005,2002,1999,1995,1991,1987,1982,1978, +1974,1969,1965,1960,1955,1951,1946,1940,1934,1929,1924,1918,1912,1906,1900,1895, +1888,1882,1875,1869,1862,1856,1849,1842,1835,1828,1821,1814,1806,1799,1791,1783, +1776,1768,1760,1753,1744,1737,1728,1720,1711,1703,1695,1686,1677,1668,1659,1651, +1641,1633,1623,1614,1605,1596,1587,1577,1567,1559,1549,1539,1529,1520,1510,1499, +1490,1480,1470,1460,1450,1440,1430,1420,1408,1398,1389,1378,1367,1357,1346,1336, +1325,1315,1304,1293,1282,1272,1261,1250,1239,1229,1218,1207,1196,1185,1174,1163, +1152,1141,1130,1119,1108,1097,1086,1075,1063,1052,1042,1030,1019,1008, 997, 986, + 974, 964, 952, 941, 930, 919, 908, 897, 886, 875, 864, 853, 842, 831, 820, 809, + 798, 787, 776, 765, 754, 744, 733, 722, 711, 700, 690, 679, 668, 658, 647, 637, + 626, 616, 605, 595, 584, 574, 564, 554, 543, 534, 524, 514, 503, 494, 483, 473, + 464, 454, 444, 435, 425, 416, 407, 397, 387, 378, 370, 360, 351, 342, 333, 325, + 315, 307, 298, 290, 281, 273, 265, 256, 248, 241, 233, 225, 216, 209, 201, 193, + 186, 178, 171, 164, 157, 150, 143, 137, 129, 123, 117, 110, 103, 97, 91, 85, + 79, 74, 68, 62, 56, 51, 46, 41, 35, 31, 27, 22, 17, 13, 8, 4, + 0 +}; + +static short const sinc [2048] = +{ + 39, -315, 666, 15642, 666, -315, 39, -38, + 38, -302, 613, 15642, 718, -328, 41, -38, + 36, -288, 561, 15641, 772, -342, 42, -38, + 35, -275, 510, 15639, 826, -355, 44, -38, + 33, -263, 459, 15636, 880, -369, 46, -38, + 32, -250, 408, 15632, 935, -383, 47, -38, + 31, -237, 358, 15628, 990, -396, 49, -38, + 29, -224, 309, 15622, 1046, -410, 51, -38, + 28, -212, 259, 15616, 1103, -425, 53, -38, + 27, -200, 211, 15609, 1159, -439, 54, -38, + 25, -188, 163, 15601, 1216, -453, 56, -38, + 24, -175, 115, 15593, 1274, -467, 58, -38, + 23, -164, 68, 15583, 1332, -482, 60, -38, + 22, -152, 22, 15573, 1391, -496, 62, -37, + 21, -140, -24, 15562, 1450, -511, 64, -37, + 19, -128, -70, 15550, 1509, -526, 66, -37, + 18, -117, -115, 15538, 1569, -540, 68, -37, + 17, -106, -159, 15524, 1629, -555, 70, -37, + 16, -94, -203, 15510, 1690, -570, 72, -36, + 15, -83, -247, 15495, 1751, -585, 74, -36, + 14, -72, -289, 15479, 1813, -600, 76, -36, + 13, -62, -332, 15462, 1875, -616, 79, -36, + 12, -51, -374, 15445, 1937, -631, 81, -35, + 11, -40, -415, 15426, 2000, -646, 83, -35, + 11, -30, -456, 15407, 2063, -662, 85, -35, + 10, -20, -496, 15387, 2127, -677, 88, -34, + 9, -9, -536, 15366, 2191, -693, 90, -34, + 8, 1, -576, 15345, 2256, -708, 92, -34, + 7, 10, -614, 15323, 2321, -724, 95, -33, + 7, 20, -653, 15300, 2386, -740, 97, -33, + 6, 30, -690, 15276, 2451, -755, 99, -33, + 5, 39, -728, 15251, 2517, -771, 102, -32, + 5, 49, -764, 15226, 2584, -787, 104, -32, + 4, 58, -801, 15200, 2651, -803, 107, -32, + 3, 67, -836, 15173, 2718, -819, 109, -31, + 3, 76, -871, 15145, 2785, -835, 112, -31, + 2, 85, -906, 15117, 2853, -851, 115, -30, + 2, 93, -940, 15087, 2921, -867, 117, -30, + 1, 102, -974, 15057, 2990, -883, 120, -29, + 1, 110, -1007, 15027, 3059, -899, 122, -29, + 0, 118, -1039, 14995, 3128, -915, 125, -29, + 0, 127, -1071, 14963, 3198, -931, 128, -28, + -1, 135, -1103, 14930, 3268, -948, 131, -28, + -1, 142, -1134, 14896, 3338, -964, 133, -27, + -1, 150, -1164, 14862, 3409, -980, 136, -27, + -2, 158, -1194, 14827, 3480, -996, 139, -26, + -2, 165, -1224, 14791, 3551, -1013, 142, -26, + -3, 172, -1253, 14754, 3622, -1029, 144, -25, + -3, 179, -1281, 14717, 3694, -1045, 147, -25, + -3, 187, -1309, 14679, 3766, -1062, 150, -24, + -3, 193, -1337, 14640, 3839, -1078, 153, -24, + -4, 200, -1363, 14601, 3912, -1094, 156, -23, + -4, 207, -1390, 14561, 3985, -1110, 159, -23, + -4, 213, -1416, 14520, 4058, -1127, 162, -22, + -4, 220, -1441, 14479, 4131, -1143, 165, -22, + -4, 226, -1466, 14437, 4205, -1159, 168, -22, + -5, 232, -1490, 14394, 4279, -1175, 171, -21, + -5, 238, -1514, 14350, 4354, -1192, 174, -21, + -5, 244, -1537, 14306, 4428, -1208, 177, -20, + -5, 249, -1560, 14261, 4503, -1224, 180, -20, + -5, 255, -1583, 14216, 4578, -1240, 183, -19, + -5, 260, -1604, 14169, 4653, -1256, 186, -19, + -5, 265, -1626, 14123, 4729, -1272, 189, -18, + -5, 271, -1647, 14075, 4805, -1288, 192, -18, + -5, 276, -1667, 14027, 4881, -1304, 195, -17, + -6, 280, -1687, 13978, 4957, -1320, 198, -17, + -6, 285, -1706, 13929, 5033, -1336, 201, -16, + -6, 290, -1725, 13879, 5110, -1352, 204, -16, + -6, 294, -1744, 13829, 5186, -1368, 207, -15, + -6, 299, -1762, 13777, 5263, -1383, 210, -15, + -6, 303, -1779, 13726, 5340, -1399, 213, -14, + -6, 307, -1796, 13673, 5418, -1414, 216, -14, + -6, 311, -1813, 13620, 5495, -1430, 219, -13, + -5, 315, -1829, 13567, 5573, -1445, 222, -13, + -5, 319, -1844, 13512, 5651, -1461, 225, -13, + -5, 322, -1859, 13458, 5728, -1476, 229, -12, + -5, 326, -1874, 13402, 5806, -1491, 232, -12, + -5, 329, -1888, 13347, 5885, -1506, 235, -11, + -5, 332, -1902, 13290, 5963, -1521, 238, -11, + -5, 335, -1915, 13233, 6041, -1536, 241, -10, + -5, 338, -1928, 13176, 6120, -1551, 244, -10, + -5, 341, -1940, 13118, 6199, -1566, 247, -10, + -5, 344, -1952, 13059, 6277, -1580, 250, -9, + -5, 347, -1964, 13000, 6356, -1595, 253, -9, + -5, 349, -1975, 12940, 6435, -1609, 256, -8, + -4, 352, -1986, 12880, 6514, -1623, 259, -8, + -4, 354, -1996, 12819, 6594, -1637, 262, -8, + -4, 356, -2005, 12758, 6673, -1651, 265, -7, + -4, 358, -2015, 12696, 6752, -1665, 268, -7, + -4, 360, -2024, 12634, 6831, -1679, 271, -7, + -4, 362, -2032, 12572, 6911, -1693, 274, -6, + -4, 364, -2040, 12509, 6990, -1706, 277, -6, + -4, 366, -2048, 12445, 7070, -1719, 280, -6, + -3, 367, -2055, 12381, 7149, -1732, 283, -5, + -3, 369, -2062, 12316, 7229, -1745, 286, -5, + -3, 370, -2068, 12251, 7308, -1758, 289, -5, + -3, 371, -2074, 12186, 7388, -1771, 291, -4, + -3, 372, -2079, 12120, 7467, -1784, 294, -4, + -3, 373, -2084, 12054, 7547, -1796, 297, -4, + -3, 374, -2089, 11987, 7626, -1808, 300, -4, + -2, 375, -2094, 11920, 7706, -1820, 303, -3, + -2, 376, -2098, 11852, 7785, -1832, 305, -3, + -2, 376, -2101, 11785, 7865, -1844, 308, -3, + -2, 377, -2104, 11716, 7944, -1855, 311, -3, + -2, 377, -2107, 11647, 8024, -1866, 313, -2, + -2, 378, -2110, 11578, 8103, -1877, 316, -2, + -2, 378, -2112, 11509, 8182, -1888, 318, -2, + -1, 378, -2113, 11439, 8262, -1899, 321, -2, + -1, 378, -2115, 11369, 8341, -1909, 323, -2, + -1, 378, -2116, 11298, 8420, -1920, 326, -2, + -1, 378, -2116, 11227, 8499, -1930, 328, -1, + -1, 378, -2116, 11156, 8578, -1940, 331, -1, + -1, 378, -2116, 11084, 8656, -1949, 333, -1, + -1, 377, -2116, 11012, 8735, -1959, 335, -1, + -1, 377, -2115, 10940, 8814, -1968, 337, -1, + -1, 377, -2114, 10867, 8892, -1977, 340, -1, + -1, 376, -2112, 10795, 8971, -1985, 342, -1, + 0, 375, -2111, 10721, 9049, -1994, 344, -1, + 0, 375, -2108, 10648, 9127, -2002, 346, 0, + 0, 374, -2106, 10574, 9205, -2010, 348, 0, + 0, 373, -2103, 10500, 9283, -2018, 350, 0, + 0, 372, -2100, 10426, 9360, -2025, 352, 0, + 0, 371, -2097, 10351, 9438, -2032, 354, 0, + 0, 370, -2093, 10276, 9515, -2039, 355, 0, + 0, 369, -2089, 10201, 9592, -2046, 357, 0, + 0, 367, -2084, 10126, 9669, -2052, 359, 0, + 0, 366, -2080, 10050, 9745, -2058, 360, 0, + 0, 365, -2075, 9974, 9822, -2064, 362, 0, + 0, 363, -2070, 9898, 9898, -2070, 363, 0, + 0, 362, -2064, 9822, 9974, -2075, 365, 0, + 0, 360, -2058, 9745, 10050, -2080, 366, 0, + 0, 359, -2052, 9669, 10126, -2084, 367, 0, + 0, 357, -2046, 9592, 10201, -2089, 369, 0, + 0, 355, -2039, 9515, 10276, -2093, 370, 0, + 0, 354, -2032, 9438, 10351, -2097, 371, 0, + 0, 352, -2025, 9360, 10426, -2100, 372, 0, + 0, 350, -2018, 9283, 10500, -2103, 373, 0, + 0, 348, -2010, 9205, 10574, -2106, 374, 0, + 0, 346, -2002, 9127, 10648, -2108, 375, 0, + -1, 344, -1994, 9049, 10721, -2111, 375, 0, + -1, 342, -1985, 8971, 10795, -2112, 376, -1, + -1, 340, -1977, 8892, 10867, -2114, 377, -1, + -1, 337, -1968, 8814, 10940, -2115, 377, -1, + -1, 335, -1959, 8735, 11012, -2116, 377, -1, + -1, 333, -1949, 8656, 11084, -2116, 378, -1, + -1, 331, -1940, 8578, 11156, -2116, 378, -1, + -1, 328, -1930, 8499, 11227, -2116, 378, -1, + -2, 326, -1920, 8420, 11298, -2116, 378, -1, + -2, 323, -1909, 8341, 11369, -2115, 378, -1, + -2, 321, -1899, 8262, 11439, -2113, 378, -1, + -2, 318, -1888, 8182, 11509, -2112, 378, -2, + -2, 316, -1877, 8103, 11578, -2110, 378, -2, + -2, 313, -1866, 8024, 11647, -2107, 377, -2, + -3, 311, -1855, 7944, 11716, -2104, 377, -2, + -3, 308, -1844, 7865, 11785, -2101, 376, -2, + -3, 305, -1832, 7785, 11852, -2098, 376, -2, + -3, 303, -1820, 7706, 11920, -2094, 375, -2, + -4, 300, -1808, 7626, 11987, -2089, 374, -3, + -4, 297, -1796, 7547, 12054, -2084, 373, -3, + -4, 294, -1784, 7467, 12120, -2079, 372, -3, + -4, 291, -1771, 7388, 12186, -2074, 371, -3, + -5, 289, -1758, 7308, 12251, -2068, 370, -3, + -5, 286, -1745, 7229, 12316, -2062, 369, -3, + -5, 283, -1732, 7149, 12381, -2055, 367, -3, + -6, 280, -1719, 7070, 12445, -2048, 366, -4, + -6, 277, -1706, 6990, 12509, -2040, 364, -4, + -6, 274, -1693, 6911, 12572, -2032, 362, -4, + -7, 271, -1679, 6831, 12634, -2024, 360, -4, + -7, 268, -1665, 6752, 12696, -2015, 358, -4, + -7, 265, -1651, 6673, 12758, -2005, 356, -4, + -8, 262, -1637, 6594, 12819, -1996, 354, -4, + -8, 259, -1623, 6514, 12880, -1986, 352, -4, + -8, 256, -1609, 6435, 12940, -1975, 349, -5, + -9, 253, -1595, 6356, 13000, -1964, 347, -5, + -9, 250, -1580, 6277, 13059, -1952, 344, -5, + -10, 247, -1566, 6199, 13118, -1940, 341, -5, + -10, 244, -1551, 6120, 13176, -1928, 338, -5, + -10, 241, -1536, 6041, 13233, -1915, 335, -5, + -11, 238, -1521, 5963, 13290, -1902, 332, -5, + -11, 235, -1506, 5885, 13347, -1888, 329, -5, + -12, 232, -1491, 5806, 13402, -1874, 326, -5, + -12, 229, -1476, 5728, 13458, -1859, 322, -5, + -13, 225, -1461, 5651, 13512, -1844, 319, -5, + -13, 222, -1445, 5573, 13567, -1829, 315, -5, + -13, 219, -1430, 5495, 13620, -1813, 311, -6, + -14, 216, -1414, 5418, 13673, -1796, 307, -6, + -14, 213, -1399, 5340, 13726, -1779, 303, -6, + -15, 210, -1383, 5263, 13777, -1762, 299, -6, + -15, 207, -1368, 5186, 13829, -1744, 294, -6, + -16, 204, -1352, 5110, 13879, -1725, 290, -6, + -16, 201, -1336, 5033, 13929, -1706, 285, -6, + -17, 198, -1320, 4957, 13978, -1687, 280, -6, + -17, 195, -1304, 4881, 14027, -1667, 276, -5, + -18, 192, -1288, 4805, 14075, -1647, 271, -5, + -18, 189, -1272, 4729, 14123, -1626, 265, -5, + -19, 186, -1256, 4653, 14169, -1604, 260, -5, + -19, 183, -1240, 4578, 14216, -1583, 255, -5, + -20, 180, -1224, 4503, 14261, -1560, 249, -5, + -20, 177, -1208, 4428, 14306, -1537, 244, -5, + -21, 174, -1192, 4354, 14350, -1514, 238, -5, + -21, 171, -1175, 4279, 14394, -1490, 232, -5, + -22, 168, -1159, 4205, 14437, -1466, 226, -4, + -22, 165, -1143, 4131, 14479, -1441, 220, -4, + -22, 162, -1127, 4058, 14520, -1416, 213, -4, + -23, 159, -1110, 3985, 14561, -1390, 207, -4, + -23, 156, -1094, 3912, 14601, -1363, 200, -4, + -24, 153, -1078, 3839, 14640, -1337, 193, -3, + -24, 150, -1062, 3766, 14679, -1309, 187, -3, + -25, 147, -1045, 3694, 14717, -1281, 179, -3, + -25, 144, -1029, 3622, 14754, -1253, 172, -3, + -26, 142, -1013, 3551, 14791, -1224, 165, -2, + -26, 139, -996, 3480, 14827, -1194, 158, -2, + -27, 136, -980, 3409, 14862, -1164, 150, -1, + -27, 133, -964, 3338, 14896, -1134, 142, -1, + -28, 131, -948, 3268, 14930, -1103, 135, -1, + -28, 128, -931, 3198, 14963, -1071, 127, 0, + -29, 125, -915, 3128, 14995, -1039, 118, 0, + -29, 122, -899, 3059, 15027, -1007, 110, 1, + -29, 120, -883, 2990, 15057, -974, 102, 1, + -30, 117, -867, 2921, 15087, -940, 93, 2, + -30, 115, -851, 2853, 15117, -906, 85, 2, + -31, 112, -835, 2785, 15145, -871, 76, 3, + -31, 109, -819, 2718, 15173, -836, 67, 3, + -32, 107, -803, 2651, 15200, -801, 58, 4, + -32, 104, -787, 2584, 15226, -764, 49, 5, + -32, 102, -771, 2517, 15251, -728, 39, 5, + -33, 99, -755, 2451, 15276, -690, 30, 6, + -33, 97, -740, 2386, 15300, -653, 20, 7, + -33, 95, -724, 2321, 15323, -614, 10, 7, + -34, 92, -708, 2256, 15345, -576, 1, 8, + -34, 90, -693, 2191, 15366, -536, -9, 9, + -34, 88, -677, 2127, 15387, -496, -20, 10, + -35, 85, -662, 2063, 15407, -456, -30, 11, + -35, 83, -646, 2000, 15426, -415, -40, 11, + -35, 81, -631, 1937, 15445, -374, -51, 12, + -36, 79, -616, 1875, 15462, -332, -62, 13, + -36, 76, -600, 1813, 15479, -289, -72, 14, + -36, 74, -585, 1751, 15495, -247, -83, 15, + -36, 72, -570, 1690, 15510, -203, -94, 16, + -37, 70, -555, 1629, 15524, -159, -106, 17, + -37, 68, -540, 1569, 15538, -115, -117, 18, + -37, 66, -526, 1509, 15550, -70, -128, 19, + -37, 64, -511, 1450, 15562, -24, -140, 21, + -37, 62, -496, 1391, 15573, 22, -152, 22, + -38, 60, -482, 1332, 15583, 68, -164, 23, + -38, 58, -467, 1274, 15593, 115, -175, 24, + -38, 56, -453, 1216, 15601, 163, -188, 25, + -38, 54, -439, 1159, 15609, 211, -200, 27, + -38, 53, -425, 1103, 15616, 259, -212, 28, + -38, 51, -410, 1046, 15622, 309, -224, 29, + -38, 49, -396, 990, 15628, 358, -237, 31, + -38, 47, -383, 935, 15632, 408, -250, 32, + -38, 46, -369, 880, 15636, 459, -263, 33, + -38, 44, -355, 826, 15639, 510, -275, 35, + -38, 42, -342, 772, 15641, 561, -288, 36, + -38, 41, -328, 718, 15642, 613, -302, 38, +}; + +inline int Spc_Dsp::interpolate( voice_t const* v ) +{ + // Make pointers into gaussian based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_cubic( voice_t const* v ) +{ + // Make pointers into cubic based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = cubic + offset; + short const* rev = cubic + 256 - offset; // mirror left half of cubic + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = fwd [ 0] * in [0]; + out += fwd [257] * in [1]; + out += rev [257] * in [2]; + out += rev [ 0] * in [3]; + out >>= 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_sinc( voice_t const* v ) +{ + // Make pointers into cubic based on fractional position between samples + int offset = v->interp_pos & 0xFF0; + short const* filt = (short const*) (((char const*)sinc) + offset); + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = filt [0] * in [0]; + out += filt [1] * in [1]; + out += filt [2] * in [2]; + out += filt [3] * in [3]; + out += filt [4] * in [4]; + out += filt [5] * in [5]; + out += filt [6] * in [6]; + out += filt [7] * in [7]; + out >>= 14; + + CLAMP16( out ); + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_linear( voice_t const* v ) +{ + int fract = v->interp_pos & 0xFFF; + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (0x1000 - fract) * in [0]; + out += fract * in [1]; + out >>= 12; + + // no need to clamp + out &= ~1; + return out; +} + +inline int Spc_Dsp::interpolate_nearest( voice_t const* v ) +{ + return v->buf [(v->interp_pos >> 12) + v->buf_pos] & ~1; +} + +//// Counters + +int const simple_counter_range = 2048 * 5 * 3; // 30720 + +static unsigned const counter_rates [32] = +{ + simple_counter_range + 1, // never fires + 2048, 1536, + 1280, 1024, 768, + 640, 512, 384, + 320, 256, 192, + 160, 128, 96, + 80, 64, 48, + 40, 32, 24, + 20, 16, 12, + 10, 8, 6, + 5, 4, 3, + 2, + 1 +}; + +static unsigned const counter_offsets [32] = +{ + 1, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 0, + 0 +}; + +inline void Spc_Dsp::init_counter() +{ + m.counter = 0; +} + +inline void Spc_Dsp::run_counters() +{ + if ( --m.counter < 0 ) + m.counter = simple_counter_range - 1; +} + +inline unsigned Spc_Dsp::read_counter( int rate ) +{ + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; +} + + +//// Envelope + +inline void Spc_Dsp::run_envelope( voice_t* const v ) +{ + int env = v->env; + if ( v->env_mode == env_release ) // 60% + { + if ( (env -= 0x8) < 0 ) + env = 0; + v->env = env; + } + else + { + int rate; + int env_data = VREG(v->regs,adsr1); + if ( m.t_adsr0 & 0x80 ) // 99% ADSR + { + if ( v->env_mode >= env_decay ) // 99% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + if ( v->env_mode == env_decay ) // 1% + rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (m.t_adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v->regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !read_counter( rate ) ) + v->env = env; // nothing else is controlled by the counter + } +} + + +//// BRR Decoding + +inline void Spc_Dsp::decode_brr( voice_t* v ) +{ + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + int const header = m.t_brr_header; + + // Write to next four samples in circular buffer + int* pos = &v->buf [v->buf_pos]; + int* end; + if ( (v->buf_pos += 4) >= brr_buf_size ) + v->buf_pos = 0; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract nybble and sign-extend + int s = (int16_t) nybbles >> 12; + + // Shift sample based on header + int const shift = header >> 4; + s = (s << shift) >> 1; + if ( shift >= 0xD ) // handle invalid range + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) + + // Apply IIR filter (8 is the most commonly used) + int const filter = header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } +} + + +//// Misc + +#define MISC_CLOCK( n ) inline void Spc_Dsp::misc_##n() + +MISC_CLOCK( 27 ) +{ + m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON +} +MISC_CLOCK( 28 ) +{ + m.t_non = REG(non); + m.t_eon = REG(eon); + m.t_dir = REG(dir); +} +MISC_CLOCK( 29 ) +{ + if ( (m.every_other_sample ^= 1) != 0 ) + m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read +} +MISC_CLOCK( 30 ) +{ + if ( m.every_other_sample ) + { + m.kon = m.new_kon; + m.t_koff = REG(koff) | m.mute_mask; + } + + run_counters(); + + // Noise + if ( !read_counter( REG(flg) & 0x1F ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } +} + + +//// Voices + +#define VOICE_CLOCK( n ) void Spc_Dsp::voice_##n( voice_t* const v ) + +inline VOICE_CLOCK( V1 ) +{ + m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; + m.t_srcn = VREG(v->regs,srcn); +} +inline VOICE_CLOCK( V2 ) +{ + // Read sample pointer (ignored if not needed) + uint8_t const* entry = &m.ram [m.t_dir_addr]; + if ( !v->kon_delay ) + entry += 2; + m.t_brr_next_addr = GET_LE16A( entry ); + + m.t_adsr0 = VREG(v->regs,adsr0); + + // Read pitch, spread over two clocks + m.t_pitch = VREG(v->regs,pitchl); +} +inline VOICE_CLOCK( V3a ) +{ + m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; +} +inline VOICE_CLOCK( V3b ) +{ + // Read BRR header and byte + m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; + m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking +} +VOICE_CLOCK( V3c ) +{ + // Pitch modulation using previous voice's output + if ( m.t_pmon & v->vbit ) + m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; + + if ( v->kon_delay ) + { + // Get ready to start BRR decoding on next sample + if ( v->kon_delay == 5 ) + { + v->brr_addr = m.t_brr_next_addr; + v->brr_offset = 1; + v->buf_pos = 0; + m.t_brr_header = 0; // header is ignored on this sample + m.kon_check = true; + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = 0; + if ( --v->kon_delay & 3 ) + v->interp_pos = 0x4000; + + // Pitch is never added during KON + m.t_pitch = 0; + } + + // Gaussian interpolation + { + int output; + + switch ( m.interpolation_level ) + { + case 0: + default: + output = interpolate( v ); + break; + + case 1: + output = interpolate_cubic( v ); + break; + + case 2: + output = interpolate_sinc( v ); + break; + + case -1: + output = interpolate_linear( v ); + break; + + case -2: + output = interpolate_nearest( v ); + break; + } + + // Noise + if ( m.t_non & v->vbit ) + output = (int16_t) (m.noise * 2); + + // Apply envelope + m.t_output = (output * v->env) >> 11 & ~1; + v->t_envx_out = (uint8_t) (v->env >> 4); + } + + // Immediate silence due to end of sample or soft reset + if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) + { + v->env_mode = env_release; + v->env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & v->vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & v->vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + } + } + + // Run envelope for next sample + if ( !v->kon_delay ) + run_envelope( v ); +} +inline void Spc_Dsp::voice_output( voice_t const* v, int ch ) +{ + // Check surround removal + int vol = (int8_t) VREG(v->regs,voll + ch); + int voln = (int8_t) VREG(v->regs,voll + ch ^ 1); + if ( vol * voln < m.surround_threshold ) + vol ^= vol >> 7; + + // Apply left/right volume + int amp = (m.t_output * vol) >> 7; + + int abs_amp = abs( amp ); + if ( abs_amp > m.max_level[v - (const Spc_Dsp::voice_t *)&m.voices][ch] ) + m.max_level[v - (const Spc_Dsp::voice_t *)&m.voices][ch] = abs_amp; + + // Add to output total + m.t_main_out [ch] += amp; + CLAMP16( m.t_main_out [ch] ); + + // Optionally add to echo total + if ( m.t_eon & v->vbit ) + { + m.t_echo_out [ch] += amp; + CLAMP16( m.t_echo_out [ch] ); + } +} +VOICE_CLOCK( V4 ) +{ + // Decode BRR + m.t_looped = 0; + if ( v->interp_pos >= 0x4000 ) + { + decode_brr( v ); + + if ( (v->brr_offset += 2) >= brr_block_size ) + { + // Start decoding next BRR block + assert( v->brr_offset == brr_block_size ); + v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + if ( m.t_brr_header & 1 ) + { + v->brr_addr = m.t_brr_next_addr; + m.t_looped = v->vbit; + } + v->brr_offset = 1; + } + } + + // Apply pitch + v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; + + // Keep from getting too far ahead (when using pitch modulation) + if ( v->interp_pos > 0x7FFF ) + v->interp_pos = 0x7FFF; + + // Output left + voice_output( v, 0 ); +} +inline VOICE_CLOCK( V5 ) +{ + // Output right + voice_output( v, 1 ); + + // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier + int endx_buf = REG(endx) | m.t_looped; + + // Clear bit in ENDX if KON just began + if ( v->kon_delay == 5 ) + endx_buf &= ~v->vbit; + m.endx_buf = (uint8_t) endx_buf; +} +inline VOICE_CLOCK( V6 ) +{ + (void) v; // avoid compiler warning about unused v + m.outx_buf = (uint8_t) (m.t_output >> 8); +} +inline VOICE_CLOCK( V7 ) +{ + // Update ENDX + REG(endx) = m.endx_buf; + + m.envx_buf = v->t_envx_out; +} +inline VOICE_CLOCK( V8 ) +{ + // Update OUTX + VREG(v->regs,outx) = m.outx_buf; +} +inline VOICE_CLOCK( V9 ) +{ + // Update ENVX + VREG(v->regs,envx) = m.envx_buf; +} + +// Most voices do all these in one clock, so make a handy composite +inline VOICE_CLOCK( V3 ) +{ + voice_V3a( v ); + voice_V3b( v ); + voice_V3c( v ); +} + +// Common combinations of voice steps on different voices. This greatly reduces +// code size and allows everything to be inlined in these functions. +VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } +VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } +VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } + + +//// Echo + +// Current echo buffer pointer for left/right channel +#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) + +// Sample in echo history buffer, where 0 is the oldest +#define ECHO_FIR( i ) (m.echo_hist_pos [i]) + +// Calculate FIR point for left/right channel +#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) + +#define ECHO_CLOCK( n ) inline void Spc_Dsp::echo_##n() + +inline void Spc_Dsp::echo_read( int ch ) +{ + int s = GET_LE16SA( ECHO_PTR( ch ) ); + // second copy simplifies wrap-around handling + ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; +} + +ECHO_CLOCK( 22 ) +{ + // History + if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + m.echo_hist_pos = m.echo_hist; + + m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; + echo_read( 0 ); + + // FIR (using l and r temporaries below helps compiler optimize) + int l = CALC_FIR( 0, 0 ); + int r = CALC_FIR( 0, 1 ); + + m.t_echo_in [0] = l; + m.t_echo_in [1] = r; +} +ECHO_CLOCK( 23 ) +{ + int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); + int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; + + echo_read( 1 ); +} +ECHO_CLOCK( 24 ) +{ + int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); + int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; +} +ECHO_CLOCK( 25 ) +{ + int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); + int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); + + l = (int16_t) l; + r = (int16_t) r; + + l += (int16_t) CALC_FIR( 7, 0 ); + r += (int16_t) CALC_FIR( 7, 1 ); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_in [0] = l & ~1; + m.t_echo_in [1] = r & ~1; +} +inline int Spc_Dsp::echo_output( int ch ) +{ + // Check surround removal + int vol = (int8_t) REG(mvoll + ch * 0x10); + int voln = (int8_t) REG(mvoll + ch * 0x10 ^ 0x10); + if ( vol * voln < m.surround_threshold ) + vol ^= vol >> 7; + + int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) + + (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); + CLAMP16( out ); + return out; +} +ECHO_CLOCK( 26 ) +{ + // Left output volumes + // (save sample for next clock so we can output both together) + m.t_main_out [0] = echo_output( 0 ); + + // Echo feedback + int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); + int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_out [0] = l & ~1; + m.t_echo_out [1] = r & ~1; +} +ECHO_CLOCK( 27 ) +{ + // Output + int l = m.t_main_out [0]; + int r = echo_output( 1 ); + m.t_main_out [0] = 0; + m.t_main_out [1] = 0; + + // TODO: global muting isn't this simple (turns DAC on and off + // or something, causing small ~37-sample pulse when first muted) + if ( REG(flg) & 0x40 ) + { + l = 0; + r = 0; + } + + // Output sample to DAC + #ifdef SPC_DSP_OUT_HOOK + SPC_DSP_OUT_HOOK( l, r ); + #else + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + #endif +} +ECHO_CLOCK( 28 ) +{ + m.t_echo_enabled = REG(flg); +} +inline void Spc_Dsp::echo_write( int ch ) +{ + if ( !(m.t_echo_enabled & 0x20) ) + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); + m.t_echo_out [ch] = 0; +} +ECHO_CLOCK( 29 ) +{ + m.t_esa = REG(esa); + + if ( !m.echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + + m.echo_offset += 4; + if ( m.echo_offset >= m.echo_length ) + m.echo_offset = 0; + + // Write left echo + echo_write( 0 ); + + m.t_echo_enabled = REG(flg); +} +ECHO_CLOCK( 30 ) +{ + // Write right echo + echo_write( 1 ); +} + + +//// Timing + +// Execute clock for a particular voice +#define V( clock, voice ) voice_##clock( &m.voices [voice] ); + +/* The most common sequence of clocks uses composite operations +for efficiency. For example, the following are equivalent to the +individual steps on the right: + +V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) +V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) +V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ + +// Voice 0 1 2 3 4 5 6 7 +#define GEN_DSP_TIMING \ +PHASE( 0) V(V5,0)V(V2,1)\ +PHASE( 1) V(V6,0)V(V3,1)\ +PHASE( 2) V(V7_V4_V1,0)\ +PHASE( 3) V(V8_V5_V2,0)\ +PHASE( 4) V(V9_V6_V3,0)\ +PHASE( 5) V(V7_V4_V1,1)\ +PHASE( 6) V(V8_V5_V2,1)\ +PHASE( 7) V(V9_V6_V3,1)\ +PHASE( 8) V(V7_V4_V1,2)\ +PHASE( 9) V(V8_V5_V2,2)\ +PHASE(10) V(V9_V6_V3,2)\ +PHASE(11) V(V7_V4_V1,3)\ +PHASE(12) V(V8_V5_V2,3)\ +PHASE(13) V(V9_V6_V3,3)\ +PHASE(14) V(V7_V4_V1,4)\ +PHASE(15) V(V8_V5_V2,4)\ +PHASE(16) V(V9_V6_V3,4)\ +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ +PHASE(18) V(V8_V5_V2,5)\ +PHASE(19) V(V9_V6_V3,5)\ +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ +PHASE(23) V(V7,7) echo_23();\ +PHASE(24) V(V8,7) echo_24();\ +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ +PHASE(26) echo_26();\ +PHASE(27) misc_27(); echo_27();\ +PHASE(28) misc_28(); echo_28();\ +PHASE(29) misc_29(); echo_29();\ +PHASE(30) misc_30();V(V3c,0) echo_30();\ +PHASE(31) V(V4,0) V(V1,2)\ + +#if !SPC_DSP_CUSTOM_RUN + +void Spc_Dsp::run( int clocks_remain ) +{ + require( clocks_remain > 0 ); + + int const phase = m.phase; + m.phase = (phase + clocks_remain) & 31; + switch ( phase ) + { + loop: + + #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: + GEN_DSP_TIMING + #undef PHASE + + if ( --clocks_remain ) + goto loop; + } +} + +#endif + + +//// Setup + +void Spc_Dsp::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + interpolation_level( 0 ); + set_output( 0, 0 ); + reset(); + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void Spc_Dsp::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void Spc_Dsp::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void Spc_Dsp::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + for ( int i = voice_count; --i >= 0; ) + { + voice_t* v = &m.voices [i]; + v->brr_offset = 1; + v->vbit = 1 << i; + v->regs = &m.regs [i * 0x10]; + } + m.new_kon = REG(kon); + m.t_dir = REG(dir); + m.t_esa = REG(esa); + + soft_reset_common(); +} + +void Spc_Dsp::reset() { load( initial_regs ); } + + +//// State save/load + +#if !SPC_NO_COPY_STATE_FUNCS + +void SPC_State_Copier::copy( void* state, size_t size ) +{ + func( buf, state, size ); +} + +int SPC_State_Copier::copy_int( int state, int size ) +{ + BOOST::uint8_t s [2]; + SET_LE16( s, state ); + func( buf, &s, size ); + return GET_LE16( s ); +} + +void SPC_State_Copier::skip( int count ) +{ + if ( count > 0 ) + { + char temp [64]; + memset( temp, 0, sizeof temp ); + do + { + int n = sizeof temp; + if ( n > count ) + n = count; + count -= n; + func( buf, temp, n ); + } + while ( count ); + } +} + +void SPC_State_Copier::extra() +{ + int n = 0; + SPC_State_Copier& copier = *this; + SPC_COPY( uint8_t, n ); + skip( n ); +} + +void Spc_Dsp::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // DSP registers + copier.copy( m.regs, register_count ); + + // Internal state + + // Voices + int i; + for ( i = 0; i < voice_count; i++ ) + { + voice_t* v = &m.voices [i]; + + // BRR buffer + int i; + for ( i = 0; i < brr_buf_size; i++ ) + { + int s = v->buf [i]; + SPC_COPY( int16_t, s ); + v->buf [i] = v->buf [i + brr_buf_size] = s; + } + + SPC_COPY( uint16_t, v->interp_pos ); + SPC_COPY( uint16_t, v->brr_addr ); + SPC_COPY( uint16_t, v->env ); + SPC_COPY( int16_t, v->hidden_env ); + SPC_COPY( uint8_t, v->buf_pos ); + SPC_COPY( uint8_t, v->brr_offset ); + SPC_COPY( uint8_t, v->kon_delay ); + { + int m = v->env_mode; + SPC_COPY( uint8_t, m ); + v->env_mode = (enum env_mode_t) m; + } + SPC_COPY( uint8_t, v->t_envx_out ); + + copier.extra(); + } + + // Echo history + for ( i = 0; i < echo_hist_size; i++ ) + { + int j; + for ( j = 0; j < 2; j++ ) + { + int s = m.echo_hist_pos [i] [j]; + SPC_COPY( int16_t, s ); + m.echo_hist [i] [j] = s; // write back at offset 0 + } + } + m.echo_hist_pos = m.echo_hist; + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); + + // Misc + SPC_COPY( uint8_t, m.every_other_sample ); + SPC_COPY( uint8_t, m.kon ); + + SPC_COPY( uint16_t, m.noise ); + SPC_COPY( uint16_t, m.counter ); + SPC_COPY( uint16_t, m.echo_offset ); + SPC_COPY( uint16_t, m.echo_length ); + SPC_COPY( uint8_t, m.phase ); + + SPC_COPY( uint8_t, m.new_kon ); + SPC_COPY( uint8_t, m.endx_buf ); + SPC_COPY( uint8_t, m.envx_buf ); + SPC_COPY( uint8_t, m.outx_buf ); + + SPC_COPY( uint8_t, m.t_pmon ); + SPC_COPY( uint8_t, m.t_non ); + SPC_COPY( uint8_t, m.t_eon ); + SPC_COPY( uint8_t, m.t_dir ); + SPC_COPY( uint8_t, m.t_koff ); + + SPC_COPY( uint16_t, m.t_brr_next_addr ); + SPC_COPY( uint8_t, m.t_adsr0 ); + SPC_COPY( uint8_t, m.t_brr_header ); + SPC_COPY( uint8_t, m.t_brr_byte ); + SPC_COPY( uint8_t, m.t_srcn ); + SPC_COPY( uint8_t, m.t_esa ); + SPC_COPY( uint8_t, m.t_echo_enabled ); + + SPC_COPY( int16_t, m.t_main_out [0] ); + SPC_COPY( int16_t, m.t_main_out [1] ); + SPC_COPY( int16_t, m.t_echo_out [0] ); + SPC_COPY( int16_t, m.t_echo_out [1] ); + SPC_COPY( int16_t, m.t_echo_in [0] ); + SPC_COPY( int16_t, m.t_echo_in [1] ); + + SPC_COPY( uint16_t, m.t_dir_addr ); + SPC_COPY( uint16_t, m.t_pitch ); + SPC_COPY( int16_t, m.t_output ); + SPC_COPY( uint16_t, m.t_echo_ptr ); + SPC_COPY( uint8_t, m.t_looped ); + + copier.extra(); +} +#endif diff --git a/Frameworks/GME/gme/Spc_Dsp.h b/Frameworks/GME/gme/Spc_Dsp.h old mode 100755 new mode 100644 index 36492275d..d53c5ebe7 --- a/Frameworks/GME/gme/Spc_Dsp.h +++ b/Frameworks/GME/gme/Spc_Dsp.h @@ -1,152 +1,341 @@ -// Super Nintendo (SNES) SPC DSP emulator - -// Game_Music_Emu 0.5.2 -#ifndef SPC_DSP_H -#define SPC_DSP_H - -#include "blargg_common.h" - -class Spc_Dsp { - typedef BOOST::int8_t int8_t; - typedef BOOST::uint8_t uint8_t; -public: - - // Keeps pointer to 64K ram - Spc_Dsp( uint8_t* ram ); - - // Mute voice n if bit n (1 << n) of mask is clear. - enum { voice_count = 8 }; - void mute_voices( int mask ); - - // Clear state and silence everything. - void reset(); - - // Set gain, where 1.0 is normal. When greater than 1.0, output is clamped to - // the 16-bit sample range. - void set_gain( double ); - - // If true, prevent channels and global volumes from being phase-negated - void disable_surround( bool disable ); - - // Read/write register 'n', where n ranges from 0 to register_count - 1. - enum { register_count = 128 }; - int read ( int n ); - void write( int n, int ); - - // Run DSP for 'count' samples. Write resulting samples to 'buf' if not NULL. - void run( long count, short* buf = NULL ); - - -// End of public interface -private: - - struct raw_voice_t { - int8_t left_vol; - int8_t right_vol; - uint8_t rate [2]; - uint8_t waveform; - uint8_t adsr [2]; // envelope rates for attack, decay, and sustain - uint8_t gain; // envelope gain (if not using ADSR) - int8_t envx; // current envelope level - int8_t outx; // current sample - int8_t unused [6]; - }; - - struct globals_t { - int8_t unused1 [12]; - int8_t left_volume; // 0C Main Volume Left (-.7) - int8_t echo_feedback; // 0D Echo Feedback (-.7) - int8_t unused2 [14]; - int8_t right_volume; // 1C Main Volume Right (-.7) - int8_t unused3 [15]; - int8_t left_echo_volume; // 2C Echo Volume Left (-.7) - uint8_t pitch_mods; // 2D Pitch Modulation on/off for each voice - int8_t unused4 [14]; - int8_t right_echo_volume; // 3C Echo Volume Right (-.7) - uint8_t noise_enables; // 3D Noise output on/off for each voice - int8_t unused5 [14]; - uint8_t key_ons; // 4C Key On for each voice - uint8_t echo_ons; // 4D Echo on/off for each voice - int8_t unused6 [14]; - uint8_t key_offs; // 5C key off for each voice (instantiates release mode) - uint8_t wave_page; // 5D source directory (wave table offsets) - int8_t unused7 [14]; - uint8_t flags; // 6C flags and noise freq - uint8_t echo_page; // 6D - int8_t unused8 [14]; - uint8_t wave_ended; // 7C - uint8_t echo_delay; // 7D ms >> 4 - char unused9 [2]; - }; - - union { - raw_voice_t voice [voice_count]; - uint8_t reg [register_count]; - globals_t g; - }; - - uint8_t* const ram; - - // Cache of echo FIR values for faster access - short fir_coeff [voice_count]; - - // fir_buf [i + 8] == fir_buf [i], to avoid wrap checking in FIR code - short fir_buf [16] [2]; - int fir_offset; // (0 to 7) - - enum { emu_gain_bits = 8 }; - int emu_gain; - - int keyed_on; // 8-bits for 8 voices - int keys; - - int echo_ptr; - int noise_amp; - int noise; - int noise_count; - - int surround_threshold; - - static BOOST::int16_t const gauss []; - - enum state_t { - state_attack, - state_decay, - state_sustain, - state_release - }; - - struct voice_t { - short volume [2]; - short fraction;// 12-bit fractional position - short interp3; // most recent four decoded samples - short interp2; - short interp1; - short interp0; - short block_remain; // number of nybbles remaining in current block - unsigned short addr; - short block_header; // header byte from current block - short envcnt; - short envx; - short on_cnt; - short enabled; // 7 if enabled, 31 if disabled - short envstate; - short unused; // pad to power of 2 - }; - - voice_t voice_state [voice_count]; - - int clock_envelope( int ); -}; - -inline void Spc_Dsp::disable_surround( bool disable ) { surround_threshold = disable ? 0 : -0x7FFF; } - -inline void Spc_Dsp::set_gain( double v ) { emu_gain = (int) (v * (1 << emu_gain_bits)); } - -inline int Spc_Dsp::read( int i ) -{ - assert( (unsigned) i < register_count ); - return reg [i]; -} - -#endif +// Highly accurate SNES SPC-700 DSP emulator + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } + +class Sfm_Emu; + +class Spc_Dsp { + friend class Sfm_Emu; + +public: + typedef BOOST::uint8_t uint8_t; + +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call run() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + + // Saves/loads exact emulator state + enum { state_size = 640 }; // maximum space needed when saving + typedef dsp_copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Returns non-zero if new key-on events occurred since last call + bool check_kon(); + +// DSP register addresses + + // Global registers + enum { + r_mvoll = 0x0C, r_mvolr = 0x1C, + r_evoll = 0x2C, r_evolr = 0x3C, + r_kon = 0x4C, r_koff = 0x5C, + r_flg = 0x6C, r_endx = 0x7C, + r_efb = 0x0D, r_pmon = 0x2D, + r_non = 0x3D, r_eon = 0x4D, + r_dir = 0x5D, r_esa = 0x6D, + r_edl = 0x7D, + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F + }; + + // Voice registers + enum { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } + void disable_surround( bool disable = true ); + void interpolation_level( int level = 0 ) { m.interpolation_level = level; } +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + enum { echo_hist_size = 8 }; + + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum { brr_buf_size = 12 }; + struct voice_t + { + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) + int buf_pos; // place in buffer where next samples will be decoded + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) + int brr_addr; // address of current BRR block + int brr_offset; // current decoding offset in BRR block + uint8_t* regs; // pointer to voice's DSP registers + int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. + int kon_delay; // KON delay/current setup phase + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + uint8_t t_envx_out; + }; + + // kill me now + const voice_t * get_voice( int ch ) const; + int get_max_level( int v, int ch ) const; +private: + enum { brr_block_size = 9 }; + + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + int every_other_sample; // toggles every sample + int kon; // KON value when last checked + int noise; + int counter; + int echo_offset; // offset from ESA in echo buffer + int echo_length; // number of bytes that echo_offset will stop at + int phase; // next clock cycle to run (0-31) + bool kon_check; // set when a new KON occurs + + // Hidden registers also written to when main register is written to + int new_kon; + uint8_t endx_buf; + uint8_t envx_buf; + uint8_t outx_buf; + + // Temporary state between clocks + + // read once per sample + int t_pmon; + int t_non; + int t_eon; + int t_dir; + int t_koff; + + // read a few clocks ahead then used + int t_brr_next_addr; + int t_adsr0; + int t_brr_header; + int t_brr_byte; + int t_srcn; + int t_esa; + int t_echo_enabled; + + // internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + // left/right sums + int t_main_out [2]; + int t_echo_out [2]; + int t_echo_in [2]; + + voice_t voices [voice_count]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + int surround_threshold; + int interpolation_level; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + + int max_level[voice_count][2]; + }; + state_t m; + + void init_counter(); + void run_counters(); + unsigned read_counter( int rate ); + + int interpolate( voice_t const* v ); + int interpolate_cubic( voice_t const* v ); + int interpolate_sinc( voice_t const* v ); + int interpolate_linear( voice_t const* v ); + int interpolate_nearest( voice_t const* v ); + void run_envelope( voice_t* const v ); + void decode_brr( voice_t* v ); + + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + void voice_output( voice_t const* v, int ch ); + void voice_V1( voice_t* const ); + void voice_V2( voice_t* const ); + void voice_V3( voice_t* const ); + void voice_V3a( voice_t* const ); + void voice_V3b( voice_t* const ); + void voice_V3c( voice_t* const ); + void voice_V4( voice_t* const ); + void voice_V5( voice_t* const ); + void voice_V6( voice_t* const ); + void voice_V7( voice_t* const ); + void voice_V8( voice_t* const ); + void voice_V9( voice_t* const ); + void voice_V7_V4_V1( voice_t* const ); + void voice_V8_V5_V2( voice_t* const ); + void voice_V9_V6_V3( voice_t* const ); + + void echo_read( int ch ); + int echo_output( int ch ); + void echo_write( int ch ); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + void soft_reset_common(); +}; + +#include + +inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; } + +inline int Spc_Dsp::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void Spc_Dsp::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + switch ( addr & 0x0F ) + { + case v_envx: + m.envx_buf = (uint8_t) data; + break; + + case v_outx: + m.outx_buf = (uint8_t) data; + break; + + case 0x0C: + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + { + m.endx_buf = 0; + m.regs [r_endx] = 0; + } + break; + } +} + +inline void Spc_Dsp::mute_voices( int mask ) { m.mute_mask = mask; } + +inline void Spc_Dsp::disable_surround( bool disable ) +{ + m.surround_threshold = disable ? 0 : -0x4000; +} + +inline const Spc_Dsp::voice_t * Spc_Dsp::get_voice( int ch ) const +{ + assert( (unsigned) ch < voice_count ); + return &m.voices[ ch ]; +} + +inline int Spc_Dsp::get_max_level( int v, int ch ) const +{ + assert( (unsigned) v < voice_count ); + assert( (unsigned) ch < 2 ); + int ret = m.max_level[ v ][ ch ]; + (( Spc_Dsp * )this)->m.max_level[ v ][ ch ] = 0; + return ret; +} + +inline bool Spc_Dsp::check_kon() +{ + bool old = m.kon_check; + m.kon_check = 0; + return old; +} + +#if !SPC_NO_COPY_STATE_FUNCS + +class SPC_State_Copier { + Spc_Dsp::copy_func_t func; + unsigned char** buf; +public: + SPC_State_Copier( unsigned char** p, Spc_Dsp::copy_func_t f ) { func = f; buf = p; } + void copy( void* state, size_t size ); + int copy_int( int state, int size ); + void skip( int count ); + void extra(); +}; + +#define SPC_COPY( type, state )\ +{\ + state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ + assert( (BOOST::type) state == state );\ +} + +#endif + +#endif diff --git a/Frameworks/GME/gme/Spc_Emu.cpp b/Frameworks/GME/gme/Spc_Emu.cpp old mode 100755 new mode 100644 index 22be9e2ad..330e9d758 --- a/Frameworks/GME/gme/Spc_Emu.cpp +++ b/Frameworks/GME/gme/Spc_Emu.cpp @@ -1,326 +1,423 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Spc_Emu.h" - -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -Spc_Emu::Spc_Emu() -{ - set_type( gme_spc_type ); - - static const char* const names [Snes_Spc::voice_count] = { - "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" - }; - set_voice_names( names ); - - set_gain( 1.4 ); -} - -Spc_Emu::~Spc_Emu() { } - -// Track info - -long const trailer_offset = 0x10200; - -byte const* Spc_Emu::trailer() const { return &file_data [min( file_size, trailer_offset )]; } - -long Spc_Emu::trailer_size() const { return max( 0L, file_size - trailer_offset ); } - -static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) -{ - // header - byte const* end = begin + size; - if ( size < 8 || memcmp( begin, "xid6", 4 ) ) - { - check( false ); - return; - } - long info_size = get_le32( begin + 4 ); - byte const* in = begin + 8; - if ( end - in > info_size ) - { - dprintf( "Extra data after SPC xid6 info\n" ); - end = in + info_size; - } - - int year = 0; - char copyright [256 + 5]; - int copyright_len = 0; - int const year_len = 5; - - while ( end - in >= 4 ) - { - // header - int id = in [0]; - int data = in [3] * 0x100 + in [2]; - int type = in [1]; - int len = type ? data : 0; - in += 4; - if ( len > end - in ) - { - check( false ); - break; // block goes past end of data - } - - // handle specific block types - char* field = 0; - switch ( id ) - { - case 0x01: field = out->song; break; - case 0x02: field = out->game; break; - case 0x03: field = out->author; break; - case 0x04: field = out->dumper; break; - case 0x07: field = out->comment; break; - case 0x14: year = data; break; - - //case 0x30: // intro length - // Many SPCs have intro length set wrong for looped tracks, making it useless - /* - case 0x30: - check( len == 4 ); - if ( len >= 4 ) - { - out->intro_length = get_le32( in ) / 64; - if ( out->length > 0 ) - { - long loop = out->length - out->intro_length; - if ( loop >= 2000 ) - out->loop_length = loop; - } - } - break; - */ - - case 0x13: - copyright_len = min( len, (int) sizeof copyright - year_len ); - memcpy( ©right [year_len], in, copyright_len ); - break; - - default: - if ( id < 0x01 || (id > 0x07 && id < 0x10) || - (id > 0x14 && id < 0x30) || id > 0x36 ) - dprintf( "Unknown SPC xid6 block: %X\n", (int) id ); - break; - } - if ( field ) - { - check( type == 1 ); - Gme_File::copy_field_( field, (char const*) in, len ); - } - - // skip to next block - in += len; - - // blocks are supposed to be 4-byte aligned with zero-padding... - byte const* unaligned = in; - while ( (in - begin) & 3 && in < end ) - { - if ( *in++ != 0 ) - { - // ...but some files have no padding - in = unaligned; - dprintf( "SPC info tag wasn't properly padded to align\n" ); - break; - } - } - } - - char* p = ©right [year_len]; - if ( year ) - { - *--p = ' '; - for ( int n = 4; n--; ) - { - *--p = char (year % 10 + '0'); - year /= 10; - } - copyright_len += year_len; - } - if ( copyright_len ) - Gme_File::copy_field_( out->copyright, p, copyright_len ); - - check( in == end ); -} - -static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid6_size, - track_info_t* out ) -{ - // decode length (can be in text or binary format, sometimes ambiguous ugh) - long len_secs = 0; - for ( int i = 0; i < 3; i++ ) - { - unsigned n = h.len_secs [i] - '0'; - if ( n > 9 ) - { - // ignore single-digit text lengths - // (except if author field is present and begins at offset 1, ugh) - if ( i == 1 && (h.author [0] || !h.author [1]) ) - len_secs = 0; - break; - } - len_secs *= 10; - len_secs += n; - } - if ( !len_secs || len_secs > 0x1FFF ) - len_secs = get_le16( h.len_secs ); - if ( len_secs < 0x1FFF ) - out->length = len_secs * 1000; - - int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); - Gme_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset ); - - GME_COPY_FIELD( h, out, song ); - GME_COPY_FIELD( h, out, game ); - GME_COPY_FIELD( h, out, dumper ); - GME_COPY_FIELD( h, out, comment ); - - if ( xid6_size ) - get_spc_xid6( xid6, xid6_size, out ); -} - -blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const -{ - get_spc_info( header(), trailer(), trailer_size(), out ); - return 0; -} - -static blargg_err_t check_spc_header( void const* header ) -{ - if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Spc_File : Gme_Info_ -{ - Spc_Emu::header_t header; - blargg_vector xid6; - - Spc_File() { set_type( gme_spc_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - long file_size = in.remain(); - if ( file_size < Snes_Spc::spc_file_size ) - return gme_wrong_file_type; - RETURN_ERR( in.read( &header, Spc_Emu::header_size ) ); - RETURN_ERR( check_spc_header( header.tag ) ); - long const xid6_offset = 0x10200; - long xid6_size = file_size - xid6_offset; - if ( xid6_size > 0 ) - { - RETURN_ERR( xid6.resize( xid6_size ) ); - RETURN_ERR( in.skip( xid6_offset - Spc_Emu::header_size ) ); - RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); - } - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - get_spc_info( header, xid6.begin(), xid6.size(), out ); - return 0; - } -}; - -static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } -static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } - -gme_type_t_ const gme_spc_type [1] = { "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }; - -// Setup - -blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) -{ - apu.set_gain( gain() ); - if ( sample_rate != native_sample_rate ) - { - RETURN_ERR( resampler.buffer_size( native_sample_rate / 20 * 2 ) ); - resampler.time_ratio( (double) native_sample_rate / sample_rate, 0.9965 ); - } - return 0; -} - -void Spc_Emu::mute_voices_( int m ) -{ - Music_Emu::mute_voices_( m ); - apu.mute_voices( m ); -} - -blargg_err_t Spc_Emu::load_mem_( byte const* in, long size ) -{ - assert( offsetof (header_t,unused2 [46]) == header_size ); - file_data = in; - file_size = size; - set_voice_count( Snes_Spc::voice_count ); - if ( size < Snes_Spc::spc_file_size ) - return gme_wrong_file_type; - return check_spc_header( in ); -} - -// Emulation - -void Spc_Emu::set_tempo_( double t ) { apu.set_tempo( t ); } - -blargg_err_t Spc_Emu::start_track_( int track ) -{ - RETURN_ERR( Music_Emu::start_track_( track ) ); - resampler.clear(); - RETURN_ERR( apu.load_spc( file_data, file_size ) ); - apu.clear_echo(); - return 0; -} - -blargg_err_t Spc_Emu::skip_( long count ) -{ - if ( sample_rate() != native_sample_rate ) - { - count = long (count * resampler.ratio()) & ~1; - count -= resampler.skip_input( count ); - } - - // TODO: shouldn't skip be adjusted for the 64 samples read afterwards? - - if ( count > 0 ) - RETURN_ERR( apu.skip( count ) ); - - // eliminate pop due to resampler - const int resampler_latency = 64; - sample_t buf [resampler_latency]; - return play_( resampler_latency, buf ); -} - -blargg_err_t Spc_Emu::play_( long count, sample_t* out ) -{ - if ( sample_rate() == native_sample_rate ) - return apu.play( count, out ); - - long remain = count; - while ( remain > 0 ) - { - remain -= resampler.read( &out [count - remain], remain ); - if ( remain > 0 ) - { - long n = resampler.max_write(); - RETURN_ERR( apu.play( n, resampler.buffer() ) ); - resampler.write( n ); - } - } - check( remain == 0 ); - return 0; -} +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Spc_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2004-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// TODO: support Spc_Filter's bass + +Spc_Emu::Spc_Emu() +{ + set_type( gme_spc_type ); + set_gain( 1.4 ); +} + +Spc_Emu::~Spc_Emu() { } + +// Track info + +int const trailer_offset = 0x10200; + +inline byte const* Spc_Emu::trailer_() const { return &file_begin() [min( file_size(), trailer_offset )]; } + +inline int Spc_Emu::trailer_size_() const { return max( 0, file_size() - trailer_offset ); } + +static void get_spc_xid6( byte const begin [], int size, track_info_t* out ) +{ + // header + byte const* end = begin + size; + if ( size < 8 || memcmp( begin, "xid6", 4 ) ) + { + check( false ); + return; + } + int info_size = get_le32( begin + 4 ); + byte const* in = begin + 8; + if ( end - in > info_size ) + { + dprintf( "SPC: Extra data after xid6\n" ); + end = in + info_size; + } + + int year = 0; + char copyright [256 + 5]; + int copyright_len = 0; + int const year_len = 5; + int disc = 0, track = 0; + + while ( end - in >= 4 ) + { + // header + int id = in [0]; + int data = in [3] * 0x100 + in [2]; + int type = in [1]; + int len = type ? data : 0; + in += 4; + if ( len > end - in ) + { + dprintf( "SPC: xid6 goes past end" ); + break; // block goes past end of data + } + + // handle specific block types + char* field = NULL; + switch ( id ) + { + case 0x01: field = out->song; break; + case 0x02: field = out->game; break; + case 0x03: field = out->author; break; + case 0x04: field = out->dumper; break; + case 0x07: field = out->comment; break; + case 0x10: field = out->ost; break; + case 0x11: disc = data; break; + case 0x12: track = data; break; + case 0x14: year = data; break; + + //case 0x30: // intro length + // Many SPCs have intro length set wrong for looped tracks, making it useless + /* + case 0x30: + check( len == 4 ); + if ( len >= 4 ) + { + out->intro_length = get_le32( in ) / 64; + if ( out->length > 0 ) + { + int loop = out->length - out->intro_length; + if ( loop >= 2000 ) + out->loop_length = loop; + } + } + break; + */ + + case 0x33: + check( len == 4 ); + if ( len >= 4 ) + { + out->fade_length = get_le32( in ) / 64; + } + break; + + case 0x13: + copyright_len = min( len, (int) sizeof copyright - year_len ); + memcpy( ©right [year_len], in, copyright_len ); + break; + + default: + if ( id < 0x01 || (id > 0x07 && id < 0x10) || + (id > 0x14 && id < 0x30) || id > 0x36 ) + dprintf( "SPC: Unknown xid6 block: %X\n", (int) id ); + break; + } + if ( field ) + { + check( type == 1 ); + Gme_File::copy_field_( field, (char const*) in, len ); + } + + // skip to next block + in += len; + + // blocks are supposed to be 4-byte aligned with zero-padding... + byte const* unaligned = in; + while ( (in - begin) & 3 && in < end ) + { + if ( *in++ != 0 ) + { + // ...but some files have no padding + in = unaligned; + //dprintf( "SPC: xid6 info tag wasn't properly padded to align\n" ); + break; + } + } + } + + char* p = ©right [year_len]; + if ( year ) + { + *--p = ' '; + // avoid using bloated printf + for ( int n = 4; n--; ) + { + *--p = char (year % 10 + '0'); + year /= 10; + } + copyright_len += year_len; + } + if ( copyright_len ) + Gme_File::copy_field_( out->copyright, p, copyright_len ); + + if ( disc > 0 && disc <= 9 ) + { + out->disc [0] = disc + '0'; + out->disc [1] = 0; + } + + if ( track > 255 && track < ( ( 100 << 8 ) - 1 ) ) + { + char* p = ©right [3]; + *p = 0; + if ( track & 255 ) *--p = char (track & 255); + track >>= 8; + for ( int n = 2; n-- && track; ) + { + *--p = char (track % 10 + '0'); + track /= 10; + } + memcpy( out->track, p, ©right [4] - p ); + } + + check( in == end ); +} + +static void get_spc_info( Spc_Emu::header_t const& h, byte const xid6 [], int xid6_size, + track_info_t* out ) +{ + // decode length (can be in text or binary format, sometimes ambiguous ugh) + int len_secs = 0; + int i; + for ( i = 0; i < 3; i++ ) + { + unsigned n = h.len_secs [i] - '0'; + if ( n > 9 ) + { + // ignore single-digit text lengths + // (except if author field is present and begins at offset 1, ugh) + if ( i == 1 && (h.author [0] || !h.author [1]) ) + len_secs = 0; + break; + } + len_secs *= 10; + len_secs += n; + } + if ( !len_secs || len_secs > 0x1FFF ) + len_secs = get_le16( h.len_secs ); + if ( len_secs < 0x1FFF ) + out->length = len_secs * 1000; + + long fade_msec = 0; + for ( i = 0; i < 4; i++ ) + { + unsigned n = h.fade_msec [i] - '0'; + if ( n > 9 ) + { + if ( i == 1 && (h.author [0] || !h.author [1]) ) + fade_msec = -1; + break; + } + fade_msec *= 10; + fade_msec += n; + } + if ( i == 4 && unsigned( h.author [0] - '0' ) <= 9 ) + fade_msec = fade_msec * 10 + h.author [0] - '0'; + if ( fade_msec < 0 || fade_msec > 0x7FFF ) + fade_msec = get_le32( h.fade_msec ); + if ( fade_msec < 0x7FFF ) + out->fade_length = fade_msec; + + int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); + Gme_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset ); + + GME_COPY_FIELD( h, out, song ); + GME_COPY_FIELD( h, out, game ); + GME_COPY_FIELD( h, out, dumper ); + GME_COPY_FIELD( h, out, comment ); + + if ( xid6_size ) + get_spc_xid6( xid6, xid6_size, out ); +} + +static void hash_spc_file( Spc_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.format, sizeof(h.format) ); + out.hash_( &h.version, sizeof(h.version) ); + out.hash_( &h.pc[0], sizeof(h.pc) ); + out.hash_( &h.a, sizeof(h.a) ); + out.hash_( &h.x, sizeof(h.x) ); + out.hash_( &h.y, sizeof(h.y) ); + out.hash_( &h.psw, sizeof(h.psw) ); + out.hash_( &h.sp, sizeof(h.sp) ); + out.hash_( &h.unused[0], sizeof(h.unused) ); + out.hash_( &h.emulator, sizeof(h.emulator) ); + out.hash_( &h.unused2[0], sizeof(h.unused2) ); + out.hash_( data, data_size ); +} + +blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const +{ + get_spc_info( header(), trailer_(), trailer_size_(), out ); + return blargg_ok; +} + +static blargg_err_t check_spc_header( void const* header ) +{ + if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) + return blargg_err_file_type; + return blargg_ok; +} + +struct Spc_File : Gme_Info_ +{ + Spc_Emu::header_t header; + blargg_vector data; + blargg_vector xid6; + + Spc_File() { set_type( gme_spc_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + int file_size = in.remain(); + if ( file_size < Snes_Spc::spc_min_file_size ) + return blargg_err_file_type; + RETURN_ERR( in.read( &header, header.size ) ); + RETURN_ERR( check_spc_header( header.tag ) ); + int const xid6_offset = 0x10200; + RETURN_ERR( data.resize( blargg_min( xid6_offset - header.size, file_size - header.size ) ) ); + RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); + int xid6_size = file_size - xid6_offset; + if ( xid6_size > 0 ) + { + RETURN_ERR( xid6.resize( xid6_size ) ); + RETURN_ERR( in.read( xid6.begin(), xid6.size() ) ); + } + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_spc_info( header, xid6.begin(), xid6.size(), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_spc_file( header, data.begin(), data.end() - data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_spc_emu () { return BLARGG_NEW Spc_Emu ; } +static Music_Emu* new_spc_file() { return BLARGG_NEW Spc_File; } + +gme_type_t_ const gme_spc_type [1] = {{ "Super Nintendo", 1, &new_spc_emu, &new_spc_file, "SPC", 0 }}; + +// Setup + +blargg_err_t Spc_Emu::set_sample_rate_( int sample_rate ) +{ + RETURN_ERR( apu.init() ); + if ( sample_rate != native_sample_rate ) + { + RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) ); + RETURN_ERR( resampler.set_rate( (double) native_sample_rate / sample_rate ) ); // 0.9965 rolloff + } + return blargg_ok; +} + +void Spc_Emu::mute_voices_( int m ) +{ + Music_Emu::mute_voices_( m ); + apu.mute_voices( m ); +} + +blargg_err_t Spc_Emu::load_mem_( byte const in [], int size ) +{ + assert( offsetof (header_t,unused2 [46]) == header_t::size ); + set_voice_count( Spc_Dsp::voice_count ); + if ( size < Snes_Spc::spc_min_file_size ) + return blargg_err_file_type; + + static const char* const names [Spc_Dsp::voice_count] = { + "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" + }; + set_voice_names( names ); + + return check_spc_header( in ); +} + +// Emulation + +void Spc_Emu::set_tempo_( double t ) +{ + apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) ); +} + +blargg_err_t Spc_Emu::start_track_( int track ) +{ + RETURN_ERR( Music_Emu::start_track_( track ) ); + resampler.clear(); + filter.clear(); + RETURN_ERR( apu.load_spc( file_begin(), file_size() ) ); + filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) ); + apu.clear_echo( true ); + return blargg_ok; +} + +blargg_err_t Spc_Emu::play_and_filter( int count, sample_t out [] ) +{ + RETURN_ERR( apu.play( count, out ) ); + filter.run( out, count ); + return blargg_ok; +} + +blargg_err_t Spc_Emu::skip_( int count ) +{ + if ( sample_rate() != native_sample_rate ) + { + count = (int) (count * resampler.rate()) & ~1; + count -= resampler.skip_input( count ); + } + + // TODO: shouldn't skip be adjusted for the 64 samples read afterwards? + + if ( count > 0 ) + { + RETURN_ERR( apu.skip( count ) ); + filter.clear(); + } + + // eliminate pop due to resampler + const int resampler_latency = 64; + sample_t buf [resampler_latency]; + return play_( resampler_latency, buf ); +} + +blargg_err_t Spc_Emu::play_( int count, sample_t out [] ) +{ + if ( sample_rate() == native_sample_rate ) + return play_and_filter( count, out ); + + int remain = count; + while ( remain > 0 ) + { + remain -= resampler.read( &out [count - remain], remain ); + if ( remain > 0 ) + { + int n = resampler.buffer_free(); + RETURN_ERR( play_and_filter( n, resampler.buffer() ) ); + resampler.write( n ); + } + } + check( remain == 0 ); + return blargg_ok; +} + +blargg_err_t Spc_Emu::hash_( Hash_Function& out ) const +{ + hash_spc_file( header(), file_begin() + header_t::size, blargg_min( (size_t) ( 0x10200 - header_t::size ), (size_t) ( file_end() - file_begin() - header_t::size ) ), out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Spc_Emu.h b/Frameworks/GME/gme/Spc_Emu.h old mode 100755 new mode 100644 index 44b54c309..02be3fc66 --- a/Frameworks/GME/gme/Spc_Emu.h +++ b/Frameworks/GME/gme/Spc_Emu.h @@ -1,12 +1,20 @@ // Super Nintendo SPC music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef SPC_EMU_H #define SPC_EMU_H -#include "Fir_Resampler.h" #include "Music_Emu.h" #include "Snes_Spc.h" +#include "Spc_Filter.h" + +#if GME_SPC_FAST_RESAMPLER + #include "Upsampler.h" + typedef Upsampler Spc_Emu_Resampler; +#else + #include "Fir_Resampler.h" + typedef Fir_Resampler<24> Spc_Emu_Resampler; +#endif class Spc_Emu : public Music_Emu { public: @@ -14,64 +22,72 @@ public: // handled by resampling the 32kHz output; emulation accuracy is not affected. enum { native_sample_rate = 32000 }; + // Disables annoying pseudo-surround effect some music uses + void disable_surround( bool disable = true ) { apu.disable_surround( disable ); } + + // Enables gaussian, cubic or sinc interpolation + void interpolation_level( int level = 0 ) { apu.interpolation_level( level ); } + + Snes_Spc const* get_apu() const; + Snes_Spc * get_apu(); + // SPC file header - enum { header_size = 0x100 }; struct header_t { - char tag [35]; + enum { size = 0x100 }; + + char tag [35]; byte format; byte version; - byte pc [2]; + byte pc [ 2]; byte a, x, y, psw, sp; - byte unused [2]; - char song [32]; - char game [32]; - char dumper [16]; - char comment [32]; - byte date [11]; - byte len_secs [3]; - byte fade_msec [4]; - char author [32]; // sometimes first char should be skipped (see official SPC spec) + byte unused [ 2]; + char song [32]; + char game [32]; + char dumper [16]; + char comment [32]; + byte date [11]; + byte len_secs [ 3]; + byte fade_msec [ 4]; + char author [32]; // sometimes first char should be skipped (see official SPC spec) byte mute_mask; byte emulator; - byte unused2 [46]; + byte unused2 [46]; }; // Header for currently loaded file - header_t const& header() const { return *(header_t const*) file_data; } - - // Prevents channels and global volumes from being phase-negated - void disable_surround( bool disable = true ); - - static gme_type_t static_type() { return gme_spc_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - byte const* trailer() const; // use track_info() - long trailer_size() const; + header_t const& header() const { return *(header_t const*) file_begin(); } + blargg_err_t hash_( Hash_Function& ) const; + + static gme_type_t static_type() { return gme_spc_type; } + +// Implementation public: Spc_Emu(); ~Spc_Emu(); + protected: - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t set_sample_rate_( long ); - blargg_err_t start_track_( int ); - blargg_err_t play_( long, sample_t* ); - blargg_err_t skip_( long ); - void mute_voices_( int ); - void set_tempo_( double ); + virtual blargg_err_t load_mem_( byte const [], int ); + virtual blargg_err_t track_info_( track_info_t*, int track ) const; + virtual blargg_err_t set_sample_rate_( int ); + virtual blargg_err_t start_track_( int ); + virtual blargg_err_t play_( int, sample_t [] ); + virtual blargg_err_t skip_( int ); + virtual void mute_voices_( int ); + virtual void set_tempo_( double ); + private: - byte const* file_data; - long file_size; - Fir_Resampler<24> resampler; + Spc_Emu_Resampler resampler; + Spc_Filter filter; Snes_Spc apu; + + byte const* trailer_() const; + int trailer_size_() const; + blargg_err_t play_and_filter( int count, sample_t out [] ); }; -inline void Spc_Emu::disable_surround( bool b ) { apu.disable_surround( b ); } +inline Snes_Spc const* Spc_Emu::get_apu() const { return &apu; } +inline Snes_Spc * Spc_Emu::get_apu() { return &apu; } #endif diff --git a/Frameworks/GME/gme/Vgm_Emu.cpp b/Frameworks/GME/gme/Vgm_Emu.cpp old mode 100755 new mode 100644 index 7d037d077..4b8523a18 --- a/Frameworks/GME/gme/Vgm_Emu.cpp +++ b/Frameworks/GME/gme/Vgm_Emu.cpp @@ -1,412 +1,538 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Vgm_Emu.h" - -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow -double const rolloff = 0.990; -double const oversample_factor = 1.5; - -Vgm_Emu::Vgm_Emu() -{ - disable_oversampling_ = false; - psg_rate = 0; - set_type( gme_vgm_type ); - - static int const types [8] = { - wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0 - }; - set_voice_types( types ); - +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Vgm_Emu.h" + +#include "blargg_endian.h" + +/* Copyright (C) 2003-2008 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +// FM emulators are internally quieter to avoid 16-bit overflow +double const fm_gain = 3.0; +double const rolloff = 0.990; +double const oversample_factor = 1.5; + +Vgm_Emu::Vgm_Emu() +{ + resampler.set_callback( play_frame_, this ); + disable_oversampling_ = false; + muted_voices = 0; + set_type( gme_vgm_type ); + set_max_initial_silence( 1 ); set_silence_lookahead( 1 ); // tracks should already be trimmed - - static equalizer_t const eq = { -14.0, 80 }; + + static equalizer_t const eq = { -14.0, 80 , 0,0,0,0,0,0,0,0 }; set_equalizer( eq ); } -Vgm_Emu::~Vgm_Emu() { } - -// Track info - -static byte const* skip_gd3_str( byte const* in, byte const* end ) -{ - while ( end - in >= 2 ) - { - in += 2; - if ( !(in [-2] | in [-1]) ) - break; - } - return in; -} - -static byte const* get_gd3_str( byte const* in, byte const* end, char* field ) -{ - byte const* mid = skip_gd3_str( in, end ); - int len = (mid - in) / 2 - 1; - if ( len > 0 ) - { - len = min( len, (int) Gme_File::max_field_ ); - field [len] = 0; - for ( int i = 0; i < len; i++ ) - field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8 - } - return mid; -} - -static byte const* get_gd3_pair( byte const* in, byte const* end, char* field ) -{ - return skip_gd3_str( get_gd3_str( in, end, field ), end ); -} - -static void parse_gd3( byte const* in, byte const* end, track_info_t* out ) -{ - in = get_gd3_pair( in, end, out->song ); - in = get_gd3_pair( in, end, out->game ); - in = get_gd3_pair( in, end, out->system ); - in = get_gd3_pair( in, end, out->author ); - in = get_gd3_str ( in, end, out->copyright ); - in = get_gd3_pair( in, end, out->dumper ); - in = get_gd3_str ( in, end, out->comment ); -} - -int const gd3_header_size = 12; - -static long check_gd3_header( byte const* h, long remain ) -{ - if ( remain < gd3_header_size ) return 0; - if ( memcmp( h, "Gd3 ", 4 ) ) return 0; - if ( get_le32( h + 4 ) >= 0x200 ) return 0; - - long gd3_size = get_le32( h + 8 ); - if ( gd3_size > remain - gd3_header_size ) return 0; - - return gd3_size; -} - -byte const* Vgm_Emu::gd3_data( int* size ) const -{ - if ( size ) - *size = 0; - - long gd3_offset = get_le32( header().gd3_offset ) - 0x2C; - if ( gd3_offset < 0 ) - return 0; - - byte const* gd3 = data + header_size + gd3_offset; - long gd3_size = check_gd3_header( gd3, data_end - gd3 ); - if ( !gd3_size ) - return 0; - - if ( size ) - *size = gd3_size + gd3_header_size; - - return gd3; -} - -static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) -{ - long length = get_le32( h.track_duration ) * 10 / 441; - if ( length > 0 ) - { - long loop = get_le32( h.loop_duration ); - if ( loop > 0 && get_le32( h.loop_offset ) ) - { - out->loop_length = loop * 10 / 441; - out->intro_length = length - out->loop_length; - } - else - { - out->length = length; // 1000 / 44100 (VGM files used 44100 as timebase) - out->intro_length = length; // make it clear that track is no longer than length - out->loop_length = 0; - } - } -} - -blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const -{ - get_vgm_length( header(), out ); - - int size; - byte const* gd3 = gd3_data( &size ); - if ( gd3 ) - parse_gd3( gd3 + gd3_header_size, gd3 + size, out ); - - return 0; -} - -static blargg_err_t check_vgm_header( Vgm_Emu::header_t const& h ) -{ - if ( memcmp( h.tag, "Vgm ", 4 ) ) - return gme_wrong_file_type; - return 0; -} - -struct Vgm_File : Gme_Info_ -{ - Vgm_Emu::header_t h; - blargg_vector gd3; - - Vgm_File() { set_type( gme_vgm_type ); } - - blargg_err_t load_( Data_Reader& in ) - { - long file_size = in.remain(); - if ( file_size <= Vgm_Emu::header_size ) - return gme_wrong_file_type; - - RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); - RETURN_ERR( check_vgm_header( h ) ); - - long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; - long remain = file_size - Vgm_Emu::header_size - gd3_offset; - byte gd3_h [gd3_header_size]; - if ( gd3_offset > 0 || remain >= gd3_header_size ) - { - RETURN_ERR( in.skip( gd3_offset ) ); - RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); - long gd3_size = check_gd3_header( gd3_h, remain ); - if ( gd3_size ) - { - RETURN_ERR( gd3.resize( gd3_size ) ); - RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); - } - } - return 0; - } - - blargg_err_t track_info_( track_info_t* out, int ) const - { - get_vgm_length( h, out ); - if ( gd3.size() ) - parse_gd3( gd3.begin(), gd3.end(), out ); - return 0; - } -}; - -static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } -static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } - -gme_type_t_ const gme_vgm_type [1] = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }; -gme_type_t_ const gme_vgz_type [1] = { "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }; - -// Setup - -void Vgm_Emu::set_tempo_( double t ) -{ - if ( psg_rate ) - { - vgm_rate = (long) (44100 * t + 0.5); - blip_time_factor = (long) floor( double (1L << blip_time_bits) / vgm_rate * psg_rate + 0.5 ); - //dprintf( "blip_time_factor: %ld\n", blip_time_factor ); - //dprintf( "vgm_rate: %ld\n", vgm_rate ); - // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only) - //blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 ); - //vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 ); - - fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); - } -} - -blargg_err_t Vgm_Emu::set_sample_rate_( long sample_rate ) -{ - RETURN_ERR( blip_buf.set_sample_rate( sample_rate, 1000 / 30 ) ); - return Classic_Emu::set_sample_rate_( sample_rate ); -} - -void Vgm_Emu::update_eq( blip_eq_t const& eq ) -{ - psg.treble_eq( eq ); - dac_synth.treble_eq( eq ); -} - -void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) -{ - if ( i < psg.osc_count ) - psg.osc_output( i, c, l, r ); -} - -void Vgm_Emu::mute_voices_( int mask ) -{ - Classic_Emu::mute_voices_( mask ); - dac_synth.output( &blip_buf ); - if ( uses_fm ) - { - psg.output( (mask & 0x80) ? 0 : &blip_buf ); - if ( ym2612.enabled() ) - { - dac_synth.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); - ym2612.mute_voices( mask ); - } - - if ( ym2413.enabled() ) - { - int m = mask & 0x3F; - if ( mask & 0x20 ) - m |= 0x01E0; // channels 5-8 - if ( mask & 0x40 ) - m |= 0x3E00; - ym2413.mute_voices( m ); - } - } -} - -blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) -{ - assert( offsetof (header_t,unused2 [8]) == header_size ); - - if ( new_size <= header_size ) - return gme_wrong_file_type; - - header_t const& h = *(header_t const*) new_data; - - RETURN_ERR( check_vgm_header( h ) ); - - check( get_le32( h.version ) <= 0x150 ); - - // psg rate - psg_rate = get_le32( h.psg_rate ); - if ( !psg_rate ) - psg_rate = 3579545; - blip_buf.clock_rate( psg_rate ); - - data = new_data; - data_end = new_data + new_size; - - // get loop - loop_begin = data_end; - if ( get_le32( h.loop_offset ) ) - loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; - - set_voice_count( psg.osc_count ); - - RETURN_ERR( setup_fm() ); - - static const char* const fm_names [] = { - "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" - }; - static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; - set_voice_names( uses_fm ? fm_names : psg_names ); - - // do after FM in case output buffer is changed - return Classic_Emu::setup_buffer( psg_rate ); -} - -blargg_err_t Vgm_Emu::setup_fm() -{ - long ym2612_rate = get_le32( header().ym2612_rate ); - long ym2413_rate = get_le32( header().ym2413_rate ); - if ( ym2413_rate && get_le32( header().version ) < 0x110 ) - update_fm_rates( &ym2413_rate, &ym2612_rate ); - - uses_fm = false; - - fm_rate = blip_buf.sample_rate() * oversample_factor; - - if ( ym2612_rate ) - { - uses_fm = true; - if ( disable_oversampling_ ) - fm_rate = ym2612_rate / 144.0; - Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); - RETURN_ERR( ym2612.set_rate( fm_rate, ym2612_rate ) ); - ym2612.enable( true ); - set_voice_count( 8 ); - } - - if ( !uses_fm && ym2413_rate ) - { - uses_fm = true; - if ( disable_oversampling_ ) - fm_rate = ym2413_rate / 72.0; - Dual_Resampler::setup( fm_rate / blip_buf.sample_rate(), rolloff, fm_gain * gain() ); - int result = ym2413.set_rate( fm_rate, ym2413_rate ); - if ( result == 2 ) - return "YM2413 FM sound isn't supported"; - CHECK_ALLOC( !result ); - ym2413.enable( true ); - set_voice_count( 8 ); - } - - if ( uses_fm ) - { - RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); - psg.volume( 0.135 * fm_gain * gain() ); - } - else - { - ym2612.enable( false ); - ym2413.enable( false ); - psg.volume( gain() ); - } - - return 0; -} - -// Emulation - -blargg_err_t Vgm_Emu::start_track_( int track ) -{ - RETURN_ERR( Classic_Emu::start_track_( track ) ); - psg.reset( get_le16( header().noise_feedback ), header().noise_width ); - - dac_disabled = -1; - pos = data + header_size; - pcm_data = pos; - pcm_pos = pos; - dac_amp = -1; - vgm_time = 0; - if ( get_le32( header().version ) >= 0x150 ) - { - long data_offset = get_le32( header().data_offset ); - check( data_offset ); - if ( data_offset ) - pos += data_offset + offsetof (header_t,data_offset) - 0x40; - } - - if ( uses_fm ) - { - if ( ym2413.enabled() ) - ym2413.reset(); - - if ( ym2612.enabled() ) - ym2612.reset(); - - fm_time_offset = 0; - blip_buf.clear(); - Dual_Resampler::clear(); - } - return 0; -} - -blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) -{ - time_io = run_commands( msec * vgm_rate / 1000 ); - psg.end_frame( time_io ); - return 0; -} - -blargg_err_t Vgm_Emu::play_( long count, sample_t* out ) -{ - if ( !uses_fm ) - return Classic_Emu::play_( count, out ); - - Dual_Resampler::dual_play( count, out, blip_buf ); - return 0; -} +Vgm_Emu::~Vgm_Emu() { } + +void Vgm_Emu::unload() +{ + core.unload(); + Classic_Emu::unload(); +} + +// Track info + +static byte const* skip_gd3_str( byte const in [], byte const* end ) +{ + while ( end - in >= 2 ) + { + in += 2; + if ( !(in [-2] | in [-1]) ) + break; + } + return in; +} + +static byte const* get_gd3_str( byte const* in, byte const* end, char field [] ) +{ + byte const* mid = skip_gd3_str( in, end ); + int len = (mid - in) / 2 - 1; + if ( len > 0 ) + { + len = min( len, (int) Gme_File::max_field_ ); + field [len] = 0; + for ( int i = 0; i < len; i++ ) + field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8 + } + return mid; +} + +static byte const* get_gd3_pair( byte const* in, byte const* end, char field [] ) +{ + return skip_gd3_str( get_gd3_str( in, end, field ), end ); +} + +static void parse_gd3( byte const in [], byte const* end, track_info_t* out ) +{ + in = get_gd3_pair( in, end, out->song ); + in = get_gd3_pair( in, end, out->game ); + in = get_gd3_pair( in, end, out->system ); + in = get_gd3_pair( in, end, out->author ); + in = get_gd3_str ( in, end, out->copyright ); + in = get_gd3_pair( in, end, out->dumper ); + in = get_gd3_str ( in, end, out->comment ); +} + +int const gd3_header_size = 12; + +static int check_gd3_header( byte const h [], int remain ) +{ + if ( remain < gd3_header_size ) return 0; + if ( memcmp( h, "Gd3 ", 4 ) ) return 0; + if ( get_le32( h + 4 ) >= 0x200 ) return 0; + + int gd3_size = get_le32( h + 8 ); + if ( gd3_size > remain - gd3_header_size ) return 0; + + return gd3_size; +} + +static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) +{ + int length = get_le32( h.track_duration ) * 10 / 441; // 1000 / 44100 + if ( length > 0 ) + { + int loop = get_le32( h.loop_duration ); + if ( loop > 0 && get_le32( h.loop_offset ) ) + { + out->loop_length = loop * 10 / 441; + out->intro_length = length - out->loop_length; + check( out->loop_length <= length ); + // TODO: Also set out->length? We now have play_length for suggested play time. + } + else + { + out->length = length; + out->intro_length = length; + out->loop_length = 0; + } + } +} + +blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const +{ + get_vgm_length( header(), out ); + + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); + if ( gd3_size ) + { + byte const* gd3_data = gd3 + gd3_header_size; + parse_gd3( gd3_data, gd3_data + gd3_size, out ); + } + + return blargg_ok; +} + +blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size ) +{ + *data = 0; + *size = 0; + + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset <= 0 ) + return blargg_ok; + + byte const* gd3 = core.file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 ); + if ( gd3_size ) + { + *data = gd3; + *size = gd3_size + gd3_header_size; + } + + return blargg_ok; +} + +static void hash_vgm_file( Vgm_Emu::header_t const& h, byte const* data, int data_size, Music_Emu::Hash_Function& out ) +{ + out.hash_( &h.data_size[0], sizeof(h.data_size) ); + out.hash_( &h.version[0], sizeof(h.version) ); + out.hash_( &h.psg_rate[0], sizeof(h.psg_rate) ); + out.hash_( &h.ym2413_rate[0], sizeof(h.ym2413_rate) ); + out.hash_( &h.track_duration[0], sizeof(h.track_duration) ); + out.hash_( &h.loop_offset[0], sizeof(h.loop_offset) ); + out.hash_( &h.loop_duration[0], sizeof(h.loop_duration) ); + out.hash_( &h.frame_rate[0], sizeof(h.frame_rate) ); + out.hash_( &h.noise_feedback[0], sizeof(h.noise_feedback) ); + out.hash_( &h.noise_width, sizeof(h.noise_width) ); + out.hash_( &h.sn76489_flags, sizeof(h.sn76489_flags) ); + out.hash_( &h.ym2612_rate[0], sizeof(h.ym2612_rate) ); + out.hash_( &h.ym2151_rate[0], sizeof(h.ym2151_rate) ); + out.hash_( &h.data_offset[0], sizeof(h.data_offset) ); + out.hash_( &h.segapcm_rate[0], sizeof(h.segapcm_rate) ); + out.hash_( &h.segapcm_reg[0], sizeof(h.segapcm_reg) ); + out.hash_( &h.rf5c68_rate[0], sizeof(h.rf5c68_rate) ); + out.hash_( &h.ym2203_rate[0], sizeof(h.ym2203_rate) ); + out.hash_( &h.ym2608_rate[0], sizeof(h.ym2608_rate) ); + out.hash_( &h.ym2610_rate[0], sizeof(h.ym2610_rate) ); + out.hash_( &h.ym3812_rate[0], sizeof(h.ym3812_rate) ); + out.hash_( &h.ym3526_rate[0], sizeof(h.ym3526_rate) ); + out.hash_( &h.y8950_rate[0], sizeof(h.y8950_rate) ); + out.hash_( &h.ymf262_rate[0], sizeof(h.ymf262_rate) ); + out.hash_( &h.ymf278b_rate[0], sizeof(h.ymf278b_rate) ); + out.hash_( &h.ymf271_rate[0], sizeof(h.ymf271_rate) ); + out.hash_( &h.ymz280b_rate[0], sizeof(h.ymz280b_rate) ); + out.hash_( &h.rf5c164_rate[0], sizeof(h.rf5c164_rate) ); + out.hash_( &h.pwm_rate[0], sizeof(h.pwm_rate) ); + out.hash_( &h.ay8910_rate[0], sizeof(h.ay8910_rate) ); + out.hash_( &h.ay8910_type, sizeof(h.ay8910_type) ); + out.hash_( &h.ay8910_flags, sizeof(h.ay8910_flags) ); + out.hash_( &h.ym2203_ay8910_flags, sizeof(h.ym2203_ay8910_flags) ); + out.hash_( &h.ym2608_ay8910_flags, sizeof(h.ym2608_ay8910_flags) ); + out.hash_( &h.reserved, sizeof(h.reserved) ); + out.hash_( &h.gbdmg_rate[0], sizeof(h.gbdmg_rate) ); + out.hash_( &h.nesapu_rate[0], sizeof(h.nesapu_rate) ); + out.hash_( &h.multipcm_rate[0], sizeof(h.multipcm_rate) ); + out.hash_( &h.upd7759_rate[0], sizeof(h.upd7759_rate) ); + out.hash_( &h.okim6258_rate[0], sizeof(h.okim6258_rate) ); + out.hash_( &h.okim6258_flags, sizeof(h.okim6258_flags) ); + out.hash_( &h.k054539_flags, sizeof(h.k054539_flags) ); + out.hash_( &h.c140_type, sizeof(h.c140_type) ); + out.hash_( &h.reserved_flags, sizeof(h.reserved_flags) ); + out.hash_( &h.okim6295_rate[0], sizeof(h.okim6295_rate) ); + out.hash_( &h.k051649_rate[0], sizeof(h.k051649_rate) ); + out.hash_( &h.k054539_rate[0], sizeof(h.k054539_rate) ); + out.hash_( &h.huc6280_rate[0], sizeof(h.huc6280_rate) ); + out.hash_( &h.c140_rate[0], sizeof(h.c140_rate) ); + out.hash_( &h.k053260_rate[0], sizeof(h.k053260_rate) ); + out.hash_( &h.pokey_rate[0], sizeof(h.pokey_rate) ); + out.hash_( &h.qsound_rate[0], sizeof(h.qsound_rate) ); + out.hash_( &h.reserved2[0], sizeof(h.reserved2) ); + out.hash_( &h.extra_offset[0], sizeof(h.extra_offset) ); + out.hash_( data, data_size ); +} + +struct Vgm_File : Gme_Info_ +{ + Vgm_Emu::header_t h; + blargg_vector data; + blargg_vector gd3; + + Vgm_File() { set_type( gme_vgm_type ); } + + blargg_err_t load_( Data_Reader& in ) + { + int file_size = in.remain(); + if ( file_size <= h.size_min ) + return blargg_err_file_type; + + RETURN_ERR( in.read( &h, h.size_min ) ); + if ( !h.valid_tag() ) + return blargg_err_file_type; + + if ( h.size() > h.size_min ) + RETURN_ERR( in.read( &h.rf5c68_rate, h.size() - h.size_min ) ); + + h.cleanup(); + + int data_offset = get_le32( h.data_offset ) + offsetof( Vgm_Core::header_t, data_offset ); + int data_size = file_size - offsetof( Vgm_Core::header_t, data_offset ) - data_offset; + int gd3_offset = get_le32( h.gd3_offset ); + if ( gd3_offset > 0 ) + gd3_offset += offsetof( Vgm_Core::header_t, gd3_offset ); + + int amount_to_skip = gd3_offset - h.size(); + + if ( gd3_offset > 0 && gd3_offset > data_offset ) + { + data_size = gd3_offset - data_offset; + amount_to_skip = 0; + + RETURN_ERR( data.resize( data_size ) ); + RETURN_ERR( in.skip( data_offset - h.size() ) ); + RETURN_ERR( in.read( data.begin(), data_size ) ); + } + + int remain = file_size - gd3_offset; + byte gd3_h [gd3_header_size]; + if ( gd3_offset > 0 && remain >= gd3_header_size ) + { + RETURN_ERR( in.skip( amount_to_skip ) ); + RETURN_ERR( in.read( gd3_h, sizeof gd3_h ) ); + int gd3_size = check_gd3_header( gd3_h, remain ); + if ( gd3_size ) + { + RETURN_ERR( gd3.resize( gd3_size ) ); + RETURN_ERR( in.read( gd3.begin(), gd3.size() ) ); + } + + if ( data_offset > gd3_offset ) + { + RETURN_ERR( data.resize( data_size ) ); + RETURN_ERR( in.skip( data_offset - gd3_offset - sizeof gd3_h - gd3.size() ) ); + RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); + } + } + + return blargg_ok; + } + + blargg_err_t track_info_( track_info_t* out, int ) const + { + get_vgm_length( h, out ); + if ( gd3.size() ) + parse_gd3( gd3.begin(), gd3.end(), out ); + return blargg_ok; + } + + blargg_err_t hash_( Hash_Function& out ) const + { + hash_vgm_file( h, data.begin(), data.end() - data.begin(), out ); + return blargg_ok; + } +}; + +static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; } +static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; } + +gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }}; + +gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }}; + +// Setup + +void Vgm_Emu::set_tempo_( double t ) +{ + core.set_tempo( t ); +} + +blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate ) +{ + RETURN_ERR( core.stereo_buf[0].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[1].set_sample_rate( sample_rate, 1000 / 30 ) ); + RETURN_ERR( core.stereo_buf[2].set_sample_rate( sample_rate, 1000 / 30 ) ); + core.set_sample_rate( sample_rate ); + return Classic_Emu::set_sample_rate_( sample_rate ); +} + +void Vgm_Emu::update_eq( blip_eq_t const& eq ) +{ + core.psg[0].treble_eq( eq ); + core.psg[1].treble_eq( eq ); + core.ay[0].treble_eq( eq ); + core.ay[1].treble_eq( eq ); + core.huc6280[0].treble_eq( eq ); + core.huc6280[1].treble_eq( eq ); + core.pcm.treble_eq( eq ); +} + +void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) +{ + if ( i < core.psg[0].osc_count ) + { + core.psg[0].set_output( i, c, l, r ); + core.psg[1].set_output( i, c, l, r ); + } +} + +void Vgm_Emu::mute_voices_( int mask ) +{ + muted_voices = mask; + + Classic_Emu::mute_voices_( mask ); + + // TODO: what was this for? + //core.pcm.output( &core.blip_buf ); + + // TODO: silence PCM if FM isn't used? + if ( core.uses_fm() ) + { + core.psg[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); + core.psg[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[0].center() ); + core.ay[0].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); + core.ay[1].set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf[1].center() ); + for ( unsigned i = 0, j = 1; i < core.huc6280[0].osc_count; i++, j <<= 1) + { + Blip_Buffer * center = ( mask & j ) ? 0 : core.stereo_buf[2].center(); + Blip_Buffer * left = ( mask & j ) ? 0 : core.stereo_buf[2].left(); + Blip_Buffer * right = ( mask & j ) ? 0 : core.stereo_buf[2].right(); + core.huc6280[0].set_output( i, center, left, right ); + core.huc6280[1].set_output( i, center, left, right ); + } + if ( core.ym2612[0].enabled() ) + { + core.pcm.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() ); + core.ym2612[0].mute_voices( mask ); + if ( core.ym2612[1].enabled() ) + core.ym2612[1].mute_voices( mask ); + } + + if ( core.ym2413[0].enabled() ) + { + int m = mask & 0x3F; + if ( mask & 0x20 ) + m |= 0x01E0; // channels 5-8 + if ( mask & 0x40 ) + m |= 0x3E00; + core.ym2413[0].mute_voices( m ); + if ( core.ym2413[1].enabled() ) + core.ym2413[1].mute_voices( m ); + } + + if ( core.ym2151[0].enabled() ) + { + core.ym2151[0].mute_voices( mask ); + if ( core.ym2151[1].enabled() ) + core.ym2151[1].mute_voices( mask ); + } + + if ( core.c140.enabled() ) + { + int m = 0; + int m_add = 7; + for ( unsigned i = 0; i < 8; i++, m_add <<= 3 ) + { + if ( mask & ( 1 << i ) ) m += m_add; + } + core.c140.mute_voices( m ); + } + + if ( core.rf5c68.enabled() ) + { + core.rf5c68.mute_voices( mask ); + } + + if ( core.rf5c164.enabled() ) + { + core.rf5c164.mute_voices( mask ); + } + } +} + +blargg_err_t Vgm_Emu::load_mem_( byte const data [], int size ) +{ + RETURN_ERR( core.load_mem( data, size ) ); + + set_voice_count( core.psg[0].osc_count ); + + double fm_rate = 0.0; + if ( !disable_oversampling_ ) + fm_rate = sample_rate() * oversample_factor; + RETURN_ERR( core.init_chips( &fm_rate ) ); + + double psg_gain = ( ( core.header().psg_rate[3] & 0xC0 ) == 0x40 ) ? 0.5 : 1.0; + + if ( core.uses_fm() ) + { + set_voice_count( 8 ); + RETURN_ERR( resampler.setup( fm_rate / sample_rate(), rolloff, gain() ) ); + RETURN_ERR( resampler.reset( core.stereo_buf[0].length() * sample_rate() / 1000 ) ); + core.psg[0].volume( 0.135 * fm_gain * psg_gain * gain() ); + core.psg[1].volume( 0.135 * fm_gain * psg_gain * gain() ); + core.ay[0].volume( 0.135 * fm_gain * gain() ); + core.ay[1].volume( 0.135 * fm_gain * gain() ); + core.huc6280[0].volume( 0.135 * fm_gain * gain() ); + core.huc6280[1].volume( 0.135 * fm_gain * gain() ); + } + else + { + core.psg[0].volume( psg_gain * gain() ); + core.psg[1].volume( psg_gain * gain() ); + } + + static const char* const fm_names [] = { + "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" + }; + static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; + set_voice_names( core.uses_fm() ? fm_names : psg_names ); + + static int const types [8] = { + wave_type+1, wave_type+2, wave_type+3, noise_type+1, + 0, 0, 0, 0 + }; + set_voice_types( types ); + + return Classic_Emu::setup_buffer( core.stereo_buf[0].center()->clock_rate() ); +} + +// Emulation + +blargg_err_t Vgm_Emu::start_track_( int track ) +{ + RETURN_ERR( Classic_Emu::start_track_( track ) ); + + core.start_track(); + + mute_voices_(muted_voices); + + if ( core.uses_fm() ) + resampler.clear(); + + return blargg_ok; +} + +inline void Vgm_Emu::check_end() +{ + if ( core.track_ended() ) + set_track_ended(); +} + +inline void Vgm_Emu::check_warning() +{ + const char* w = core.warning(); + if ( w ) + set_warning( w ); +} + +blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec ) +{ + check_end(); + time_io = core.run_psg( msec ); + check_warning(); + return blargg_ok; +} + +inline int Vgm_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ) +{ + check_end(); + int result = core.play_frame( blip_time, sample_count, buf ); + check_warning(); + return result; +} + +int Vgm_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] ) +{ + return STATIC_CAST(Vgm_Emu*,p)->play_frame( a, b, c ); +} + +blargg_err_t Vgm_Emu::play_( int count, sample_t out [] ) +{ + if ( !core.uses_fm() ) + return Classic_Emu::play_( count, out ); + + Stereo_Buffer * secondaries[] = { &core.stereo_buf[1], &core.stereo_buf[2] }; + resampler.dual_play( count, out, core.stereo_buf[0], secondaries, 2 ); + return blargg_ok; +} + +blargg_err_t Vgm_Emu::hash_( Hash_Function& out ) const +{ + byte const* p = file_begin() + header().size(); + byte const* e = file_end(); + int data_offset = get_le32( header().data_offset ); + if ( data_offset ) + p += data_offset + offsetof( header_t, data_offset ) - header().size(); + int gd3_offset = get_le32( header().gd3_offset ); + if ( gd3_offset > 0 && gd3_offset + offsetof( header_t, gd3_offset ) > data_offset + offsetof( header_t, data_offset ) ) + e = file_begin() + gd3_offset + offsetof( header_t, gd3_offset ); + hash_vgm_file( header(), p, e - p, out ); + return blargg_ok; +} diff --git a/Frameworks/GME/gme/Vgm_Emu.h b/Frameworks/GME/gme/Vgm_Emu.h old mode 100755 new mode 100644 index bcb784d56..3aaddf851 --- a/Frameworks/GME/gme/Vgm_Emu.h +++ b/Frameworks/GME/gme/Vgm_Emu.h @@ -1,84 +1,69 @@ -// Sega Master System/Mark III, Sega Genesis/Mega Drive, BBC Micro VGM music file emulator +// Sega VGM music file emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef VGM_EMU_H #define VGM_EMU_H -#include "Vgm_Emu_Impl.h" +#include "Classic_Emu.h" +#include "Dual_Resampler.h" +#include "Vgm_Core.h" -// Emulates VGM music using SN76489/SN76496 PSG, YM2612, and YM2413 FM sound chips. -// Supports custom sound buffer and frequency equalization when VGM uses just the PSG. -// FM sound chips can be run at their proper rates, or slightly higher to reduce -// aliasing on high notes. Currently YM2413 support requires that you supply a -// YM2413 sound chip emulator. I can provide one I've modified to work with the library. -class Vgm_Emu : public Vgm_Emu_Impl { +/* Emulates VGM music using SN76489/SN76496 PSG, and YM2612 and YM2413 FM sound chips. +Supports custom sound buffer and frequency equalization when VGM uses just the PSG. FM +sound chips can be run at their proper rates, or slightly higher to reduce aliasing on +high notes. A YM2413 is supported but not provided separately from the library. */ +class Vgm_Emu : public Classic_Emu { public: + // True if custom buffer and custom equalization are supported // TODO: move into Music_Emu and rename to something like supports_custom_buffer() - bool is_classic_emu() const { return !uses_fm; } + bool is_classic_emu() const { return !core.uses_fm(); } - // Disable running FM chips at higher than normal rate. Will result in slightly + // Disables running FM chips at higher than normal rate. Will result in slightly // more aliasing of high notes. - void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } + void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } - // VGM header format - enum { header_size = 0x40 }; - struct header_t - { - char tag [4]; - byte data_size [4]; - byte version [4]; - byte psg_rate [4]; - byte ym2413_rate [4]; - byte gd3_offset [4]; - byte track_duration [4]; - byte loop_offset [4]; - byte loop_duration [4]; - byte frame_rate [4]; - byte noise_feedback [2]; - byte noise_width; - byte unused1; - byte ym2612_rate [4]; - byte ym2151_rate [4]; - byte data_offset [4]; - byte unused2 [8]; - }; + // VGM file header (see Vgm_Core.h) + typedef Vgm_Core::header_t header_t; // Header for currently loaded file - header_t const& header() const { return *(header_t const*) data; } - - static gme_type_t static_type() { return gme_vgm_type; } - -public: - // deprecated - Music_Emu::load; - blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader - { return load_remaining_( &h, sizeof h, in ); } - byte const* gd3_data( int* size_out = 0 ) const; // use track_info() + header_t const& header() const { return core.header(); } + blargg_err_t hash_( Hash_Function& ) const; + + // Gd3 tag for currently loaded file + blargg_err_t gd3_data( const unsigned char ** data, int * size ); + + static gme_type_t static_type() { return gme_vgm_type; } + +// Implementation public: Vgm_Emu(); ~Vgm_Emu(); + protected: blargg_err_t track_info_( track_info_t*, int track ) const; - blargg_err_t load_mem_( byte const*, long ); - blargg_err_t set_sample_rate_( long sample_rate ); + blargg_err_t load_mem_( byte const [], int ); + blargg_err_t set_sample_rate_( int sample_rate ); blargg_err_t start_track_( int ); - blargg_err_t play_( long count, sample_t* ); + blargg_err_t play_( int count, sample_t []); blargg_err_t run_clocks( blip_time_t&, int ); - void set_tempo_( double ); - void mute_voices_( int mask ); - void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); - void update_eq( blip_eq_t const& ); + virtual void set_tempo_( double ); + virtual void mute_voices_( int mask ); + virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ); + virtual void update_eq( blip_eq_t const& ); + virtual void unload(); + private: - // removed; use disable_oversampling() and set_tempo() instead - Vgm_Emu( bool oversample, double tempo = 1.0 ); - double fm_rate; - long psg_rate; - long vgm_rate; bool disable_oversampling_; - bool uses_fm; - blargg_err_t setup_fm(); + unsigned muted_voices; + Dual_Resampler resampler; + Vgm_Core core; + + void check_end(); + void check_warning(); + int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] ); + static int play_frame_( void*, blip_time_t, int, sample_t [] ); }; #endif diff --git a/Frameworks/GME/gme/Ym2413_Emu.cpp b/Frameworks/GME/gme/Ym2413_Emu.cpp old mode 100755 new mode 100644 index ede673045..bd4349e01 --- a/Frameworks/GME/gme/Ym2413_Emu.cpp +++ b/Frameworks/GME/gme/Ym2413_Emu.cpp @@ -1,21 +1,78 @@ - -// Use in place of Ym2413_Emu.cpp and ym2413.c to disable support for this chip - -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -#include "Ym2413_Emu.h" - -Ym2413_Emu::Ym2413_Emu() { } - -Ym2413_Emu::~Ym2413_Emu() { } - -int Ym2413_Emu::set_rate( double, double ) { return 2; } - -void Ym2413_Emu::reset() { } - -void Ym2413_Emu::write( int, int ) { } - -void Ym2413_Emu::mute_voices( int ) { } - -void Ym2413_Emu::run( int, sample_t* ) { } - +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Ym2413_Emu.h" +#include "ym2413.h" + +Ym2413_Emu::Ym2413_Emu() { opll = 0; } + +Ym2413_Emu::~Ym2413_Emu() +{ + if ( opll ) ym2413_shutdown( opll ); +} + +int Ym2413_Emu::set_rate( int sample_rate, int clock_rate ) +{ + if ( opll ) + { + ym2413_shutdown( opll ); + opll = 0; + } + + opll = ym2413_init( clock_rate, sample_rate, 0 ); + if ( !opll ) + return 1; + + reset(); + return 0; +} + +void Ym2413_Emu::reset() +{ + ym2413_reset_chip( opll ); + ym2413_set_mask( opll, 0 ); +} + +static stream_sample_t* DUMMYBUF[0x02] = {(stream_sample_t*)NULL, (stream_sample_t*)NULL}; + +void Ym2413_Emu::write( int addr, int data ) +{ + ym2413_update_one( opll, DUMMYBUF, 0 ); + ym2413_write( opll, 0, addr ); + ym2413_write( opll, 1, data ); +} + +void Ym2413_Emu::mute_voices( int mask ) +{ + ym2413_set_mask( opll, mask ); +} + +void Ym2413_Emu::run( int pair_count, sample_t* out ) +{ + SAMP bufMO[ 1024 ]; + SAMP bufRO[ 1024 ]; + SAMP * buffers[2] = { bufMO, bufRO }; + + while (pair_count > 0) + { + int todo = pair_count; + if (todo > 1024) todo = 1024; + ym2413_update_one( opll, buffers, todo ); + + for (int i = 0; i < todo; i++) + { + int output_l, output_r; + int output = bufMO [i]; + output += bufRO [i]; + output *= 3; + output_l = output + out [0]; + output_r = output + out [1]; + if ( (short)output_l != output_l ) output_l = 0x7FFF ^ ( output_l >> 31 ); + if ( (short)output_r != output_r ) output_r = 0x7FFF ^ ( output_r >> 31 ); + out [0] = output_l; + out [1] = output_r; + out += 2; + } + + pair_count -= todo; + } +} diff --git a/Frameworks/GME/gme/Ym2413_Emu.h b/Frameworks/GME/gme/Ym2413_Emu.h old mode 100755 new mode 100644 index 98a2a48e1..59cb61bf8 --- a/Frameworks/GME/gme/Ym2413_Emu.h +++ b/Frameworks/GME/gme/Ym2413_Emu.h @@ -1,33 +1,37 @@ -// YM2413 FM sound chip emulator interface - -// Game_Music_Emu 0.5.2 -#ifndef YM2413_EMU_H -#define YM2413_EMU_H - -class Ym2413_Emu { - struct OPLL* opll; -public: - Ym2413_Emu(); - ~Ym2413_Emu(); - - // Set output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - int set_rate( double sample_rate, double clock_rate ); - - // Reset to power-up state - void reset(); - - // Mute voice n if bit n (1 << n) of mask is set - enum { channel_count = 14 }; - void mute_voices( int mask ); - - // Write 'data' to 'addr' - void write( int addr, int data ); - - // Run and write pair_count samples to output - typedef short sample_t; - enum { out_chan_count = 2 }; // stereo - void run( int pair_count, sample_t* out ); -}; - -#endif +// YM2413 FM sound chip emulator interface + +// Game_Music_Emu $vers +#ifndef YM2413_EMU_H +#define YM2413_EMU_H + +struct OPLL; + +class Ym2413_Emu { + void* opll; +public: + Ym2413_Emu(); + ~Ym2413_Emu(); + + static bool supported() { return true; } + + // Sets output sample rate and chip clock rates, in Hz. Returns non-zero + // if error. + int set_rate( int sample_rate, int clock_rate ); + + // Resets to power-up state + void reset(); + + // Mutes voice n if bit n (1 << n) of mask is set + enum { channel_count = 14 }; + void mute_voices( int mask ); + + // Writes data to addr + void write( int addr, int data ); + + // Runs and writes pair_count*2 samples to output + typedef short sample_t; + enum { out_chan_count = 2 }; // stereo + void run( int pair_count, sample_t* out ); +}; + +#endif diff --git a/Frameworks/GME/gme/Ym2612_Emu.cpp b/Frameworks/GME/gme/Ym2612_Emu.cpp old mode 100755 new mode 100644 index 41ebb093d..dd850e740 --- a/Frameworks/GME/gme/Ym2612_Emu.cpp +++ b/Frameworks/GME/gme/Ym2612_Emu.cpp @@ -1,1319 +1,7 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ - -// Based on Gens 2.10 ym2612.c - #include "Ym2612_Emu.h" -#include -#include -#include -#include -#include -#include - -/* Copyright (C) 2002 Stéphane Dallongeville (gens AT consolemul.com) */ -/* Copyright (C) 2004-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -// This is mostly the original source in its C style and all. -// -// Somewhat optimized and simplified. Uses a template to generate the many -// variants of Update_Chan. Rewrote header file. In need of full rewrite by -// someone more familiar with FM sound and the YM2612. Has some inaccuracies -// compared to the Sega Genesis sound, particularly being mixed at such a -// high sample accuracy (the Genesis sounds like it has only 8 bit samples). -// - Shay - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -const int output_bits = 14; - -struct slot_t -{ - const int *DT; // parametre detune - int MUL; // parametre "multiple de frequence" - int TL; // Total Level = volume lorsque l'enveloppe est au plus haut - int TLL; // Total Level ajusted - int SLL; // Sustin Level (ajusted) = volume où l'enveloppe termine sa premiere phase de regression - int KSR_S; // Key Scale Rate Shift = facteur de prise en compte du KSL dans la variations de l'enveloppe - int KSR; // Key Scale Rate = cette valeur est calculee par rapport à la frequence actuelle, elle va influer - // sur les differents parametres de l'enveloppe comme l'attaque, le decay ... comme dans la realite ! - int SEG; // Type enveloppe SSG - int env_xor; - int env_max; - - const int *AR; // Attack Rate (table pointeur) = Taux d'attaque (AR[KSR]) - const int *DR; // Decay Rate (table pointeur) = Taux pour la regression (DR[KSR]) - const int *SR; // Sustin Rate (table pointeur) = Taux pour le maintien (SR[KSR]) - const int *RR; // Release Rate (table pointeur) = Taux pour le rel'chement (RR[KSR]) - int Fcnt; // Frequency Count = compteur-frequence pour determiner l'amplitude actuelle (SIN[Finc >> 16]) - int Finc; // frequency step = pas d'incrementation du compteur-frequence - // plus le pas est grand, plus la frequence est aïgu (ou haute) - int Ecurp; // Envelope current phase = cette variable permet de savoir dans quelle phase - // de l'enveloppe on se trouve, par exemple phase d'attaque ou phase de maintenue ... - // en fonction de la valeur de cette variable, on va appeler une fonction permettant - // de mettre à jour l'enveloppe courante. - int Ecnt; // Envelope counter = le compteur-enveloppe permet de savoir où l'on se trouve dans l'enveloppe - int Einc; // Envelope step courant - int Ecmp; // Envelope counter limite pour la prochaine phase - int EincA; // Envelope step for Attack = pas d'incrementation du compteur durant la phase d'attaque - // cette valeur est egal à AR[KSR] - int EincD; // Envelope step for Decay = pas d'incrementation du compteur durant la phase de regression - // cette valeur est egal à DR[KSR] - int EincS; // Envelope step for Sustain = pas d'incrementation du compteur durant la phase de maintenue - // cette valeur est egal à SR[KSR] - int EincR; // Envelope step for Release = pas d'incrementation du compteur durant la phase de rel'chement - // cette valeur est egal à RR[KSR] - int *OUTp; // pointeur of SLOT output = pointeur permettant de connecter la sortie de ce slot à l'entree - // d'un autre ou carrement à la sortie de la voie - int INd; // input data of the slot = donnees en entree du slot - int ChgEnM; // Change envelop mask. - int AMS; // AMS depth level of this SLOT = degre de modulation de l'amplitude par le LFO - int AMSon; // AMS enable flag = drapeau d'activation de l'AMS -}; - -struct channel_t -{ - int S0_OUT[4]; // anciennes sorties slot 0 (pour le feed back) - int LEFT; // LEFT enable flag - int RIGHT; // RIGHT enable flag - int ALGO; // Algorythm = determine les connections entre les operateurs - int FB; // shift count of self feed back = degre de "Feed-Back" du SLOT 1 (il est son unique entree) - int FMS; // Frequency Modulation Sensitivity of channel = degre de modulation de la frequence sur la voie par le LFO - int AMS; // Amplitude Modulation Sensitivity of channel = degre de modulation de l'amplitude sur la voie par le LFO - int FNUM[4]; // hauteur frequence de la voie (+ 3 pour le mode special) - int FOCT[4]; // octave de la voie (+ 3 pour le mode special) - int KC[4]; // Key Code = valeur fonction de la frequence (voir KSR pour les slots, KSR = KC >> KSR_S) - slot_t SLOT[4]; // four slot.operators = les 4 slots de la voie - int FFlag; // Frequency step recalculation flag -}; - -struct state_t -{ - int TimerBase; // TimerBase calculation - int Status; // YM2612 Status (timer overflow) - int TimerA; // timerA limit = valeur jusqu'à laquelle le timer A doit compter - int TimerAL; - int TimerAcnt; // timerA counter = valeur courante du Timer A - int TimerB; // timerB limit = valeur jusqu'à laquelle le timer B doit compter - int TimerBL; - int TimerBcnt; // timerB counter = valeur courante du Timer B - int Mode; // Mode actuel des voie 3 et 6 (normal / special) - int DAC; // DAC enabled flag - channel_t CHANNEL[Ym2612_Emu::channel_count]; // Les 6 voies du YM2612 - int REG[2][0x100]; // Sauvegardes des valeurs de tout les registres, c'est facultatif - // cela nous rend le debuggage plus facile -}; - -#ifndef PI -#define PI 3.14159265358979323846 -#endif - -#define ATTACK 0 -#define DECAY 1 -#define SUBSTAIN 2 -#define RELEASE 3 - -// SIN_LBITS <= 16 -// LFO_HBITS <= 16 -// (SIN_LBITS + SIN_HBITS) <= 26 -// (ENV_LBITS + ENV_HBITS) <= 28 -// (LFO_LBITS + LFO_HBITS) <= 28 - -#define SIN_HBITS 12 // Sinus phase counter int part -#define SIN_LBITS (26 - SIN_HBITS) // Sinus phase counter float part (best setting) - -#if (SIN_LBITS > 16) -#define SIN_LBITS 16 // Can't be greater than 16 bits -#endif - -#define ENV_HBITS 12 // Env phase counter int part -#define ENV_LBITS (28 - ENV_HBITS) // Env phase counter float part (best setting) - -#define LFO_HBITS 10 // LFO phase counter int part -#define LFO_LBITS (28 - LFO_HBITS) // LFO phase counter float part (best setting) - -#define SIN_LENGHT (1 << SIN_HBITS) -#define ENV_LENGHT (1 << ENV_HBITS) -#define LFO_LENGHT (1 << LFO_HBITS) - -#define TL_LENGHT (ENV_LENGHT * 3) // Env + TL scaling + LFO - -#define SIN_MASK (SIN_LENGHT - 1) -#define ENV_MASK (ENV_LENGHT - 1) -#define LFO_MASK (LFO_LENGHT - 1) - -#define ENV_STEP (96.0 / ENV_LENGHT) // ENV_MAX = 96 dB - -#define ENV_ATTACK ((ENV_LENGHT * 0) << ENV_LBITS) -#define ENV_DECAY ((ENV_LENGHT * 1) << ENV_LBITS) -#define ENV_END ((ENV_LENGHT * 2) << ENV_LBITS) - -#define MAX_OUT_BITS (SIN_HBITS + SIN_LBITS + 2) // Modulation = -4 <--> +4 -#define MAX_OUT ((1 << MAX_OUT_BITS) - 1) - -#define PG_CUT_OFF ((int) (78.0 / ENV_STEP)) -#define ENV_CUT_OFF ((int) (68.0 / ENV_STEP)) - -#define AR_RATE 399128 -#define DR_RATE 5514396 - -//#define AR_RATE 426136 -//#define DR_RATE (AR_RATE * 12) - -#define LFO_FMS_LBITS 9 // FIXED (LFO_FMS_BASE gives somethink as 1) -#define LFO_FMS_BASE ((int) (0.05946309436 * 0.0338 * (double) (1 << LFO_FMS_LBITS))) - -#define S0 0 // Stupid typo of the YM2612 -#define S1 2 -#define S2 1 -#define S3 3 - -inline void set_seg( slot_t& s, int seg ) -{ - s.env_xor = 0; - s.env_max = INT_MAX; - s.SEG = seg; - if ( seg & 4 ) - { - s.env_xor = ENV_MASK; - s.env_max = ENV_MASK; - } -} - -struct tables_t -{ - short SIN_TAB [SIN_LENGHT]; // SINUS TABLE (offset into TL TABLE) - int LFOcnt; // LFO counter = compteur-frequence pour le LFO - int LFOinc; // LFO step counter = pas d'incrementation du compteur-frequence du LFO - // plus le pas est grand, plus la frequence est grande - unsigned int AR_TAB [128]; // Attack rate table - unsigned int DR_TAB [96]; // Decay rate table - unsigned int DT_TAB [8] [32]; // Detune table - unsigned int SL_TAB [16]; // Substain level table - unsigned int NULL_RATE [32]; // Table for NULL rate - int LFO_INC_TAB [8]; // LFO step table - - short ENV_TAB [2 * ENV_LENGHT + 8]; // ENV CURVE TABLE (attack & decay) - - short LFO_ENV_TAB [LFO_LENGHT]; // LFO AMS TABLE (adjusted for 11.8 dB) - short LFO_FREQ_TAB [LFO_LENGHT]; // LFO FMS TABLE - int TL_TAB [TL_LENGHT * 2]; // TOTAL LEVEL TABLE (positif and minus) - unsigned int DECAY_TO_ATTACK [ENV_LENGHT]; // Conversion from decay to attack phase - unsigned int FINC_TAB [2048]; // Frequency step table -}; - -static const unsigned char DT_DEF_TAB [4 * 32] = -{ -// FD = 0 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - -// FD = 1 - 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, - 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, - -// FD = 2 - 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, - 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, 16, 16, 16, 16, - -// FD = 3 - 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, - 8 , 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 20, 22, 22, 22, 22 -}; - -static const unsigned char FKEY_TAB [16] = -{ - 0, 0, 0, 0, - 0, 0, 0, 1, - 2, 3, 3, 3, - 3, 3, 3, 3 -}; - -static const unsigned char LFO_AMS_TAB [4] = -{ - 31, 4, 1, 0 -}; - -static const unsigned char LFO_FMS_TAB [8] = -{ - LFO_FMS_BASE * 0, LFO_FMS_BASE * 1, - LFO_FMS_BASE * 2, LFO_FMS_BASE * 3, - LFO_FMS_BASE * 4, LFO_FMS_BASE * 6, - LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 -}; - -inline void YM2612_Special_Update() { } - -struct Ym2612_Impl -{ - enum { channel_count = Ym2612_Emu::channel_count }; - - state_t YM2612; - int mute_mask; - tables_t g; - - void KEY_ON( channel_t&, int ); - void KEY_OFF( channel_t&, int ); - int SLOT_SET( int, int ); - int CHANNEL_SET( int, int ); - int YM_SET( int, int ); - - void set_rate( double sample_rate, double clock_factor ); - void reset(); - void write0( int addr, int data ); - void write1( int addr, int data ); - void run_timer( int ); - void run( int pair_count, Ym2612_Emu::sample_t* ); -}; - -void Ym2612_Impl::KEY_ON( channel_t& ch, int nsl) -{ - slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot - - if (SL->Ecurp == RELEASE) // la touche est-elle rel'chee ? - { - SL->Fcnt = 0; - - // Fix Ecco 2 splash sound - - SL->Ecnt = (g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK) & SL->ChgEnM; - SL->ChgEnM = ~0; - -// SL->Ecnt = g.DECAY_TO_ATTACK [g.ENV_TAB [SL->Ecnt >> ENV_LBITS]] + ENV_ATTACK; -// SL->Ecnt = 0; - - SL->Einc = SL->EincA; - SL->Ecmp = ENV_DECAY; - SL->Ecurp = ATTACK; - } -} - - -void Ym2612_Impl::KEY_OFF(channel_t& ch, int nsl) -{ - slot_t *SL = &(ch.SLOT [nsl]); // on recupere le bon pointeur de slot - - if (SL->Ecurp != RELEASE) // la touche est-elle appuyee ? - { - if (SL->Ecnt < ENV_DECAY) // attack phase ? - { - SL->Ecnt = (g.ENV_TAB [SL->Ecnt >> ENV_LBITS] << ENV_LBITS) + ENV_DECAY; - } - - SL->Einc = SL->EincR; - SL->Ecmp = ENV_END; - SL->Ecurp = RELEASE; - } -} - - -int Ym2612_Impl::SLOT_SET( int Adr, int data ) -{ - int nch = Adr & 3; - if ( nch == 3 ) - return 1; - - channel_t& ch = YM2612.CHANNEL [nch + (Adr & 0x100 ? 3 : 0)]; - slot_t& sl = ch.SLOT [(Adr >> 2) & 3]; - - switch ( Adr & 0xF0 ) - { - case 0x30: - if ( (sl.MUL = (data & 0x0F)) != 0 ) sl.MUL <<= 1; - else sl.MUL = 1; - - sl.DT = (int*) g.DT_TAB [(data >> 4) & 7]; - - ch.SLOT [0].Finc = -1; - - break; - - case 0x40: - sl.TL = data & 0x7F; - - // SOR2 do a lot of TL adjustement and this fix R.Shinobi jump sound... - YM2612_Special_Update(); - -#if ((ENV_HBITS - 7) < 0) - sl.TLL = sl.TL >> (7 - ENV_HBITS); +#ifdef USE_GENS +#include "Ym2612_Emu_Gens.cpp" #else - sl.TLL = sl.TL << (ENV_HBITS - 7); +#include "Ym2612_Emu_MAME.cpp" #endif - - break; - - case 0x50: - sl.KSR_S = 3 - (data >> 6); - - ch.SLOT [0].Finc = -1; - - if (data &= 0x1F) sl.AR = (int*) &g.AR_TAB [data << 1]; - else sl.AR = (int*) &g.NULL_RATE [0]; - - sl.EincA = sl.AR [sl.KSR]; - if (sl.Ecurp == ATTACK) sl.Einc = sl.EincA; - break; - - case 0x60: - if ( (sl.AMSon = (data & 0x80)) != 0 ) sl.AMS = ch.AMS; - else sl.AMS = 31; - - if (data &= 0x1F) sl.DR = (int*) &g.DR_TAB [data << 1]; - else sl.DR = (int*) &g.NULL_RATE [0]; - - sl.EincD = sl.DR [sl.KSR]; - if (sl.Ecurp == DECAY) sl.Einc = sl.EincD; - break; - - case 0x70: - if (data &= 0x1F) sl.SR = (int*) &g.DR_TAB [data << 1]; - else sl.SR = (int*) &g.NULL_RATE [0]; - - sl.EincS = sl.SR [sl.KSR]; - if ((sl.Ecurp == SUBSTAIN) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincS; - break; - - case 0x80: - sl.SLL = g.SL_TAB [data >> 4]; - - sl.RR = (int*) &g.DR_TAB [((data & 0xF) << 2) + 2]; - - sl.EincR = sl.RR [sl.KSR]; - if ((sl.Ecurp == RELEASE) && (sl.Ecnt < ENV_END)) sl.Einc = sl.EincR; - break; - - case 0x90: - // SSG-EG envelope shapes : - /* - E At Al H - - 1 0 0 0 \\\\ - 1 0 0 1 \___ - 1 0 1 0 \/\/ - 1 0 1 1 \ - 1 1 0 0 //// - 1 1 0 1 / - 1 1 1 0 /\/\ - 1 1 1 1 /___ - - E = SSG-EG enable - At = Start negate - Al = Altern - H = Hold */ - - set_seg( sl, (data & 8) ? (data & 0x0F) : 0 ); - break; - } - - return 0; -} - - -int Ym2612_Impl::CHANNEL_SET( int Adr, int data ) -{ - int num = Adr & 3; - if ( num == 3 ) - return 1; - - channel_t& ch = YM2612.CHANNEL [num + (Adr & 0x100 ? 3 : 0)]; - - switch ( Adr & 0xFC ) - { - case 0xA0: - YM2612_Special_Update(); - - ch.FNUM [0] = (ch.FNUM [0] & 0x700) + data; - ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; - - ch.SLOT [0].Finc = -1; - break; - - case 0xA4: - YM2612_Special_Update(); - - ch.FNUM [0] = (ch.FNUM [0] & 0x0FF) + ((data & 0x07) << 8); - ch.FOCT [0] = (data & 0x38) >> 3; - ch.KC [0] = (ch.FOCT [0] << 2) | FKEY_TAB [ch.FNUM [0] >> 7]; - - ch.SLOT [0].Finc = -1; - break; - - case 0xA8: - if ( Adr < 0x100 ) - { - num++; - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x700) + data; - YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | - FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; - } - break; - - case 0xAC: - if ( Adr < 0x100 ) - { - num++; - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].FNUM [num] = (YM2612.CHANNEL [2].FNUM [num] & 0x0FF) + ((data & 0x07) << 8); - YM2612.CHANNEL [2].FOCT [num] = (data & 0x38) >> 3; - YM2612.CHANNEL [2].KC [num] = (YM2612.CHANNEL [2].FOCT [num] << 2) | - FKEY_TAB [YM2612.CHANNEL [2].FNUM [num] >> 7]; - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; - } - break; - - case 0xB0: - if ( ch.ALGO != (data & 7) ) - { - // Fix VectorMan 2 heli sound (level 1) - YM2612_Special_Update(); - - ch.ALGO = data & 7; - - ch.SLOT [0].ChgEnM = 0; - ch.SLOT [1].ChgEnM = 0; - ch.SLOT [2].ChgEnM = 0; - ch.SLOT [3].ChgEnM = 0; - } - - ch.FB = 9 - ((data >> 3) & 7); // Real thing ? - -// if (ch.FB = ((data >> 3) & 7)) ch.FB = 9 - ch.FB; // Thunder force 4 (music stage 8), Gynoug, Aladdin bug sound... -// else ch.FB = 31; - break; - - case 0xB4: { - YM2612_Special_Update(); - - ch.LEFT = 0 - ((data >> 7) & 1); - ch.RIGHT = 0 - ((data >> 6) & 1); - - ch.AMS = LFO_AMS_TAB [(data >> 4) & 3]; - ch.FMS = LFO_FMS_TAB [data & 7]; - - for ( int i = 0; i < 4; i++ ) - { - slot_t& sl = ch.SLOT [i]; - sl.AMS = (sl.AMSon ? ch.AMS : 31); - } - break; - } - } - - return 0; -} - - -int Ym2612_Impl::YM_SET(int Adr, int data) -{ - switch ( Adr ) - { - case 0x22: - if (data & 8) // LFO enable - { - // Cool Spot music 1, LFO modified severals time which - // distord the sound, have to check that on a real genesis... - - g.LFOinc = g.LFO_INC_TAB [data & 7]; - } - else - { - g.LFOinc = g.LFOcnt = 0; - } - break; - - case 0x24: - YM2612.TimerA = (YM2612.TimerA & 0x003) | (((int) data) << 2); - - if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) - { - YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; - } - break; - - case 0x25: - YM2612.TimerA = (YM2612.TimerA & 0x3FC) | (data & 3); - - if (YM2612.TimerAL != (1024 - YM2612.TimerA) << 12) - { - YM2612.TimerAcnt = YM2612.TimerAL = (1024 - YM2612.TimerA) << 12; - } - break; - - case 0x26: - YM2612.TimerB = data; - - if (YM2612.TimerBL != (256 - YM2612.TimerB) << (4 + 12)) - { - YM2612.TimerBcnt = YM2612.TimerBL = (256 - YM2612.TimerB) << (4 + 12); - } - break; - - case 0x27: - // Parametre divers - // b7 = CSM MODE - // b6 = 3 slot mode - // b5 = reset b - // b4 = reset a - // b3 = timer enable b - // b2 = timer enable a - // b1 = load b - // b0 = load a - - if ((data ^ YM2612.Mode) & 0x40) - { - // We changed the channel 2 mode, so recalculate phase step - // This fix the punch sound in Street of Rage 2 - - YM2612_Special_Update(); - - YM2612.CHANNEL [2].SLOT [0].Finc = -1; // recalculate phase step - } - -// if ((data & 2) && (YM2612.Status & 2)) YM2612.TimerBcnt = YM2612.TimerBL; -// if ((data & 1) && (YM2612.Status & 1)) YM2612.TimerAcnt = YM2612.TimerAL; - -// YM2612.Status &= (~data >> 4); // Reset du Status au cas ou c'est demande - YM2612.Status &= (~data >> 4) & (data >> 2); // Reset Status - - YM2612.Mode = data; - break; - - case 0x28: { - int nch = data & 3; - if ( nch == 3 ) - return 1; - if ( data & 4 ) - nch += 3; - channel_t& ch = YM2612.CHANNEL [nch]; - - YM2612_Special_Update(); - - if (data & 0x10) KEY_ON(ch, S0); // On appuie sur la touche pour le slot 1 - else KEY_OFF(ch, S0); // On rel'che la touche pour le slot 1 - if (data & 0x20) KEY_ON(ch, S1); // On appuie sur la touche pour le slot 3 - else KEY_OFF(ch, S1); // On rel'che la touche pour le slot 3 - if (data & 0x40) KEY_ON(ch, S2); // On appuie sur la touche pour le slot 2 - else KEY_OFF(ch, S2); // On rel'che la touche pour le slot 2 - if (data & 0x80) KEY_ON(ch, S3); // On appuie sur la touche pour le slot 4 - else KEY_OFF(ch, S3); // On rel'che la touche pour le slot 4 - break; - } - - case 0x2B: - if (YM2612.DAC ^ (data & 0x80)) YM2612_Special_Update(); - - YM2612.DAC = data & 0x80; // activation/desactivation du DAC - break; - } - - return 0; -} - -void Ym2612_Impl::set_rate( double sample_rate, double clock_rate ) -{ - assert( sample_rate ); - assert( clock_rate > sample_rate ); - - int i; - - // 144 = 12 * (prescale * 2) = 12 * 6 * 2 - // prescale set to 6 by default - - double Frequence = clock_rate / sample_rate / 144.0; - if ( fabs( Frequence - 1.0 ) < 0.0000001 ) - Frequence = 1.0; - YM2612.TimerBase = int (Frequence * 4096.0); - - // Tableau TL : - // [0 - 4095] = +output [4095 - ...] = +output overflow (fill with 0) - // [12288 - 16383] = -output [16384 - ...] = -output overflow (fill with 0) - - for(i = 0; i < TL_LENGHT; i++) - { - if (i >= PG_CUT_OFF) // YM2612 cut off sound after 78 dB (14 bits output ?) - { - g.TL_TAB [TL_LENGHT + i] = g.TL_TAB [i] = 0; - } - else - { - double x = MAX_OUT; // Max output - x /= pow( 10.0, (ENV_STEP * i) / 20.0 ); // Decibel -> Voltage - - g.TL_TAB [i] = (int) x; - g.TL_TAB [TL_LENGHT + i] = -g.TL_TAB [i]; - } - } - - // Tableau SIN : - // g.SIN_TAB [x] [y] = sin(x) * y; - // x = phase and y = volume - - g.SIN_TAB [0] = g.SIN_TAB [SIN_LENGHT / 2] = PG_CUT_OFF; - - for(i = 1; i <= SIN_LENGHT / 4; i++) - { - double x = sin(2.0 * PI * (double) (i) / (double) (SIN_LENGHT)); // Sinus - x = 20 * log10(1 / x); // convert to dB - - int j = (int) (x / ENV_STEP); // Get TL range - - if (j > PG_CUT_OFF) j = (int) PG_CUT_OFF; - - g.SIN_TAB [i] = g.SIN_TAB [(SIN_LENGHT / 2) - i] = j; - g.SIN_TAB [(SIN_LENGHT / 2) + i] = g.SIN_TAB [SIN_LENGHT - i] = TL_LENGHT + j; - } - - // Tableau LFO (LFO wav) : - - for(i = 0; i < LFO_LENGHT; i++) - { - double x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus - x += 1.0; - x /= 2.0; // positive only - x *= 11.8 / ENV_STEP; // ajusted to MAX enveloppe modulation - - g.LFO_ENV_TAB [i] = (int) x; - - x = sin(2.0 * PI * (double) (i) / (double) (LFO_LENGHT)); // Sinus - x *= (double) ((1 << (LFO_HBITS - 1)) - 1); - - g.LFO_FREQ_TAB [i] = (int) x; - - } - - // Tableau Enveloppe : - // g.ENV_TAB [0] -> g.ENV_TAB [ENV_LENGHT - 1] = attack curve - // g.ENV_TAB [ENV_LENGHT] -> g.ENV_TAB [2 * ENV_LENGHT - 1] = decay curve - - for(i = 0; i < ENV_LENGHT; i++) - { - // Attack curve (x^8 - music level 2 Vectorman 2) - double x = pow(((double) ((ENV_LENGHT - 1) - i) / (double) (ENV_LENGHT)), 8); - x *= ENV_LENGHT; - - g.ENV_TAB [i] = (int) x; - - // Decay curve (just linear) - x = pow(((double) (i) / (double) (ENV_LENGHT)), 1); - x *= ENV_LENGHT; - - g.ENV_TAB [ENV_LENGHT + i] = (int) x; - } - for ( i = 0; i < 8; i++ ) - g.ENV_TAB [i + ENV_LENGHT * 2] = 0; - - g.ENV_TAB [ENV_END >> ENV_LBITS] = ENV_LENGHT - 1; // for the stopped state - - // Tableau pour la conversion Attack -> Decay and Decay -> Attack - - int j = ENV_LENGHT - 1; - for ( i = 0; i < ENV_LENGHT; i++ ) - { - while ( j && g.ENV_TAB [j] < i ) - j--; - - g.DECAY_TO_ATTACK [i] = j << ENV_LBITS; - } - - // Tableau pour le Substain Level - - for(i = 0; i < 15; i++) - { - double x = i * 3; // 3 and not 6 (Mickey Mania first music for test) - x /= ENV_STEP; - - g.SL_TAB [i] = ((int) x << ENV_LBITS) + ENV_DECAY; - } - - g.SL_TAB [15] = ((ENV_LENGHT - 1) << ENV_LBITS) + ENV_DECAY; // special case : volume off - - // Tableau Frequency Step - - for(i = 0; i < 2048; i++) - { - double x = (double) (i) * Frequence; - -#if ((SIN_LBITS + SIN_HBITS - (21 - 7)) < 0) - x /= (double) (1 << ((21 - 7) - SIN_LBITS - SIN_HBITS)); -#else - x *= (double) (1 << (SIN_LBITS + SIN_HBITS - (21 - 7))); -#endif - - x /= 2.0; // because MUL = value * 2 - - g.FINC_TAB [i] = (unsigned int) x; - } - - // Tableaux Attack & Decay Rate - - for(i = 0; i < 4; i++) - { - g.AR_TAB [i] = 0; - g.DR_TAB [i] = 0; - } - - for(i = 0; i < 60; i++) - { - double x = Frequence; - - x *= 1.0 + ((i & 3) * 0.25); // bits 0-1 : x1.00, x1.25, x1.50, x1.75 - x *= (double) (1 << ((i >> 2))); // bits 2-5 : shift bits (x2^0 - x2^15) - x *= (double) (ENV_LENGHT << ENV_LBITS); // on ajuste pour le tableau g.ENV_TAB - - g.AR_TAB [i + 4] = (unsigned int) (x / AR_RATE); - g.DR_TAB [i + 4] = (unsigned int) (x / DR_RATE); - } - - for(i = 64; i < 96; i++) - { - g.AR_TAB [i] = g.AR_TAB [63]; - g.DR_TAB [i] = g.DR_TAB [63]; - - g.NULL_RATE [i - 64] = 0; - } - - for ( i = 96; i < 128; i++ ) - g.AR_TAB [i] = 0; - - // Tableau Detune - - for(i = 0; i < 4; i++) - { - for (int j = 0; j < 32; j++) - { -#if ((SIN_LBITS + SIN_HBITS - 21) < 0) - double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence / (double) (1 << (21 - SIN_LBITS - SIN_HBITS)); -#else - double y = (double) DT_DEF_TAB [(i << 5) + j] * Frequence * (double) (1 << (SIN_LBITS + SIN_HBITS - 21)); -#endif - - g.DT_TAB [i + 0] [j] = (int) y; - g.DT_TAB [i + 4] [j] = (int) -y; - } - } - - // Tableau LFO - g.LFO_INC_TAB [0] = (unsigned int) (3.98 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [1] = (unsigned int) (5.56 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [2] = (unsigned int) (6.02 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [3] = (unsigned int) (6.37 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [4] = (unsigned int) (6.88 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [5] = (unsigned int) (9.63 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [6] = (unsigned int) (48.1 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - g.LFO_INC_TAB [7] = (unsigned int) (72.2 * (double) (1 << (LFO_HBITS + LFO_LBITS)) / sample_rate); - - reset(); -} - -const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate ) -{ - if ( !impl ) - { - impl = (Ym2612_Impl*) malloc( sizeof *impl ); - if ( !impl ) - return "Out of memory"; - impl->mute_mask = 0; - } - memset( &impl->YM2612, 0, sizeof impl->YM2612 ); - - impl->set_rate( sample_rate, clock_rate ); - - return 0; -} - -Ym2612_Emu::~Ym2612_Emu() -{ - free( impl ); -} - -inline void Ym2612_Impl::write0( int opn_addr, int data ) -{ - assert( (unsigned) data <= 0xFF ); - - if ( opn_addr < 0x30 ) - { - YM2612.REG [0] [opn_addr] = data; - YM_SET( opn_addr, data ); - } - else if ( YM2612.REG [0] [opn_addr] != data ) - { - YM2612.REG [0] [opn_addr] = data; - - if ( opn_addr < 0xA0 ) - SLOT_SET( opn_addr, data ); - else - CHANNEL_SET( opn_addr, data ); - } -} - -inline void Ym2612_Impl::write1( int opn_addr, int data ) -{ - assert( (unsigned) data <= 0xFF ); - - if ( opn_addr >= 0x30 && YM2612.REG [1] [opn_addr] != data ) - { - YM2612.REG [1] [opn_addr] = data; - - if ( opn_addr < 0xA0 ) - SLOT_SET( opn_addr + 0x100, data ); - else - CHANNEL_SET( opn_addr + 0x100, data ); - } -} - -void Ym2612_Emu::reset() -{ - impl->reset(); -} - -void Ym2612_Impl::reset() -{ - g.LFOcnt = 0; - YM2612.TimerA = 0; - YM2612.TimerAL = 0; - YM2612.TimerAcnt = 0; - YM2612.TimerB = 0; - YM2612.TimerBL = 0; - YM2612.TimerBcnt = 0; - YM2612.DAC = 0; - - YM2612.Status = 0; - - int i; - for ( i = 0; i < channel_count; i++ ) - { - channel_t& ch = YM2612.CHANNEL [i]; - - ch.LEFT = ~0; - ch.RIGHT = ~0; - ch.ALGO = 0; - ch.FB = 31; - ch.FMS = 0; - ch.AMS = 0; - - for ( int j = 0 ;j < 4 ; j++ ) - { - ch.S0_OUT [j] = 0; - ch.FNUM [j] = 0; - ch.FOCT [j] = 0; - ch.KC [j] = 0; - - ch.SLOT [j].Fcnt = 0; - ch.SLOT [j].Finc = 0; - ch.SLOT [j].Ecnt = ENV_END; // Put it at the end of Decay phase... - ch.SLOT [j].Einc = 0; - ch.SLOT [j].Ecmp = 0; - ch.SLOT [j].Ecurp = RELEASE; - - ch.SLOT [j].ChgEnM = 0; - } - } - - for ( i = 0; i < 0x100; i++ ) - { - YM2612.REG [0] [i] = -1; - YM2612.REG [1] [i] = -1; - } - - for ( i = 0xB6; i >= 0xB4; i-- ) - { - write0( i, 0xC0 ); - write1( i, 0xC0 ); - } - - for ( i = 0xB2; i >= 0x22; i-- ) - { - write0( i, 0 ); - write1( i, 0 ); - } - - write0( 0x2A, 0x80 ); -} - -void Ym2612_Emu::write0( int addr, int data ) -{ - impl->write0( addr, data ); -} - -void Ym2612_Emu::write1( int addr, int data ) -{ - impl->write1( addr, data ); -} - -void Ym2612_Emu::mute_voices( int mask ) { impl->mute_mask = mask; } - -static void update_envelope_( slot_t* sl ) -{ - switch ( sl->Ecurp ) - { - case 0: - // Env_Attack_Next - - // Verified with Gynoug even in HQ (explode SFX) - sl->Ecnt = ENV_DECAY; - - sl->Einc = sl->EincD; - sl->Ecmp = sl->SLL; - sl->Ecurp = DECAY; - break; - - case 1: - // Env_Decay_Next - - // Verified with Gynoug even in HQ (explode SFX) - sl->Ecnt = sl->SLL; - - sl->Einc = sl->EincS; - sl->Ecmp = ENV_END; - sl->Ecurp = SUBSTAIN; - break; - - case 2: - // Env_Substain_Next(slot_t *SL) - if (sl->SEG & 8) // SSG envelope type - { - int release = sl->SEG & 1; - - if ( !release ) - { - // re KEY ON - - // sl->Fcnt = 0; - // sl->ChgEnM = ~0; - - sl->Ecnt = 0; - sl->Einc = sl->EincA; - sl->Ecmp = ENV_DECAY; - sl->Ecurp = ATTACK; - } - - set_seg( *sl, (sl->SEG << 1) & 4 ); - - if ( !release ) - break; - } - // fall through - - case 3: - // Env_Release_Next - sl->Ecnt = ENV_END; - sl->Einc = 0; - sl->Ecmp = ENV_END + 1; - break; - - // default: no op - } -} - -inline void update_envelope( slot_t& sl ) -{ - int ecmp = sl.Ecmp; - if ( (sl.Ecnt += sl.Einc) >= ecmp ) - update_envelope_( &sl ); -} - -template -struct ym2612_update_chan { - static void func( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); -}; - -typedef void (*ym2612_update_chan_t)( tables_t&, channel_t&, Ym2612_Emu::sample_t*, int ); - -template -void ym2612_update_chan::func( tables_t& g, channel_t& ch, - Ym2612_Emu::sample_t* buf, int length ) -{ - int not_end = ch.SLOT [S3].Ecnt - ENV_END; - - // algo is a compile-time constant, so all conditions based on it are resolved - // during compilation - - // special cases - if ( algo == 7 ) - not_end |= ch.SLOT [S0].Ecnt - ENV_END; - - if ( algo >= 5 ) - not_end |= ch.SLOT [S2].Ecnt - ENV_END; - - if ( algo >= 4 ) - not_end |= ch.SLOT [S1].Ecnt - ENV_END; - - int CH_S0_OUT_1 = ch.S0_OUT [1]; - - int in0 = ch.SLOT [S0].Fcnt; - int in1 = ch.SLOT [S1].Fcnt; - int in2 = ch.SLOT [S2].Fcnt; - int in3 = ch.SLOT [S3].Fcnt; - - int YM2612_LFOinc = g.LFOinc; - int YM2612_LFOcnt = g.LFOcnt + YM2612_LFOinc; - - if ( !not_end ) - return; - - do - { - // envelope - int const env_LFO = g.LFO_ENV_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK]; - - short const* const ENV_TAB = g.ENV_TAB; - - #define CALC_EN( x ) \ - int temp##x = ENV_TAB [ch.SLOT [S##x].Ecnt >> ENV_LBITS] + ch.SLOT [S##x].TLL; \ - int en##x = ((temp##x ^ ch.SLOT [S##x].env_xor) + (env_LFO >> ch.SLOT [S##x].AMS)) & \ - ((temp##x - ch.SLOT [S##x].env_max) >> 31); - - CALC_EN( 0 ) - CALC_EN( 1 ) - CALC_EN( 2 ) - CALC_EN( 3 ) - - int const* const TL_TAB = g.TL_TAB; - - #define SINT( i, o ) (TL_TAB [g.SIN_TAB [(i)] + (o)]) - - // feedback - int CH_S0_OUT_0 = ch.S0_OUT [0]; - { - int temp = in0 + ((CH_S0_OUT_0 + CH_S0_OUT_1) >> ch.FB); - CH_S0_OUT_1 = CH_S0_OUT_0; - CH_S0_OUT_0 = SINT( (temp >> SIN_LBITS) & SIN_MASK, en0 ); - } - - int CH_OUTd; - if ( algo == 0 ) - { - int temp = in1 + CH_S0_OUT_1; - temp = in2 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 1 ) - { - int temp = in2 + CH_S0_OUT_1 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 2 ) - { - int temp = in2 + SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ); - temp = in3 + CH_S0_OUT_1 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 3 ) - { - int temp = in1 + CH_S0_OUT_1; - temp = in3 + SINT( (temp >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ); - } - else if ( algo == 4 ) - { - int temp = in3 + SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - CH_OUTd = SINT( (temp >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ); - //DO_LIMIT - } - else if ( algo == 5 ) - { - int temp = CH_S0_OUT_1; - CH_OUTd = SINT( ((in3 + temp) >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + temp) >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( ((in2 + temp) >> SIN_LBITS) & SIN_MASK, en2 ); - //DO_LIMIT - } - else if ( algo == 6 ) - { - CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( ((in1 + CH_S0_OUT_1) >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ); - //DO_LIMIT - } - else if ( algo == 7 ) - { - CH_OUTd = SINT( (in3 >> SIN_LBITS) & SIN_MASK, en3 ) + - SINT( (in1 >> SIN_LBITS) & SIN_MASK, en1 ) + - SINT( (in2 >> SIN_LBITS) & SIN_MASK, en2 ) + CH_S0_OUT_1; - //DO_LIMIT - } - - CH_OUTd >>= MAX_OUT_BITS - output_bits + 2; - - // update phase - unsigned freq_LFO = ((g.LFO_FREQ_TAB [YM2612_LFOcnt >> LFO_LBITS & LFO_MASK] * - ch.FMS) >> (LFO_HBITS - 1 + 1)) + (1L << (LFO_FMS_LBITS - 1)); - YM2612_LFOcnt += YM2612_LFOinc; - in0 += (ch.SLOT [S0].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in1 += (ch.SLOT [S1].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in2 += (ch.SLOT [S2].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - in3 += (ch.SLOT [S3].Finc * freq_LFO) >> (LFO_FMS_LBITS - 1); - - int t0 = buf [0] + (CH_OUTd & ch.LEFT); - int t1 = buf [1] + (CH_OUTd & ch.RIGHT); - - update_envelope( ch.SLOT [0] ); - update_envelope( ch.SLOT [1] ); - update_envelope( ch.SLOT [2] ); - update_envelope( ch.SLOT [3] ); - - ch.S0_OUT [0] = CH_S0_OUT_0; - buf [0] = t0; - buf [1] = t1; - buf += 2; - } - while ( --length ); - - ch.S0_OUT [1] = CH_S0_OUT_1; - - ch.SLOT [S0].Fcnt = in0; - ch.SLOT [S1].Fcnt = in1; - ch.SLOT [S2].Fcnt = in2; - ch.SLOT [S3].Fcnt = in3; -} - -static const ym2612_update_chan_t UPDATE_CHAN [8] = { - &ym2612_update_chan<0>::func, - &ym2612_update_chan<1>::func, - &ym2612_update_chan<2>::func, - &ym2612_update_chan<3>::func, - &ym2612_update_chan<4>::func, - &ym2612_update_chan<5>::func, - &ym2612_update_chan<6>::func, - &ym2612_update_chan<7>::func -}; - -void Ym2612_Impl::run_timer( int length ) -{ - int const step = 6; - int remain = length; - do - { - int n = step; - if ( n > remain ) - n = remain; - remain -= n; - - long i = n * YM2612.TimerBase; - if (YM2612.Mode & 1) // Timer A ON ? - { - // if ((YM2612.TimerAcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) - if ((YM2612.TimerAcnt -= i) <= 0) - { - // timer a overflow - - YM2612.Status |= (YM2612.Mode & 0x04) >> 2; - YM2612.TimerAcnt += YM2612.TimerAL; - - if (YM2612.Mode & 0x80) - { - KEY_ON( YM2612.CHANNEL [2], 0 ); - KEY_ON( YM2612.CHANNEL [2], 1 ); - KEY_ON( YM2612.CHANNEL [2], 2 ); - KEY_ON( YM2612.CHANNEL [2], 3 ); - } - } - } - - if (YM2612.Mode & 2) // Timer B ON ? - { - // if ((YM2612.TimerBcnt -= 14073) <= 0) // 13879=NTSC (old: 14475=NTSC 14586=PAL) - if ((YM2612.TimerBcnt -= i) <= 0) - { - // timer b overflow - YM2612.Status |= (YM2612.Mode & 0x08) >> 2; - YM2612.TimerBcnt += YM2612.TimerBL; - } - } - } - while ( remain > 0 ); -} - -void Ym2612_Impl::run( int pair_count, Ym2612_Emu::sample_t* out ) -{ - if ( pair_count <= 0 ) - return; - - if ( YM2612.Mode & 3 ) - run_timer( pair_count ); - - // Mise à jour des pas des compteurs-frequences s'ils ont ete modifies - - for ( int chi = 0; chi < channel_count; chi++ ) - { - channel_t& ch = YM2612.CHANNEL [chi]; - if ( ch.SLOT [0].Finc != -1 ) - continue; - - int i2 = 0; - if ( chi == 2 && (YM2612.Mode & 0x40) ) - i2 = 2; - - for ( int i = 0; i < 4; i++ ) - { - // static int seq [4] = { 2, 1, 3, 0 }; - // if ( i2 ) i2 = seq [i]; - - slot_t& sl = ch.SLOT [i]; - int finc = g.FINC_TAB [ch.FNUM [i2]] >> (7 - ch.FOCT [i2]); - int ksr = ch.KC [i2] >> sl.KSR_S; // keycode attenuation - sl.Finc = (finc + sl.DT [ch.KC [i2]]) * sl.MUL; - if (sl.KSR != ksr) // si le KSR a change alors - { // les differents taux pour l'enveloppe sont mis à jour - sl.KSR = ksr; - - sl.EincA = sl.AR [ksr]; - sl.EincD = sl.DR [ksr]; - sl.EincS = sl.SR [ksr]; - sl.EincR = sl.RR [ksr]; - - if (sl.Ecurp == ATTACK) - { - sl.Einc = sl.EincA; - } - else if (sl.Ecurp == DECAY) - { - sl.Einc = sl.EincD; - } - else if (sl.Ecnt < ENV_END) - { - if (sl.Ecurp == SUBSTAIN) - sl.Einc = sl.EincS; - else if (sl.Ecurp == RELEASE) - sl.Einc = sl.EincR; - } - } - - if ( i2 ) - i2 = (i2 ^ 2) ^ (i2 >> 1); - } - } - - for ( int i = 0; i < channel_count; i++ ) - { - if ( !(mute_mask & (1 << i)) && (i != 5 || !YM2612.DAC) ) - UPDATE_CHAN [YM2612.CHANNEL [i].ALGO]( g, YM2612.CHANNEL [i], out, pair_count ); - } - - g.LFOcnt += g.LFOinc * pair_count; -} - -void Ym2612_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } diff --git a/Frameworks/GME/gme/Ym2612_Emu.h b/Frameworks/GME/gme/Ym2612_Emu.h old mode 100755 new mode 100644 index 383ac72d7..d99359187 --- a/Frameworks/GME/gme/Ym2612_Emu.h +++ b/Frameworks/GME/gme/Ym2612_Emu.h @@ -1,10 +1,16 @@ -// YM2612 FM sound chip emulator interface +// YM2612 FM sound chip emulator -// Game_Music_Emu 0.5.2 +// Game_Music_Emu $vers #ifndef YM2612_EMU_H #define YM2612_EMU_H +// Gens is buggy and inaccurate, but faster +// #define USE_GENS +#ifdef USE_GENS struct Ym2612_Impl; +#else +typedef void Ym2612_Impl; +#endif class Ym2612_Emu { Ym2612_Impl* impl; @@ -12,24 +18,24 @@ public: Ym2612_Emu() { impl = 0; } ~Ym2612_Emu(); - // Set output sample rate and chip clock rates, in Hz. Returns non-zero - // if error. - const char* set_rate( double sample_rate, double clock_rate ); + // Sets sample rate and chip clock rate, in Hz. Returns non-zero + // if error. If clock_rate=0, uses sample_rate*144 + const char* set_rate( double sample_rate, double clock_rate = 0 ); - // Reset to power-up state + // Resets to power-up state void reset(); - // Mute voice n if bit n (1 << n) of mask is set + // Mutes voice n if bit n (1 << n) of mask is set enum { channel_count = 6 }; void mute_voices( int mask ); - // Write addr to register 0 then data to register 1 + // Writes addr to register 0 then data to register 1 void write0( int addr, int data ); - // Write addr to register 2 then data to register 3 + // Writes addr to register 2 then data to register 3 void write1( int addr, int data ); - // Run and add pair_count samples into current output buffer contents + // Runs and adds pair_count*2 samples into current output buffer contents typedef short sample_t; enum { out_chan_count = 2 }; // stereo void run( int pair_count, sample_t* out ); diff --git a/Frameworks/GME/gme/blargg_common.h b/Frameworks/GME/gme/blargg_common.h old mode 100755 new mode 100644 index e48d6469b..05c74ffb2 --- a/Frameworks/GME/gme/blargg_common.h +++ b/Frameworks/GME/gme/blargg_common.h @@ -1,175 +1,224 @@ -// Sets up common environment for Shay Green's libraries. -// To change configuration options, modify blargg_config.h, not this file. - -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -#include -#include -#include -#include - -#undef BLARGG_COMMON_H -// allow blargg_config.h to #include blargg_common.h -#include "blargg_config.h" -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -// STATIC_CAST(T,expr): Used in place of static_cast (expr) -#ifndef STATIC_CAST - #define STATIC_CAST(T,expr) ((T) (expr)) -#endif - -// blargg_err_t (0 on success, otherwise error string) -#ifndef blargg_err_t - typedef const char* blargg_err_t; -#endif - -// blargg_vector - very lightweight vector of POD types (no constructor/destructor) -template -class blargg_vector { - T* begin_; - size_t size_; -public: - blargg_vector() : begin_( 0 ), size_( 0 ) { } - ~blargg_vector() { free( begin_ ); } - size_t size() const { return size_; } - T* begin() const { return begin_; } - T* end() const { return begin_ + size_; } - blargg_err_t resize( size_t n ) - { - void* p = realloc( begin_, n * sizeof (T) ); - if ( !p && n ) - return "Out of memory"; - begin_ = (T*) p; - size_ = n; - return 0; - } - void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } - T& operator [] ( size_t n ) const - { - assert( n <= size_ ); // <= to allow past-the-end value - return begin_ [n]; - } -}; - -#ifndef BLARGG_DISABLE_NOTHROW - #if __cplusplus < 199711 - #define BLARGG_THROWS( spec ) - #else - #define BLARGG_THROWS( spec ) throw spec - #endif - #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ - void operator delete ( void* p ) { free( p ); } - #define BLARGG_NEW new -#else - #include - #define BLARGG_NEW new (std::nothrow) -#endif - -#define BLARGG_4CHAR( a, b, c, d ) \ - ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) - -// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -#ifndef BOOST_STATIC_ASSERT - #ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) - #else - // Some other compilers fail when declaring same function multiple times in class, - // so differentiate them by line - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) - #endif -#endif - -// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, -// compiler is assumed to support bool. If undefined, availability is determined. -#ifndef BLARGG_COMPILER_HAS_BOOL - #if defined (__MWERKS__) - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (_MSC_VER) - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (__GNUC__) - // supports bool - #elif __cplusplus < 199711 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif -#endif -#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL - // If you get errors here, modify your blargg_config.h file - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough -#include - -#if INT_MAX >= 0x7FFFFFFF - typedef int blargg_long; -#else - typedef long blargg_long; -#endif - -#if UINT_MAX >= 0xFFFFFFFF - typedef unsigned blargg_ulong; -#else - typedef unsigned long blargg_ulong; -#endif - -// BOOST::int8_t etc. - -// HAVE_STDINT_H: If defined, use for int8_t etc. -#if defined (HAVE_STDINT_H) - #include - #define BOOST - -// HAVE_INTTYPES_H: If defined, use for int8_t etc. -#elif defined (HAVE_INTTYPES_H) - #include - #define BOOST - -#else - struct BOOST - { - #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F - typedef signed char int8_t; - typedef unsigned char uint8_t; - #else - // No suitable 8-bit type available - typedef struct see_blargg_common_h int8_t; - typedef struct see_blargg_common_h uint8_t; - #endif - - #if USHRT_MAX == 0xFFFF - typedef short int16_t; - typedef unsigned short uint16_t; - #else - // No suitable 16-bit type available - typedef struct see_blargg_common_h int16_t; - typedef struct see_blargg_common_h uint16_t; - #endif - - #if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; - #elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; - #else - // No suitable 32-bit type available - typedef struct see_blargg_common_h int32_t; - typedef struct see_blargg_common_h uint32_t; - #endif - }; -#endif - -#endif -#endif +// Sets up common environment for Shay Green's libraries. +// To change configuration options, modify blargg_config.h, not this file. + +// $package +#ifndef BLARGG_COMMON_H +#define BLARGG_COMMON_H + +#include +#include +#include + +typedef const char* blargg_err_t; // 0 on success, otherwise error string + +// Success; no error +int const blargg_ok = 0; + +// BLARGG_RESTRICT: equivalent to C99's restrict, where supported +#if __GNUC__ >= 3 || _MSC_VER >= 1100 + #define BLARGG_RESTRICT __restrict +#else + #define BLARGG_RESTRICT +#endif + +#if __cplusplus >= 199711 + #define BLARGG_MUTABLE mutable +#else + #define BLARGG_MUTABLE +#endif + +/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant). +I don't just use 'abcd' because that's implementation-dependent. */ +#define BLARGG_4CHAR( a, b, c, d ) \ + ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF)) + +/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0. +Can be used at file, function, or class scope. */ +#ifdef _MSC_VER + // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) +#else + // Others fail when declaring same function multiple times in class, + // so differentiate them by line + #define BLARGG_STATIC_ASSERT( expr ) \ + void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) +#endif + +/* Pure virtual functions cause a vtable entry to a "called pure virtual" +error handler, requiring linkage to the C++ runtime library. This macro is +used in place of the "= 0", and simply expands to its argument. During +development, it expands to "= 0", allowing detection of missing overrides. */ +#define BLARGG_PURE( def ) def + +/* My code depends on ASCII anywhere a character or string constant is +compared with data read from a file, and anywhere file data is read and +treated as a string. */ +#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61 + #error "ASCII character set required" +#endif + +/* My code depends on int being at least 32 bits. Almost everything these days +uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints +to test with. The issue can't be gotten around by using a suitable blargg_int +everywhere either, because int is often converted to implicitly when doing +arithmetic on smaller types. */ +#if UINT_MAX < 0xFFFFFFFF + #error "int must be at least 32 bits" +#endif + +// In case compiler doesn't support these properly. Used rarely. +#define STATIC_CAST(T,expr) static_cast (expr) +#define CONST_CAST( T,expr) const_cast (expr) + +// User configuration can override the above macros if necessary +#include "blargg_config.h" + +#ifdef BLARGG_NAMESPACE + #define BLARGG_NAMESPACE_BEGIN namespace BLARGG_NAMESPACE { + #define BLARGG_NAMESPACE_END } + + BLARGG_NAMESPACE_BEGIN + BLARGG_NAMESPACE_END + using namespace BLARGG_NAMESPACE; +#else + #define BLARGG_NAMESPACE_BEGIN + #define BLARGG_NAMESPACE_END +#endif + +BLARGG_NAMESPACE_BEGIN + +/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a +future version. In GCC, we can let the compiler warn. In other compilers, +we strip it out unless BLARGG_LEGACY is true. */ +#if BLARGG_LEGACY + // Allow old client code to work without warnings + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) text +#elif __GNUC__ >= 4 + // In GCC, we can mark declarations and let the compiler warn + #define BLARGG_DEPRECATED_TEXT( text ) text + #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text +#else + // By default, deprecated items are removed, to avoid use in new code + #define BLARGG_DEPRECATED_TEXT( text ) + #define BLARGG_DEPRECATED( text ) +#endif + +/* BOOST::int8_t, BOOST::int32_t, etc. +I used BOOST since I originally was going to allow use of the boost library +for prividing the definitions. If I'm defining them, they must be scoped or +else they could conflict with the standard ones at global scope. Even if +HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at +global scope already. */ +#if defined (HAVE_STDINT_H) || \ + UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF + #include + #define BOOST +#else + struct BOOST + { + typedef signed char int8_t; + typedef unsigned char uint8_t; + typedef short int16_t; + typedef unsigned short uint16_t; + typedef int int32_t; + typedef unsigned int uint32_t; + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + }; +#endif + +/* My code is not written with exceptions in mind, so either uses new (nothrow) +OR overrides operator new in my classes. The former is best since clients +creating objects will get standard exceptions on failure, but that causes it +to require the standard C++ library. So, when the client is using the C +interface, I override operator new to use malloc. */ + +// BLARGG_DISABLE_NOTHROW is put inside classes +#ifndef BLARGG_DISABLE_NOTHROW + // throw spec mandatory in ISO C++ if NULL can be returned + #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300 + #define BLARGG_THROWS_NOTHING throw () + #else + #define BLARGG_THROWS_NOTHING + #endif + + #define BLARGG_DISABLE_NOTHROW \ + void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\ + void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); } + + #define BLARGG_NEW new +#else + // BLARGG_NEW is used in place of new in library code + #include + #define BLARGG_NEW new (std::nothrow) +#endif + + class blargg_vector_ { + protected: + void* begin_; + size_t size_; + void init(); + blargg_err_t resize_( size_t n, size_t elem_size ); + public: + size_t size() const { return size_; } + void clear(); + }; + +// Very lightweight vector for POD types (no constructor/destructor) +template +class blargg_vector : public blargg_vector_ { + union T_must_be_pod { T t; }; // fails if T is not POD +public: + blargg_vector() { init(); } + ~blargg_vector() { clear(); } + + blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); } + + T* begin() { return static_cast (begin_); } + const T* begin() const { return static_cast (begin_); } + + T* end() { return static_cast (begin_) + size_; } + const T* end() const { return static_cast (begin_) + size_; } + + T& operator [] ( size_t n ) + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } + + const T& operator [] ( size_t n ) const + { + assert( n < size_ ); + return static_cast (begin_) [n]; + } +}; + +// Callback function with user data. +// blargg_callback set_callback; // for user, this acts like... +// void set_callback( T func, void* user_data = NULL ); // ...this +// To call function, do set_callback.f( .. set_callback.data ... ); +template +struct blargg_callback +{ + T f; + void* data; + blargg_callback() { f = NULL; } + void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; } +}; + +#ifndef _WIN32 + // Not supported on any other platforms + #undef BLARGG_UTF8_PATHS +#endif + +BLARGG_DEPRECATED( typedef signed int blargg_long; ) +BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; ) +#if BLARGG_LEGACY + #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT +#endif + +BLARGG_NAMESPACE_END + +#endif diff --git a/Frameworks/GME/gme/blargg_config.h b/Frameworks/GME/gme/blargg_config.h old mode 100755 new mode 100644 index 9e9c751d6..c31f7d53b --- a/Frameworks/GME/gme/blargg_config.h +++ b/Frameworks/GME/gme/blargg_config.h @@ -1,30 +1,56 @@ -// Library configuration. Modify this file as necessary. - -#ifndef BLARGG_CONFIG_H -#define BLARGG_CONFIG_H - -// Uncomment to use zlib for transparent decompression of gzipped files -//#define HAVE_ZLIB_H - -// Uncomment to support only the listed game music types. See gme_type_list.cpp -// for a list of all types. -//#define GME_TYPE_LIST gme_nsf_type, gme_gbs_type - -// Uncomment to enable platform-specific optimizations -//#define BLARGG_NONPORTABLE 1 - -// Uncomment to use faster, lower quality sound synthesis -//#define BLIP_BUFFER_FAST 1 - -// Uncomment if automatic byte-order determination doesn't work -//#define BLARGG_BIG_ENDIAN 1 - -// Uncomment if you get errors in the bool section of blargg_common.h -//#define BLARGG_COMPILER_HAS_BOOL 1 - -// Use standard config.h if present -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#endif +// Library configuration. Modify this file as necessary. + +// $package +#ifndef BLARGG_CONFIG_H +#define BLARGG_CONFIG_H + +// Uncomment a #define line below to have effect described. + +// Allow static linking with this library and one of my other libraries +// in the same program. +//#define BLARGG_NAMESPACE blargg_gme + +// Use zlib for transparent decompression of gzipped files. +//#define HAVE_ZLIB_H + +// Support only listed music types. Remove a line to disable that type. +/* #define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_sfm_type,\ + gme_sgc_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type +*/ + +// Enable platform-specific optimizations. +//#define BLARGG_NONPORTABLE 1 + +// Use faster sample rate convertor for SPC music. +//#define GME_SPC_FAST_RESAMPLER 1 + +// Use faster sample rate convertor for VGM and GYM music. +//#define GME_VGM_FAST_RESAMPLER 1 + +// Use faster, significantly lower quality sound synthesis for classic emulators. +//#define BLIP_BUFFER_FAST 1 + +// Reduce memory usage of gme.h by disabling gme_set_effects_config(). +//#define GME_DISABLE_EFFECTS 1 + +// Force library to use assume big-endian processor. +//#define BLARGG_BIG_ENDIAN 1 + +// Use standard config.h if present +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#endif diff --git a/Frameworks/GME/gme/blargg_endian.h b/Frameworks/GME/gme/blargg_endian.h old mode 100755 new mode 100644 index 671655654..290175f71 --- a/Frameworks/GME/gme/blargg_endian.h +++ b/Frameworks/GME/gme/blargg_endian.h @@ -1,158 +1,203 @@ -// CPU Byte Order Utilities - -// Game_Music_Emu 0.5.2 -#ifndef BLARGG_ENDIAN -#define BLARGG_ENDIAN - -#include "blargg_common.h" - -// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) -#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 1 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one may be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else - -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 -#endif - -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__mips__) || defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#else - // No endian specified; assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN -#endif - -inline void blargg_verify_byte_order() -{ - #ifndef NDEBUG - #if BLARGG_BIG_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i == 0 ); - #elif BLARGG_LITTLE_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i != 0 ); - #endif - #endif -} - -inline unsigned get_le16( void const* p ) { - return ((unsigned char const*) p) [1] * 0x100u + - ((unsigned char const*) p) [0]; -} -inline unsigned get_be16( void const* p ) { - return ((unsigned char const*) p) [0] * 0x100u + - ((unsigned char const*) p) [1]; -} -inline blargg_ulong get_le32( void const* p ) { - return ((unsigned char const*) p) [3] * 0x01000000u + - ((unsigned char const*) p) [2] * 0x00010000u + - ((unsigned char const*) p) [1] * 0x00000100u + - ((unsigned char const*) p) [0]; -} -inline blargg_ulong get_be32( void const* p ) { - return ((unsigned char const*) p) [0] * 0x01000000u + - ((unsigned char const*) p) [1] * 0x00010000u + - ((unsigned char const*) p) [2] * 0x00000100u + - ((unsigned char const*) p) [3]; -} -inline void set_le16( void* p, unsigned n ) { - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be16( void* p, unsigned n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} -inline void set_le32( void* p, blargg_ulong n ) { - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} -inline void set_be32( void* p, blargg_ulong n ) { - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [3] = (unsigned char) n; -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #endif - - #if BLARGG_CPU_POWERPC && defined (__MWERKS__) - // PowerPC has special byte-reversed instructions - // to do: assumes that PowerPC is running in big-endian mode - // to do: implement for other compilers which don't support these macros - #define GET_LE16( addr ) (__lhbrx( (addr), 0 )) - #define GET_LE32( addr ) (__lwbrx( (addr), 0 )) - #define SET_LE16( addr, data ) (__sthbrx( (data), (addr), 0 )) - #define SET_LE32( addr, data ) (__stwbrx( (data), (addr), 0 )) - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } - -#endif +// CPU Byte Order Utilities + +// $package +#ifndef BLARGG_ENDIAN_H +#define BLARGG_ENDIAN_H + +#include "blargg_common.h" + +// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) +#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64) + #define BLARGG_CPU_X86 1 + #define BLARGG_CPU_CISC 1 +#endif + +#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \ + defined (__POWERPC__) || defined (__powerc) + #define BLARGG_CPU_POWERPC 1 + #define BLARGG_CPU_RISC 1 +#endif + +// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only +// one may be #defined to 1. Only needed if something actually depends on byte order. +#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) +#ifdef __GLIBC__ + // GCC handles this for us + #include + #if __BYTE_ORDER == __LITTLE_ENDIAN + #define BLARGG_LITTLE_ENDIAN 1 + #elif __BYTE_ORDER == __BIG_ENDIAN + #define BLARGG_BIG_ENDIAN 1 + #endif +#else + +#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ + (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) + #define BLARGG_LITTLE_ENDIAN 1 +#endif + +#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ + defined (__sparc__) || BLARGG_CPU_POWERPC || \ + (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) + #define BLARGG_BIG_ENDIAN 1 +#elif !defined (__mips__) + // No endian specified; assume little-endian, since it's most common + #define BLARGG_LITTLE_ENDIAN 1 +#endif +#endif +#endif + +#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN + #undef BLARGG_LITTLE_ENDIAN + #undef BLARGG_BIG_ENDIAN +#endif + +BLARGG_NAMESPACE_BEGIN + +inline void blargg_verify_byte_order() +{ + #ifndef NDEBUG + #if BLARGG_BIG_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i == 0 ); + #elif BLARGG_LITTLE_ENDIAN + volatile int i = 1; + assert( *(volatile char*) &i != 0 ); + #endif + #endif +} + +inline unsigned get_le16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be16( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 8 | + (unsigned) ((unsigned char const*) p) [1]; +} + +inline unsigned get_le24( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be24( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [2]; +} + +inline unsigned get_le32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [3] << 24 | + (unsigned) ((unsigned char const*) p) [2] << 16 | + (unsigned) ((unsigned char const*) p) [1] << 8 | + (unsigned) ((unsigned char const*) p) [0]; +} + +inline unsigned get_be32( void const* p ) +{ + return (unsigned) ((unsigned char const*) p) [0] << 24 | + (unsigned) ((unsigned char const*) p) [1] << 16 | + (unsigned) ((unsigned char const*) p) [2] << 8 | + (unsigned) ((unsigned char const*) p) [3]; +} + +inline void set_le16( void* p, unsigned n ) +{ + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [0] = (unsigned char) n; +} + +inline void set_be16( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) n; +} + +inline void set_le32( void* p, unsigned n ) +{ + ((unsigned char*) p) [0] = (unsigned char) n; + ((unsigned char*) p) [1] = (unsigned char) (n >> 8); + ((unsigned char*) p) [2] = (unsigned char) (n >> 16); + ((unsigned char*) p) [3] = (unsigned char) (n >> 24); +} + +inline void set_be32( void* p, unsigned n ) +{ + ((unsigned char*) p) [3] = (unsigned char) n; + ((unsigned char*) p) [2] = (unsigned char) (n >> 8); + ((unsigned char*) p) [1] = (unsigned char) (n >> 16); + ((unsigned char*) p) [0] = (unsigned char) (n >> 24); +} + +#if BLARGG_NONPORTABLE + // Optimized implementation if byte order is known + #if BLARGG_LITTLE_ENDIAN + #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + #elif BLARGG_BIG_ENDIAN + #define GET_BE16( addr ) (*(BOOST::uint16_t const*) (addr)) + #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr)) + #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) + #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) + + #if BLARGG_CPU_POWERPC + // PowerPC has special byte-reversed instructions + #if defined (__MWERKS__) + #define GET_LE16( addr ) (__lhbrx( addr, 0 )) + #define GET_LE32( addr ) (__lwbrx( addr, 0 )) + #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) + #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) + #elif defined (__GNUC__) + #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) + #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) + #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) + #endif + #endif + #endif +#endif + +#ifndef GET_LE16 + #define GET_LE16( addr ) get_le16( addr ) + #define SET_LE16( addr, data ) set_le16( addr, data ) +#endif + +#ifndef GET_LE32 + #define GET_LE32( addr ) get_le32( addr ) + #define SET_LE32( addr, data ) set_le32( addr, data ) +#endif + +#ifndef GET_BE16 + #define GET_BE16( addr ) get_be16( addr ) + #define SET_BE16( addr, data ) set_be16( addr, data ) +#endif + +#ifndef GET_BE32 + #define GET_BE32( addr ) get_be32( addr ) + #define SET_BE32( addr, data ) set_be32( addr, data ) +#endif + +// auto-selecting versions + +inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( BOOST::uint32_t* p, unsigned n ) { SET_LE32( p, n ); } +inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( BOOST::uint32_t* p, unsigned n ) { SET_BE32( p, n ); } +inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); } +inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); } +inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); } +inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); } + +BLARGG_NAMESPACE_END + +#endif diff --git a/Frameworks/GME/gme/blargg_source.h b/Frameworks/GME/gme/blargg_source.h old mode 100755 new mode 100644 index 945bf3498..a81673619 --- a/Frameworks/GME/gme/blargg_source.h +++ b/Frameworks/GME/gme/blargg_source.h @@ -1,78 +1,174 @@ -// Included at the beginning of library source files, after all other #include lines -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert( expr ) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -inline void blargg_dprintf_( const char*, ... ) { } -#undef dprintf -#define dprintf (1) ? (void) 0 : blargg_dprintf_ - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#define check( expr ) ((void) 0) - -// If expr yields error string, return it from current function, otherwise continue. -#undef RETURN_ERR -#define RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is 0, return out of memory error string. -#undef CHECK_ALLOC -#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) - -// Avoid any macros which evaluate their arguments multiple times -#undef min -#undef max - -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -template -inline T min( T x, T y ) -{ - if ( x < y ) - return x; - return y; -} - -template -inline T max( T x, T y ) -{ - if ( x < y ) - return y; - return x; -} - -// TODO: good idea? bad idea? -#undef byte -#define byte byte_ -typedef unsigned char byte; - -// deprecated -#define BLARGG_CHECK_ALLOC CHECK_ALLOC -#define BLARGG_RETURN_ERR RETURN_ERR - -// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check -#ifdef BLARGG_SOURCE_BEGIN - #include BLARGG_SOURCE_BEGIN -#endif - -#endif +/* Included at the beginning of library source files, AFTER all other #include +lines. Sets up helpful macros and services used in my source code. Since this +is only "active" in my source code, I don't have to worry about polluting the +global namespace with unprefixed names. */ + +// $package +#ifndef BLARGG_SOURCE_H +#define BLARGG_SOURCE_H + +#ifndef BLARGG_COMMON_H // optimization only + #include "blargg_common.h" +#endif +#include "blargg_errors.h" +#include "gme_custom_dprintf.h" + +#include /* memcpy(), memset(), memmove() */ +#include /* offsetof() */ + +/* The following four macros are for debugging only. Some or all might be +defined to do nothing, depending on the circumstances. Described is what +happens when a particular macro is defined to do something. When defined to +do nothing, the macros do NOT evaluate their argument(s). */ + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking internal state and consistency. A failed assertion indicates a bug +in MY code. + +void assert( bool expr ); */ +#include + +/* If expr is false, prints file and line number, then aborts program. Meant +for checking caller-supplied parameters and operations that are outside the +control of the module. A failed requirement probably indicates a bug in YOUR +code. + +void require( bool expr ); */ +#undef require +#define require( expr ) assert( expr ) + +/* Like printf() except output goes to debugging console/file. + +void dprintf( const char format [], ... ); */ + +#ifdef CUSTOM_DPRINTF_FUNCTION + +static inline void dprintf( const char * fmt, ... ) +{ + if (gme_custom_dprintf) + { + va_list vl; + va_start(vl, fmt); + gme_custom_dprintf(fmt, vl); + va_end(vl); + } +} + +#else + +#ifdef NDEBUG +static inline void blargg_dprintf_( const char [], ... ) { } +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ +#else +#include +#include +#undef dprintf +#define dprintf (1) ? (void) 0 : blargg_dprintf_ +#ifndef _WIN32 +#include +static inline void blargg_dprintf_( const char * fmt, ... ) +{ + char error[512]; + va_list vl; + va_start(vl, fmt); + vsnprintf( error, 511, fmt, vl ); + va_end(vl); + fputs( error, stderr ); +} +#else +#include +static inline void blargg_dprintf_( const char * fmt, ... ) +{ + char error[512]; + va_list vl; + va_start(vl, fmt); + vsnprintf_s( error, 511, 511, fmt, vl ); + va_end(vl); + OutputDebugStringA( error ); +} +#endif +#endif + +#endif + +/* If expr is false, prints file and line number to debug console/log, then +continues execution normally. Meant for flagging potential problems or things +that should be looked into, but that aren't serious problems. + +void check( bool expr ); */ +#undef check +#define check( expr ) ((void) 0) + +/* If expr yields non-NULL error string, returns it from current function, +otherwise continues normally. */ +#undef RETURN_ERR +#define RETURN_ERR( expr ) \ + do {\ + blargg_err_t blargg_return_err_ = (expr);\ + if ( blargg_return_err_ )\ + return blargg_return_err_;\ + } while ( 0 ) + +/* If ptr is NULL, returns out-of-memory error, otherwise continues normally. */ +#undef CHECK_ALLOC +#define CHECK_ALLOC( ptr ) \ + do {\ + if ( !(ptr) )\ + return blargg_err_memory;\ + } while ( 0 ) + +/* The usual min/max functions for built-in types. + +template T min( T x, T y ) { return x < y ? x : y; } +template T max( T x, T y ) { return x > y ? x : y; } */ +#define BLARGG_DEF_MIN_MAX( type ) \ + static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\ + static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; } + +BLARGG_DEF_MIN_MAX( int ) +BLARGG_DEF_MIN_MAX( unsigned ) +BLARGG_DEF_MIN_MAX( long ) +BLARGG_DEF_MIN_MAX( unsigned long ) +BLARGG_DEF_MIN_MAX( float ) +BLARGG_DEF_MIN_MAX( double ) + +#undef min +#define min blargg_min + +#undef max +#define max blargg_max + +// typedef unsigned char byte; +typedef unsigned char blargg_byte; +#undef byte +#define byte blargg_byte + +#ifndef BLARGG_EXPORT + #if defined (_WIN32) && BLARGG_BUILD_DLL + #define BLARGG_EXPORT __declspec(dllexport) + #elif defined (__GNUC__) + // can always set visibility, even when not building DLL + #define BLARGG_EXPORT __attribute__ ((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif +#endif + +#if BLARGG_LEGACY + #define BLARGG_CHECK_ALLOC CHECK_ALLOC + #define BLARGG_RETURN_ERR RETURN_ERR +#endif + +// Called after failed operation when overall operation may still complete OK. +// Only used by unit testing framework. +#undef ACK_FAILURE +#define ACK_FAILURE() ((void)0) + +/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf etc. +and check */ +#ifdef BLARGG_SOURCE_BEGIN + #include BLARGG_SOURCE_BEGIN +#endif + +#endif diff --git a/Frameworks/GME/gme/gme.cpp b/Frameworks/GME/gme/gme.cpp old mode 100755 new mode 100644 index d6cebfa8a..59e699161 --- a/Frameworks/GME/gme/gme.cpp +++ b/Frameworks/GME/gme/gme.cpp @@ -1,256 +1,433 @@ -// Game_Music_Emu 0.5.2. http://www.slack.net/~ant/ +// Game_Music_Emu $vers. http://www.slack.net/~ant/ + +#include "Music_Emu.h" + +#if !GME_DISABLE_EFFECTS +#include "Effects_Buffer.h" +#endif +#include "blargg_endian.h" +#include +#include + +/* Copyright (C) 2003-2009 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +#ifndef GME_TYPE_LIST + +// Default list of all supported game music types (copy this to blargg_config.h +// if you want to modify it) +#define GME_TYPE_LIST \ + gme_ay_type,\ + gme_gbs_type,\ + gme_gym_type,\ + gme_hes_type,\ + gme_kss_type,\ + gme_nsf_type,\ + gme_nsfe_type,\ + gme_sap_type,\ + gme_sfm_type,\ + gme_sgc_type,\ + gme_spc_type,\ + gme_vgm_type,\ + gme_vgz_type + +#endif + +static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; + +gme_type_t const* gme_type_list() +{ + return gme_type_list_; +} + +const char* gme_identify_header( void const* header ) +{ + switch ( get_be32( header ) ) + { + case BLARGG_4CHAR('Z','X','A','Y'): return "AY"; + case BLARGG_4CHAR('G','B','S',0x01): return "GBS"; + case BLARGG_4CHAR('G','Y','M','X'): return "GYM"; + case BLARGG_4CHAR('H','E','S','M'): return "HES"; + case BLARGG_4CHAR('K','S','C','C'): + case BLARGG_4CHAR('K','S','S','X'): return "KSS"; + case BLARGG_4CHAR('N','E','S','M'): return "NSF"; + case BLARGG_4CHAR('N','S','F','E'): return "NSFE"; + case BLARGG_4CHAR('S','A','P',0x0D): return "SAP"; + case BLARGG_4CHAR('S','F','M','1'): return "SFM"; + case BLARGG_4CHAR('S','G','C',0x1A): return "SGC"; + case BLARGG_4CHAR('S','N','E','S'): return "SPC"; + case BLARGG_4CHAR('V','g','m',' '): return "VGM"; + } + return ""; +} + +static void to_uppercase( const char in [], int len, char out [] ) +{ + for ( int i = 0; i < len; i++ ) + { + if ( !(out [i] = toupper( in [i] )) ) + return; + } + *out = 0; // extension too long +} + +gme_type_t gme_identify_extension( const char extension_ [] ) +{ + char const* end = strrchr( extension_, '.' ); + if ( end ) + extension_ = end + 1; + + char extension [6]; + to_uppercase( extension_, sizeof extension, extension ); + + gme_type_t const* types = gme_type_list_; + for ( ; *types; types++ ) + if ( !strcmp( extension, (*types)->extension_ ) ) + break; + return *types; +} + +gme_err_t gme_identify_file( const char path [], gme_type_t* type_out ) +{ + *type_out = gme_identify_extension( path ); + // TODO: don't examine header if file has extension? + if ( !*type_out ) + { + char header [4]; + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + RETURN_ERR( in.read( header, sizeof header ) ); + *type_out = gme_identify_extension( gme_identify_header( header ) ); + } + return blargg_ok; +} + +gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, int sample_rate ) +{ + require( (data || !size) && out ); + *out = NULL; + + gme_type_t file_type = 0; + if ( size >= 4 ) + file_type = gme_identify_extension( gme_identify_header( data ) ); + if ( !file_type ) + return blargg_err_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + gme_err_t err = gme_load_data( emu, data, size ); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +gme_err_t gme_open_file( const char path [], Music_Emu** out, int sample_rate ) +{ + require( path && out ); + *out = NULL; + + GME_FILE_READER in; + RETURN_ERR( in.open( path ) ); + + char header [4]; + int header_size = 0; + + gme_type_t file_type = gme_identify_extension( path ); + if ( !file_type ) + { + header_size = sizeof header; + RETURN_ERR( in.read( header, sizeof header ) ); + file_type = gme_identify_extension( gme_identify_header( header ) ); + } + if ( !file_type ) + return blargg_err_file_type; + + Music_Emu* emu = gme_new_emu( file_type, sample_rate ); + CHECK_ALLOC( emu ); + + // optimization: avoids seeking/re-reading header + Remaining_Reader rem( header, header_size, &in ); + gme_err_t err = emu->load( rem ); + in.close(); + + if ( err ) + delete emu; + else + *out = emu; + + return err; +} + +Music_Emu* gme_new_emu( gme_type_t type, int rate ) +{ + if ( type ) + { + if ( rate == gme_info_only ) + return type->new_info(); + + Music_Emu* gme = type->new_emu(); + if ( gme ) + { + #if !GME_DISABLE_EFFECTS + if ( type->flags_ & 1 ) + { + gme->effects_buffer_ = BLARGG_NEW Simple_Effects_Buffer; + if ( gme->effects_buffer_ ) + gme->set_buffer( gme->effects_buffer_ ); + } + + if ( !(type->flags_ & 1) || gme->effects_buffer_ ) + #endif + { + if ( !gme->set_sample_rate( rate ) ) + { + check( gme->type() == type ); + return gme; + } + } + delete gme; + } + } + return NULL; +} + +gme_err_t gme_load_file( Music_Emu* gme, const char path [] ) { return gme->load_file( path ); } + +gme_err_t gme_load_data( Music_Emu* gme, void const* data, long size ) +{ + Mem_File_Reader in( data, size ); + return gme->load( in ); +} + +gme_err_t gme_load_custom( Music_Emu* gme, gme_reader_t func, long size, void* data ) +{ + Callback_Reader in( func, size, data ); + return gme->load( in ); +} + +void gme_delete( Music_Emu* gme ) { delete gme; } + +gme_type_t gme_type( Music_Emu const* gme ) { return gme->type(); } + +const char* gme_warning( Music_Emu* gme ) { return gme->warning(); } + +int gme_track_count( Music_Emu const* gme ) { return gme->track_count(); } + +struct gme_info_t_ : gme_info_t +{ + track_info_t info; + + BLARGG_DISABLE_NOTHROW +}; + +gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track ) +{ + *out = NULL; + + gme_info_t_* info = BLARGG_NEW gme_info_t_; + CHECK_ALLOC( info ); + + gme_err_t err = me->track_info( &info->info, track ); + if ( err ) + { + gme_free_info( info ); + return err; + } + + #define COPY(name) info->name = info->info.name; + + COPY( length ); + COPY( intro_length ); + COPY( loop_length ); + + info->i4 = -1; + info->i5 = -1; + info->i6 = -1; + info->i7 = -1; + info->i8 = -1; + info->i9 = -1; + info->i10 = -1; + info->i11 = -1; + info->i12 = -1; + info->i13 = -1; + info->i14 = -1; + info->i15 = -1; + + info->s7 = ""; + info->s8 = ""; + info->s9 = ""; + info->s10 = ""; + info->s11 = ""; + info->s12 = ""; + info->s13 = ""; + info->s14 = ""; + info->s15 = ""; + + COPY( system ); + COPY( game ); + COPY( song ); + COPY( author ); + COPY( copyright ); + COPY( comment ); + COPY( dumper ); + + #undef COPY + + info->play_length = info->length; + if ( info->play_length <= 0 ) + { + info->play_length = info->intro_length + 2 * info->loop_length; // intro + 2 loops + if ( info->play_length <= 0 ) + info->play_length = 150 * 1000; // 2.5 minutes + } + + *out = info; + + return blargg_ok; +} + +void gme_free_info( gme_info_t* info ) +{ + delete STATIC_CAST(gme_info_t_*,info); +} + +void* gme_user_data ( Music_Emu const* gme ) { return gme->user_data(); } +void gme_set_user_data ( Music_Emu* gme, void* new_user_data ) { gme->set_user_data( new_user_data ); } +void gme_set_user_cleanup(Music_Emu* gme, gme_user_cleanup_t func ){ gme->set_user_cleanup( func ); } + +gme_err_t gme_start_track ( Music_Emu* gme, int index ) { return gme->start_track( index ); } +gme_err_t gme_play ( Music_Emu* gme, int n, short p [] ) { return gme->play( n, p ); } +void gme_set_fade ( Music_Emu* gme, int start_msec ) { gme->set_fade( start_msec ); } +gme_bool gme_track_ended ( Music_Emu const* gme ) { return gme->track_ended(); } +int gme_tell ( Music_Emu const* gme ) { return gme->tell(); } +gme_err_t gme_seek ( Music_Emu* gme, int msec ) { return gme->seek( msec ); } +int gme_voice_count ( Music_Emu const* gme ) { return gme->voice_count(); } +void gme_ignore_silence ( Music_Emu* gme, gme_bool disable ) { gme->ignore_silence( disable != 0 ); } +void gme_set_tempo ( Music_Emu* gme, double t ) { gme->set_tempo( t ); } +void gme_mute_voice ( Music_Emu* gme, int index, gme_bool mute ){ gme->mute_voice( index, mute != 0 ); } +void gme_mute_voices ( Music_Emu* gme, int mask ) { gme->mute_voices( mask ); } +void gme_set_equalizer ( Music_Emu* gme, gme_equalizer_t const* eq ) { gme->set_equalizer( *eq ); } +void gme_equalizer ( Music_Emu const* gme, gme_equalizer_t* o ) { *o = gme->equalizer(); } +const char* gme_voice_name ( Music_Emu const* gme, int i ) { return gme->voice_name( i ); } -#include "Music_Emu.h" - -#if !GME_DISABLE_STEREO_DEPTH -#include "Effects_Buffer.h" -#endif -#include "blargg_endian.h" -#include -#include - -/* Copyright (C) 2003-2006 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifndef GME_TYPE_LIST - -// Default list of all supported game music types (copy this to blargg_config.h -// if you want to modify it) -#define GME_TYPE_LIST \ - gme_ay_type,\ - gme_gbs_type,\ - gme_gym_type,\ - gme_hes_type,\ - gme_kss_type,\ - gme_nsf_type,\ - gme_nsfe_type,\ - gme_sap_type,\ - gme_spc_type,\ - gme_vgm_type,\ - gme_vgz_type - -#endif - -static gme_type_t const gme_type_list_ [] = { GME_TYPE_LIST, 0 }; - -gme_type_t const* gme_type_list() +void gme_effects( Music_Emu const* gme, gme_effects_t* out ) { - return gme_type_list_; -} + static gme_effects_t const zero = { 0, 0, 0,0,0,0,0,0, 0, 0, 0,0,0,0,0,0 }; + *out = zero; -const char* gme_identify_header( void const* header ) -{ - switch ( get_be32( header ) ) - { - case BLARGG_4CHAR('Z','X','A','Y'): return "AY"; - case BLARGG_4CHAR('G','B','S',0x01): return "GBS"; - case BLARGG_4CHAR('G','Y','M','X'): return "GYM"; - case BLARGG_4CHAR('H','E','S','M'): return "HES"; - case BLARGG_4CHAR('K','S','C','C'): - case BLARGG_4CHAR('K','S','S','X'): return "KSS"; - case BLARGG_4CHAR('N','E','S','M'): return "NSF"; - case BLARGG_4CHAR('N','S','F','E'): return "NSFE"; - case BLARGG_4CHAR('S','A','P',0x0D): return "SAP"; - case BLARGG_4CHAR('S','N','E','S'): return "SPC"; - case BLARGG_4CHAR('V','g','m',' '): return "VGM"; - } - return ""; -} - -static void to_uppercase( const char* in, int len, char* out ) -{ - for ( int i = 0; i < len; i++ ) - { - if ( !(out [i] = toupper( in [i] )) ) - return; - } - *out = 0; // extension too long -} - -gme_type_t gme_identify_extension( const char* extension_ ) -{ - char const* end = strrchr( extension_, '.' ); - if ( end ) - extension_ = end + 1; - - char extension [6]; - to_uppercase( extension_, sizeof extension, extension ); - - for ( gme_type_t const* types = gme_type_list_; *types; types++ ) - if ( !strcmp( extension, (*types)->extension_ ) ) - return *types; - return 0; -} - -gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ) -{ - *type_out = gme_identify_extension( path ); - // TODO: don't examine header if file has extension? - if ( !*type_out ) - { - char header [4]; - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - RETURN_ERR( in.read( header, sizeof header ) ); - *type_out = gme_identify_extension( gme_identify_header( header ) ); - } - return 0; -} - -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ) -{ - require( (data || !size) && out ); - *out = 0; - - gme_type_t file_type = 0; - if ( size >= 4 ) - file_type = gme_identify_extension( gme_identify_header( data ) ); - if ( !file_type ) - return gme_wrong_file_type; - - Music_Emu* emu = gme_new_emu( file_type, sample_rate ); - CHECK_ALLOC( emu ); - - gme_err_t err = gme_load_data( emu, data, size ); - - if ( err ) - delete emu; - else - *out = emu; - - return err; -} - -gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ) -{ - require( path && out ); - *out = 0; - - GME_FILE_READER in; - RETURN_ERR( in.open( path ) ); - - char header [4]; - int header_size = 0; - - gme_type_t file_type = gme_identify_extension( path ); - if ( !file_type ) - { - header_size = sizeof header; - RETURN_ERR( in.read( header, sizeof header ) ); - file_type = gme_identify_extension( gme_identify_header( header ) ); - } - if ( !file_type ) - return gme_wrong_file_type; - - Music_Emu* emu = gme_new_emu( file_type, sample_rate ); - CHECK_ALLOC( emu ); - - // optimization: avoids seeking/re-reading header - Remaining_Reader rem( header, header_size, &in ); - gme_err_t err = emu->load( rem ); - in.close(); - - if ( err ) - delete emu; - else - *out = emu; - - return err; -} - -Music_Emu* gme_new_emu( gme_type_t type, long rate ) -{ - if ( type ) - { - if ( rate == gme_info_only ) - return type->new_info(); - - Music_Emu* me = type->new_emu(); - if ( me ) - { - #if !GME_DISABLE_STEREO_DEPTH - if ( type->flags_ & 1 ) - { - me->effects_buffer = BLARGG_NEW Effects_Buffer; - if ( me->effects_buffer ) - me->set_buffer( me->effects_buffer ); - } - - if ( !(type->flags_ & 1) || me->effects_buffer ) - #endif - { - if ( !me->set_sample_rate( rate ) ) - { - check( me->type() == type ); - return me; - } - } - delete me; - } - } - return 0; -} - -gme_err_t gme_load_file( Music_Emu* me, const char* path ) { return me->load_file( path ); } - -gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) -{ - Mem_File_Reader in( data, size ); - return me->load( in ); -} - -gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) -{ - Callback_Reader in( func, size, data ); - return me->load( in ); -} - -void gme_delete( Music_Emu* me ) { delete me; } - -gme_type_t gme_type( Music_Emu const* me ) { return me->type(); } - -const char* gme_warning( Music_Emu* me ) { return me->warning(); } - -int gme_track_count( Music_Emu const* me ) { return me->track_count(); } - -const char* gme_track_info( Music_Emu const* me, track_info_t* out, int track ) -{ - return me->track_info( out, track ); -} - -void gme_set_stereo_depth( Music_Emu* me, double depth ) -{ -#if !GME_DISABLE_STEREO_DEPTH - if ( me->effects_buffer ) - STATIC_CAST(Effects_Buffer*,me->effects_buffer)->set_depth( depth ); -#endif -} - -void* gme_user_data ( Music_Emu const* me ) { return me->user_data(); } -void gme_set_user_data ( Music_Emu* me, void* new_user_data ) { me->set_user_data( new_user_data ); } -void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->set_user_cleanup( func ); } - -gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } -gme_err_t gme_play ( Music_Emu* me, long n, short* p ) { return me->play( n, p ); } -void gme_set_fade ( Music_Emu* me, long start_msec ) { me->set_fade( start_msec ); } -int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } -long gme_tell ( Music_Emu const* me ) { return me->tell(); } -gme_err_t gme_seek ( Music_Emu* me, long msec ) { return me->seek( msec ); } -int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } -void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } -void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } -void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } -void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } -void gme_set_equalizer ( Music_Emu* me, gme_equalizer_t const* eq ) { me->set_equalizer( *eq ); } -gme_equalizer_t gme_equalizer( Music_Emu const* me ) { return me->equalizer(); } -const char** gme_voice_names ( Music_Emu const* me ) { return me->voice_names(); } + #if !GME_DISABLE_EFFECTS + { + Simple_Effects_Buffer* b = STATIC_CAST(Simple_Effects_Buffer*,gme->effects_buffer_); + if ( b ) + { + out->enabled = b->config().enabled; + out->echo = b->config().echo; + out->stereo = b->config().stereo; + out->surround = b->config().surround; + } + } + #endif +} + +void gme_set_effects( Music_Emu* gme, gme_effects_t const* in ) +{ + #if !GME_DISABLE_EFFECTS + { + Simple_Effects_Buffer* b = STATIC_CAST(Simple_Effects_Buffer*,gme->effects_buffer_); + if ( b ) + { + b->config().enabled = false; + if ( in ) + { + b->config().enabled = in->enabled; + b->config().echo = in->echo; + b->config().stereo = in->stereo; + b->config().surround = in->surround; + } + b->apply_config(); + } + } + #endif +} + +void gme_set_stereo_depth( Music_Emu* gme, double depth ) +{ + #if !GME_DISABLE_EFFECTS + { + if ( gme->effects_buffer_ ) + { + gme_effects_t cfg; + gme_effects( gme, &cfg ); + cfg.enabled = (depth > 0.0); + cfg.echo = depth; + cfg.stereo = depth; + cfg.surround = true; + gme_set_effects( gme, &cfg ); + } + } + #endif +} + +#define ENTRY( name ) { blargg_err_##name, gme_err_##name } +static blargg_err_to_code_t const gme_codes [] = +{ + ENTRY( generic ), + ENTRY( memory ), + ENTRY( caller ), + ENTRY( internal ), + ENTRY( limitation ), + + ENTRY( file_missing ), + ENTRY( file_read ), + ENTRY( file_io ), + ENTRY( file_eof ), + + ENTRY( file_type ), + ENTRY( file_feature ), + ENTRY( file_corrupt ), + + { 0, -1 } +}; +#undef ENTRY + +static int err_code( gme_err_t err ) +{ + return blargg_err_to_code( err, gme_codes ); +} + +int gme_err_code( gme_err_t err ) +{ + int code = err_code( err ); + return (code >= 0 ? code : gme_err_generic); +} + +gme_err_t gme_code_to_err( int code ) +{ + return blargg_code_to_err( code, gme_codes ); +} + +const char* gme_err_details( gme_err_t err ) +{ + // If we don't have error code assigned, return entire string + return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err )); +} + +const char* gme_err_str( gme_err_t err ) +{ + return blargg_err_str( err ); +} diff --git a/Frameworks/GME/gme/gme.h b/Frameworks/GME/gme/gme.h old mode 100755 new mode 100644 index 469c901cd..41c363a8f --- a/Frameworks/GME/gme/gme.h +++ b/Frameworks/GME/gme/gme.h @@ -1,222 +1,298 @@ -/* Game music emulator library C interface (also usable from C++) */ - -/* Game_Music_Emu 0.5.2 */ -#ifndef GME_H -#define GME_H - -#ifdef __cplusplus - extern "C" { -#endif - -/* Error string returned by library functions, or NULL if no error (success) */ -typedef const char* gme_err_t; - -/* First parameter of most gme_ functions is a pointer to the Music_Emu */ -typedef struct Music_Emu Music_Emu; - - -/******** Basic operations ********/ - -/* Create emulator and load game music file/data into it. Sets *out to new emulator. */ -gme_err_t gme_open_file( const char* path, Music_Emu** out, long sample_rate ); - -/* Number of tracks available */ -int gme_track_count( Music_Emu const* ); - -/* Start a track, where 0 is the first track */ -gme_err_t gme_start_track( Music_Emu*, int index ); - -/* Generate 'count' 16-bit signed samples info 'out'. Output is in stereo. */ -gme_err_t gme_play( Music_Emu*, long count, short* out ); - -/* Finish using emulator and free memory */ -void gme_delete( Music_Emu* ); - - -/******** Track position/length ********/ - -/* Set time to start fading track out. Once fade ends track_ended() returns true. -Fade time can be changed while track is playing. */ -void gme_set_fade( Music_Emu*, long start_msec ); - -/* True if a track has reached its end */ -int gme_track_ended( Music_Emu const* ); - -/* Number of milliseconds (1000 = one second) played since beginning of track */ -long gme_tell( Music_Emu const* ); - -/* Seek to new time in track. Seeking backwards or far forward can take a while. */ -gme_err_t gme_seek( Music_Emu*, long msec ); - - -/******** Informational ********/ - -/* If you only need track information from a music file, pass gme_info_only for -sample_rate to open/load. */ -enum { gme_info_only = -1 }; - -/* Most recent warning string, or NULL if none. Clears current warning after returning. -Warning is also cleared when loading a file and starting a track. */ -const char* gme_warning( Music_Emu* ); - -/* Load m3u playlist file (must be done after loading music) */ -gme_err_t gme_load_m3u( Music_Emu*, const char* path ); - -/* Clear any loaded m3u playlist and any internal playlist that the music format -supports (NSFE for example). */ -void gme_clear_playlist( Music_Emu* ); - -/* Get information for a particular track (length, name, author, etc.) */ -typedef struct track_info_t track_info_t; -gme_err_t gme_track_info( Music_Emu const*, track_info_t* out, int track ); - -struct track_info_t -{ - long track_count; - - /* times in milliseconds; -1 if unknown */ - long length; - long intro_length; - long loop_length; - - /* empty string if not available */ - char system [256]; - char game [256]; - char song [256]; - char author [256]; - char copyright [256]; - char comment [256]; - char dumper [256]; -}; -enum { gme_max_field = 255 }; - - -/******** Advanced playback ********/ - -/* Adjust stereo echo depth, where 0.0 = off and 1.0 = maximum. Has no effect for -GYM, SPC, and Sega Genesis VGM music */ -void gme_set_stereo_depth( Music_Emu*, double depth ); - -/* Disable automatic end-of-track detection and skipping of silence at beginning -if ignore is true */ -void gme_ignore_silence( Music_Emu*, int ignore ); - -/* Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. -Track length as returned by track_info() assumes a tempo of 1.0. */ -void gme_set_tempo( Music_Emu*, double tempo ); - -/* Number of voices used by currently loaded file */ -int gme_voice_count( Music_Emu const* ); - -/* Names of voices */ -const char** gme_voice_names( Music_Emu const* ); - -/* Mute/unmute voice i, where voice 0 is first voice */ -void gme_mute_voice( Music_Emu*, int index, int mute ); - -/* Set muting state of all voices at once using a bit mask, where -1 mutes all -voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ -void gme_mute_voices( Music_Emu*, int muting_mask ); - -/* Frequency equalizer parameters (see gme.txt) */ -typedef struct gme_equalizer_t -{ - double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ - long bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ -} gme_equalizer_t; - -/* Get current frequency equalizater parameters */ -gme_equalizer_t gme_equalizer( Music_Emu const* ); - -/* Change frequency equalizer parameters */ -void gme_set_equalizer( Music_Emu*, gme_equalizer_t const* eq ); - - - -/******** Game music types ********/ - -/* gme_type_t is a pointer to this structure. For example, gme_nsf_type->system is -"Nintendo NES" and gme_nsf_type->new_emu() is equilvant to new Nsf_Emu (in C++). */ -typedef struct gme_type_t_ const* gme_type_t; -struct gme_type_t_ -{ - const char* system; /* name of system this music file type is generally for */ - int track_count; /* non-zero for formats with a fixed number of tracks */ - Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ - Music_Emu* (*new_info)(); /* Create new info reader for this type */ - - /* internal */ - const char* extension_; - int flags_; -}; - -/* Emulator type constants for each supported file type */ -extern struct gme_type_t_ const gme_ay_type [], gme_gbs_type [], gme_gym_type [], - gme_hes_type [], gme_kss_type [], gme_nsf_type [], gme_nsfe_type [], - gme_sap_type [], gme_spc_type [], gme_vgm_type [], gme_vgz_type []; - -/* Type of this emulator */ -gme_type_t gme_type( Music_Emu const* ); - -/* Pointer to array of all music types, with NULL entry at end. Allows a player linked -to this library to support new music types without having to be updated. */ -gme_type_t const* gme_type_list(); - - -/******** Advanced file loading ********/ - -/* Error returned if file type is not supported */ -extern const char gme_wrong_file_type []; - -/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ -gme_err_t gme_open_data( void const* data, long size, Music_Emu** out, long sample_rate ); - -/* Determine likely game music type based on first four bytes of file. Returns -string containing proper file suffix (i.e. "NSF", "SPC", etc.) or "" if -file header is not recognized. */ -const char* gme_identify_header( void const* header ); - -/* Get corresponding music type for file path or extension passed in. */ -gme_type_t gme_identify_extension( const char* path_or_extension ); - -/* Determine file type based on file's extension or header (if extension isn't recognized). -Sets *type_out to type, or 0 if unrecognized or error. */ -gme_err_t gme_identify_file( const char* path, gme_type_t* type_out ); - -/* Create new emulator and set sample rate. Returns NULL if out of memory. If you only need -track information, pass gme_info_only for sample_rate. */ -Music_Emu* gme_new_emu( gme_type_t, long sample_rate ); - -/* Load music file into emulator */ -gme_err_t gme_load_file( Music_Emu*, const char* path ); - -/* Load music file from memory into emulator. Makes a copy of data passed. */ -gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); - -/* Load music file using custom data reader function that will be called to -read file data. Most emulators load the entire file in one read call. */ -typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, long count ); -gme_err_t gme_load_custom( Music_Emu*, gme_reader_t, long file_size, void* your_data ); - -/* Load m3u playlist file from memory (must be done after loading music) */ -gme_err_t gme_load_m3u_data( Music_Emu*, void const* data, long size ); - - -/******** User data ********/ - -/* Set/get pointer to data you want to associate with this emulator. -You can use this for whatever you want. */ -void gme_set_user_data( Music_Emu*, void* new_user_data ); -void* gme_user_data( Music_Emu const* ); - -/* Register cleanup function to be called when deleting emulator, or NULL to -clear it. Passes user_data to cleanup function. */ -typedef void (*gme_user_cleanup_t)( void* user_data ); -void gme_set_user_cleanup( Music_Emu*, gme_user_cleanup_t func ); - - -#ifdef __cplusplus - } -#endif - -#endif +/* Loads and plays video game music files into sample buffer */ + +/* Game_Music_Emu $vers */ +#ifndef GME_H +#define GME_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Pointer to error, or NULL if function was successful. See Errors below. */ +#ifndef gme_err_t /* (#ifndef allows better testing of library) */ + typedef const char* gme_err_t; +#endif + +/* First parameter of most functions is gme_t*, or const gme_t* if nothing is +changed. */ +typedef struct gme_t gme_t; + +/* Boolean; false = 0, true = 1 */ +typedef int gme_bool; + + +/******** Basic operations ********/ + +/* Opens game music file and points *out at it. If error, sets *out to NULL. */ +gme_err_t gme_open_file( const char path [], gme_t** out, int sample_rate ); + +/* Number of tracks */ +int gme_track_count( const gme_t* ); + +/* Starts a track, where 0 is the first track. Requires that 0 <= index < gme_track_count(). */ +gme_err_t gme_start_track( gme_t*, int index ); + +/* Generates 'count' 16-bit signed samples info 'out'. Output is in stereo, so count +must be even. */ +gme_err_t gme_play( gme_t*, int count, short out [] ); + +/* Closes file and frees memory. OK to pass NULL. */ +void gme_delete( gme_t* ); + + +/******** Track position/length ********/ + +/* Sets time to start fading track out. Once fade ends track_ended() returns true. +Fade time can be changed while track is playing. */ +void gme_set_fade( gme_t*, int start_msec ); + +/* True if a track has reached its end */ +gme_bool gme_track_ended( const gme_t* ); + +/* Number of milliseconds played since beginning of track (1000 = one second) */ +int gme_tell( const gme_t* ); + +/* Seeks to new time in track. Seeking backwards or far forward can take a while. */ +gme_err_t gme_seek( gme_t*, int msec ); + + +/******** Informational ********/ + +/* Use in place of sample rate for open/load if you only need to get track +information from a music file */ +enum { gme_info_only = -1 }; + +/* Most recent warning string, or NULL if none. Clears current warning after returning. +Warning is also cleared when loading a file and starting a track. */ +const char* gme_warning( gme_t* ); + +/* Loads m3u playlist file (must be done after loading music) */ +gme_err_t gme_load_m3u( gme_t*, const char path [] ); + +/* Clears any loaded m3u playlist and any internal playlist that the music format +supports (NSFE for example). */ +void gme_clear_playlist( gme_t* ); + +/* Passes back pointer to information for a particular track (length, name, author, etc.). +Must be freed after use. */ +typedef struct gme_info_t gme_info_t; +gme_err_t gme_track_info( const gme_t*, gme_info_t** out, int track ); + +/* Frees track information */ +void gme_free_info( gme_info_t* ); + +struct gme_info_t +{ + /* times in milliseconds; -1 if unknown */ + int length; /* total length, if file specifies it */ + int intro_length; /* length of song up to looping section */ + int loop_length; /* length of looping section */ + + /* Length if available, otherwise intro_length+loop_length*2 if available, + otherwise a default of 150000 (2.5 minutes). */ + int play_length; + + int i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */ + + /* empty string ("") if not available */ + const char* system; + const char* game; + const char* song; + const char* author; + const char* copyright; + const char* comment; + const char* dumper; + + const char *s7,*s8,*s9,*s10,*s11,*s12,*s13,*s14,*s15; /* reserved */ +}; + + +/******** Advanced playback ********/ + +/* Disables automatic end-of-track detection and skipping of silence at beginning +if ignore is true */ +void gme_ignore_silence( gme_t*, gme_bool ignore ); + +/* Adjusts song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed, etc. +Track length as returned by track_info() ignores tempo (assumes it's 1.0). */ +void gme_set_tempo( gme_t*, double tempo ); + +/* Number of voices used by currently loaded file */ +int gme_voice_count( const gme_t* ); + +/* Name of voice i, from 0 to gme_voice_count() - 1 */ +const char* gme_voice_name( const gme_t*, int i ); + +/* Mutes/unmutes single voice i, where voice 0 is first voice */ +void gme_mute_voice( gme_t*, int index, gme_bool mute ); + +/* Sets muting state of ALL voices at once using a bit mask, where -1 mutes all +voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ +void gme_mute_voices( gme_t*, int muting_mask ); + +/* Frequency equalizer parameters (see gme.txt) */ +typedef struct gme_equalizer_t +{ + double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ + double bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ + + double d2,d3,d4,d5,d6,d7,d8,d9; /* reserved */ +} gme_equalizer_t; + +/* Gets current frequency equalizer parameters */ +void gme_equalizer( const gme_t*, gme_equalizer_t* out ); + +/* Changes frequency equalizer parameters */ +void gme_set_equalizer( gme_t*, gme_equalizer_t const* eq ); + + + +/******** Effects processor ********/ + +/* Adds stereo surround and echo to music that's usually mono or has little +stereo. Has no effect on GYM, SPC, and Sega Genesis VGM music. */ + +/* Simplified control using a single value, where 0.0 = off and 1.0 = maximum */ +void gme_set_stereo_depth( gme_t*, double depth ); + +struct gme_effects_t +{ + double echo; /* Amount of echo, where 0.0 = none, 1.0 = lots */ + double stereo; /* Separation, where 0.0 = mono, 1.0 = hard left and right */ + + double d2,d3,d4,d5,d6,d7; /* reserved */ + + gme_bool enabled; /* If 0, no effects are added */ + gme_bool surround;/* If 1, some channels are put in "back", using phase inversion */ + + int i1,i3,i4,i5,i6,i7; /* reserved */ +}; +typedef struct gme_effects_t gme_effects_t; + +/* Sets effects configuration, or disables effects if NULL */ +void gme_set_effects( gme_t*, gme_effects_t const* ); + +/* Passes back current effects configuration */ +void gme_effects( const gme_t*, gme_effects_t* out ); + + +/******** Game music types ********/ + +/* Music file type identifier. Can also hold NULL. */ +typedef const struct gme_type_t_* gme_type_t; + +/* Type of this emulator */ +gme_type_t gme_type( const gme_t* ); + +/* Pointer to array of all music types, with NULL entry at end. Allows a player linked +to this library to support new music types without having to be updated. */ +gme_type_t const* gme_type_list(); + +/* Name of game system for this music file type */ +const char* gme_type_system( gme_type_t ); + +/* True if this music file type supports multiple tracks */ +gme_bool gme_type_multitrack( gme_type_t ); + + +/******** Advanced file loading ********/ + +/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */ +gme_err_t gme_open_data( void const* data, long size, gme_t** emu_out, int sample_rate ); + +/* Determines likely game music type based on first four bytes of file. Returns +string containing proper file suffix ("NSF", "SPC", etc.) or "" if file header +is not recognized. */ +const char* gme_identify_header( void const* header ); + +/* Gets corresponding music type for file path or extension passed in. */ +gme_type_t gme_identify_extension( const char path_or_extension [] ); + +/* Determines file type based on file's extension or header (if extension isn't recognized). +Sets *type_out to type, or 0 if unrecognized or error. */ +gme_err_t gme_identify_file( const char path [], gme_type_t* type_out ); + +/* Creates new emulator and sets sample rate. Returns NULL if out of memory. If you only need +track information, pass gme_info_only for sample_rate. */ +gme_t* gme_new_emu( gme_type_t, int sample_rate ); + +/* Loads music file into emulator */ +gme_err_t gme_load_file( gme_t*, const char path [] ); + +/* Loads music file from memory into emulator. Makes a copy of data passed. */ +gme_err_t gme_load_data( gme_t*, void const* data, long size ); + +/* Loads music file using custom data reader function that will be called to +read file data. Most emulators load the entire file in one read call. */ +typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, long count ); +gme_err_t gme_load_custom( gme_t*, gme_reader_t, long file_size, void* your_data ); + +/* Loads m3u playlist file from memory (must be done after loading music) */ +gme_err_t gme_load_m3u_data( gme_t*, void const* data, long size ); + + +/******** User data ********/ + +/* Sets/gets pointer to data you want to associate with this emulator. +You can use this for whatever you want. */ +void gme_set_user_data( gme_t*, void* new_user_data ); +void* gme_user_data( const gme_t* ); + +/* Registers cleanup function to be called when deleting emulator, or NULL to +clear it. Passes user_data when calling cleanup function. */ +typedef void (*gme_user_cleanup_t)( void* user_data ); +void gme_set_user_cleanup( gme_t*, gme_user_cleanup_t func ); + + +/******** Errors ********/ + +/* Internally, a gme_err_t is a const char* that points to a normal C string. +This means that other strings can be passed to the following functions. In the +descriptions below, these other strings are referred to as being not gme_err_t +strings. */ + +/* Error string associated with err. Returns "" if err is NULL. Returns err +unchanged if it isn't a gme_err_t string. */ +const char* gme_err_str( gme_err_t err ); + +/* Details of error beyond main cause, or "" if none or err is NULL. Returns +err unchanged if it isn't a gme_err_t string. */ +const char* gme_err_details( gme_err_t err ); + +/* Numeric code corresponding to err. Returns gme_ok if err is NULL. Returns +gme_err_generic if err isn't a gme_err_t string. */ +int gme_err_code( gme_err_t err ); + +enum { + gme_ok = 0,/* Successful call. Guaranteed to be zero. */ + gme_err_generic = 0x01,/* Error of unspecified type */ + gme_err_memory = 0x02,/* Out of memory */ + gme_err_caller = 0x03,/* Caller violated requirements of function */ + gme_err_internal = 0x04,/* Internal problem, corruption, etc. */ + gme_err_limitation = 0x05,/* Exceeded program limit */ + + gme_err_file_missing = 0x20,/* File not found at specified path */ + gme_err_file_read = 0x21,/* Couldn't open file for reading */ + gme_err_file_io = 0x23,/* Read/write error */ + gme_err_file_eof = 0x25,/* Tried to read past end of file */ + + gme_err_file_type = 0x30,/* File is of wrong type */ + gme_err_file_feature = 0x32,/* File requires unsupported feature */ + gme_err_file_corrupt = 0x33 /* File is corrupt */ +}; + +/* gme_err_t corresponding to numeric code. Note that this might not recover +the original gme_err_t before it was converted to a numeric code; in +particular, gme_err_details(gme_code_to_err(code)) will be "" in most cases. */ +gme_err_t gme_code_to_err( int code ); + + + +/* Deprecated */ +typedef gme_t Music_Emu; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Frameworks/MAC/MAC.xcodeproj/project.pbxproj b/Frameworks/MAC/MAC.xcodeproj/project.pbxproj index 435dbac54..1e1fa6332 100644 --- a/Frameworks/MAC/MAC.xcodeproj/project.pbxproj +++ b/Frameworks/MAC/MAC.xcodeproj/project.pbxproj @@ -414,9 +414,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "MAC" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MAC */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -511,6 +517,7 @@ INSTALL_PATH = "@loader_path/../Frameworks"; PER_ARCH_CFLAGS_ppc = "-maltivec"; PRODUCT_NAME = MAC; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "mac-src"; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -537,6 +544,7 @@ OTHER_LDFLAGS = "-Wl,-read_only_relocs,warning"; PER_ARCH_CFLAGS_ppc = "-maltivec"; PRODUCT_NAME = MAC; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "mac-src"; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp index 74bd51b39..40caa5014 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.cpp @@ -23,7 +23,7 @@ CAPEDecompress::CAPEDecompress(int * pErrorCode, CAPEInfo * pAPEInfo, int nStart } // get format information - GetInfo(APE_INFO_WAVEFORMATEX, (int) &m_wfeInput); + GetInfo(APE_INFO_WAVEFORMATEX, (long) &m_wfeInput); m_nBlockAlign = GetInfo(APE_INFO_BLOCK_ALIGN); // initialize other stuff @@ -367,9 +367,9 @@ int CAPEDecompress::SeekToFrame(int nFrameIndex) /***************************************************************************************** Get information from the decompressor *****************************************************************************************/ -int CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) +long CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1, long nParam2) { - int nRetVal = 0; + long nRetVal = 0; BOOL bHandled = TRUE; switch (Field) @@ -452,7 +452,7 @@ int CAPEDecompress::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam } else { - WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0); + WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeFormat, 0); WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, (m_nFinishBlock - m_nStartBlock) * GetInfo(APE_INFO_BLOCK_ALIGN), &wfeFormat, 0); diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h index ec49c071f..18ffb881c 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h +++ b/Frameworks/MAC/mac-src/src/MACLib/APEDecompress.h @@ -22,7 +22,7 @@ public: int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved); int Seek(int nBlockOffset); - int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0); + long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0); protected: diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp index d904c9d13..30baa0e3d 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.cpp @@ -129,9 +129,9 @@ int CAPEInfo::GetFileInformation(BOOL bGetTagInformation) /***************************************************************************************** Primary query function *****************************************************************************************/ -int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) +long CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1, long nParam2) { - int nRetVal = -1; + long nRetVal = -1; switch (Field) { @@ -254,7 +254,7 @@ int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) } else { - WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0); + WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeFormat, 0); WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, m_APEFileInfo.nWAVDataBytes, &wfeFormat, m_APEFileInfo.nWAVTerminatingBytes); memcpy(pBuffer, &WAVHeader, sizeof(WAVE_HEADER)); @@ -311,7 +311,7 @@ int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) break; } case APE_INFO_IO_SOURCE: - nRetVal = (int) m_spIO.GetPtr(); + nRetVal = (long) m_spIO.GetPtr(); break; case APE_INFO_FRAME_BYTES: { @@ -350,10 +350,10 @@ int CAPEInfo::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) break; } case APE_INFO_TAG: - nRetVal = (int) m_spAPETag.GetPtr(); + nRetVal = (long) m_spAPETag.GetPtr(); break; case APE_INTERNAL_INFO: - nRetVal = (int) &m_APEFileInfo; + nRetVal = (long) &m_APEFileInfo; break; default: break; diff --git a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h index 49e5c2fb0..5e113f1ba 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h +++ b/Frameworks/MAC/mac-src/src/MACLib/APEInfo.h @@ -82,7 +82,7 @@ public: virtual ~CAPEInfo(); // query for information - int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0); + long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0); private: diff --git a/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp b/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp index a757218d8..b4587bf4a 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/APESimple.cpp @@ -291,14 +291,14 @@ int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFi if (spAPEDecompress == NULL || nFunctionRetVal != ERROR_SUCCESS) throw(nFunctionRetVal); // get the input format - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeInput)) + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeInput)) // allocate space for the header spTempBuffer.Assign(new unsigned char [spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES)], TRUE); if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY); // get the header - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_DATA, (int) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES))); + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_DATA, (long) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_HEADER_BYTES))); // initialize the output if (nOutputMode == UNMAC_DECODER_OUTPUT_WAV) @@ -370,7 +370,7 @@ int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFi { spTempBuffer.Assign(new unsigned char[spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES)], TRUE); if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY); - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (int) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES))) + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (long) spTempBuffer.GetPtr(), spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES))) unsigned int nBytesToWrite = spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_BYTES); unsigned int nBytesWritten = 0; @@ -392,7 +392,7 @@ int DecompressCore(const str_utf16 * pInputFilename, const str_utf16 * pOutputFi spTempBuffer.Assign(new unsigned char[nTerminatingBytes], TRUE); if (spTempBuffer == NULL) throw(ERROR_INSUFFICIENT_MEMORY); - THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (int) spTempBuffer.GetPtr(), nTerminatingBytes)) + THROW_ON_ERROR(spAPEDecompress->GetInfo(APE_INFO_WAV_TERMINATING_DATA, (long) spTempBuffer.GetPtr(), nTerminatingBytes)) if (bHasTag) { diff --git a/Frameworks/MAC/mac-src/src/MACLib/MACLib.h b/Frameworks/MAC/mac-src/src/MACLib/MACLib.h index c2f25b13e..b548d624b 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/MACLib.h +++ b/Frameworks/MAC/mac-src/src/MACLib/MACLib.h @@ -259,7 +259,7 @@ public: // int nParam2 // generic parameter... usage is listed in APE_DECOMPRESS_FIELDS ////////////////////////////////////////////////////////////////////////////////////////////// - virtual int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0) = 0; + virtual long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0) = 0; }; /************************************************************************************************* diff --git a/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp b/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp index 3efa090c6..641c7e387 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/MD5.cpp @@ -100,7 +100,7 @@ __MD5Transform ( uint32_t state [4], CopyToLittleEndian (tempBuffer, in, 16); x = tempBuffer; #else - if ( (unsigned int)in & 3 ) { + if ( (unsigned long)in & 3 ) { memcpy ( tempBuffer, in, 64 ); x = tempBuffer; } diff --git a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp index 1d69defd8..9c1ebea6d 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.cpp @@ -165,7 +165,7 @@ int CAPEDecompressOld::Seek(int nBlockOffset) return ERROR_SUCCESS; } -int CAPEDecompressOld::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nParam2) +long CAPEDecompressOld::GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1, long nParam2) { int nRetVal = 0; BOOL bHandled = TRUE; @@ -250,7 +250,7 @@ int CAPEDecompressOld::GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1, int nPa } else { - WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (int) &wfeFormat, 0); + WAVEFORMATEX wfeFormat; GetInfo(APE_INFO_WAVEFORMATEX, (long) &wfeFormat, 0); WAVE_HEADER WAVHeader; FillWaveHeader(&WAVHeader, (m_nFinishBlock - m_nStartBlock) * GetInfo(APE_INFO_BLOCK_ALIGN), &wfeFormat, 0); diff --git a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h index c3b3b61a6..08126d5f5 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h +++ b/Frameworks/MAC/mac-src/src/MACLib/Old/APEDecompressOld.h @@ -13,7 +13,7 @@ public: int GetData(char * pBuffer, int nBlocks, int * pBlocksRetrieved); int Seek(int nBlockOffset); - int GetInfo(APE_DECOMPRESS_FIELDS Field, int nParam1 = 0, int nParam2 = 0); + long GetInfo(APE_DECOMPRESS_FIELDS Field, long nParam1 = 0, long nParam2 = 0); protected: diff --git a/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp b/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp index 43d373262..bc2b3a96d 100644 --- a/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp +++ b/Frameworks/MAC/mac-src/src/MACLib/Old/UnMAC.cpp @@ -78,7 +78,7 @@ int CUnMAC::Initialize(IAPEDecompress *pAPEDecompress) // set the initialized flag to TRUE m_bInitialized = TRUE; - m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &m_wfeInput); + m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &m_wfeInput); // return a successful value return ERROR_SUCCESS; @@ -197,7 +197,7 @@ int CUnMAC::DecompressFrameOld(unsigned char *pOutputData, int32 FrameIndex, int { m_pAPEDecompressCore->GenerateDecodedArrays(nBlocks, nSpecialCodes, FrameIndex, CPULoadBalancingFactor); - WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &WaveFormatEx); + WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &WaveFormatEx); m_pPrepare->UnprepareOld(m_pAPEDecompressCore->GetDataX(), m_pAPEDecompressCore->GetDataY(), nBlocks, &WaveFormatEx, pOutputData, (unsigned int *) &CRC, (int *) &nSpecialCodes, m_pAPEDecompress->GetInfo(APE_INFO_FILE_VERSION)); } @@ -205,7 +205,7 @@ int CUnMAC::DecompressFrameOld(unsigned char *pOutputData, int32 FrameIndex, int { m_pAPEDecompressCore->GenerateDecodedArrays(nBlocks, nSpecialCodes, FrameIndex, CPULoadBalancingFactor); - WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (int) &WaveFormatEx); + WAVEFORMATEX WaveFormatEx; m_pAPEDecompress->GetInfo(APE_INFO_WAVEFORMATEX, (long) &WaveFormatEx); m_pPrepare->UnprepareOld(m_pAPEDecompressCore->GetDataX(), NULL, nBlocks, &WaveFormatEx, pOutputData, (unsigned int *) &CRC, (int *) &nSpecialCodes, m_pAPEDecompress->GetInfo(APE_INFO_FILE_VERSION)); } diff --git a/Frameworks/MAD/MAD.xcodeproj/project.pbxproj b/Frameworks/MAD/MAD.xcodeproj/project.pbxproj index bab827734..9e2e4d94f 100644 --- a/Frameworks/MAD/MAD.xcodeproj/project.pbxproj +++ b/Frameworks/MAD/MAD.xcodeproj/project.pbxproj @@ -196,9 +196,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "MAD" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MAD */; productRefGroup = 034768DDFF38A45A11DB9C8B /* Products */; projectDirPath = ""; @@ -276,6 +282,7 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = FPM_DEFAULT; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; LIBRARY_STYLE = DYNAMIC; @@ -285,6 +292,7 @@ PER_ARCH_CFLAGS_ppc = "-DFPM_PPC"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -304,6 +312,7 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; + GCC_PREPROCESSOR_DEFINITIONS = FPM_INTEL; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; LIBRARY_STYLE = DYNAMIC; @@ -313,6 +322,7 @@ PER_ARCH_CFLAGS_ppc = "-DFPM_PPC"; PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR = NO; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj b/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj index 54c39eb68..6bbb7c535 100644 --- a/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj +++ b/Frameworks/MPCDec/MPCDec.xcodeproj/project.pbxproj @@ -213,9 +213,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "MPCDec" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* MPCDec */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -284,6 +290,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = mpcdec; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -306,6 +313,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = mpcdec; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = Files/include/; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj b/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj index d2766a7f6..5f6f03a2e 100644 --- a/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj +++ b/Frameworks/Ogg/ogg.xcodeproj/project.pbxproj @@ -154,9 +154,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "ogg" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* ogg */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -219,6 +225,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = ogg; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "libogg-src/include"; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -243,6 +250,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = ogg; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "libogg-src/include"; WRAPPER_EXTENSION = framework; }; @@ -254,7 +262,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -265,7 +273,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/Frameworks/Shorten/Files/shorten/include/shn_reader.h b/Frameworks/Shorten/Files/shorten/include/shn_reader.h index ce52b3416..2f122e9a4 100644 --- a/Frameworks/Shorten/Files/shorten/include/shn_reader.h +++ b/Frameworks/Shorten/Files/shorten/include/shn_reader.h @@ -176,7 +176,7 @@ class shn_reader void *pmalloc(ulong size); slong **long2d(ulong n0, ulong n1); - friend void *thread_runner(shn_reader *reader) + static void *thread_runner(shn_reader *reader) { reader->Run(); return NULL; diff --git a/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj b/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj index 6b10654d7..4183aa76f 100644 --- a/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj +++ b/Frameworks/Shorten/Shorten.xcodeproj/project.pbxproj @@ -171,9 +171,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Shorten" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* Shorten */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -237,10 +243,11 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; - GCC_VERSION = 4.0; + GCC_VERSION = ""; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -261,11 +268,12 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = NO; GCC_PREFIX_HEADER = ""; - GCC_VERSION = 4.0; + GCC_VERSION = ""; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; OTHER_CFLAGS = "-DHAVE_CONFIG_H"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; SHARED_PRECOMPS_DIR = "$(CACHE_ROOT)/SharedPrecompiledHeaders"; USER_HEADER_SEARCH_PATHS = Files/shorten/include; WRAPPER_EXTENSION = framework; diff --git a/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj b/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj index ce6794de3..9ed069ab0 100644 --- a/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj +++ b/Frameworks/TagLib/TagLib.xcodeproj/project.pbxproj @@ -924,9 +924,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "TagLib" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* TagLib */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -1085,6 +1091,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -1111,6 +1118,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj b/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj index fdec2a98d..53c47eb36 100644 --- a/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj +++ b/Frameworks/Vorbis/Vorbis.xcodeproj/project.pbxproj @@ -351,9 +351,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Vorbis" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* Vorbis */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -489,6 +495,7 @@ Ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; USER_HEADER_SEARCH_PATHS = "Files/lib/ Files/include/"; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -525,6 +532,7 @@ Ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Frameworks/WMA/WMA.xcodeproj/project.pbxproj b/Frameworks/WMA/WMA.xcodeproj/project.pbxproj index ec578fd96..7f69ff31d 100644 --- a/Frameworks/WMA/WMA.xcodeproj/project.pbxproj +++ b/Frameworks/WMA/WMA.xcodeproj/project.pbxproj @@ -234,9 +234,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "WMA" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* WMA */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -301,7 +307,7 @@ 1DEB91AE08733DA50010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(NATIVE_ARCH_32_BIT)"; + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -315,6 +321,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; @@ -324,6 +331,10 @@ 1DEB91AF08733DA50010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; @@ -334,6 +345,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Frameworks"; PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = framework; }; diff --git a/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj b/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj index 323d8a400..0667da571 100644 --- a/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj +++ b/Frameworks/WavPack/WavPack.xcodeproj/project.pbxproj @@ -190,9 +190,15 @@ /* Begin PBXProject section */ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "WavPack" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 0867D691FE84028FC02AAC07 /* WavPack */; productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; projectDirPath = ""; @@ -279,6 +285,7 @@ ); OTHER_CFLAGS_QUOTED_1 = "-DPACKAGE_STRING=\\\"wavpack\\ 4.2\\\""; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; ZERO_LINK = YES; }; @@ -316,6 +323,7 @@ ); OTHER_CFLAGS_QUOTED_1 = "-DPACKAGE_STRING=\\\"wavpack\\ 4.2\\\""; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = framework; }; name = Release; diff --git a/Plugins/APL/APL.xcodeproj/project.pbxproj b/Plugins/APL/APL.xcodeproj/project.pbxproj index be0811ae4..cfe963f94 100644 --- a/Plugins/APL/APL.xcodeproj/project.pbxproj +++ b/Plugins/APL/APL.xcodeproj/project.pbxproj @@ -149,9 +149,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "APL" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* CueSheet */; projectDirPath = ""; projectRoot = ""; @@ -229,6 +235,7 @@ ); PREBINDING = NO; PRODUCT_NAME = APL; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -237,6 +244,10 @@ 99B989F90CC7E10500C256E9 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + i386, + ppc, + ); COPY_PHASE_STRIP = YES; GCC_ENABLE_FIX_AND_CONTINUE = NO; GCC_GENERATE_DEBUGGING_SYMBOLS = NO; @@ -253,6 +264,7 @@ ); PREBINDING = NO; PRODUCT_NAME = APL; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = NO; }; diff --git a/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj b/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj index a2273e9dd..f478e8849 100644 --- a/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj +++ b/Plugins/AudioOverload/AudioOverload.xcodeproj/project.pbxproj @@ -188,9 +188,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "AudioOverload" */; compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* AudioOverload */; projectDirPath = ""; projectReferences = ( @@ -263,6 +269,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_FIX_AND_CONTINUE = YES; @@ -273,6 +280,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Debug; @@ -281,6 +289,10 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; @@ -288,6 +300,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = AudioOverload; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; @@ -302,7 +315,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; ONLY_ACTIVE_ARCH = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -315,7 +328,7 @@ GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; PREBINDING = NO; - SDKROOT = macosx10.5; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj b/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj index e8430ed0d..b29ed45ff 100644 --- a/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj +++ b/Plugins/CoreAudio/CoreAudio.xcodeproj/project.pbxproj @@ -143,9 +143,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "CoreAudio" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* CoreAudio */; projectDirPath = ""; projectRoot = ""; @@ -190,6 +196,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CoreAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -209,6 +216,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CoreAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj index 911740f77..35f2e6c87 100644 --- a/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj +++ b/Plugins/CueSheet/CueSheet.xcodeproj/project.pbxproj @@ -158,9 +158,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "CueSheet" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* CueSheet */; projectDirPath = ""; projectRoot = ""; @@ -221,6 +227,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CueSheet; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -231,8 +238,8 @@ isa = XCBuildConfiguration; buildSettings = { ARCHS = ( - ppc, i386, + ppc, ); GCC_GENERATE_DEBUGGING_SYMBOLS = NO; GCC_MODEL_TUNING = G5; @@ -241,6 +248,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = CueSheet; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj b/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj index 756273985..b5069e8bd 100644 --- a/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj +++ b/Plugins/Dumb/Dumb.xcodeproj/project.pbxproj @@ -192,9 +192,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Dumb" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Dumb */; projectDirPath = ""; projectReferences = ( @@ -273,10 +279,15 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Dumb_Prefix.pch; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -294,10 +305,15 @@ GCC_MODEL_TUNING = G5; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = Dumb_Prefix.pch; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = Dumb; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/Dumb/DumbDecoder.h b/Plugins/Dumb/DumbDecoder.h index 8772120d4..8e4fbc4bb 100755 --- a/Plugins/Dumb/DumbDecoder.h +++ b/Plugins/Dumb/DumbDecoder.h @@ -10,6 +10,10 @@ #import +#define __FRAMEWORK__ +#import +#undef __FRAMEWORK__ + #import "Plugin.h" @interface DumbDecoder : NSObject { diff --git a/Plugins/Dumb/DumbDecoder.m b/Plugins/Dumb/DumbDecoder.m index 832942e24..d0f0c4392 100755 --- a/Plugins/Dumb/DumbDecoder.m +++ b/Plugins/Dumb/DumbDecoder.m @@ -49,6 +49,33 @@ void closeCallback(void *f) NSLog(@"CLOSE"); //I DO NOTHING } +int seekCallback(void *f, long n) +{ + DumbDecoder *decoder = (DumbDecoder *)f; + + if (![[decoder source] seekable]) return -1; + + if ([[decoder source] seek:n whence:SEEK_SET]) return 0; + else return -1; +} + +long getsizeCallback(void *f) +{ + DumbDecoder *decoder = (DumbDecoder *)f; + + if (![[decoder source] seekable]) return 0; + + long current_offset = [[decoder source] tell]; + + [[decoder source] seek:0 whence:SEEK_END]; + + long size = [[decoder source] tell]; + + [[decoder source] seek:current_offset whence:SEEK_SET]; + + return size; +} + - (BOOL)open:(id)s { [self setSource:s]; @@ -58,6 +85,8 @@ void closeCallback(void *f) dfs.getc = getCharCallback; dfs.getnc = readCallback; dfs.close = closeCallback; + dfs.seek = seekCallback; + dfs.get_size = getsizeCallback; // dumb_register_stdfiles(); @@ -69,18 +98,7 @@ void closeCallback(void *f) } NSString *ext = [[[[s url] path] pathExtension] lowercaseString]; - if ([ext isEqualToString:@"it"]) - duh = dumb_read_it(df); - else if ([ext isEqualToString:@"xm"]) - duh = dumb_read_xm(df); - else if ([ext isEqualToString:@"s3m"]) - duh = dumb_read_s3m(df); - else if ([ext isEqualToString:@"mod"]) - duh = dumb_read_mod(df); - else { - NSLog(@"DUH IS NUL!!!"); - duh = NULL; - } + duh = dumb_read_any(df, [ext isEqualToString:@"mod"] ? 0 : 1, 0); if (!duh) { NSLog(@"Failed to create duh"); @@ -97,7 +115,10 @@ void closeCallback(void *f) return NO; } - [self willChangeValueForKey:@"properties"]; + DUMB_IT_SIGRENDERER * itsr = duh_get_it_sigrenderer( dsr ); + dumb_it_set_ramp_style( itsr, 2 ); + + [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; return YES; @@ -187,7 +208,7 @@ void closeCallback(void *f) + (NSArray *)fileTypes { - return [NSArray arrayWithObjects:@"it", @"xm", @"s3m", @"mod", nil]; + return [NSArray arrayWithObjects:@"it", @"xm", @"s3m", @"mod", @"stm", @"ptm", @"mtm", @"669", @"psm", @"am", @"dsm", @"amf", @"okt", @"okta", nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/Dumb/DumbMetadataReader.m b/Plugins/Dumb/DumbMetadataReader.m index c03802d76..73121f089 100644 --- a/Plugins/Dumb/DumbMetadataReader.m +++ b/Plugins/Dumb/DumbMetadataReader.m @@ -32,17 +32,7 @@ DUH *duh; NSString *ext = [[[url path] pathExtension] lowercaseString]; - if ([ext isEqualToString:@"it"]) - duh = dumb_load_it_quick([[url path] UTF8String]); - else if ([ext isEqualToString:@"xm"]) - duh = dumb_load_xm_quick([[url path] UTF8String]); - else if ([ext isEqualToString:@"s3m"]) - duh = dumb_load_s3m_quick([[url path] UTF8String]); - else if ([ext isEqualToString:@"mod"]) - duh = dumb_load_mod_quick([[url path] UTF8String]); - else { - duh = NULL; - } + duh = dumb_load_any_quick([[url path] UTF8String], [ext isEqualToString:@"mod"] ? 0 : 1, 0); if (!duh) { diff --git a/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj b/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj index 8d9975fdd..fa136cd4e 100644 --- a/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj +++ b/Plugins/FileSource/FileSource.xcodeproj/project.pbxproj @@ -135,9 +135,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "FileSource" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* FileSource */; projectDirPath = ""; projectRoot = ""; @@ -182,6 +188,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = FileSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -201,6 +208,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = FileSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Flac/Flac.xcodeproj/project.pbxproj b/Plugins/Flac/Flac.xcodeproj/project.pbxproj index 6ab077163..9de2f93a3 100644 --- a/Plugins/Flac/Flac.xcodeproj/project.pbxproj +++ b/Plugins/Flac/Flac.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Flac" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Flac */; projectDirPath = ""; projectReferences = ( @@ -258,6 +264,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Flac; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -282,6 +289,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Flac; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/GME/GME.xcodeproj/project.pbxproj b/Plugins/GME/GME.xcodeproj/project.pbxproj index 300738900..a7618c0d3 100644 --- a/Plugins/GME/GME.xcodeproj/project.pbxproj +++ b/Plugins/GME/GME.xcodeproj/project.pbxproj @@ -201,9 +201,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "GME" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* GME */; projectDirPath = ""; projectReferences = ( @@ -286,6 +292,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -306,6 +313,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = GME; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/GME/GameContainer.m b/Plugins/GME/GameContainer.m index 29b4a1e74..79d2d659e 100755 --- a/Plugins/GME/GameContainer.m +++ b/Plugins/GME/GameContainer.m @@ -16,7 +16,7 @@ + (NSArray *)fileTypes { //There doesn't seem to be a way to get this list. These are the only multitrack types. - return [NSArray arrayWithObjects:@"ay", @"gbs", @"nsf", @"nsfe", @"sap", nil]; + return [NSArray arrayWithObjects:@"ay", @"gbs", @"hes", @"kss", @"nsf", @"nsfe", @"sap", @"sgc", nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/GME/GameDecoder.m b/Plugins/GME/GameDecoder.m index f5490766d..ca34414a3 100755 --- a/Plugins/GME/GameDecoder.m +++ b/Plugins/GME/GameDecoder.m @@ -67,7 +67,7 @@ gme_err_t readCallback( void* data, void* out, long count ) int track_num = [[[source url] fragment] intValue]; //What if theres no fragment? Assuming we get 0. - track_info_t info; + gme_info_t * info; error = gme_track_info( emu, &info, track_num ); if (error) { @@ -75,18 +75,20 @@ gme_err_t readCallback( void* data, void* out, long count ) } //As recommended - if (info.length > 0) { - NSLog(@"Using length: %li", info.length); - length = info.length; + if (info->length > 0) { + NSLog(@"Using length: %i", info->length); + length = info->length; } - else if (info.loop_length > 0) { - NSLog(@"Using loop length: %li", info.loop_length); - length = info.intro_length + 2*info.loop_length; + else if (info->loop_length > 0) { + NSLog(@"Using loop length: %i", info->loop_length); + length = info->intro_length + 2*info->loop_length; } else { length = 150000; NSLog(@"Setting default: %li", length); } + + gme_free_info( info ); NSLog(@"Length: %li", length); @@ -157,17 +159,7 @@ gme_err_t readCallback( void* data, void* out, long count ) + (NSArray *)fileTypes { - NSMutableArray *types = [NSMutableArray array]; - gme_type_t const* type = gme_type_list(); - while(*type) - { - //We're digging a little deep here, but there seems to be no other choice. - [types addObject:[NSString stringWithCString:(*type)->extension_ encoding: NSASCIIStringEncoding]]; - - type++; - } - - return [[types copy] autorelease]; + return [NSArray arrayWithObjects:@"ay", @"gbs", @"hes", @"kss", @"nsf", @"nsfe", @"sap", @"sgc", @"spc", @"vgm", nil]; } + (NSArray *)mimeTypes diff --git a/Plugins/GME/GameMetadataReader.m b/Plugins/GME/GameMetadataReader.m index 9bc82b79e..d86b6afb4 100644 --- a/Plugins/GME/GameMetadataReader.m +++ b/Plugins/GME/GameMetadataReader.m @@ -60,7 +60,7 @@ else track_num = [[url fragment] intValue]; - track_info_t info; + gme_info_t * info; error = gme_track_info( emu, &info, track_num ); if (error) { @@ -69,13 +69,17 @@ gme_delete(emu); - return [NSDictionary dictionaryWithObjectsAndKeys: - [NSString stringWithUTF8String: info.system], @"genre", - [NSString stringWithUTF8String: info.game], @"album", - [NSString stringWithUTF8String: info.song], @"title", - [NSString stringWithUTF8String: info.author], @"artist", + NSDictionary * dict = [NSDictionary dictionaryWithObjectsAndKeys: + [NSString stringWithUTF8String: info->system], @"genre", + [NSString stringWithUTF8String: info->game], @"album", + [NSString stringWithUTF8String: info->song], @"title", + [NSString stringWithUTF8String: info->author], @"artist", [NSNumber numberWithInt:track_num+1], @"track", nil]; + + gme_free_info( info ); + + return dict; } @end diff --git a/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj b/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj index 501a89331..165eabbad 100644 --- a/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj +++ b/Plugins/HTTPSource/HTTPSource.xcodeproj/project.pbxproj @@ -166,9 +166,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "HTTPSource" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* HTTPSource */; projectDirPath = ""; projectRoot = ""; @@ -217,6 +223,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = HTTPSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -238,6 +245,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = HTTPSource; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/M3u/M3u.xcodeproj/project.pbxproj b/Plugins/M3u/M3u.xcodeproj/project.pbxproj index e701eb8c0..e3fd02ff6 100644 --- a/Plugins/M3u/M3u.xcodeproj/project.pbxproj +++ b/Plugins/M3u/M3u.xcodeproj/project.pbxproj @@ -138,9 +138,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "M3u" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* M3u */; projectDirPath = ""; projectRoot = ""; @@ -198,6 +204,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = M3u; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -219,6 +226,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; OBJROOT = ../../build; PRODUCT_NAME = M3u; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = NO; diff --git a/Plugins/MAD/MAD.xcodeproj/project.pbxproj b/Plugins/MAD/MAD.xcodeproj/project.pbxproj index 8e1a59c55..11c4de22a 100644 --- a/Plugins/MAD/MAD.xcodeproj/project.pbxproj +++ b/Plugins/MAD/MAD.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "MAD" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* MAD */; projectDirPath = ""; projectReferences = ( @@ -259,10 +265,12 @@ GCC_OPTIMIZATION_LEVEL = 0; GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = MAD_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = FPM_DEFAULT; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; OTHER_CFLAGS = "-Wall"; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -292,6 +300,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; OTHER_CFLAGS = "-Wall"; PRODUCT_NAME = MAD; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj b/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj index 4fbc0518a..30da74d4c 100644 --- a/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj +++ b/Plugins/MonkeysAudio/MonkeysAudio.xcodeproj/project.pbxproj @@ -189,9 +189,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "MonkeysAudio" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* MonkeysAudio */; projectDirPath = ""; projectReferences = ( @@ -266,6 +272,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = MonkeysAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -290,6 +297,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = MonkeysAudio; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj b/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj index 0afefbfae..d7360a0e9 100644 --- a/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj +++ b/Plugins/Musepack/Musepack.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Musepack" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Musepack */; projectDirPath = ""; projectReferences = ( @@ -259,6 +265,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Musepack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -284,6 +291,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Musepack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Pls/Pls.xcodeproj/project.pbxproj b/Plugins/Pls/Pls.xcodeproj/project.pbxproj index 8490e3b84..cfc60b876 100644 --- a/Plugins/Pls/Pls.xcodeproj/project.pbxproj +++ b/Plugins/Pls/Pls.xcodeproj/project.pbxproj @@ -138,9 +138,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Pls" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Pls */; projectDirPath = ""; projectRoot = ""; @@ -197,6 +203,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Pls; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -217,6 +224,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Pls; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj b/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj index f4e7f32c2..48ab7443a 100644 --- a/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj +++ b/Plugins/Shorten/Shorten.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Shorten" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Shorten */; projectDirPath = ""; projectReferences = ( @@ -258,6 +264,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -282,6 +289,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = Shorten; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj b/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj index bf5db7f9b..b82050f7b 100644 --- a/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj +++ b/Plugins/TagLib/TagLib.xcodeproj/project.pbxproj @@ -198,9 +198,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "TagLib" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* TagLib */; projectDirPath = ""; projectReferences = ( @@ -271,6 +277,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -291,6 +298,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = TagLib; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj b/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj index c1f206248..1d6e98564 100644 --- a/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj +++ b/Plugins/Vorbis/Vorbis.xcodeproj/project.pbxproj @@ -182,9 +182,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "Vorbis" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* Vorbis */; projectDirPath = ""; projectReferences = ( @@ -265,6 +271,7 @@ ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -296,6 +303,7 @@ ogg, ); PRODUCT_NAME = Vorbis; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Plugins/WMA/WMA.xcodeproj/project.pbxproj b/Plugins/WMA/WMA.xcodeproj/project.pbxproj index 6b9abaa52..12032e21d 100644 --- a/Plugins/WMA/WMA.xcodeproj/project.pbxproj +++ b/Plugins/WMA/WMA.xcodeproj/project.pbxproj @@ -184,9 +184,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "WMA" */; compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* WMA */; projectDirPath = ""; projectReferences = ( @@ -245,6 +251,7 @@ 1DEB913B08733D840010E9CD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = "$(ARCHS_STANDARD)"; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -265,6 +272,7 @@ "\"$(SRCROOT)/../../Frameworks/WMA/build/Debug/WMA.framework\"", ); PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -274,6 +282,10 @@ 1DEB913C08733D840010E9CD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ARCHS = ( + i386, + ppc, + ); DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -286,6 +298,7 @@ INSTALL_PATH = "$(HOME)/Library/Bundles"; LIBRARY_SEARCH_PATHS = ""; PRODUCT_NAME = WMA; + SDKROOT = macosx10.6; SYMROOT = ../../build; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj b/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj index 40c4d9d1f..0f4c1a1f9 100644 --- a/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj +++ b/Plugins/WavPack/WavPack.xcodeproj/project.pbxproj @@ -180,9 +180,15 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "WavPack" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; + knownRegions = ( + en, + ); mainGroup = 089C166AFE841209C02AAC07 /* WavPack */; projectDirPath = ""; projectReferences = ( @@ -258,6 +264,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; }; @@ -284,6 +291,7 @@ INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; PRODUCT_NAME = WavPack; + SDKROOT = macosx10.6; WRAPPER_EXTENSION = bundle; }; name = Release; diff --git a/Preferences/General/General.xcodeproj/project.pbxproj b/Preferences/General/General.xcodeproj/project.pbxproj index 6000f1326..d480153db 100644 --- a/Preferences/General/General.xcodeproj/project.pbxproj +++ b/Preferences/General/General.xcodeproj/project.pbxproj @@ -240,8 +240,11 @@ /* Begin PBXProject section */ 089C1669FE841209C02AAC07 /* Project object */ = { isa = PBXProject; + attributes = { + }; buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "General" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 1; knownRegions = ( English, @@ -371,8 +374,12 @@ buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Debug; @@ -382,8 +389,12 @@ buildSettings = { GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); PREBINDING = NO; - SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; + SDKROOT = macosx10.6; SYMROOT = ../../build; }; name = Release; diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl b/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl index 9ac3653fa..e35673015 100755 Binary files a/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl and b/ThirdParty/Frameworks/Growl.framework/Versions/A/Growl differ diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h index e2a44255d..7b1a3247d 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/Growl.h @@ -1,6 +1,5 @@ -#include "GrowlDefines.h" +#include #ifdef __OBJC__ -# include "GrowlApplicationBridge.h" +# include #endif -#include "GrowlApplicationBridge-Carbon.h" diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h index 4341f3fbd..363975762 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h @@ -18,14 +18,11 @@ #import #import -#import "GrowlDefines.h" +#import //Forward declarations @protocol GrowlApplicationBridgeDelegate; -//Internal notification when the user chooses not to install (to avoid continuing to cache notifications awaiting installation) -#define GROWL_USER_CHOSE_NOT_TO_INSTALL_NOTIFICATION @"User chose not to install" - //------------------------------------------------------------------------------ #pragma mark - @@ -45,9 +42,9 @@ * @method isGrowlInstalled * @abstract Detects whether Growl is installed. * @discussion Determines if the Growl prefpane and its helper app are installed. - * @result Returns YES if Growl is installed, NO otherwise. + * @result this method will forever return YES. */ -+ (BOOL) isGrowlInstalled; ++ (BOOL) isGrowlInstalled __attribute__((deprecated)); /*! * @method isGrowlRunning @@ -57,6 +54,34 @@ */ + (BOOL) isGrowlRunning; + +/*! + * @method isMistEnabled + * @abstract Gives the caller a fairly good indication of whether or not built-in notifications(Mist) will be used. + * @discussion since this call makes use of isGrowlRunning it is entirely possible for this value to change between call and + * executing a notification dispatch + * @result Returns YES if Growl isn't reachable and the developer has not opted-out of + * Mist and the user hasn't set the global mist enable key to false. + */ ++ (BOOL)isMistEnabled; + +/*! + * @method setShouldUseBuiltInNotifications + * @abstract opt-out mechanism for the mist notification style in the event growl can't be reached. + * @discussion if growl is unavailable due to not being installed or as a result of being turned off then + * this option can enable/disable a built-in fire and forget display style + * @param should Specifies whether or not the developer wants to opt-in (default) or opt out + * of the built-in Mist style in the event Growl is unreachable. + */ ++ (void)setShouldUseBuiltInNotifications:(BOOL)should; + +/*! + * @method shouldUseBuiltInNotifications + * @abstract returns the current opt-in state of the framework's use of the Mist display style. + * @result Returns NO if the developer opt-ed out of Mist, the default value is YES. + */ ++ (BOOL)shouldUseBuiltInNotifications; + #pragma mark - /*! @@ -87,7 +112,7 @@ * * @param inDelegate The delegate for the GrowlApplicationBridge. It must conform to the GrowlApplicationBridgeDelegate protocol. */ -+ (void) setGrowlDelegate:(NSObject *)inDelegate; ++ (void) setGrowlDelegate:(id)inDelegate; /*! * @method growlDelegate @@ -95,7 +120,7 @@ * @discussion See setGrowlDelegate: for details. * @result The Growl delegate. */ -+ (NSObject *) growlDelegate; ++ (id) growlDelegate; #pragma mark - @@ -165,40 +190,6 @@ clickContext:(id)clickContext identifier:(NSString *)identifier; -/*! - * @method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:identifier: - * @abstract Send a Growl notification. - * @discussion This is the preferred means for sending a Growl notification. - * The notification name and at least one of the title and description are - * required (all three are preferred). All other parameters may be - * nil (or 0 or NO as appropriate) to accept default values. - * - * If using the Growl-WithInstaller framework, if Growl is not installed the - * user will be prompted to install Growl. If the user cancels, this method - * will have no effect until the next application session, at which time when - * it is called the user will be prompted again. The user is also given the - * option to not be prompted again. If the user does choose to install Growl, - * the requested notification will be displayed once Growl is installed and - * running. - * - * @param title The title of the notification displayed to the user. - * @param description The full description of the notification displayed to the user. - * @param notifName The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane. - * @param iconData NSData object to show with the notification as its icon. If nil, the application's icon will be used instead. - * @param priority The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority. - * @param isSticky If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications. - * @param clickContext A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of NSString, NSArray, NSNumber, NSDictionary, and NSData types). - * @param identifier An identifier for this notification. Notifications with equal identifiers are coalesced. - */ -+ (void) notifyWithTitle:(NSString *)title - description:(NSString *)description - notificationName:(NSString *)notifName - iconData:(NSData *)iconData - priority:(signed int)priority - isSticky:(BOOL)isSticky - clickContext:(id)clickContext - identifier:(NSString *)identifier; - /*! @method notifyWithDictionary: * @abstract Notifies using a userInfo dictionary suitable for passing to * NSDistributedNotificationCenter. @@ -269,6 +260,7 @@ * Growl when next it is ready; NO if not. */ + (void) setWillRegisterWhenGrowlIsReady:(BOOL)flag; + /*! @method willRegisterWhenGrowlIsReady * @abstract Reports whether GrowlApplicationBridge will register with Growl * when Growl next launches. @@ -357,7 +349,7 @@ * Key Value * --- ----- * GROWL_APP_NAME CFBundleExecutableName - * GROWL_APP_ICON The icon of the application. + * GROWL_APP_ICON_DATA The data of the icon of the application. * GROWL_APP_LOCATION The location of the application. * GROWL_NOTIFICATIONS_DEFAULT GROWL_NOTIFICATIONS_ALL * @@ -370,6 +362,7 @@ * copy of regDict. */ + (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict; + /*! @method registrationDictionaryByFillingInDictionary:restrictToKeys: * @abstract Tries to fill in missing keys in a registration dictionary. * @discussion This method examines the passed-in dictionary for missing keys, @@ -378,7 +371,7 @@ * Key Value * --- ----- * GROWL_APP_NAME CFBundleExecutableName - * GROWL_APP_ICON The icon of the application. + * GROWL_APP_ICON_DATA The data of the icon of the application. * GROWL_APP_LOCATION The location of the application. * GROWL_NOTIFICATIONS_DEFAULT GROWL_NOTIFICATIONS_ALL * @@ -402,13 +395,39 @@ * the keys that it will look for are: * * \li GROWL_APP_NAME - * \li GROWL_APP_ICON + * \li GROWL_APP_ICON_DATA * * @since Growl.framework 1.1 */ + (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)regDict; + (NSDictionary *) frameworkInfoDictionary; + +#pragma mark - + +/*! + *@method growlURLSchemeAvailable + *@abstract Lets the app know whether growl:// is registered on the system, used for certain methods below this + *@return Returns whether growl:// is registered on the system + *@discussion Methods such as openGrowlPreferences rely on the growl:// URL scheme to function + * Further, this method can provide a check on whether Growl is installed, + * however, the framework will not be relying on this method for choosing when/how to notify, + * and it is not recommended that the app rely on it for other than whether to use growl:// methods + *@since Growl.framework 1.4 + */ ++ (BOOL) isGrowlURLSchemeAvailable; + +/*! + * @method openGrowlPreferences: + * @abstract Open Growl preferences, optionally to this app's settings, growl:// method + * @param showApp Whether to show the application's settings, otherwise just opens to the last position + * @return Return's whether opening the URL was succesfull or not. + * @discussion Will launch if Growl is installed, but not running, and open the preferences window + * Uses growl:// URL scheme + * @since Growl.framework 1.4 + */ ++ (BOOL) openGrowlPreferences:(BOOL)showApp; + @end //------------------------------------------------------------------------------ @@ -417,27 +436,15 @@ /*! * @protocol GrowlApplicationBridgeDelegate * @abstract Required protocol for the Growl delegate. - * @discussion The methods in this protocol are required and are called + * @discussion The methods in this protocol are optional and are called * automatically as needed by GrowlApplicationBridge. See * +[GrowlApplicationBridge setGrowlDelegate:]. * See also GrowlApplicationBridgeDelegate_InformalProtocol. */ -@protocol GrowlApplicationBridgeDelegate +@protocol GrowlApplicationBridgeDelegate -// -registrationDictionaryForGrowl has moved to the informal protocol as of 0.7. - -@end - -//------------------------------------------------------------------------------ -#pragma mark - - -/*! - * @category NSObject(GrowlApplicationBridgeDelegate_InformalProtocol) - * @abstract Methods which may be optionally implemented by the GrowlDelegate. - * @discussion The methods in this informal protocol will only be called if implemented by the delegate. - */ -@interface NSObject (GrowlApplicationBridgeDelegate_InformalProtocol) +@optional /*! * @method registrationDictionaryForGrowl @@ -544,66 +551,17 @@ */ - (void) growlNotificationTimedOut:(id)clickContext; + +/*! + * @method hasNetworkClientEntitlement + * @abstract Used only in sandboxed situations since we don't know whether the app has com.apple.security.network.client entitlement + * @discussion GrowlDelegate calls to find out if we have the com.apple.security.network.client entitlement, + * since we can't find this out without hitting the sandbox. We only call it if we detect that the application is sandboxed. + */ +- (BOOL) hasNetworkClientEntitlement; + @end #pragma mark - -/*! - * @category NSObject(GrowlApplicationBridgeDelegate_Installation_InformalProtocol) - * @abstract Methods which may be optionally implemented by the Growl delegate when used with Growl-WithInstaller.framework. - * @discussion The methods in this informal protocol will only be called if - * implemented by the delegate. They allow greater control of the information - * presented to the user when installing or upgrading Growl from within your - * application when using Growl-WithInstaller.framework. - */ -@interface NSObject (GrowlApplicationBridgeDelegate_Installation_InformalProtocol) - -/*! - * @method growlInstallationWindowTitle - * @abstract Return the title of the installation window. - * @discussion If not implemented, Growl will use a default, localized title. - * @result An NSString object to use as the title. - */ -- (NSString *)growlInstallationWindowTitle; - -/*! - * @method growlUpdateWindowTitle - * @abstract Return the title of the upgrade window. - * @discussion If not implemented, Growl will use a default, localized title. - * @result An NSString object to use as the title. - */ -- (NSString *)growlUpdateWindowTitle; - -/*! - * @method growlInstallationInformation - * @abstract Return the information to display when installing. - * @discussion This information may be as long or short as desired (the window - * will be sized to fit it). It will be displayed to the user as an - * explanation of what Growl is and what it can do in your application. It - * should probably note that no download is required to install. - * - * If this is not implemented, Growl will use a default, localized explanation. - * @result An NSAttributedString object to display. - */ -- (NSAttributedString *)growlInstallationInformation; - -/*! - * @method growlUpdateInformation - * @abstract Return the information to display when upgrading. - * @discussion This information may be as long or short as desired (the window - * will be sized to fit it). It will be displayed to the user as an - * explanation that an updated version of Growl is included in your - * application and no download is required. - * - * If this is not implemented, Growl will use a default, localized explanation. - * @result An NSAttributedString object to display. - */ -- (NSAttributedString *)growlUpdateInformation; - -@end - -//private -@interface GrowlApplicationBridge (GrowlInstallationPrompt_private) -+ (void) _userChoseNotToInstallGrowl; -@end #endif /* __GrowlApplicationBridge_h__ */ diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h index 2b971cfe5..0a196f1e3 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h @@ -7,10 +7,8 @@ #ifdef __OBJC__ #define XSTR(x) (@x) -#define STRING_TYPE NSString * #else #define XSTR CFSTR -#define STRING_TYPE CFStringRef #endif /*! @header GrowlDefines.h @@ -56,7 +54,7 @@ * This key is optional. */ #define GROWL_APP_ID XSTR("ApplicationId") -/*! @defined GROWL_APP_ICON +/*! @defined GROWL_APP_ICON_DATA * @abstract The image data for your application's icon. * @discussion Image data representing your application's icon. This may be * superimposed on a notification icon as a badge, used as the notification @@ -66,7 +64,7 @@ * * Optional. Not supported by all display plugins. */ -#define GROWL_APP_ICON XSTR("ApplicationIcon") +#define GROWL_APP_ICON_DATA XSTR("ApplicationIcon") /*! @defined GROWL_NOTIFICATIONS_DEFAULT * @abstract The array of notifications to turn on by default. * @discussion These are the names of the notifications that should be enabled @@ -101,6 +99,14 @@ * This key is optional. */ #define GROWL_NOTIFICATIONS_DESCRIPTIONS XSTR("NotificationDescriptions") +/*! @defined GROWL_NOTIFICATIONS_ICONS + * @abstract A dictionary of icons for each notification + * @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are + * icons for each notification, for GNTP spec + * + * This key is optional. + */ +#define GROWL_NOTIFICATIONS_ICONS XSTR("NotificationIcons") /*! @defined GROWL_TICKET_VERSION * @abstract The version of your registration ticket. @@ -144,20 +150,20 @@ */ #define GROWL_NOTIFICATION_DESCRIPTION XSTR("NotificationDescription") /*! @defined GROWL_NOTIFICATION_ICON - * @discussion Image data for the notification icon. Must be in a format + * @discussion Image data for the notification icon. Image data must be in a format * supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. * * Optional. Not supported by all display plugins. */ -#define GROWL_NOTIFICATION_ICON XSTR("NotificationIcon") +#define GROWL_NOTIFICATION_ICON_DATA XSTR("NotificationIcon") /*! @defined GROWL_NOTIFICATION_APP_ICON * @discussion Image data for the application icon, in case GROWL_APP_ICON does - * not apply for some reason. Must be in a format supported by NSImage, such + * not apply for some reason. Image data be in a format supported by NSImage, such * as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. * * Optional. Not supported by all display plugins. */ -#define GROWL_NOTIFICATION_APP_ICON XSTR("NotificationAppIcon") +#define GROWL_NOTIFICATION_APP_ICON_DATA XSTR("NotificationAppIcon") /*! @defined GROWL_NOTIFICATION_PRIORITY * @discussion The priority of the notification as an integer number from * -2 to +2 (+2 being highest). @@ -185,16 +191,6 @@ */ #define GROWL_NOTIFICATION_CLICK_CONTEXT XSTR("NotificationClickContext") -/*! @defined GROWL_DISPLAY_PLUGIN - * @discussion The name of a display plugin which should be used for this notification. - * Optional. If this key is not set or the specified display plugin does not - * exist, the display plugin stored in the application ticket is used. This key - * allows applications to use different default display plugins for their - * notifications. The user can still override those settings in the preference - * pane. - */ -#define GROWL_DISPLAY_PLUGIN XSTR("NotificationDisplayPlugin") - /*! @defined GROWL_NOTIFICATION_IDENTIFIER * @abstract An identifier for the notification for coalescing purposes. * Notifications with the same identifier fall into the same class; only @@ -224,6 +220,19 @@ */ #define GROWL_NOTIFICATION_PROGRESS XSTR("NotificationProgress") +/*! @defined GROWL_NOTIFICATION_ALREADY_SHOWN + * @abstract If this key is set, it should contain a bool value wrapped + * in a NSNumber which describes whether the notification has + * already been displayed, for instance by built in Notification + * Center support. This value can be used to allow display + * plugins to skip a notification, while still allowing Growl + * actions to run on them. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_ALREADY_SHOWN XSTR("AlreadyShown") + + // Notifications #pragma mark Notifications @@ -245,7 +254,7 @@ * The userInfo dictionary for this notification can contain these keys: *
    *
  • GROWL_APP_NAME
  • - *
  • GROWL_APP_ICON
  • + *
  • GROWL_APP_ICON_DATA
  • *
  • GROWL_NOTIFICATIONS_ALL
  • *
  • GROWL_NOTIFICATIONS_DEFAULT
  • *
@@ -288,12 +297,6 @@ * Growl_PostNotification. */ #define GROWL_NOTIFICATION XSTR("GrowlNotification") -/*! @defined GROWL_SHUTDOWN -* @abstract The distributed notification name that tells Growl to shutdown. -* @discussion The Growl preference pane posts this notification when the -* "Stop Growl" button is clicked. -*/ -#define GROWL_SHUTDOWN XSTR("GrowlShutdown") /*! @defined GROWL_PING * @abstract A distributed notification to check whether Growl is running. * @discussion This is used by the Growl preference pane. If it receives a @@ -313,15 +316,48 @@ * registration dictionary supplied by its delegate. */ #define GROWL_IS_READY XSTR("Lend Me Some Sugar; I Am Your Neighbor!") -/*! @defined GROWL_NOTIFICATION_CLICKED - * @abstract The distributed notification sent when a supported notification is clicked. + + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX + * @abstract Part of the name of the distributed notification sent when a supported notification is clicked. * @discussion When a Growl notification with a click context is clicked on by - * the user, Growl posts this distributed notification. - * The GrowlApplicationBridge responds to this notification by calling a - * callback in its delegate. + * the user, Growl posts a distributed notification whose name is in the format: + * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX] + * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. */ -#define GROWL_NOTIFICATION_CLICKED XSTR("GrowlClicked!") -#define GROWL_NOTIFICATION_TIMED_OUT XSTR("GrowlTimedOut!") +#define GROWL_DISTRIBUTED_NOTIFICATION_CLICKED_SUFFIX XSTR("GrowlClicked!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX + * @abstract Part of the name of the distributed notification sent when a supported notification times out without being clicked. + * @discussion When a Growl notification with a click context times out, Growl posts a distributed notification + * whose name is in the format: + * [NSString stringWithFormat:@"%@-%d-%@", appName, pid, GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX] + * The GrowlApplicationBridge responds to this notification by calling a callback in its delegate. + * NOTE: The user may have actually clicked the 'close' button; this triggers an *immediate* time-out of the notification. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_TIMED_OUT_SUFFIX XSTR("GrowlTimedOut!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON + * @abstract The distributed notification sent when the Notification Center support is toggled on in Growl 2.0 + * @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent + * to inform all running apps that they should now speak to Notification Center directly. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_ON XSTR("GrowlNotificationCenterOn!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF + * @abstract The distributed notification sent when the Notification Center support is toggled off in Growl 2.0 + * @discussion When the user enables Notification Center support in Growl 2.0, this notification is sent + * to inform all running apps that they should no longer speak to Notification Center directly. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_OFF XSTR("GrowlNotificationCenterOff!") + +/*! @defined GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY + * @abstract The distributed notification sent by an application to query Growl 2.0's notification center support. + * @discussion When an app starts up, it will send this query to get Growl 2.0 to spit out whether notification + * center support is on or off. + */ +#define GROWL_DISTRIBUTED_NOTIFICATION_NOTIFICATIONCENTER_QUERY XSTR("GrowlNotificationCenterYN?") + /*! @group Other symbols */ /* Symbols which don't fit into any of the other categories. */ @@ -345,4 +381,6 @@ #define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition" +#define GROWL_PLUGIN_CONFIG_ID XSTR("GrowlPluginConfigurationID") + #endif //ndef _GROWLDEFINES_H diff --git a/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist b/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist index ab7194d2d..6a90f41b9 100644 --- a/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +++ b/ThirdParty/Frameworks/Growl.framework/Versions/A/Resources/Info.plist @@ -2,6 +2,8 @@ + BuildMachineOSBuild + 12C60 CFBundleDevelopmentRegion English CFBundleExecutable @@ -13,11 +15,25 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.1.2 + 2.0.1 CFBundleSignature GRRR CFBundleVersion - 1.1.2 + 2.0.1 + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformBuild + 4G2008a + DTPlatformVersion + GM + DTSDKBuild + 12C37 + DTSDKName + macosx10.8 + DTXcode + 0452 + DTXcodeBuild + 4G2008a NSPrincipalClass GrowlApplicationBridge