diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index b46476090..75424c6a9 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -179,6 +179,7 @@ 838491871808591F00E7332D /* NDHotKey.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8384917E1808585D00E7332D /* NDHotKey.framework */; }; 838491881808593200E7332D /* NDHotKey.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8384917E1808585D00E7332D /* NDHotKey.framework */; }; 8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; }; + 83A0F4E31816DBF900119DB4 /* playptmod.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83A0F4891816CE5E00119DB4 /* playptmod.bundle */; }; 83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066A1180D5669008E3612 /* MIDI.bundle */; }; 83BCB8DE17FC971300760340 /* FFMPEG.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = B09E94350D747F7B0064F138 /* FFMPEG.bundle */; }; 83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */; }; @@ -467,6 +468,20 @@ remoteGlobalIDString = 32F1615514E6BB3B00D6AB2F; remoteInfo = NDHotKey; }; + 83A0F4881816CE5E00119DB4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83A0F46F1816CE5E00119DB4; + remoteInfo = playptmod; + }; + 83A0F4E11816DBE800119DB4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83A0F46E1816CE5E00119DB4; + remoteInfo = playptmod; + }; 83B066A0180D5669008E3612 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 83B0669C180D5668008E3612 /* MIDI.xcodeproj */; @@ -590,6 +605,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + 83A0F4E31816DBF900119DB4 /* playptmod.bundle in CopyFiles */, 83B06704180D579E008E3612 /* MIDI.bundle in CopyFiles */, 8375B36517FFEF130092A79F /* Opus.bundle in CopyFiles */, 8359009D17FF06570060F3ED /* ArchiveSource.bundle in CopyFiles */, @@ -877,6 +893,7 @@ 838491791808585C00E7332D /* NDHotKey.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = NDHotKey.xcodeproj; path = Frameworks/NDHotKey/NDHotKey.xcodeproj; sourceTree = ""; }; 8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = ""; }; 8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = ""; }; + 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = playptmod.xcodeproj; path = Plugins/playptmod/playptmod.xcodeproj; sourceTree = ""; }; 83B0669C180D5668008E3612 /* MIDI.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MIDI.xcodeproj; path = Plugins/MIDI/MIDI.xcodeproj; sourceTree = ""; }; 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = miniModeOffTemplate.pdf; path = Images/miniModeOffTemplate.pdf; sourceTree = ""; }; 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = miniModeOnTemplate.pdf; path = Images/miniModeOnTemplate.pdf; sourceTree = ""; }; @@ -1185,6 +1202,7 @@ 8359FF2C17FEF35C0060F3ED /* ArchiveSource.xcodeproj */, 8375B05117FFEA400092A79F /* Opus.xcodeproj */, 83B0669C180D5668008E3612 /* MIDI.xcodeproj */, + 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */, ); name = PlugIns; sourceTree = ""; @@ -1550,6 +1568,14 @@ name = Products; sourceTree = ""; }; + 83A0F4851816CE5E00119DB4 /* Products */ = { + isa = PBXGroup; + children = ( + 83A0F4891816CE5E00119DB4 /* playptmod.bundle */, + ); + name = Products; + sourceTree = ""; + }; 83B0669D180D5668008E3612 /* Products */ = { isa = PBXGroup; children = ( @@ -1702,6 +1728,7 @@ buildRules = ( ); dependencies = ( + 83A0F4E21816DBE800119DB4 /* PBXTargetDependency */, 83E6B7641816138800D4576D /* PBXTargetDependency */, 83B06703180D5776008E3612 /* PBXTargetDependency */, 838491861808591400E7332D /* PBXTargetDependency */, @@ -1830,6 +1857,10 @@ ProductGroup = 8375B05217FFEA400092A79F /* Products */; ProjectRef = 8375B05117FFEA400092A79F /* Opus.xcodeproj */; }, + { + ProductGroup = 83A0F4851816CE5E00119DB4 /* Products */; + ProjectRef = 83A0F4841816CE5E00119DB4 /* playptmod.xcodeproj */; + }, { ProductGroup = 8E8D41C30CBB0DA000135C1B /* Products */; ProjectRef = 8E8D41C20CBB0DA000135C1B /* Pls.xcodeproj */; @@ -1996,6 +2027,13 @@ remoteRef = 8384917D1808585D00E7332D /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 83A0F4891816CE5E00119DB4 /* playptmod.bundle */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = playptmod.bundle; + remoteRef = 83A0F4881816CE5E00119DB4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 83B066A1180D5669008E3612 /* MIDI.bundle */ = { isa = PBXReferenceProxy; fileType = wrapper.cfbundle; @@ -2351,6 +2389,11 @@ name = NDHotKey; targetProxy = 838491851808591400E7332D /* PBXContainerItemProxy */; }; + 83A0F4E21816DBE800119DB4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = playptmod; + targetProxy = 83A0F4E11816DBE800119DB4 /* PBXContainerItemProxy */; + }; 83B06703180D5776008E3612 /* PBXTargetDependency */ = { isa = PBXTargetDependency; name = MIDI; diff --git a/Frameworks/playptmod/playptmod.xcodeproj/project.pbxproj b/Frameworks/playptmod/playptmod.xcodeproj/project.pbxproj new file mode 100644 index 000000000..1e42b0d19 --- /dev/null +++ b/Frameworks/playptmod/playptmod.xcodeproj/project.pbxproj @@ -0,0 +1,308 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 83A0F4A61816CEAD00119DB4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 83A0F4A41816CEAD00119DB4 /* InfoPlist.strings */; }; + 83A0F4D31816CF9500119DB4 /* blip_buf.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A0F4CF1816CF9500119DB4 /* blip_buf.c */; }; + 83A0F4D41816CF9500119DB4 /* blip_buf.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A0F4D01816CF9500119DB4 /* blip_buf.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 83A0F4D51816CF9500119DB4 /* playptmod.c in Sources */ = {isa = PBXBuildFile; fileRef = 83A0F4D11816CF9500119DB4 /* playptmod.c */; }; + 83A0F4D61816CF9500119DB4 /* playptmod.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A0F4D21816CF9500119DB4 /* playptmod.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 83A0F4981816CEAD00119DB4 /* playptmod.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = playptmod.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 83A0F4A31816CEAD00119DB4 /* playptmod-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "playptmod-Info.plist"; sourceTree = ""; }; + 83A0F4A51816CEAD00119DB4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 83A0F4CF1816CF9500119DB4 /* blip_buf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = blip_buf.c; sourceTree = ""; }; + 83A0F4D01816CF9500119DB4 /* blip_buf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = blip_buf.h; sourceTree = ""; }; + 83A0F4D11816CF9500119DB4 /* playptmod.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = playptmod.c; sourceTree = ""; }; + 83A0F4D21816CF9500119DB4 /* playptmod.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = playptmod.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83A0F4941816CEAD00119DB4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83A0F48E1816CEAD00119DB4 = { + isa = PBXGroup; + children = ( + 83A0F4A11816CEAD00119DB4 /* playptmod */, + 83A0F49A1816CEAD00119DB4 /* Frameworks */, + 83A0F4991816CEAD00119DB4 /* Products */, + ); + sourceTree = ""; + }; + 83A0F4991816CEAD00119DB4 /* Products */ = { + isa = PBXGroup; + children = ( + 83A0F4981816CEAD00119DB4 /* playptmod.framework */, + ); + name = Products; + sourceTree = ""; + }; + 83A0F49A1816CEAD00119DB4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83A0F49D1816CEAD00119DB4 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 83A0F49D1816CEAD00119DB4 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 83A0F4A11816CEAD00119DB4 /* playptmod */ = { + isa = PBXGroup; + children = ( + 83A0F4CF1816CF9500119DB4 /* blip_buf.c */, + 83A0F4D01816CF9500119DB4 /* blip_buf.h */, + 83A0F4D11816CF9500119DB4 /* playptmod.c */, + 83A0F4D21816CF9500119DB4 /* playptmod.h */, + 83A0F4A21816CEAD00119DB4 /* Supporting Files */, + ); + path = playptmod; + sourceTree = ""; + }; + 83A0F4A21816CEAD00119DB4 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 83A0F4A31816CEAD00119DB4 /* playptmod-Info.plist */, + 83A0F4A41816CEAD00119DB4 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 83A0F4951816CEAD00119DB4 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A0F4D41816CF9500119DB4 /* blip_buf.h in Headers */, + 83A0F4D61816CF9500119DB4 /* playptmod.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 83A0F4971816CEAD00119DB4 /* playptmod */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83A0F4C01816CEAD00119DB4 /* Build configuration list for PBXNativeTarget "playptmod" */; + buildPhases = ( + 83A0F4931816CEAD00119DB4 /* Sources */, + 83A0F4941816CEAD00119DB4 /* Frameworks */, + 83A0F4951816CEAD00119DB4 /* Headers */, + 83A0F4961816CEAD00119DB4 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = playptmod; + productName = playptmod; + productReference = 83A0F4981816CEAD00119DB4 /* playptmod.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83A0F48F1816CEAD00119DB4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = "Christopher Snowhill"; + }; + buildConfigurationList = 83A0F4921816CEAD00119DB4 /* Build configuration list for PBXProject "playptmod" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83A0F48E1816CEAD00119DB4; + productRefGroup = 83A0F4991816CEAD00119DB4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 83A0F4971816CEAD00119DB4 /* playptmod */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 83A0F4961816CEAD00119DB4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A0F4A61816CEAD00119DB4 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83A0F4931816CEAD00119DB4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A0F4D51816CF9500119DB4 /* playptmod.c in Sources */, + 83A0F4D31816CF9500119DB4 /* blip_buf.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 83A0F4A41816CEAD00119DB4 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 83A0F4A51816CEAD00119DB4 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 83A0F4BE1816CEAD00119DB4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Christopher Snowhill"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + 83A0F4BF1816CEAD00119DB4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Christopher Snowhill"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + SDKROOT = macosx; + }; + name = Release; + }; + 83A0F4C11816CEAD00119DB4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "playptmod/playptmod-Info.plist"; + INSTALL_PATH = "@loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 83A0F4C21816CEAD00119DB4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = "playptmod/playptmod-Info.plist"; + INSTALL_PATH = "@loader_path/../Frameworks"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83A0F4921816CEAD00119DB4 /* Build configuration list for PBXProject "playptmod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83A0F4BE1816CEAD00119DB4 /* Debug */, + 83A0F4BF1816CEAD00119DB4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83A0F4C01816CEAD00119DB4 /* Build configuration list for PBXNativeTarget "playptmod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83A0F4C11816CEAD00119DB4 /* Debug */, + 83A0F4C21816CEAD00119DB4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83A0F48F1816CEAD00119DB4 /* Project object */; +} diff --git a/Frameworks/playptmod/playptmod/blip_buf.c b/Frameworks/playptmod/playptmod/blip_buf.c new file mode 100644 index 000000000..e7887f244 --- /dev/null +++ b/Frameworks/playptmod/playptmod/blip_buf.c @@ -0,0 +1,197 @@ +/* blip_buf 1.1.0. http://www.slack.net/~ant/ */ + +#include "blip_buf.h" + +#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 + +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. +*/ + +/* probably not totally portable */ +#define SAMPLES( buf ) (buf->samples) + +/* 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;\ + } + +void ptm_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 = time_unit / 2; + m->index = 0; + m->integrator = 0; + m->last_value = 0; + memset( SAMPLES( m ), 0, 128 * sizeof (buf_t) ); +} + +int ptm_blip_read_sample( blip_t* m ) +{ + int retval; + + { + buf_t * in = SAMPLES( m ) + m->index; + int sum = m->integrator; + { + /* Eliminate fraction */ + int s = ARITH_SHIFT( sum, delta_bits ); + + sum += *in; + + retval = s; + } + m->integrator = sum; + + *in = 0; + + m->index = ( m->index + 1 ) % 128; + } + + return retval; +} + +/* 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 ptm_blip_add_delta( blip_t* m, float time, int delta ) +{ + unsigned fixed = (unsigned) ((int)(time * time_unit + m->offset) >> pre_shift); + buf_t* out = SAMPLES( m ) + (m->index + (fixed >> frac_bits)) % 128; + buf_t* end = SAMPLES( m ) + 128; + + 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; + + *out++ += in[0]*delta + in[half_width+0]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[1]*delta + in[half_width+1]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[2]*delta + in[half_width+2]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[3]*delta + in[half_width+3]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[4]*delta + in[half_width+4]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[5]*delta + in[half_width+5]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[6]*delta + in[half_width+6]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[7]*delta + in[half_width+7]*delta2; + if (out >= end) out = SAMPLES( m ); + + in = rev; + *out++ += in[7]*delta + in[7-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[6]*delta + in[6-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[5]*delta + in[5-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[4]*delta + in[4-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[3]*delta + in[3-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[2]*delta + in[2-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[1]*delta + in[1-half_width]*delta2; + if (out >= end) out = SAMPLES( m ); + *out++ += in[0]*delta + in[0-half_width]*delta2; +} diff --git a/Frameworks/playptmod/playptmod/blip_buf.h b/Frameworks/playptmod/playptmod/blip_buf.h new file mode 100644 index 000000000..c772f8959 --- /dev/null +++ b/Frameworks/playptmod/playptmod/blip_buf.h @@ -0,0 +1,72 @@ +/** \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 + +#include + +/* 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 + +typedef int buf_t; + +/** Sample buffer that resamples to output rate and accumulates samples +until they're read out */ +struct blip_t +{ + fixed_t offset; + int index; + int avail; + int size; + int integrator; + int last_value; + buf_t samples[128]; +}; + +#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; + +/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ +void ptm_blip_clear( blip_t* ); + +/** Adds positive/negative delta into buffer at specified clock time. */ +void ptm_blip_add_delta( blip_t*, float clock_time, int delta ); + +/** Number of buffered samples available for reading. */ +int ptm_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 ptm_blip_read_sample( blip_t* ); + + +/* Deprecated */ +typedef blip_t blip_buffer_t; + +#ifdef __cplusplus + } +#endif + +#endif diff --git a/Frameworks/playptmod/playptmod/en.lproj/InfoPlist.strings b/Frameworks/playptmod/playptmod/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..477b28ff8 --- /dev/null +++ b/Frameworks/playptmod/playptmod/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Frameworks/playptmod/playptmod/playptmod-Info.plist b/Frameworks/playptmod/playptmod/playptmod-Info.plist new file mode 100644 index 000000000..a0fc003b7 --- /dev/null +++ b/Frameworks/playptmod/playptmod/playptmod-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + net.kode54.lib.playptmod + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2013 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Frameworks/playptmod/playptmod/playptmod.c b/Frameworks/playptmod/playptmod/playptmod.c new file mode 100644 index 000000000..5959efa18 --- /dev/null +++ b/Frameworks/playptmod/playptmod/playptmod.c @@ -0,0 +1,2927 @@ +/* +** - --=playptmod v1.05+ - 8bitbubsy 2010-2013=-- - +** This is the native Win32 API version, no DLL needed in you +** production zip/rar whatever. +** +** Thanks to mukunda for learning me how to code a .MOD player +** some years back! +** +** Thanks to ad_/aciddose/adejr for the BLEP and LED filter +** routines. +** +** Note: There's a lot of weird behavior in the coding to +** "emulate" the weird stuff ProTracker on the Amiga does. +** If you see something fishy in the code, it's probably +** supposed to be like that. Please don't change it, you're +** literally asking for hours of hard debugging if you do. +** +** HOW DO I USE THIS FILE? +** Make a new file called main.c, and put this on top: +** +** #include +** +** void * playptmod_Create(int soundFrequency); +** int playptmod_Load(void *, const char *filename); +** void playptmod_Play(void *); +** void playptmod_Render(void *, signed short *, int); +** void playptmod_Free(void *); +** +** void main(void) +** { +** int app_running = 1; +** +** void *p = playptmod_Create(44100); +** playptmod_Load(p, "hello.mod"); +** playptmod_Play(p); +** +** while (app_running) +** { +** signed short samples[1024]; +** +** if (someone_pressed_a_key()) +** { +** app_running = 0; +** } +** +** playptmod_Render(p, samples, 512); +** // output samples to system here +** +** // Make sure to delay a bit here +** } +** +** playptmod_Free(p); +** +** return 0; +** } +** +** +** You can also integrate it as a resource in the EXE, +** and use some Win32 API functions to copy the MOD +** to memory and get a pointer to it. Then you call +** playptmod_LoadMem instead. +** +*/ + +#include "playptmod.h" +#include "blip_buf.h" + +#include +#include // memcpy() +#include // malloc(), calloc(), free() +#include // floorf(), sinf() + +#define HI_NYBBLE(x) ((x) >> 4) +#define LO_NYBBLE(x) ((x) & 0x0F) +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) +#define DENORMAL_OFFSET 1E-10f +#define PT_MIN_PERIOD 108 +#define PT_MAX_PERIOD 907 +#define MAX_CHANNELS 32 +#define MOD_ROWS 64 +#define MOD_SAMPLES 31 + +#ifndef true +#define true 1 +#define false 0 +#endif + +#ifdef _MSC_VER +#define inline __forceinline +#endif + +enum +{ + FORMAT_MK, // ProTracker 1.x + FORMAT_MK2, // ProTracker 2.x (if tune has >64 patterns) + FORMAT_FLT4, // StarTrekker + FORMAT_FLT8, + FORMAT_NCHN, // FastTracker II (only 1-9 channel MODs) + FORMAT_NNCH, // FastTracker II (10-32 channel MODs) + FORMAT_16CN, // FastTracker II (16 channel MODs) + FORMAT_32CN, // FastTracker II (32 channel MODs) + FORMAT_STK, // The Ultimate SoundTracker (15 samples) + FORMAT_NT, // NoiseTracker 1.0 + + FORMAT_MTM, // MultiTracker + + FORMAT_UNKNOWN, + + FLAG_NOTE = 1, + FLAG_SAMPLE = 2, + FLAG_NEWSAMPLE = 4, + TEMPFLAG_START = 1, + TEMPFLAG_DELAY = 2, + TEMPFLAG_NEW_SAMPLE = 4 +}; + +enum +{ + soundBufferSize = 2048 * 4 +}; + +typedef struct modnote +{ + unsigned char sample; + unsigned char command; + unsigned char param; + short period; +} modnote_t; + +typedef struct +{ + unsigned char orderCount; + unsigned char patternCount; + unsigned char rowCount; + unsigned char restartPos; + unsigned char order[128]; + unsigned char volume[MAX_CHANNELS]; + unsigned char pan[MAX_CHANNELS]; + unsigned char ticks; + unsigned char format; + unsigned char channelCount; + short tempo; + short initBPM; + int moduleSize; + int totalSampleSize; +} MODULE_HEADER; + +typedef struct +{ + unsigned char fineTune; + unsigned char volume; + int iffSize; + int loopStart; + int loopLength; + int length; + int reallength; + int tmpLoopStart; + int offset; + unsigned char attribute; +} MODULE_SAMPLE; + +typedef struct +{ + char patternLoopRow; + char patternLoopCounter; + char volume; + unsigned char sample; + unsigned char command; + unsigned char param; + unsigned char flags; + unsigned char tempFlags; + unsigned char tempFlagsBackup; + unsigned char fineTune; + unsigned char tremoloPos; + unsigned char vibratoPos; + unsigned char tremoloControl; + unsigned char tremoloSpeed; + unsigned char tremoloDepth; + unsigned char vibratoControl; + unsigned char vibratoSpeed; + unsigned char vibratoDepth; + unsigned char glissandoControl; + unsigned char glissandoSpeed; + unsigned char invertLoopDelay; + unsigned char invertLoopSpeed; + unsigned char chanIndex; + short period; + short tempPeriod; + int noNote; + int invertLoopOffset; + int offset; + int offsetTemp; + int offsetBugNotAdded; +} mod_channel; + +typedef struct +{ + char moduleLoaded; + char *sampleData; + char *originalSampleData; + MODULE_HEADER head; + MODULE_SAMPLE samples[31]; + modnote_t *patterns[256]; + mod_channel channels[MAX_CHANNELS]; +} MODULE; + +typedef struct paula_filter_state +{ + float LED[4]; + float high[2]; +} Filter; + +typedef struct paula_filter_coefficients +{ + float LED; + float LEDFb; + float high; +} FilterC; + +typedef struct voice_data +{ + const char *newData; + const char *data; + int index; + int length; + int loopLength; + int loopEnd; + int newLength; + int newLoopLength; + int newLoopEnd; + int swapSampleFlag; + int vol; + int panL; + int panR; + int step; + int newStep; + float frac; + float rate; + int mute; +} Voice; + +typedef struct +{ + unsigned long length; + unsigned long remain; + const unsigned char *buf; + const unsigned char *t_buf; +} BUF; + +typedef struct +{ + int numChans; + char pattBreakBugPos; + char pattBreakFlag; + char pattDelayFlag; + char forceEffectsOff; + char tempVolume; + unsigned char modRow; + unsigned char modSpeed; + unsigned short modBPM; + unsigned char modTick; + unsigned char modPattern; + unsigned char modOrder; + unsigned char tempFlags; + unsigned char PBreakPosition; + unsigned char PattDelayTime; + unsigned char PattDelayTime2; + unsigned char PosJumpAssert; + unsigned char PBreakFlag; + short tempPeriod; + int tempoTimerVal; + char moduleLoaded; + char modulePlaying; + char useLEDFilter; + unsigned short soundBufferSize; + unsigned int soundFrequency; + char soundBuffers; + float *frequencyTable; + float *extendedFrequencyTable; + unsigned char *sinusTable; + int minPeriod; + int maxPeriod; + int loopCounter; + int sampleCounter; + int samplesPerTick; + int vBlankTiming; + Voice v[MAX_CHANNELS]; + Filter filter; + FilterC filterC; + float *mixBufferL; + float *mixBufferR; + blip_t blep[MAX_CHANNELS]; + blip_t blepVol[MAX_CHANNELS]; + unsigned int orderPlayed[256]; + MODULE *source; +} player; + +static const unsigned char invertLoopSpeeds[16] = +{ + 0x00, 0x05, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0D, 0x0F, 0x13, 0x16, 0x1A, 0x20, 0x2B, 0x40, 0x80 +}; + +static const short rawAmigaPeriods[606] = +{ + 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,0, + 850,802,757,715,674,637,601,567,535,505,477,450, + 425,401,379,357,337,318,300,284,268,253,239,225, + 213,201,189,179,169,159,150,142,134,126,119,113,0, + 844,796,752,709,670,632,597,563,532,502,474,447, + 422,398,376,355,335,316,298,282,266,251,237,224, + 211,199,188,177,167,158,149,141,133,125,118,112,0, + 838,791,746,704,665,628,592,559,528,498,470,444, + 419,395,373,352,332,314,296,280,264,249,235,222, + 209,198,187,176,166,157,148,140,132,125,118,111,0, + 832,785,741,699,660,623,588,555,524,495,467,441, + 416,392,370,350,330,312,294,278,262,247,233,220, + 208,196,185,175,165,156,147,139,131,124,117,110,0, + 826,779,736,694,655,619,584,551,520,491,463,437, + 413,390,368,347,328,309,292,276,260,245,232,219, + 206,195,184,174,164,155,146,138,130,123,116,109,0, + 820,774,730,689,651,614,580,547,516,487,460,434, + 410,387,365,345,325,307,290,274,258,244,230,217, + 205,193,183,172,163,154,145,137,129,122,115,109,0, + 814,768,725,684,646,610,575,543,513,484,457,431, + 407,384,363,342,323,305,288,272,256,242,228,216, + 204,192,181,171,161,152,144,136,128,121,114,108,0, + 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,0, + 900,850,802,757,715,675,636,601,567,535,505,477, + 450,425,401,379,357,337,318,300,284,268,253,238, + 225,212,200,189,179,169,159,150,142,134,126,119,0, + 894,844,796,752,709,670,632,597,563,532,502,474, + 447,422,398,376,355,335,316,298,282,266,251,237, + 223,211,199,188,177,167,158,149,141,133,125,118,0, + 887,838,791,746,704,665,628,592,559,528,498,470, + 444,419,395,373,352,332,314,296,280,264,249,235, + 222,209,198,187,176,166,157,148,140,132,125,118,0, + 881,832,785,741,699,660,623,588,555,524,494,467, + 441,416,392,370,350,330,312,294,278,262,247,233, + 220,208,196,185,175,165,156,147,139,131,123,117,0, + 875,826,779,736,694,655,619,584,551,520,491,463, + 437,413,390,368,347,328,309,292,276,260,245,232, + 219,206,195,184,174,164,155,146,138,130,123,116,0, + 868,820,774,730,689,651,614,580,547,516,487,460, + 434,410,387,365,345,325,307,290,274,258,244,230, + 217,205,193,183,172,163,154,145,137,129,122,115,0, + 862,814,768,725,684,646,610,575,543,513,484,457, + 431,407,384,363,342,323,305,288,272,256,242,228, + 216,203,192,181,171,161,152,144,136,128,121,114,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0 +}; + +static short extendedRawPeriods[16 * 85 + 13]; + +static const short npertab[84] = +{ + /* Octaves 6 -> 0 */ + /* C C# D D# E F F# G G# A A# B */ + 0x6b0,0x650,0x5f4,0x5a0,0x54c,0x500,0x4b8,0x474,0x434,0x3f8,0x3c0,0x38a, + 0x358,0x328,0x2fa,0x2d0,0x2a6,0x280,0x25c,0x23a,0x21a,0x1fc,0x1e0,0x1c5, + 0x1ac,0x194,0x17d,0x168,0x153,0x140,0x12e,0x11d,0x10d,0x0fe,0x0f0,0x0e2, + 0x0d6,0x0ca,0x0be,0x0b4,0x0aa,0x0a0,0x097,0x08f,0x087,0x07f,0x078,0x071, + 0x06b,0x065,0x05f,0x05a,0x055,0x050,0x04b,0x047,0x043,0x03f,0x03c,0x038, + 0x035,0x032,0x02f,0x02d,0x02a,0x028,0x025,0x023,0x021,0x01f,0x01e,0x01c, + 0x01b,0x019,0x018,0x016,0x015,0x014,0x013,0x012,0x011,0x010,0x00f,0x00e +}; + +static const short finetune[16] = +{ + 8363,8413,8463,8529,8581,8651,8723,8757, + 7895,7941,7985,8046,8107,8169,8232,8280 +}; + +static float calcRcCoeff(float sampleRate, float cutOffFreq) +{ + if (cutOffFreq >= (sampleRate / 2.0f)) + return (1.0f); + + return ((2.0f * 3.141592f) * cutOffFreq / sampleRate); +} + +static BUF *bufopen(const unsigned char *bufToCopy, unsigned long bufferSize) +{ + BUF *b; + + b = (BUF *)malloc(sizeof (BUF)); + + b->t_buf = bufToCopy; + b->buf = bufToCopy; + b->length = bufferSize; + b->remain = bufferSize; + + return (b); +} + +static void bufclose(BUF *_SrcBuf) +{ + if (_SrcBuf != NULL) + free(_SrcBuf); +} + +static unsigned long buftell(BUF *_SrcBuf) +{ + if (_SrcBuf->buf > _SrcBuf->t_buf) + return (_SrcBuf->buf - _SrcBuf->t_buf); + else + return (_SrcBuf->t_buf - _SrcBuf->buf); +} + +static void bufread(void *_DstBuf, size_t _ElementSize, size_t _Count, BUF *_SrcBuf) +{ + _Count *= _ElementSize; + if (_Count > _SrcBuf->remain) + _Count = _SrcBuf->remain; + + _SrcBuf->remain -= _Count; + memcpy(_DstBuf, _SrcBuf->buf, _Count); + _SrcBuf->buf += _Count; +} + +static void bufseek(BUF *_SrcBuf, long _Offset, int _Origin) +{ + if (_SrcBuf->buf) + { + switch (_Origin) + { + case SEEK_SET: _SrcBuf->buf = _SrcBuf->t_buf + _Offset; break; + case SEEK_CUR: _SrcBuf->buf += _Offset; break; + case SEEK_END: _SrcBuf->buf = _SrcBuf->t_buf + _SrcBuf->length + _Offset; break; + default: break; + } + + _Offset = _SrcBuf->buf - _SrcBuf->t_buf; + _SrcBuf->remain = (unsigned int)(_Offset) > _SrcBuf->length ? 0 : _SrcBuf->length - _Offset; + } +} + +static inline int periodToNote(player *p, char finetune, short period) +{ + char l; + char m; + char h; + short *tablePointer; + + if (p->minPeriod == PT_MIN_PERIOD) + { + l = 0; + h = 35; + + tablePointer = (short *)&rawAmigaPeriods[finetune * 37]; + while (h >= l) + { + m = (h + l) / 2; + + if (tablePointer[m] == period) + break; + else if (tablePointer[m] > period) + l = m + 1; + else + h = m - 1; + } + } + else + { + l = 0; + h = 83; + + tablePointer = (short *)&extendedRawPeriods[finetune * 85]; + while (h >= l) + { + m = (h + l) / 2; + + if (tablePointer[m] == period) + break; + else if (tablePointer[m] > period) + l = m + 1; + else + h = m - 1; + } + } + + return (m); +} + +static void mixerSwapChSource(player *p, int ch, const char *src, int length, int loopStart, int loopLength, int step) +{ + p->v[ch].swapSampleFlag = true; + p->v[ch].newData = src; + p->v[ch].newLength = length; + p->v[ch].newLoopLength = loopLength; + p->v[ch].newLoopEnd = loopLength + loopStart; + p->v[ch].newStep = step; +} + +static void mixerSetChSource(player *p, int ch, const char *src, int length, int loopStart, int loopLength, int offset, int step) +{ + p->v[ch].swapSampleFlag = false; + p->v[ch].data = src; + p->v[ch].length = length; + p->v[ch].index = offset; + p->v[ch].frac = 0.0f; + p->v[ch].loopEnd = loopStart + loopLength; + p->v[ch].loopLength = loopLength; + p->v[ch].step = step; + + if (p->v[ch].index > 0) + { + if (p->v[ch].loopLength > 2) + { + if (p->v[ch].index >= p->v[ch].loopEnd) + p->v[ch].index = 0; + } + else if (p->v[ch].index >= p->v[ch].length) + { + p->v[ch].data = NULL; + } + } +} + +static void mixerSetChPan(player *p, int ch, int pan) +{ + p->v[ch].panL = 256 - pan; + p->v[ch].panR = pan; +} + +static void mixerSetChVol(player *p, int ch, int vol) +{ + p->v[ch].vol = vol; +} + +static void mixerCutChannels(player *p) +{ + int i; + + memset(p->v, 0, sizeof (p->v)); + for (i = 0; i < MAX_CHANNELS; i++) + { + ptm_blip_clear(&p->blep[i]); + ptm_blip_clear(&p->blepVol[i]); + } + + memset(&p->filter, 0, sizeof (p->filter)); + + if (p->source) + { + for (i = 0; i < MAX_CHANNELS; i++) + { + mixerSetChVol(p, i, p->source->head.volume[i]); + mixerSetChPan(p, i, p->source->head.pan[i]); + } + } + else + { + for (i = 0; i < MAX_CHANNELS; i++) + { + mixerSetChVol(p, i, 64); + mixerSetChPan(p, i, (i + 1) & 2 ? 160 : 96); + } + } +} + +static void mixerSetChRate(player *p, int ch, float rate) +{ + p->v[ch].rate = rate; +} + +static void outputAudio(player *p, short *target, int numSamples) +{ + short *out; + int i; + int j; + int step; + short tempSample; + int tempVolume; + float L; + float R; + + memset(p->mixBufferL, 0, numSamples * sizeof (float)); + memset(p->mixBufferR, 0, numSamples * sizeof (float)); + + for (i = 0; i < p->source->head.channelCount; ++i) + { + j = 0; + + if (p->v[i].data && p->v[i].rate) + { + step = p->v[i].step; + for (j = 0; j < numSamples;) + { + tempSample = (p->v[i].data ? (step == 2 ? (p->v[i].data[p->v[i].index] + p->v[i].data[p->v[i].index + 1] * 0x100) : p->v[i].data[p->v[i].index] * 0x100) : 0); + tempVolume = (p->v[i].data && !p->v[i].mute ? p->v[i].vol : 0); + + while (j < numSamples && (!p->v[i].data || p->v[i].frac >= 1.0f)) + { + float t_vol = 0.0f; + float t_smp = 0.0f; + signed int i_smp; + + if (p->v[i].data) + p->v[i].frac -= 1.0f; + + t_vol += ptm_blip_read_sample(&p->blepVol[i]); + t_smp += ptm_blip_read_sample(&p->blep[i]); + + t_smp *= t_vol; + i_smp = (signed int)t_smp; + + p->mixBufferL[j] += i_smp * p->v[i].panL; + p->mixBufferR[j] += i_smp * p->v[i].panR; + + j++; + } + + if (j >= numSamples) break; + + if (tempSample != p->blep[i].last_value) + { + float delta = (float)(tempSample - p->blep[i].last_value); + p->blep[i].last_value = tempSample; + ptm_blip_add_delta(&p->blep[i], p->v[i].frac, delta); + } + + if (tempVolume != p->blepVol[i].last_value) + { + float delta = (float)(tempVolume - p->blepVol[i].last_value); + p->blepVol[i].last_value = tempVolume; + ptm_blip_add_delta(&p->blepVol[i], 0, delta); + } + + if (p->v[i].data) + { + p->v[i].index += step; + p->v[i].frac += p->v[i].rate; + + if (p->v[i].loopLength > (2 * step)) + { + if (p->v[i].index >= p->v[i].loopEnd) + { + if (p->v[i].swapSampleFlag == true) + { + p->v[i].swapSampleFlag = false; + + if (p->v[i].newLoopLength <= (2 * step)) + { + p->v[i].data = NULL; + + continue; + } + + p->v[i].data = p->v[i].newData; + p->v[i].length = p->v[i].newLength; + p->v[i].loopEnd = p->v[i].newLoopEnd; + p->v[i].loopLength = p->v[i].newLoopLength; + step = p->v[i].step = p->v[i].newStep; + + p->v[i].index = p->v[i].loopEnd - p->v[i].loopLength; + } + else + { + p->v[i].index -= p->v[i].loopLength; + } + } + } + else if (p->v[i].index >= p->v[i].length) + { + if (p->v[i].swapSampleFlag == true) + { + p->v[i].swapSampleFlag = false; + p->v[i].data = p->v[i].newData; + p->v[i].length = p->v[i].newLength; + p->v[i].loopEnd = p->v[i].newLoopEnd; + p->v[i].loopLength = p->v[i].newLoopLength; + step = p->v[i].step = p->v[i].newStep; + } + else + { + p->v[i].data = NULL; + } + } + } + } + } + else if (p->v[i].swapSampleFlag == true) + { + p->v[i].swapSampleFlag = false; + p->v[i].data = p->v[i].newData; + p->v[i].length = p->v[i].newLength; + p->v[i].loopEnd = p->v[i].newLoopEnd; + p->v[i].loopLength = p->v[i].newLoopLength; + p->v[i].step = p->v[i].newStep; + } + + if ((j < numSamples) && (p->v[i].data == NULL)) + { + for (; j < numSamples; ++j) + { + int i_smp; + + tempVolume = (float)p->blepVol[i].last_value; + tempSample = (float)p->blep[i].last_value; + + tempVolume += ptm_blip_read_sample(&p->blepVol[i]); + tempSample += ptm_blip_read_sample(&p->blep[i]); + + tempSample *= tempVolume; + i_smp = (signed int)tempSample; + + p->mixBufferL[j] += i_smp * p->v[i].panL; + p->mixBufferR[j] += i_smp * p->v[i].panR; + } + } + } + + out = target; + + { + float downscale; + + if (p->numChans <= 4) + downscale = 1.0f / (96.0f * 256.0f); + else + downscale = 1.0f / (160.0f * 256.0f); + + for (i = 0; i < numSamples; ++i) + { + L = p->mixBufferL[i]; + R = p->mixBufferR[i]; + + if (p->useLEDFilter == true) + { + p->filter.LED[0] += (p->filterC.LED * (L - p->filter.LED[0]) + + p->filterC.LEDFb * (p->filter.LED[0] - p->filter.LED[1]) + DENORMAL_OFFSET); + p->filter.LED[1] += (p->filterC.LED * (p->filter.LED[0] - p->filter.LED[1]) + DENORMAL_OFFSET); + p->filter.LED[2] += (p->filterC.LED * (R - p->filter.LED[2]) + + p->filterC.LEDFb * (p->filter.LED[2] - p->filter.LED[3]) + DENORMAL_OFFSET); + p->filter.LED[3] += (p->filterC.LED * (p->filter.LED[2] - p->filter.LED[3]) + DENORMAL_OFFSET); + + L = p->filter.LED[1]; + R = p->filter.LED[3]; + } + + L -= p->filter.high[0]; + R -= p->filter.high[1]; + + p->filter.high[0] += (p->filterC.high * L + DENORMAL_OFFSET); + p->filter.high[1] += (p->filterC.high * R + DENORMAL_OFFSET); + + L *= downscale; + R *= downscale; + + L = CLAMP(L, -32768.0f, 32767.0f); + R = CLAMP(R, -32768.0f, 32767.0f); + + if ( out ) + { + *out++ = (short)(int)(L); + *out++ = (short)(int)(R); + } + } + } +} + +static unsigned short bufGetWordBigEndian(BUF *in) +{ + unsigned char bytes[2]; + + bufread(bytes, 1, 2, in); + return ((bytes[0] << 8) | bytes[1]); +} + +static unsigned short bufGetWordLittleEndian(BUF *in) +{ + unsigned char bytes[2]; + + bufread(bytes, 1, 2, in); + return ((bytes[1] << 8) | bytes[0]); +} + +static unsigned int bufGetDwordLittleEndian(BUF *in) +{ + unsigned char bytes[4]; + + bufread(bytes, 1, 4, in); + return ((bytes[3] << 24) | (bytes[2] << 16) | (bytes[1] << 8) | bytes[0]); +} + +static int playptmod_LoadMTM(player *p, BUF *fmodule) +{ + int i, j, k; + unsigned int trackCount, commentLength; + unsigned char sampleCount; + unsigned long tracksOffset, sequencesOffset, commentOffset; + unsigned int totalSampleSize = 0, sampleOffset = 0; + + modnote_t *note = NULL; + + bufseek(fmodule, 24, SEEK_SET); + trackCount = bufGetWordLittleEndian(fmodule); + bufread(&p->source->head.patternCount, 1, 1, fmodule); p->source->head.patternCount++; + bufread(&p->source->head.orderCount, 1, 1, fmodule); p->source->head.orderCount++; + commentLength = bufGetWordLittleEndian(fmodule); + bufread(&sampleCount, 1, 1, fmodule); + bufseek(fmodule, 1, SEEK_CUR); + bufread(&p->source->head.rowCount, 1, 1, fmodule); + bufread(&p->source->head.channelCount, 1, 1, fmodule); + + if (!trackCount || !sampleCount || !p->source->head.rowCount || p->source->head.rowCount > 64 || !p->source->head.channelCount || p->source->head.channelCount > 32) + return (false); + + bufread(&p->source->head.pan, 1, 32, fmodule); + + for (i = 0; i < 32; ++i) + { + if (p->source->head.pan[i] <= 15) + { + p->source->head.pan[i] -= (p->source->head.pan[i] & 8) / 8; + p->source->head.pan[i] = (((int)p->source->head.pan[i]) * 255) / 14; + p->source->head.volume[i] = 64; + } + else + { + p->source->head.pan[i] = 128; + p->source->head.volume[i] = 0; + } + } + + for (i = 0; i < sampleCount; ++i) + { + bufseek(fmodule, 22, SEEK_CUR); + + p->source->samples[i].length = bufGetDwordLittleEndian(fmodule); + p->source->samples[i].loopStart = bufGetDwordLittleEndian(fmodule); + p->source->samples[i].loopLength = bufGetDwordLittleEndian(fmodule) - p->source->samples[i].loopStart; + if (p->source->samples[i].loopLength < 2) + p->source->samples[i].loopLength = 2; + + bufread(&p->source->samples[i].fineTune, 1, 1, fmodule); + p->source->samples[i].fineTune = p->source->samples[i].fineTune & 0x0F; + + bufread(&p->source->samples[i].volume, 1, 1, fmodule); + bufread(&p->source->samples[i].attribute, 1, 1, fmodule); + + totalSampleSize += p->source->samples[i].length; + } + + bufread(&p->source->head.order, 1, 128, fmodule); + + tracksOffset = fmodule->length - fmodule->remain; + sequencesOffset = tracksOffset + 192 * trackCount; + commentOffset = sequencesOffset + 64 * p->source->head.patternCount; + + for (i = 0; i < p->source->head.patternCount; ++i) + { + note = p->source->patterns[i] = (modnote_t *)calloc(1, sizeof (modnote_t) * p->source->head.rowCount * p->source->head.channelCount); + if (!note) + { + for (j = 0; j < i; ++j) + { + if (p->source->patterns[j]) + { + free(p->source->patterns[j]); + p->source->patterns[j] = NULL; + } + } + return 0; + } + + for (j = 0; j < p->source->head.channelCount; ++j) + { + int trackNumber; + bufseek(fmodule, sequencesOffset + 64 * i + 2 * j, SEEK_SET); + trackNumber = bufGetWordLittleEndian(fmodule); + if (trackNumber--) + { + bufseek(fmodule, tracksOffset + 192 * trackNumber, SEEK_SET); + for (k = 0; k < p->source->head.rowCount; ++k) + { + unsigned char buf[3]; + bufread(buf, 1, 3, fmodule); + if (buf[0] || buf[1] || buf[2]) + { + note[k * p->source->head.channelCount + j].period = (buf[0] / 4) ? extendedRawPeriods[buf[0] / 4] : 0; + note[k * p->source->head.channelCount + j].sample = ((buf[0] << 4) + (buf[1] >> 4)) & 0x3f; + note[k * p->source->head.channelCount + j].command = buf[1] & 0xf; + note[k * p->source->head.channelCount + j].param = buf[2]; + if (note[k * p->source->head.channelCount + j].command == 0xf && note[k * p->source->head.channelCount + j].param == 0x00) + note[k * p->source->head.channelCount + j].command = 0; + } + } + } + } + } + + p->source->sampleData = (char *)malloc(totalSampleSize); + if (!p->source->sampleData) + { + for (i = 0; i < 256; ++i) + { + if (p->source->patterns[i] != NULL) + { + free(p->source->patterns[i]); + p->source->patterns[i] = NULL; + } + } + + return (false); + } + + bufseek(fmodule, commentOffset + commentLength, SEEK_SET); + + for (i = 0; i < sampleCount; ++i) + { + p->source->samples[i].offset = sampleOffset; + bufread(&p->source->sampleData[sampleOffset], 1, p->source->samples[i].length, fmodule); + + if (!(p->source->samples[i].attribute & 1)) + { + for (j = (int)sampleOffset; (unsigned int)j < sampleOffset + p->source->samples[i].length; ++j) + p->source->sampleData[(unsigned int)j] ^= 0x80; + } + + sampleOffset += p->source->samples[i].length; + } + + p->source->originalSampleData = (char *)malloc(totalSampleSize); + if (p->source->originalSampleData == NULL) + { + free(p->source->sampleData); + p->source->sampleData = NULL; + + for (i = 0; i < 256; ++i) + { + if (p->source->patterns[i] != NULL) + { + free(p->source->patterns[i]); + p->source->patterns[i] = NULL; + } + } + + return (false); + } + + memcpy(p->source->originalSampleData, p->source->sampleData, totalSampleSize); + p->source->head.totalSampleSize = totalSampleSize; + + p->useLEDFilter = false; + p->moduleLoaded = true; + + p->minPeriod = 14; + p->maxPeriod = 1712; + + p->source->head.initBPM = 125; + + return (true); +} + +static void checkModType(MODULE_HEADER *h, player *p, const char *buf) +{ + if (!strncmp(buf, "M.K.", 4)) + { + h->format = FORMAT_MK; // ProTracker v1.x + p->numChans = h->channelCount = 4; + p->minPeriod = PT_MIN_PERIOD; + p->maxPeriod = PT_MAX_PERIOD; + return; + } + else if (!strncmp(buf, "M!K!", 4)) + { + h->format = FORMAT_MK2; // ProTracker v2.x (if >64 patterns) + p->numChans = h->channelCount = 4; + p->minPeriod = PT_MIN_PERIOD; + p->maxPeriod = PT_MAX_PERIOD; + return; + } + else if (!strncmp(buf, "FLT4", 4)) + { + h->format = FORMAT_FLT4; // StarTrekker (4 channel MODs only) + p->numChans = h->channelCount = 4; + p->minPeriod = PT_MIN_PERIOD; + p->maxPeriod = PT_MAX_PERIOD; + return; + } + else if (!strncmp(buf, "FLT8", 4)) + { + h->format = FORMAT_FLT8; + p->numChans = h->channelCount = 8; + p->minPeriod = PT_MIN_PERIOD; + p->maxPeriod = PT_MAX_PERIOD; + return; + } + else if (!strncmp(buf + 1, "CHN", 3) && buf[0] >= '1' && buf[0] <= '9') + { + h->format = FORMAT_NCHN; // FastTracker II (1-9 channel MODs) + p->numChans = h->channelCount = buf[0] - '0'; + p->minPeriod = 14; + p->maxPeriod = 1712; + return; + } + else if (!strncmp(buf + 2, "CH", 2) && buf[0] >= '1' && buf[0] <= '3' && buf[1] >= '0' && buf[1] <= '9') + { + h->format = FORMAT_NNCH; // FastTracker II (10-32 channel MODs) + p->numChans = h->channelCount = (buf[0] - '0') * 10 + (buf[1] - '0'); + if (h->channelCount > 32) + { + h->format = FORMAT_UNKNOWN; + h->channelCount = 4; + } + + p->minPeriod = 14; + p->maxPeriod = 1712; + return; + } + else if (!strncmp(buf, "16CN", 4)) + { + h->format = FORMAT_16CN; + p->numChans = h->channelCount = 16; + p->minPeriod = 14; + p->maxPeriod = 1712; + return; + } + else if (!strncmp(buf, "32CN", 4)) + { + h->format = FORMAT_32CN; + p->numChans = h->channelCount = 32; + p->minPeriod = 14; + p->maxPeriod = 1712; + return; + } + else if (!strncmp(buf, "N.T.", 4)) + { + h->format = FORMAT_MK; // NoiseTracker 1.0, same as ProTracker v1.x (?) + p->numChans = h->channelCount = 4; + p->minPeriod = PT_MIN_PERIOD; + p->maxPeriod = PT_MAX_PERIOD; + return; + } + + h->format = FORMAT_UNKNOWN; // May be The Ultimate SoundTracker, 15 samples + p->numChans = h->channelCount = 4; + p->minPeriod = PT_MIN_PERIOD; + p->maxPeriod = PT_MAX_PERIOD; +} + +int playptmod_LoadMem(void *_p, const unsigned char *buf, unsigned long bufLength) +{ + player *p = (player *)_p; + unsigned char bytes[4]; + char modSig[4]; + char *smpDat8; + char tempSample[131070]; + char iffHdrFound; + int i; + int j; + int pattern; + int row; + int channel; + int sampleOffset; + int mightBeSTK; + int lateVerSTKFlag; + int numSamples; + int tmp; + unsigned int tempOffset; + modnote_t *note; + MODULE_SAMPLE *s; + BUF *fmodule; + + sampleOffset = 0; + lateVerSTKFlag = false; + mightBeSTK = false; + + p->source = (MODULE *)calloc(1, sizeof (MODULE)); + if (p->source == NULL) + return (false); + + fmodule = bufopen(buf, bufLength); + if (fmodule == NULL) + { + free(p->source); + + return (false); + } + + if (bufLength <= 1624) + { + free(p->source); + bufclose(fmodule); + + return (false); + } + + bufread(modSig, 1, 3, fmodule); + if (!strncmp(modSig, "MTM", 3)) + { + i = playptmod_LoadMTM(p, fmodule); + bufclose(fmodule); + return i; + } + + bufseek(fmodule, 0x0438, SEEK_SET); + bufread(modSig, 1, 4, fmodule); + + checkModType(&p->source->head, p, modSig); + if (p->source->head.format == FORMAT_UNKNOWN) + mightBeSTK = true; + + bufseek(fmodule, 20, SEEK_SET); + + for (i = 0; i < MOD_SAMPLES; ++i) + { + if ((mightBeSTK == true) && (i > 14)) + { + p->source->samples[i].loopLength = 2; + } + else + { + bufseek(fmodule, 22, SEEK_CUR); + + p->source->samples[i].length = bufGetWordBigEndian(fmodule) * 2; + if (p->source->samples[i].length > 9999) + lateVerSTKFlag = true; + + bufread(&p->source->samples[i].fineTune, 1, 1, fmodule); + p->source->samples[i].fineTune = p->source->samples[i].fineTune & 0x0F; + + bufread(&p->source->samples[i].volume, 1, 1, fmodule); + if (p->source->samples[i].volume > 64) + p->source->samples[i].volume = 64; + + if (mightBeSTK == true) + p->source->samples[i].loopStart = bufGetWordBigEndian(fmodule); + else + p->source->samples[i].loopStart = bufGetWordBigEndian(fmodule) * 2; + + p->source->samples[i].loopLength = bufGetWordBigEndian(fmodule) * 2; + + if (p->source->samples[i].loopLength < 2) + p->source->samples[i].loopLength = 2; + + if (mightBeSTK == true) + { + if (p->source->samples[i].loopLength > 2) + { + tmp = p->source->samples[i].loopStart; + p->source->samples[i].length -= p->source->samples[i].loopStart; + p->source->samples[i].loopStart = 0; + p->source->samples[i].tmpLoopStart = tmp; + } + + p->source->samples[i].fineTune = 0; + } + + p->source->samples[i].attribute = 0; + } + } + + bufread(&p->source->head.orderCount, 1, 1, fmodule); + if ((p->source->head.orderCount == 0) || (p->source->head.orderCount > 128)) + { + free(p->source); + bufclose(fmodule); + + return (false); + } + + bufread(&p->source->head.restartPos, 1, 1, fmodule); + if ((mightBeSTK == true) && ((p->source->head.restartPos == 0) + || (p->source->head.restartPos > 220))) + { + + free(p->source); + bufclose(fmodule); + + return (false); + } + + p->source->head.initBPM = 125; + + if (mightBeSTK == true) + { + p->source->head.format = FORMAT_STK; + + if (p->source->head.restartPos == 120) + { + p->source->head.restartPos = 125; + } + else + { + if (p->source->head.restartPos > 239) + p->source->head.restartPos = 239; + + p->source->head.initBPM = (short)(1773447 / ((240 - p->source->head.restartPos) * 122)); + } + } + + for (i = 0; i < 128; ++i) + { + bufread(&p->source->head.order[i], 1, 1, fmodule); + + if (p->source->head.order[i] > p->source->head.patternCount) + p->source->head.patternCount = p->source->head.order[i]; + } + + p->source->head.patternCount++; + + if (p->source->head.format != FORMAT_STK) + bufseek(fmodule, 4, SEEK_CUR); + + for (pattern = 0; pattern < p->source->head.patternCount; ++pattern) + { + p->source->patterns[pattern] = (modnote_t *)calloc(64 * p->source->head.channelCount, sizeof (modnote_t)); + if (p->source->patterns[pattern] == NULL) + { + for (i = 0; i < pattern; ++i) + { + if (p->source->patterns[i] != NULL) + { + free(p->source->patterns[i]); + p->source->patterns[i] = NULL; + } + } + + bufclose(fmodule); + free(p->source); + + return (false); + } + } + + for (pattern = 0; pattern < p->source->head.patternCount; ++pattern) + { + note = p->source->patterns[pattern]; + if (p->source->head.format == FORMAT_FLT8) + { + for (row = 0; row < 64; ++row) + { + for (channel = 0; channel < 8; ++channel) + { + unsigned char bytes[4]; + + if (channel == 0 && row > 0) bufseek(fmodule, -1024, SEEK_CUR); + else if (channel == 4) bufseek(fmodule, 1024 - 4 * 4, SEEK_CUR); + + bufread(bytes, 1, 4, fmodule); + + note->period = (LO_NYBBLE(bytes[0]) << 8) | bytes[1]; + if (note->period != 0) // FLT8 is 113..856 only + note->period = CLAMP(note->period, 113, 856); + + note->sample = (bytes[0] & 0xF0) | HI_NYBBLE(bytes[2]); + note->command = LO_NYBBLE(bytes[2]); + note->param = bytes[3]; + + note++; + } + } + } + else + { + for (row = 0; row < 64; ++row) + { + for (channel = 0; channel < p->source->head.channelCount; ++channel) + { + bufread(bytes, 1, 4, fmodule); + + note->period = (LO_NYBBLE(bytes[0]) << 8) | bytes[1]; + + if (note->period != 0) + { + if ((unsigned)(note->period - 113) > (856-113)) + { + p->minPeriod = 14; + p->maxPeriod = 1712; + } + note->period = CLAMP(note->period, p->minPeriod, p->maxPeriod); + } + + note->sample = (bytes[0] & 0xF0) | HI_NYBBLE(bytes[2]); + note->command = LO_NYBBLE(bytes[2]); + note->param = bytes[3]; + + if (mightBeSTK == true) + { + if (lateVerSTKFlag == false) + { + if (note->command == 0x01) + { + note->command = 0x00; + } + else if (note->command == 0x02) + { + if (note->param & 0xF0) + { + note->command = 0x02; + note->param >>= 4; + } + else if (note->param & 0x0F) + { + note->command = 0x01; + note->param &= 0x0F; + } + } + } + + if (note->command == 0x0D) + { + if (note->param == 0) + note->command = 0x0D; + else + note->command = 0x0A; + } + } + + note++; + } + } + } + } + + tempOffset = buftell(fmodule); + + sampleOffset = 0; + + numSamples = (p->source->head.format == FORMAT_STK) ? 15 : 31; + for (i = 0; i < numSamples; ++i) + { + iffHdrFound = 0; + + s = &p->source->samples[i]; + s->offset = sampleOffset; + + j = (s->length + 1) / 2 + 5 + 16; + + bufread(tempSample, 1, j, fmodule); + + smpDat8 = tempSample; + + if (s->length > 5 && memcmp(smpDat8, "ADPCM", 5) == 0) + { + s->reallength = j; + } + else + { + s->reallength = s->length; + bufread(tempSample + j, 1, s->length - j, fmodule); + } + + if (s->length > 8) + { + for (j = 0; j < (s->length - 8); ++j) + { + if (memcmp(smpDat8, "8SVXVHDR", 8) == 0) + iffHdrFound = 1; + + if (iffHdrFound) + { + if (memcmp(smpDat8, "BODY", 4) == 0) + { + s->iffSize = j + 8; + s->length -= s->iffSize; + break; + } + } + + ++smpDat8; + } + } + + sampleOffset += s->length; + p->source->head.totalSampleSize += s->length; + } + + p->source->sampleData = (char *)malloc(p->source->head.totalSampleSize); + if (p->source->sampleData == NULL) + { + bufclose(fmodule); + for (pattern = 0; pattern < 256; ++i) + { + if (p->source->patterns[pattern] != NULL) + { + free(p->source->patterns[pattern]); + p->source->patterns[pattern] = NULL; + } + } + free(p->source); + + return (false); + } + + bufseek(fmodule, tempOffset, SEEK_SET); + + numSamples = (p->source->head.format == FORMAT_STK) ? 15 : 31; + for (i = 0; i < numSamples; ++i) + { + s = &p->source->samples[i]; + + if (s->iffSize > 0) + bufseek(fmodule, s->iffSize, SEEK_CUR); + + if (s->reallength < s->length) + { + const signed char * compressionTable = tempSample + 5; + const unsigned char * adpcmData = tempSample + 5 + 16; + int delta = 0; + bufread(tempSample, 1, s->reallength, fmodule); + for ( j = 0; j < s->length; ++j ) + { + delta += compressionTable[ LO_NYBBLE( *adpcmData ) ]; + p->source->sampleData[s->offset + j] = delta; + if ( ++j >= s->length ) break; + delta += compressionTable[ HI_NYBBLE( *adpcmData ) ]; + p->source->sampleData[s->offset + j] = delta; + ++adpcmData; + } + } + else if ((mightBeSTK == true) && (p->source->samples[i].loopLength > 2)) + { + for (j = 0; j < p->source->samples[i].tmpLoopStart; ++j) + bufseek(fmodule, 1, SEEK_CUR); + + bufread(&p->source->sampleData[s->offset], 1, p->source->samples[i].length - p->source->samples[i].loopStart, fmodule); + } + else + { + bufread(&p->source->sampleData[s->offset], 1, p->source->samples[i].length, fmodule); + } + } + + p->source->originalSampleData = (char *) malloc(p->source->head.totalSampleSize); + if (p->source->originalSampleData == NULL) + { + bufclose(fmodule); + free(p->source->sampleData); + for (pattern = 0; pattern < 256; ++i) + { + if (p->source->patterns[pattern] != NULL) + { + free(p->source->patterns[pattern]); + p->source->patterns[pattern] = NULL; + } + } + free(p->source); + + return (false); + } + + memcpy(p->source->originalSampleData, p->source->sampleData, p->source->head.totalSampleSize); + + bufclose(fmodule); + + p->source->head.rowCount = MOD_ROWS; + memset(p->source->head.volume, 64, MAX_CHANNELS); + for (i = 0; i < MAX_CHANNELS; ++i) + p->source->head.pan[i] = ((i + 1) & 2) ? 160 : 96; + + p->useLEDFilter = false; + p->moduleLoaded = true; + + return (true); +} + +int playptmod_Load(void *_p, const char *filename) +{ + player *p = (player *)_p; + if (!p->moduleLoaded) + { + int i; + unsigned char *buffer; + unsigned long fileSize; + FILE *fileModule; + + fileModule = fopen(filename, "rb"); + if (fileModule == NULL) + return (false); + + fseek(fileModule, 0, SEEK_END); + fileSize = ftell(fileModule); + fseek(fileModule, 0, SEEK_SET); + + buffer = (unsigned char *)malloc(fileSize); + if (buffer == NULL) + { + fclose(fileModule); + return (false); + } + + fread(buffer, 1, fileSize, fileModule); + fclose(fileModule); + + i = playptmod_LoadMem(_p, buffer, fileSize); + + free(buffer); + + return i; + } + + return (false); +} + +static void fxArpeggio(player *p, mod_channel *ch); +static void fxPortamentoSlideUp(player *p, mod_channel *ch); +static void fxPortamentoSlideDown(player *p, mod_channel *ch); +static void fxGlissando(player *p, mod_channel *ch); +static void fxVibrato(player *p, mod_channel *ch); +static void fxGlissandoVolumeSlide(player *p, mod_channel *ch); +static void fxVibratoVolumeSlide(player *p, mod_channel *ch); +static void fxTremolo(player *p, mod_channel *ch); +static void fxNotInUse(player *p, mod_channel *ch); +static void fxSampleOffset(player *p, mod_channel *ch); +static void fxVolumeSlide(player *p, mod_channel *ch); +static void fxPositionJump(player *p, mod_channel *ch); +static void fxSetVolume(player *p, mod_channel *ch); +static void fxPatternBreak(player *p, mod_channel *ch); +static void fxExtended(player *p, mod_channel *ch); +static void fxSetTempo(player *p, mod_channel *ch); +static void efxSetLEDFilter(player *p, mod_channel *ch); +static void efxFinePortamentoSlideUp(player *p, mod_channel *ch); +static void efxFinePortamentoSlideDown(player *p, mod_channel *ch); +static void efxGlissandoControl(player *p, mod_channel *ch); +static void efxVibratoControl(player *p, mod_channel *ch); +static void efxSetFineTune(player *p, mod_channel *ch); +static void efxPatternLoop(player *p, mod_channel *ch); +static void efxTremoloControl(player *p, mod_channel *ch); +static void efxKarplusStrong(player *p, mod_channel *ch); +static void efxRetrigNote(player *p, mod_channel *ch); +static void efxFineVolumeSlideUp(player *p, mod_channel *ch); +static void efxFineVolumeSlideDown(player *p, mod_channel *ch); +static void efxNoteCut(player *p, mod_channel *ch); +static void efxNoteDelay(player *p, mod_channel *ch); +static void efxPatternDelay(player *p, mod_channel *ch); +static void efxInvertLoop(player *p, mod_channel *ch); + +static void fxExtended_FT2(player *p, mod_channel *ch); +static void fxPan(player *p, mod_channel *ch); +static void efxPan(player *p, mod_channel *ch); + +typedef void (*effect_routine)(player *p, mod_channel *); + +static effect_routine fxRoutines[16] = +{ + fxArpeggio, + fxPortamentoSlideUp, + fxPortamentoSlideDown, + fxGlissando, + fxVibrato, + fxGlissandoVolumeSlide, + fxVibratoVolumeSlide, + fxTremolo, + fxNotInUse, + fxSampleOffset, + fxVolumeSlide, + fxPositionJump, + fxSetVolume, + fxPatternBreak, + fxExtended, + fxSetTempo +}; + +static effect_routine fxRoutines_FT2[16] = +{ + fxArpeggio, + fxPortamentoSlideUp, + fxPortamentoSlideDown, + fxGlissando, + fxVibrato, + fxGlissandoVolumeSlide, + fxVibratoVolumeSlide, + fxTremolo, + fxPan, + fxSampleOffset, + fxVolumeSlide, + fxPositionJump, + fxSetVolume, + fxPatternBreak, + fxExtended_FT2, + fxSetTempo +}; + +static effect_routine efxRoutines[16] = +{ + efxSetLEDFilter, + efxFinePortamentoSlideUp, + efxFinePortamentoSlideDown, + efxGlissandoControl, + efxVibratoControl, + efxSetFineTune, + efxPatternLoop, + efxTremoloControl, + efxKarplusStrong, + efxRetrigNote, + efxFineVolumeSlideUp, + efxFineVolumeSlideDown, + efxNoteCut, + efxNoteDelay, + efxPatternDelay, + efxInvertLoop +}; + +static effect_routine efxRoutines_FT2[16] = +{ + fxNotInUse, + efxFinePortamentoSlideUp, + efxFinePortamentoSlideDown, + efxGlissandoControl, + efxVibratoControl, + efxSetFineTune, + efxPatternLoop, + efxTremoloControl, + efxPan, + efxRetrigNote, + efxFineVolumeSlideUp, + efxFineVolumeSlideDown, + efxNoteCut, + efxNoteDelay, + efxPatternDelay, + fxNotInUse +}; + +static void processInvertLoop(player *p, mod_channel *ch) +{ + char invertLoopTemp; + char *invertLoopData; + MODULE_SAMPLE *s; + + if (ch->invertLoopSpeed > 0) + { + ch->invertLoopDelay += invertLoopSpeeds[ch->invertLoopSpeed]; + if (ch->invertLoopDelay >= 128) + { + ch->invertLoopDelay = 0; + + if (ch->sample != 0) + { + s = &p->source->samples[ch->sample - 1]; + if (s->loopLength > 2) + { + ch->invertLoopOffset++; + if (ch->invertLoopOffset >= (s->loopStart + s->loopLength)) + ch->invertLoopOffset = s->loopStart; + + invertLoopData = &p->source->sampleData[s->offset + ch->invertLoopOffset]; + invertLoopTemp = -1 - *invertLoopData; + *invertLoopData = invertLoopTemp; + } + } + } + } +} + +static void efxSetLEDFilter(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + p->useLEDFilter = !(ch->param & 1); +} + +static void efxFinePortamentoSlideUp(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (p->tempPeriod > 0) + { + ch->period -= LO_NYBBLE(ch->param); + + if (p->minPeriod == PT_MIN_PERIOD) + { + if (ch->period < 113) + ch->period = 113; + } + else + { + if (ch->period < p->minPeriod) + ch->period = p->minPeriod; + } + + p->tempPeriod = ch->period; + } + } +} + +static void efxFinePortamentoSlideDown(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (p->tempPeriod > 0) + { + ch->period += LO_NYBBLE(ch->param); + + if (p->minPeriod == PT_MIN_PERIOD) + { + if (ch->period > 856) + ch->period = 856; + } + else + { + if (ch->period > p->maxPeriod) + ch->period = p->maxPeriod; + } + + p->tempPeriod = ch->period; + } + } +} + +static void efxGlissandoControl(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + ch->glissandoControl = LO_NYBBLE(ch->param); +} + +static void efxVibratoControl(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + ch->vibratoControl = LO_NYBBLE(ch->param); +} + +static void efxSetFineTune(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + ch->fineTune = LO_NYBBLE(ch->param); +} + +static void efxPatternLoop(player *p, mod_channel *ch) +{ + unsigned char tempParam; + + if (p->modTick == 0) + { + tempParam = LO_NYBBLE(ch->param); + if (tempParam == 0) + { + ch->patternLoopRow = p->modRow; + + return; + } + + if (ch->patternLoopCounter == 0) + { + ch->patternLoopCounter = tempParam; + } + else + { + ch->patternLoopCounter--; + if (ch->patternLoopCounter == 0) + return; + } + + p->PBreakPosition = ch->patternLoopRow; + p->PBreakFlag = true; + } +} + +static void efxTremoloControl(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + ch->tremoloControl = LO_NYBBLE(ch->param); +} + +static void efxKarplusStrong(player *p, mod_channel *ch) +{ + char *sampleLoopData; + unsigned int loopLength; + unsigned int loopLengthCounter; + MODULE_SAMPLE *s; + + if (ch->sample > 0) + { + s = &p->source->samples[ch->sample - 1]; + + sampleLoopData = p->source->sampleData + s->offset + s->loopStart; + + loopLength = s->loopLength - 2; + loopLengthCounter = loopLength; + + while (loopLengthCounter--) + { + *sampleLoopData = (*sampleLoopData + *(sampleLoopData + 1)) / 2; + sampleLoopData++; + } + + *sampleLoopData = (*sampleLoopData + *(sampleLoopData - loopLength)) / 2; + } +} + +static void efxRetrigNote(player *p, mod_channel *ch) +{ + unsigned char retrigTick; + + retrigTick = LO_NYBBLE(ch->param); + if (retrigTick > 0) + { + if ((p->modTick % retrigTick) == 0) + p->tempFlags |= TEMPFLAG_START; + } +} + +static void efxFineVolumeSlideUp(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + ch->volume += LO_NYBBLE(ch->param); + + if (ch->volume > 64) + ch->volume = 64; + + p->tempVolume = ch->volume; + } +} + +static void efxFineVolumeSlideDown(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + ch->volume -= LO_NYBBLE(ch->param); + + if (ch->volume < 0) + ch->volume = 0; + + p->tempVolume = ch->volume; + } +} + +static void efxNoteCut(player *p, mod_channel *ch) +{ + if (p->modTick == LO_NYBBLE(ch->param)) + { + ch->volume = 0; + p->tempVolume = 0; + } +} + +static void efxNoteDelay(player *p, mod_channel *ch) +{ + unsigned char delayTick; + + delayTick = LO_NYBBLE(ch->param); + + if (p->modTick == 0) + ch->tempFlagsBackup = p->tempFlags; + + if (p->modTick < delayTick) + p->tempFlags = TEMPFLAG_DELAY; + else if (p->modTick == delayTick) + p->tempFlags = ch->tempFlagsBackup; +} + +static void efxPatternDelay(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (p->PattDelayTime2 == 0) + { + p->pattDelayFlag = true; + p->PattDelayTime = LO_NYBBLE(ch->param) + 1; + } + } +} + +static void efxInvertLoop(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + ch->invertLoopSpeed = LO_NYBBLE(ch->param); + + if (ch->invertLoopSpeed > 0) + processInvertLoop(p, ch); + } +} + +static void handleGlissando(player *p, mod_channel *ch) +{ + char l; + char m; + char h; + + short *tablePointer; + + if (p->tempPeriod > 0) + { + if (ch->period < ch->tempPeriod) + { + ch->period += ch->glissandoSpeed; + + if (ch->period > ch->tempPeriod) + ch->period = ch->tempPeriod; + } + else + { + ch->period -= ch->glissandoSpeed; + + if (ch->period < ch->tempPeriod) + ch->period = ch->tempPeriod; + } + + if (ch->glissandoControl != 0) + { + if (p->minPeriod == PT_MIN_PERIOD) + { + l = 0; + h = 35; + + tablePointer = (short *)&rawAmigaPeriods[ch->fineTune * 37]; + while (h >= l) + { + m = (h + l) / 2; + + if (tablePointer[m] == ch->period) + { + p->tempPeriod = tablePointer[m]; + break; + } + else if (tablePointer[m] > ch->period) + { + l = m + 1; + } + else + { + h = m - 1; + } + } + } + else + { + l = 0; + h = 83; + + tablePointer = (short *)&extendedRawPeriods[ch->fineTune * 85]; + while (h >= l) + { + m = (h + l) / 2; + + if (tablePointer[m] == ch->period) + { + p->tempPeriod = tablePointer[m]; + break; + } + else if (tablePointer[m] > ch->period) + { + l = m + 1; + } + else + { + h = m - 1; + } + } + } + } + else + { + p->tempPeriod = ch->period; + } + } +} + +static void processVibrato(player *p, mod_channel *ch) +{ + unsigned char vibratoTemp; + int vibratoData; + int applyVibrato; + + applyVibrato = 1; + if ((p->minPeriod == PT_MIN_PERIOD) && (p->modTick == 0)) // PT/NT/UST/STK + applyVibrato = 0; + + if (applyVibrato) + { + if (p->tempPeriod > 0) + { + vibratoTemp = ch->vibratoPos >> 2; + vibratoTemp &= 0x1F; + + switch (ch->vibratoControl & 3) + { + case 0: + vibratoData = p->sinusTable[vibratoTemp]; + break; + + case 1: + { + if (ch->vibratoPos < 128) + vibratoData = vibratoTemp << 3; + else + vibratoData = 255 - (vibratoTemp << 3); + } + break; + + default: + vibratoData = 255; + break; + } + + vibratoData = (vibratoData * ch->vibratoDepth) >> 7; + + if (ch->vibratoPos < 128) + { + p->tempPeriod += (short)vibratoData; + if (p->tempPeriod > p->maxPeriod) + p->tempPeriod = p->maxPeriod; + } + else + { + p->tempPeriod -= (short)vibratoData; + if (p->tempPeriod < p->minPeriod) + p->tempPeriod = p->minPeriod; + } + } + } + + if (p->modTick > 0) + ch->vibratoPos += (ch->vibratoSpeed << 2); +} + +static void processTremolo(player *p, mod_channel *ch) +{ + unsigned char tremoloTemp; + int tremoloData; + int applyTremolo; + + applyTremolo = 1; + if ((p->minPeriod == PT_MIN_PERIOD) && (p->modTick == 0)) // PT/NT/UST/STK + applyTremolo = 0; + + if (applyTremolo) + { + if (p->tempVolume > 0) + { + tremoloTemp = ch->tremoloPos >> 2; + tremoloTemp &= 0x1F; + + switch (ch->tremoloControl & 3) + { + case 0: + tremoloData = p->sinusTable[tremoloTemp]; + break; + + case 1: + { + if (ch->vibratoPos < 128) + tremoloData = tremoloTemp << 3; + else + tremoloData = 255 - (tremoloTemp << 3); + } + break; + + default: + tremoloData = 255; + break; + } + + tremoloData = (tremoloData * ch->tremoloDepth) >> 6; + + if (ch->tremoloPos < 128) + { + p->tempVolume += (char)tremoloData; + if (p->tempVolume > 64) + p->tempVolume = 64; + } + else + { + p->tempVolume -= (char)tremoloData; + if (p->tempVolume < 0) + p->tempVolume = 0; + } + } + } + + if (p->modTick > 0) + ch->tremoloPos += (ch->tremoloSpeed << 2); +} + +static void fxArpeggio(player *p, mod_channel *ch) +{ + char noteToAdd; + char l; + char m; + char h; + char arpeggioTick; + short *tablePointer; + + noteToAdd = 0; + + arpeggioTick = p->modTick % 3; + if (arpeggioTick == 0) + { + p->tempPeriod = ch->period; + return; + } + else if (arpeggioTick == 1) + { + noteToAdd = HI_NYBBLE(ch->param); + } + else if (arpeggioTick == 2) + { + noteToAdd = LO_NYBBLE(ch->param); + } + + if (p->minPeriod == PT_MIN_PERIOD) // PT/NT/UST/STK + { + l = 0; + h = 35; + + tablePointer = (short *)&rawAmigaPeriods[ch->fineTune * 37]; + while (h >= l) + { + m = (h + l) / 2; + + if (tablePointer[m] == ch->period) + { + p->tempPeriod = tablePointer[m + noteToAdd]; + break; + } + else if (tablePointer[m] > ch->period) + { + l = m + 1; + } + else + { + h = m - 1; + } + } + } + else + { + l = 0; + h = 83; + + tablePointer = (short *)&extendedRawPeriods[ch->fineTune * 85]; + while (h >= l) + { + m = (h + l) / 2; + + if (tablePointer[m] == ch->period) + { + p->tempPeriod = tablePointer[m + noteToAdd]; + break; + } + else if (tablePointer[m] > ch->period) + { + l = m + 1; + } + else + { + h = m - 1; + } + } + } +} + +static void fxPortamentoSlideUp(player *p, mod_channel *ch) +{ + if ((p->modTick > 0) && (p->tempPeriod > 0)) + { + ch->period -= ch->param; + + if (p->minPeriod == PT_MIN_PERIOD) + { + if (ch->period < 113) + ch->period = 113; + } + else + { + if (ch->period < p->minPeriod) + ch->period = p->minPeriod; + } + + p->tempPeriod = ch->period; + } +} + +static void fxPortamentoSlideDown(player *p, mod_channel *ch) +{ + if ((p->modTick > 0) && (p->tempPeriod > 0)) + { + ch->period += ch->param; + + if (p->minPeriod == PT_MIN_PERIOD) + { + if (ch->period > 856) + ch->period = 856; + } + else + { + if (ch->period > p->maxPeriod) + ch->period = p->maxPeriod; + } + + p->tempPeriod = ch->period; + } +} + +static void fxGlissando(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (ch->param != 0) + ch->glissandoSpeed = ch->param; + } + else + { + handleGlissando(p, ch); + } +} + +static void fxVibrato(player *p, mod_channel *ch) +{ + unsigned char hiNybble; + unsigned char loNybble; + + if (p->modTick == 0) + { + hiNybble = HI_NYBBLE(ch->param); + loNybble = LO_NYBBLE(ch->param); + + if (hiNybble != 0) + ch->vibratoSpeed = hiNybble; + + if (loNybble != 0) + ch->vibratoDepth = loNybble; + } + + processVibrato(p, ch); +} + +static void fxGlissandoVolumeSlide(player *p, mod_channel *ch) +{ + if (p->modTick > 0) + { + handleGlissando(p, ch); + fxVolumeSlide(p, ch); + } +} + +static void fxVibratoVolumeSlide(player *p, mod_channel *ch) +{ + if (p->modTick > 0) + { + processVibrato(p, ch); + fxVolumeSlide(p, ch); + } +} + +static void fxTremolo(player *p, mod_channel *ch) +{ + unsigned char hiNybble; + unsigned char loNybble; + + if (p->modTick == 0) + { + hiNybble = HI_NYBBLE(ch->param); + loNybble = LO_NYBBLE(ch->param); + + if (hiNybble > 0) + ch->tremoloSpeed = hiNybble; + + if (loNybble > 0) + ch->tremoloDepth = loNybble; + } + + processTremolo(p, ch); +} + +static void fxNotInUse(player *p, mod_channel *ch) +{ + (void)p; + (void)ch; +} + +static void fxSampleOffset(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (ch->param > 0) + ch->offsetTemp = ch->param * 256; + + ch->offset += ch->offsetTemp; + } +} + +static void fxVolumeSlide(player *p, mod_channel *ch) +{ + unsigned char hiNybble; + unsigned char loNybble; + + if (p->modTick > 0) + { + hiNybble = HI_NYBBLE(ch->param); + loNybble = LO_NYBBLE(ch->param); + + if (hiNybble == 0) + { + ch->volume -= loNybble; + if (ch->volume < 0) + ch->volume = 0; + + p->tempVolume = ch->volume; + } + else + { + ch->volume += hiNybble; + if (ch->volume > 64) + ch->volume = 64; + + p->tempVolume = ch->volume; + } + } +} + +static void fxPositionJump(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + p->modOrder = ch->param - 1; + p->PBreakPosition = 0; + p->PosJumpAssert = true; + } +} + +static void fxSetVolume(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (ch->param > 64) + ch->param = 64; + + ch->volume = ch->param; + p->tempVolume = ch->param; + } +} + +static void fxPatternBreak(player *p, mod_channel *ch) +{ + unsigned char pos; + + if (p->modTick == 0) + { + pos = ((HI_NYBBLE(ch->param) * 10) + LO_NYBBLE(ch->param)); + + if (pos > 63) + p->PBreakPosition = 0; + else + p->PBreakPosition = pos; + + p->pattBreakBugPos = p->PBreakPosition; + p->pattBreakFlag = true; + p->PosJumpAssert = true; + } +} + +static void fxExtended(player *p, mod_channel *ch) +{ + efxRoutines[HI_NYBBLE(ch->param)](p, ch); +} + +static void fxExtended_FT2(player *p, mod_channel *ch) +{ + efxRoutines_FT2[HI_NYBBLE(ch->param)](p, ch); +} + +static void modSetSpeed(player *p, unsigned char speed) +{ + p->modSpeed = speed; +} + +static void modSetTempo(player *p, unsigned short bpm) +{ + p->modBPM = bpm; + p->samplesPerTick = p->tempoTimerVal / bpm; +} + +static void fxSetTempo(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + { + if (ch->param > 0) + { + if ((ch->param < 32) || p->vBlankTiming) + modSetSpeed(p, ch->param); + else + modSetTempo(p, ch->param); + } + else + { + /* Bit of a hack, will alert caller that song has restarted */ + p->modOrder = p->source->head.restartPos; + p->PBreakPosition = 0; + p->PosJumpAssert = true; + } + } +} + +static void processEffects(player *p, mod_channel *ch) +{ + processInvertLoop(p, ch); + + if ((!ch->command && !ch->param) == 0) + { + switch (p->source->head.format) + { + case FORMAT_NCHN: + case FORMAT_NNCH: + case FORMAT_16CN: + case FORMAT_32CN: + fxRoutines_FT2[ch->command](p, ch); + break; + + default: + fxRoutines[ch->command](p, ch); + break; + } + } +} + +static void fxPan(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + mixerSetChPan(p, ch->chanIndex, (int)((float)ch->param * (256.0f / 255.0f))); +} + +static void efxPan(player *p, mod_channel *ch) +{ + if (p->modTick == 0) + mixerSetChPan(p, ch->chanIndex, (int)((float)LO_NYBBLE(ch->param) * (256.0f / 15.0f))); +} + +void playptmod_Stop(void *_p) +{ + player *p = (player *)_p; + int i; + + mixerCutChannels(p); + + p->modulePlaying = false; + + for (i = 0; i < p->source->head.channelCount; ++i) + { + p->source->channels[i].patternLoopCounter = 0; + p->source->channels[i].glissandoControl = 0; + p->source->channels[i].vibratoControl = 0; + p->source->channels[i].tremoloControl = 0; + p->source->channels[i].fineTune = 0; + p->source->channels[i].invertLoopSpeed = 0; + p->source->channels[i].period = 0; + p->source->channels[i].tempPeriod = 0; + p->source->channels[i].offsetBugNotAdded = false; + } + + p->tempFlags = 0; + p->pattBreakBugPos = -1; + p->pattBreakFlag = false; + p->pattDelayFlag = false; + p->forceEffectsOff = false; + p->PattDelayTime = 0; + p->PattDelayTime2 = 0; + p->PBreakPosition = 0; + p->PosJumpAssert = false; +} + +static void fetchPatternData(player *p, mod_channel *ch) +{ + int tempNote; + modnote_t *note; + + note = &p->source->patterns[p->modPattern][(p->modRow * p->source->head.channelCount) + ch->chanIndex]; + if ((note->sample > 0) && (note->sample <= 32)) + { + if (ch->sample != note->sample) + ch->flags |= FLAG_NEWSAMPLE; + + ch->sample = note->sample; + ch->flags |= FLAG_SAMPLE; + ch->fineTune = p->source->samples[ch->sample - 1].fineTune; + } + + ch->command = note->command; + ch->param = note->param; + + if (note->period > 0) + { + if (ch->command == 0x0E) + { + if (HI_NYBBLE(ch->param) == 0x05) + ch->fineTune = LO_NYBBLE(ch->param); + } + + tempNote = periodToNote(p, 0, note->period); + + ch->noNote = false; + ch->tempPeriod = (p->minPeriod == PT_MIN_PERIOD) ? rawAmigaPeriods[(ch->fineTune * 37) + tempNote] : extendedRawPeriods[(ch->fineTune * 85) + tempNote]; + ch->flags |= FLAG_NOTE; + } + else + { + ch->noNote = true; + } +} + +static void processChannel(player *p, mod_channel *ch) +{ + MODULE_SAMPLE *s; + + p->tempFlags = 0; + + if (p->modTick == 0) + { + if (p->PattDelayTime2 == 0) + fetchPatternData(p, ch); + + if (ch->flags & FLAG_NOTE) + { + ch->flags &= ~FLAG_NOTE; + + if ((ch->command != 0x03) && (ch->command != 0x05)) + { + ch->period = ch->tempPeriod; + + if (ch->sample > 0) + p->tempFlags |= TEMPFLAG_START; + } + + ch->tempFlagsBackup = 0; + + if ((ch->vibratoControl & 4) == 0) + ch->vibratoPos = 0; + + if ((ch->tremoloControl & 4) == 0) + ch->tremoloPos = 0; + } + + if (ch->flags & FLAG_SAMPLE) + { + ch->flags &= ~FLAG_SAMPLE; + + if (ch->sample > 0) + { + s = &p->source->samples[ch->sample - 1]; + + ch->volume = s->volume; + ch->invertLoopOffset = s->loopStart; + + if ((ch->command != 0x03) && (ch->command != 0x05)) + { + ch->offset = 0; + ch->offsetBugNotAdded = false; + } + + if (ch->flags & FLAG_NEWSAMPLE) + { + ch->flags &= ~FLAG_NEWSAMPLE; + + if ((ch->period > 0) && ((ch->noNote == true) + || (ch->command == 0x03) + || (ch->command == 0x05))) + p->tempFlags |= TEMPFLAG_NEW_SAMPLE; + } + } + } + } + + p->tempPeriod = ch->period; + p->tempVolume = ch->volume; + + if (p->forceEffectsOff == false) + processEffects(p, ch); + + if (!(p->tempFlags & TEMPFLAG_DELAY)) + { + if (p->tempFlags & TEMPFLAG_NEW_SAMPLE) + { + if (ch->sample > 0) + { + s = &p->source->samples[ch->sample - 1]; + + if (s->length > 0) + mixerSwapChSource(p, ch->chanIndex, p->source->sampleData + s->offset, s->length, s->loopStart, s->loopLength, s->attribute & 1 ? 2 : 1); + else + mixerSetChSource(p, ch->chanIndex, NULL, 0, 0, 0, 0, 0); + } + } + else if (p->tempFlags & TEMPFLAG_START) + { + if (ch->sample > 0) + { + s = &p->source->samples[ch->sample - 1]; + + if (s->length > 0) + { + if (ch->offset > 0) + { + mixerSetChSource(p, ch->chanIndex, p->source->sampleData + s->offset, s->length, s->loopStart, s->loopLength, ch->offset, s->attribute & 1 ? 2 : 1); + + if (p->minPeriod == PT_MIN_PERIOD) // PT/NT/STK/UST bug only + { + if (ch->offsetBugNotAdded == false) + { + ch->offset += ch->offsetTemp; + ch->offsetBugNotAdded = true; + } + } + } + else + { + mixerSetChSource(p, ch->chanIndex, p->source->sampleData + s->offset, s->length, s->loopStart, s->loopLength, 0, s->attribute & 1 ? 2 : 1); + } + } + else + { + mixerSetChSource(p, ch->chanIndex, NULL, 0, 0, 0, 0, 0); + } + } + } + + mixerSetChVol(p, ch->chanIndex, p->tempVolume); + + if ((p->tempPeriod >= p->minPeriod) && (p->tempPeriod <= p->maxPeriod)) + mixerSetChRate(p, ch->chanIndex, (p->minPeriod == PT_MIN_PERIOD) ? p->frequencyTable[(int)p->tempPeriod] : p->extendedFrequencyTable[(int)p->tempPeriod]); + else + mixerSetChVol(p, ch->chanIndex, 0.0f); // arp override bugfix + } +} + +static void nextPosition(player *p) +{ + p->modRow = p->PBreakPosition; + + p->PBreakPosition = 0; + p->PosJumpAssert = false; + + p->modOrder++; + if (p->modOrder >= p->source->head.orderCount) + p->modOrder = 0; + + p->modPattern = p->source->head.order[p->modOrder]; + + if (p->modRow == 0) + p->loopCounter = p->orderPlayed[p->modOrder]++; +} + +static void processTick(player *p) +{ + int i; + + if (p->minPeriod == PT_MIN_PERIOD) // PT/NT/STK/UST bug only + { + if (p->modTick == 0) + { + if (p->forceEffectsOff == true) + { + if (p->modRow != p->pattBreakBugPos) + { + p->forceEffectsOff = false; + p->pattBreakBugPos = -1; + } + } + } + } + + for (i = 0; i < p->source->head.channelCount; ++i) + processChannel(p, p->source->channels + i); + + if (p->minPeriod == PT_MIN_PERIOD) // PT/NT/STK/UST bug only + { + if (p->modTick == 0) + { + if ((p->pattBreakFlag == true) && (p->pattDelayFlag == true)) + p->forceEffectsOff = true; + } + } + + p->modTick++; + if (p->modTick >= p->modSpeed) + { + p->modTick = 0; + + p->pattBreakFlag = false; + p->pattDelayFlag = false; + + p->modRow++; + + if (p->PattDelayTime > 0) + { + p->PattDelayTime2 = p->PattDelayTime; + p->PattDelayTime = 0; + } + + if (p->PattDelayTime2 > 0) + { + p->PattDelayTime2--; + if (p->PattDelayTime2 > 0) + p->modRow--; + } + + if (p->PBreakFlag == true) + { + p->PBreakFlag = false; + p->modRow = p->PBreakPosition; + p->PBreakPosition = 0; + } + + if ((p->modRow == p->source->head.rowCount) || (p->PosJumpAssert == true)) + nextPosition(p); + } +} + +static int pulsateSamples(player *p, int samples) +{ + if (p->sampleCounter == 0) + { + processTick(p); + p->sampleCounter += p->samplesPerTick; + } + + p->sampleCounter -= samples; + if (p->sampleCounter < 0) + { + int retSamples = samples + p->sampleCounter; + p->sampleCounter = 0; + + return retSamples; + } + + return samples; +} + +void playptmod_Render(void *_p, short *target, int length) +{ + player *p = (player *)_p; + + if (p->modulePlaying == true) + { + static const int soundBufferSamples = soundBufferSize / 4; + + while (length) + { + int tempSamples = CLAMP(length, 0, soundBufferSamples); + tempSamples = pulsateSamples(p, tempSamples); + length -= tempSamples; + + outputAudio(p, target, tempSamples); + if ( target ) target += (tempSamples * 2); + } + } +} + +void *playptmod_Create(int samplingFrequency) +{ + player *p = (player *) calloc(1, sizeof(player)); + + int i, j; + + p->tempoTimerVal = (samplingFrequency * 125) / 50; + + p->sinusTable = (unsigned char *)malloc(32); + for (i = 0; i < 32; ++i) + p->sinusTable[i] = (unsigned char)floorf(255.0f * sinf(((float)i * 3.141592f) / 32.0f)); + + p->frequencyTable = (float *)malloc(sizeof (float) * 908); + for (i = 108; i <= 907; ++i) // 0..107 will never be looked up, junk is OK + p->frequencyTable[i] = (float)samplingFrequency / (7093790.0f / (2.0f * (float)i)); + + for (j = 0; j < 16; ++j) + for (i = 0; i < 85; ++i) + extendedRawPeriods[(j * 85) + i] = i == 84 ? 0 : npertab[i] * 8363 / finetune[j]; + + for (i = 0; i < 13; ++i) + extendedRawPeriods[16 * 85 + i] = 0; + + p->soundFrequency = samplingFrequency; + + p->extendedFrequencyTable = (float *)malloc(sizeof (float) * 1713); + for (i = 14; i <= 1712; ++i) // 0..14 will never be looked up, junk is OK + p->extendedFrequencyTable[i] = (float)samplingFrequency / (7093790.0f / (2.0f * (float)i)); + + p->mixBufferL = (float *)malloc(soundBufferSize * sizeof (float)); + p->mixBufferR = (float *)malloc(soundBufferSize * sizeof (float)); + + p->filterC.LED = calcRcCoeff((float)samplingFrequency, 3090.0f); + p->filterC.LEDFb = 0.125f + 0.125f / (1.0f - p->filterC.LED); + p->filterC.high = calcRcCoeff((float)p->soundFrequency, 5.2f); + + p->useLEDFilter = false; + + mixerCutChannels(p); + + return p; +} + +void playptmod_Config(void *_p, int option, int value) +{ + player *p = (player *)_p; + switch (option) + { + case PTMOD_OPTION_VSYNC_TIMING: + p->vBlankTiming = value ? true : false; + break; + } +} + +void playptmod_Play(void *_p, unsigned int startOrder) +{ + player *p = (player *)_p; + int i; + + if (!p->modulePlaying && p->moduleLoaded) + { + mixerCutChannels(p); + + for (i = 0; i < p->source->head.channelCount; ++i) + { + p->source->channels[i].volume = 64; + p->source->channels[i].chanIndex = (char)i; + p->source->channels[i].patternLoopRow = 0; + p->source->channels[i].patternLoopCounter = 0; + p->source->channels[i].glissandoControl = 0; + p->source->channels[i].vibratoControl = 0; + p->source->channels[i].vibratoPos = 0; + p->source->channels[i].tremoloControl = 0; + p->source->channels[i].tremoloPos = 0; + p->source->channels[i].fineTune = 0; + p->source->channels[i].offsetBugNotAdded = false; + } + + p->sampleCounter = 0; + + modSetTempo(p, p->source->head.initBPM); + modSetSpeed(p, 6); + + p->modOrder = startOrder; + p->modPattern = p->source->head.order[startOrder]; + p->modRow = 0; + p->modTick = 0; + p->tempFlags = 0; + p->modTick = 0; + + p->PBreakPosition = 0; + p->PosJumpAssert = false; + p->pattBreakBugPos = -1; + p->pattBreakFlag = false; + p->pattDelayFlag = false; + p->forceEffectsOff = false; + p->PattDelayTime = 0; + p->PattDelayTime2 = 0; + p->PBreakFlag = false; + + memcpy(p->source->sampleData, p->source->originalSampleData, p->source->head.totalSampleSize); + memset(p->orderPlayed, 0, sizeof(p->orderPlayed)); + + p->orderPlayed[startOrder] = 1; + p->modulePlaying = true; + } +} + +void playptmod_Free(void *_p) +{ + player *p = (player *)_p; + int i; + + if (p->moduleLoaded == true) + { + p->modulePlaying = false; + + for (i = 0; i < 256; ++i) + { + if (p->source->patterns[i] != NULL) + free(p->source->patterns[i]); + } + + free(p->source->originalSampleData); + free(p->source->sampleData); + free(p->source); + + p->moduleLoaded = false; + } + + free(p->mixBufferL); + free(p->mixBufferR); + free(p->sinusTable); + free(p->frequencyTable); + free(p->extendedFrequencyTable); + free(p); +} + +unsigned int playptmod_LoopCounter(void *_p) +{ + player *p = (player *)_p; + return p->loopCounter; +} + +void playptmod_GetInfo(void *_p, playptmod_info *i) +{ + int n, c; + player *p = (player *)_p; + int order = p->modOrder; + int row = p->modRow; + int pattern = p->modPattern; + + if ((p->modRow >= p->source->head.rowCount) || p->PosJumpAssert) + { + order++; + if (order >= p->source->head.orderCount) + order = p->source->head.restartPos; + + row = p->PBreakPosition; + pattern = p->source->head.order[order]; + } + + i->order = order; + i->pattern = pattern; + i->row = row; + i->speed = p->modSpeed; + i->tempo = p->modBPM; + + for (c = 0, n = 0; n < p->source->head.channelCount; ++n) + { + if (p->v[n].data) c++; + } + + i->channelsPlaying = c; +} + +void playptmod_Mute(void *_p, int channel, int mute) +{ + player *p = (player *)_p; + p->v[channel].mute = mute; +} + +/* END OF FILE */ diff --git a/Frameworks/playptmod/playptmod/playptmod.h b/Frameworks/playptmod/playptmod/playptmod.h new file mode 100644 index 000000000..6cdb872b8 --- /dev/null +++ b/Frameworks/playptmod/playptmod/playptmod.h @@ -0,0 +1,50 @@ +#ifndef _PLAYPTMOD_H_ +#define _PLAYPTMOD_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void * playptmod_Create(int samplingFrequency); + +#define PTMOD_OPTION_CLAMP_PERIODS 0 +/* Set before loading module, or notes will be clamped accordingly + 1 (default) = Amiga / Protracker range + * 0 = MSDOS / MTM / extended range */ + +#define PTMOD_OPTION_VSYNC_TIMING 1 +/* 0 (default) = Speed command 20 or higher sets tempo + * 1 = Speed command always sets speed */ + +void playptmod_Config(void *p, int option, int value); + +int playptmod_LoadMem(void *p, const unsigned char *buf, unsigned long bufLength); +int playptmod_Load(void *p, const char *filename); + +void playptmod_Play(void *p, unsigned int startOrder); +void playptmod_Stop(void *p); +void playptmod_Render(void *p, signed short *target, int length); + +void playptmod_Mute(void *p, int channel, int mute); + +unsigned int playptmod_LoopCounter(void *p); + +typedef struct _ptmi +{ + unsigned char order; + unsigned char pattern; + unsigned char row; + unsigned char speed; + unsigned char tempo; + unsigned char channelsPlaying; +} playptmod_info; + +void playptmod_GetInfo(void *p, playptmod_info *i); + +void playptmod_Free(void *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Plugins/playptmod/playptmod.xcodeproj/project.pbxproj b/Plugins/playptmod/playptmod.xcodeproj/project.pbxproj new file mode 100644 index 000000000..d5d9f2a8c --- /dev/null +++ b/Plugins/playptmod/playptmod.xcodeproj/project.pbxproj @@ -0,0 +1,370 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 83A0F4731816CE5E00119DB4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83A0F4721816CE5E00119DB4 /* Cocoa.framework */; }; + 83A0F47D1816CE5E00119DB4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 83A0F47B1816CE5E00119DB4 /* InfoPlist.strings */; }; + 83A0F4D91816D03C00119DB4 /* playptmod.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83A0F4CC1816CEAE00119DB4 /* playptmod.framework */; }; + 83A0F4DB1816D04E00119DB4 /* playptmod.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83A0F4CC1816CEAE00119DB4 /* playptmod.framework */; }; + 83A0F4DE1816D09000119DB4 /* ptmodDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A0F4DD1816D09000119DB4 /* ptmodDecoder.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 83A0F4CB1816CEAE00119DB4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83A0F4C61816CEAD00119DB4 /* playptmod.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83A0F4981816CEAD00119DB4; + remoteInfo = playptmod; + }; + 83A0F4D71816D03200119DB4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83A0F4C61816CEAD00119DB4 /* playptmod.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 83A0F4971816CEAD00119DB4; + remoteInfo = playptmod; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 83A0F4DA1816D04400119DB4 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 83A0F4DB1816D04E00119DB4 /* playptmod.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 83A0F46F1816CE5E00119DB4 /* playptmod.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = playptmod.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 83A0F4721816CE5E00119DB4 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 83A0F4751816CE5E00119DB4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 83A0F4761816CE5E00119DB4 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + 83A0F4771816CE5E00119DB4 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 83A0F47A1816CE5E00119DB4 /* playptmod-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "playptmod-Info.plist"; sourceTree = ""; }; + 83A0F47C1816CE5E00119DB4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 83A0F47E1816CE5E00119DB4 /* playptmod-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "playptmod-Prefix.pch"; sourceTree = ""; }; + 83A0F4C61816CEAD00119DB4 /* playptmod.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = playptmod.xcodeproj; path = ../../Frameworks/playptmod/playptmod.xcodeproj; sourceTree = ""; }; + 83A0F4DC1816D09000119DB4 /* ptmodDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ptmodDecoder.h; sourceTree = ""; }; + 83A0F4DD1816D09000119DB4 /* ptmodDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ptmodDecoder.m; sourceTree = ""; }; + 83A0F4DF1816D0AF00119DB4 /* Plugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Plugin.h; path = ../../../Audio/Plugin.h; sourceTree = ""; }; + 83A0F4E01816D13000119DB4 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../../Utils/Logging.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 83A0F46C1816CE5E00119DB4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A0F4D91816D03C00119DB4 /* playptmod.framework in Frameworks */, + 83A0F4731816CE5E00119DB4 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 83A0F4661816CE5E00119DB4 = { + isa = PBXGroup; + children = ( + 83A0F4781816CE5E00119DB4 /* playptmod */, + 83A0F4711816CE5E00119DB4 /* Frameworks */, + 83A0F4701816CE5E00119DB4 /* Products */, + ); + sourceTree = ""; + }; + 83A0F4701816CE5E00119DB4 /* Products */ = { + isa = PBXGroup; + children = ( + 83A0F46F1816CE5E00119DB4 /* playptmod.bundle */, + ); + name = Products; + sourceTree = ""; + }; + 83A0F4711816CE5E00119DB4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 83A0F4721816CE5E00119DB4 /* Cocoa.framework */, + 83A0F4741816CE5E00119DB4 /* Other Frameworks */, + 83A0F4C61816CEAD00119DB4 /* playptmod.xcodeproj */, + ); + name = Frameworks; + sourceTree = ""; + }; + 83A0F4741816CE5E00119DB4 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 83A0F4751816CE5E00119DB4 /* Foundation.framework */, + 83A0F4761816CE5E00119DB4 /* CoreData.framework */, + 83A0F4771816CE5E00119DB4 /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 83A0F4781816CE5E00119DB4 /* playptmod */ = { + isa = PBXGroup; + children = ( + 83A0F4E01816D13000119DB4 /* Logging.h */, + 83A0F4DF1816D0AF00119DB4 /* Plugin.h */, + 83A0F4DC1816D09000119DB4 /* ptmodDecoder.h */, + 83A0F4DD1816D09000119DB4 /* ptmodDecoder.m */, + 83A0F4791816CE5E00119DB4 /* Supporting Files */, + ); + path = playptmod; + sourceTree = ""; + }; + 83A0F4791816CE5E00119DB4 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 83A0F47A1816CE5E00119DB4 /* playptmod-Info.plist */, + 83A0F47B1816CE5E00119DB4 /* InfoPlist.strings */, + 83A0F47E1816CE5E00119DB4 /* playptmod-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 83A0F4C71816CEAD00119DB4 /* Products */ = { + isa = PBXGroup; + children = ( + 83A0F4CC1816CEAE00119DB4 /* playptmod.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 83A0F46E1816CE5E00119DB4 /* playptmod */ = { + isa = PBXNativeTarget; + buildConfigurationList = 83A0F4811816CE5E00119DB4 /* Build configuration list for PBXNativeTarget "playptmod" */; + buildPhases = ( + 83A0F46B1816CE5E00119DB4 /* Sources */, + 83A0F46C1816CE5E00119DB4 /* Frameworks */, + 83A0F46D1816CE5E00119DB4 /* Resources */, + 83A0F4DA1816D04400119DB4 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + 83A0F4D81816D03200119DB4 /* PBXTargetDependency */, + ); + name = playptmod; + productName = playptmod; + productReference = 83A0F46F1816CE5E00119DB4 /* playptmod.bundle */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83A0F4671816CE5E00119DB4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = "Christopher Snowhill"; + }; + buildConfigurationList = 83A0F46A1816CE5E00119DB4 /* Build configuration list for PBXProject "playptmod" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 83A0F4661816CE5E00119DB4; + productRefGroup = 83A0F4701816CE5E00119DB4 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 83A0F4C71816CEAD00119DB4 /* Products */; + ProjectRef = 83A0F4C61816CEAD00119DB4 /* playptmod.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 83A0F46E1816CE5E00119DB4 /* playptmod */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 83A0F4CC1816CEAE00119DB4 /* playptmod.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = playptmod.framework; + remoteRef = 83A0F4CB1816CEAE00119DB4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 83A0F46D1816CE5E00119DB4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A0F47D1816CE5E00119DB4 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 83A0F46B1816CE5E00119DB4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 83A0F4DE1816D09000119DB4 /* ptmodDecoder.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 83A0F4D81816D03200119DB4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = playptmod; + targetProxy = 83A0F4D71816D03200119DB4 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 83A0F47B1816CE5E00119DB4 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 83A0F47C1816CE5E00119DB4 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 83A0F47F1816CE5E00119DB4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Christopher Snowhill"; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 83A0F4801816CE5E00119DB4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Christopher Snowhill"; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.7; + SDKROOT = macosx; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 83A0F4821816CE5E00119DB4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "playptmod/playptmod-Prefix.pch"; + INFOPLIST_FILE = "playptmod/playptmod-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 83A0F4831816CE5E00119DB4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "playptmod/playptmod-Prefix.pch"; + INFOPLIST_FILE = "playptmod/playptmod-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 83A0F46A1816CE5E00119DB4 /* Build configuration list for PBXProject "playptmod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83A0F47F1816CE5E00119DB4 /* Debug */, + 83A0F4801816CE5E00119DB4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83A0F4811816CE5E00119DB4 /* Build configuration list for PBXNativeTarget "playptmod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83A0F4821816CE5E00119DB4 /* Debug */, + 83A0F4831816CE5E00119DB4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83A0F4671816CE5E00119DB4 /* Project object */; +} diff --git a/Plugins/playptmod/playptmod/en.lproj/InfoPlist.strings b/Plugins/playptmod/playptmod/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..477b28ff8 --- /dev/null +++ b/Plugins/playptmod/playptmod/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/Plugins/playptmod/playptmod/playptmod-Info.plist b/Plugins/playptmod/playptmod/playptmod-Info.plist new file mode 100644 index 000000000..2a214d551 --- /dev/null +++ b/Plugins/playptmod/playptmod/playptmod-Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + net.kode54.playptmod + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2013 Christopher Snowhill. All rights reserved. + NSPrincipalClass + + + diff --git a/Plugins/playptmod/playptmod/playptmod-Prefix.pch b/Plugins/playptmod/playptmod/playptmod-Prefix.pch new file mode 100644 index 000000000..35d76409f --- /dev/null +++ b/Plugins/playptmod/playptmod/playptmod-Prefix.pch @@ -0,0 +1,9 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/Plugins/playptmod/playptmod/ptmodDecoder.h b/Plugins/playptmod/playptmod/ptmodDecoder.h new file mode 100755 index 000000000..d0fbfd8d2 --- /dev/null +++ b/Plugins/playptmod/playptmod/ptmodDecoder.h @@ -0,0 +1,27 @@ +// +// ptmodDecoder.h +// playptmod +// +// Created by Christopher Snowhill on 10/22/13. +// Copyright 2013 __NoWork, Inc__. All rights reserved. +// + +#import + +#import + +#import "Plugin.h" + +@interface ptmodDecoder : NSObject { + void *ptmod; + void *data; + long size; + int track_num; + + int isVblank; + + long framesLength; + long totalFrames; + long framesRead; +} +@end diff --git a/Plugins/playptmod/playptmod/ptmodDecoder.m b/Plugins/playptmod/playptmod/ptmodDecoder.m new file mode 100755 index 000000000..dfe324152 --- /dev/null +++ b/Plugins/playptmod/playptmod/ptmodDecoder.m @@ -0,0 +1,224 @@ +// +// ptmodDecoder.m +// playptmod +// +// Created by Christopher Snowhill on 10/22/13. +// Copyright 2013 __NoWork, Inc__. All rights reserved. +// + +#import "ptmodDecoder.h" + +#import "Logging.h" + +@implementation ptmodDecoder + +BOOL probe_length( unsigned long * intro_length, unsigned long * loop_length, int test_vblank, const void * src, unsigned long size, unsigned int subsong ) +{ + void * ptmod = playptmod_Create( 44100 ); + if ( !ptmod ) return NO; + + playptmod_Config( ptmod, PTMOD_OPTION_CLAMP_PERIODS, 0 ); + playptmod_Config( ptmod, PTMOD_OPTION_VSYNC_TIMING, test_vblank ); + + if ( !playptmod_LoadMem( ptmod, src, size ) ) + { + playptmod_Free( ptmod ); + return NO; + } + + playptmod_Play( ptmod, subsong ); + + unsigned long length_total = 0; + unsigned long length_saved; + + while ( playptmod_LoopCounter( ptmod ) < 1 ) + { + playptmod_Render( ptmod, NULL, 512 ); + length_total += 512; + } + + length_saved = length_total; + + while ( playptmod_LoopCounter( ptmod ) < 2 ) + { + playptmod_Render( ptmod, NULL, 512 ); + length_total += 512; + } + + playptmod_Stop( ptmod ); + playptmod_Free( ptmod ); + + *loop_length = length_total - length_saved; + *intro_length = length_saved - *loop_length; + + return YES; +} + +- (BOOL)open:(id)s +{ + [s seek:0 whence:SEEK_END]; + size = [s tell]; + [s seek:0 whence:SEEK_SET]; + + data = malloc(size); + [s read:data amount:size]; + + if ([[[s url] fragment] length] == 0) + track_num = 0; + else + track_num = [[[s url] fragment] intValue]; + + unsigned long normal_intro_length, normal_loop_length, vblank_intro_length, vblank_loop_length; + + if ( !probe_length(&normal_intro_length, &normal_loop_length, 0, data, size, track_num) ) + return NO; + if ( !probe_length(&vblank_intro_length, &vblank_loop_length, 1, data, size, track_num) ) + return NO; + + isVblank = ( vblank_intro_length + vblank_loop_length ) < ( normal_intro_length + normal_loop_length ); + + unsigned long intro_length = isVblank ? vblank_intro_length : normal_intro_length; + unsigned long loop_length = isVblank ? vblank_loop_length : normal_loop_length; + + framesLength = intro_length + loop_length * 2; + totalFrames = framesLength + 44100 * 8; + + [self willChangeValueForKey:@"properties"]; + [self didChangeValueForKey:@"properties"]; + + return YES; +} + +- (BOOL)decoderInitialize +{ + ptmod = playptmod_Create( 44100 ); + if ( !ptmod ) + return NO; + + playptmod_Config( ptmod, PTMOD_OPTION_CLAMP_PERIODS, 0 ); + playptmod_Config( ptmod, PTMOD_OPTION_VSYNC_TIMING, isVblank ); + + if ( !playptmod_LoadMem( ptmod, data, size ) ) + return NO; + + playptmod_Play( ptmod, track_num ); + + framesRead = 0; + + return YES; +} + +- (void)decoderShutdown +{ + if ( ptmod ) + { + playptmod_Stop( ptmod ); + playptmod_Free( ptmod ); + ptmod = NULL; + } +} + +- (NSDictionary *)properties +{ + return [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt:0], @"bitrate", + [NSNumber numberWithFloat:44100], @"sampleRate", + [NSNumber numberWithDouble:totalFrames], @"totalFrames", + [NSNumber numberWithInt:16], @"bitsPerSample", //Samples are short + [NSNumber numberWithBool:NO], @"floatingPoint", + [NSNumber numberWithInt:2], @"channels", //output from gme_play is in stereo + [NSNumber numberWithBool:YES], @"seekable", + @"host", @"endian", + nil]; +} + +- (int)readAudio:(void *)buf frames:(UInt32)frames +{ + if ( framesRead >= totalFrames ) + return 0; + + if ( !ptmod ) + { + if ( ![self decoderInitialize] ) + return 0; + } + + int total = 0; + while ( total < frames ) { + int framesToRender = 512; + if ( framesToRender > totalFrames - framesRead ) + framesToRender = totalFrames - framesRead; + if ( framesToRender > frames - total ) + framesToRender = frames - total; + + int16_t * sampleBuf = ( int16_t * ) buf + total * 2; + + playptmod_Render( ptmod, sampleBuf, framesToRender ); + + if ( framesRead + framesToRender > framesLength ) { + long fadeStart = ( framesLength > framesRead ) ? framesLength : framesRead; + long fadeEnd = ( framesRead + framesToRender < totalFrames ) ? framesRead + framesToRender : totalFrames; + const long fadeTotal = totalFrames - framesLength; + for ( long fadePos = fadeStart; fadePos < fadeEnd; ++fadePos ) { + const long scale = ( fadeTotal - ( fadePos - framesLength ) ); + const long offset = fadePos - framesRead; + int16_t * samples = sampleBuf + offset * 2; + samples[ 0 ] = samples[ 0 ] * scale / fadeTotal; + samples[ 1 ] = samples[ 1 ] * scale / fadeTotal; + } + + framesToRender = fadeEnd - framesRead; + } + + if ( !framesToRender ) + break; + + total += framesToRender; + framesRead += framesToRender; + } + + return total; +} + +- (long)seek:(long)frame +{ + if ( frame < framesRead || !ptmod ) + { + [self decoderShutdown]; + if ( ![self decoderInitialize] ) + return 0; + } + + playptmod_Render( ptmod, NULL, frame - framesRead ); + + framesRead = frame; + + return frame; +} + +- (void)close +{ + [self decoderShutdown]; + + if (data) { + free( data ); + data = NULL; + } +} + ++ (NSArray *)fileTypes +{ + return [NSArray arrayWithObjects:@"mod", @"mdz", @"stk", @"m15", @"fst", nil]; +} + ++ (NSArray *)mimeTypes +{ + return [NSArray arrayWithObjects:@"audio/x-mod", nil]; +} + ++ (float)priority +{ + return 1.5; +} + +@end