├── - Test ├── FFT_Tester │ ├── FFT_Tester.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── FFT_Tester.xcscheme │ └── FFT_Tester │ │ └── main.cpp ├── IR_Manipulation_Tester │ ├── IR_Manipulation_Tester.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── IR_Manipulation_Tester.xcscheme │ └── IR_Manipulation_Tester │ │ └── main.cpp ├── Peak_Tester │ ├── Peak_Tester.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Peak_Tester │ │ └── main.cpp └── Window_Tester │ ├── Window_Tester.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── Window_Tester │ └── main.cpp ├── .gitignore ├── Allocator.hpp ├── AudioFile ├── BaseAudioFile.cpp ├── BaseAudioFile.h ├── IAudioFile.cpp ├── IAudioFile.h ├── OAudioFile.cpp └── OAudioFile.h ├── HIRT_Multichannel_Convolution ├── ConvolveErrors.h ├── ConvolveSIMD.h ├── Convolver.cpp ├── Convolver.h ├── MemorySwap.h ├── MonoConvolve.cpp ├── MonoConvolve.h ├── NToMonoConvolve.cpp ├── NToMonoConvolve.h ├── PartitionedConvolve.cpp ├── PartitionedConvolve.h ├── TimeDomainConvolve.cpp └── TimeDomainConvolve.h ├── HISSTools_FFT ├── Documentation │ └── Doxyfile ├── HISSTools_FFT.cpp ├── HISSTools_FFT.h └── HISSTools_FFT_Core.h ├── Interpolation.hpp ├── KernelSmoother.hpp ├── LICENSE ├── PartialTracker.hpp ├── README.md ├── RandomGenerator.hpp ├── SIMDSupport.hpp ├── SpectralFunctions.hpp ├── SpectralProcessor.hpp ├── Statistics.hpp ├── TableReader.hpp ├── ThreadLocks.hpp └── WindowFunctions.hpp /- Test/FFT_Tester/FFT_Tester.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B82AAC2C1F61FC0C0015CDCF /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B82AAC2B1F61FC0C0015CDCF /* main.cpp */; }; 11 | B82AAC341F61FC1C0015CDCF /* HISSTools_FFT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B82AAC321F61FC1C0015CDCF /* HISSTools_FFT.cpp */; }; 12 | B8378E1022BCC9FF004F8598 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8378E0F22BCC9FF004F8598 /* Accelerate.framework */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | B82AAC261F61FC0C0015CDCF /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | B82AAC281F61FC0C0015CDCF /* FFT Tester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "FFT Tester"; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | B82AAC2B1F61FC0C0015CDCF /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 30 | B82AAC321F61FC1C0015CDCF /* HISSTools_FFT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HISSTools_FFT.cpp; path = ../../../HISSTools_FFT/HISSTools_FFT.cpp; sourceTree = ""; }; 31 | B82AAC331F61FC1C0015CDCF /* HISSTools_FFT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HISSTools_FFT.h; path = ../../../HISSTools_FFT/HISSTools_FFT.h; sourceTree = ""; }; 32 | B82AAC371F6217970015CDCF /* HISSTools_FFT_Core.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = HISSTools_FFT_Core.h; path = ../../../HISSTools_FFT/HISSTools_FFT_Core.h; sourceTree = ""; }; 33 | B8378E0F22BCC9FF004F8598 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | B82AAC251F61FC0C0015CDCF /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | B8378E1022BCC9FF004F8598 /* Accelerate.framework in Frameworks */, 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | B82AAC1F1F61FC0C0015CDCF = { 49 | isa = PBXGroup; 50 | children = ( 51 | B82AAC2A1F61FC0C0015CDCF /* FFT_Tester */, 52 | B82AAC291F61FC0C0015CDCF /* Products */, 53 | B8378E0E22BCC9FE004F8598 /* Frameworks */, 54 | ); 55 | sourceTree = ""; 56 | }; 57 | B82AAC291F61FC0C0015CDCF /* Products */ = { 58 | isa = PBXGroup; 59 | children = ( 60 | B82AAC281F61FC0C0015CDCF /* FFT Tester */, 61 | ); 62 | name = Products; 63 | sourceTree = ""; 64 | }; 65 | B82AAC2A1F61FC0C0015CDCF /* FFT_Tester */ = { 66 | isa = PBXGroup; 67 | children = ( 68 | B82AAC331F61FC1C0015CDCF /* HISSTools_FFT.h */, 69 | B82AAC321F61FC1C0015CDCF /* HISSTools_FFT.cpp */, 70 | B82AAC371F6217970015CDCF /* HISSTools_FFT_Core.h */, 71 | B82AAC2B1F61FC0C0015CDCF /* main.cpp */, 72 | ); 73 | path = FFT_Tester; 74 | sourceTree = ""; 75 | }; 76 | B8378E0E22BCC9FE004F8598 /* Frameworks */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | B8378E0F22BCC9FF004F8598 /* Accelerate.framework */, 80 | ); 81 | name = Frameworks; 82 | sourceTree = ""; 83 | }; 84 | /* End PBXGroup section */ 85 | 86 | /* Begin PBXNativeTarget section */ 87 | B82AAC271F61FC0C0015CDCF /* FFT_Tester */ = { 88 | isa = PBXNativeTarget; 89 | buildConfigurationList = B82AAC2F1F61FC0C0015CDCF /* Build configuration list for PBXNativeTarget "FFT_Tester" */; 90 | buildPhases = ( 91 | B82AAC241F61FC0C0015CDCF /* Sources */, 92 | B82AAC251F61FC0C0015CDCF /* Frameworks */, 93 | B82AAC261F61FC0C0015CDCF /* CopyFiles */, 94 | ); 95 | buildRules = ( 96 | ); 97 | dependencies = ( 98 | ); 99 | name = FFT_Tester; 100 | productName = "FFT Tester"; 101 | productReference = B82AAC281F61FC0C0015CDCF /* FFT Tester */; 102 | productType = "com.apple.product-type.tool"; 103 | }; 104 | /* End PBXNativeTarget section */ 105 | 106 | /* Begin PBXProject section */ 107 | B82AAC201F61FC0C0015CDCF /* Project object */ = { 108 | isa = PBXProject; 109 | attributes = { 110 | LastUpgradeCheck = 0830; 111 | ORGANIZATIONNAME = "Alex Harker"; 112 | TargetAttributes = { 113 | B82AAC271F61FC0C0015CDCF = { 114 | CreatedOnToolsVersion = 8.3.3; 115 | ProvisioningStyle = Automatic; 116 | }; 117 | }; 118 | }; 119 | buildConfigurationList = B82AAC231F61FC0C0015CDCF /* Build configuration list for PBXProject "FFT_Tester" */; 120 | compatibilityVersion = "Xcode 3.2"; 121 | developmentRegion = English; 122 | hasScannedForEncodings = 0; 123 | knownRegions = ( 124 | en, 125 | ); 126 | mainGroup = B82AAC1F1F61FC0C0015CDCF; 127 | productRefGroup = B82AAC291F61FC0C0015CDCF /* Products */; 128 | projectDirPath = ""; 129 | projectRoot = ""; 130 | targets = ( 131 | B82AAC271F61FC0C0015CDCF /* FFT_Tester */, 132 | ); 133 | }; 134 | /* End PBXProject section */ 135 | 136 | /* Begin PBXSourcesBuildPhase section */ 137 | B82AAC241F61FC0C0015CDCF /* Sources */ = { 138 | isa = PBXSourcesBuildPhase; 139 | buildActionMask = 2147483647; 140 | files = ( 141 | B82AAC2C1F61FC0C0015CDCF /* main.cpp in Sources */, 142 | B82AAC341F61FC1C0015CDCF /* HISSTools_FFT.cpp in Sources */, 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXSourcesBuildPhase section */ 147 | 148 | /* Begin XCBuildConfiguration section */ 149 | B82AAC2D1F61FC0C0015CDCF /* Debug */ = { 150 | isa = XCBuildConfiguration; 151 | buildSettings = { 152 | ALWAYS_SEARCH_USER_PATHS = NO; 153 | CLANG_ANALYZER_NONNULL = YES; 154 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 156 | CLANG_CXX_LIBRARY = "libc++"; 157 | CLANG_ENABLE_MODULES = YES; 158 | CLANG_ENABLE_OBJC_ARC = YES; 159 | CLANG_WARN_BOOL_CONVERSION = YES; 160 | CLANG_WARN_CONSTANT_CONVERSION = YES; 161 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 162 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 163 | CLANG_WARN_EMPTY_BODY = YES; 164 | CLANG_WARN_ENUM_CONVERSION = YES; 165 | CLANG_WARN_INFINITE_RECURSION = YES; 166 | CLANG_WARN_INT_CONVERSION = YES; 167 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 168 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 169 | CLANG_WARN_UNREACHABLE_CODE = YES; 170 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 171 | CODE_SIGN_IDENTITY = "-"; 172 | COPY_PHASE_STRIP = NO; 173 | DEBUG_INFORMATION_FORMAT = dwarf; 174 | ENABLE_STRICT_OBJC_MSGSEND = YES; 175 | ENABLE_TESTABILITY = YES; 176 | GCC_C_LANGUAGE_STANDARD = gnu99; 177 | GCC_DYNAMIC_NO_PIC = NO; 178 | GCC_NO_COMMON_BLOCKS = YES; 179 | GCC_OPTIMIZATION_LEVEL = 0; 180 | GCC_PREPROCESSOR_DEFINITIONS = ( 181 | "DEBUG=1", 182 | "$(inherited)", 183 | ); 184 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 185 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 186 | GCC_WARN_UNDECLARED_SELECTOR = YES; 187 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 188 | GCC_WARN_UNUSED_FUNCTION = YES; 189 | GCC_WARN_UNUSED_VARIABLE = YES; 190 | MACOSX_DEPLOYMENT_TARGET = 10.12; 191 | MTL_ENABLE_DEBUG_INFO = YES; 192 | ONLY_ACTIVE_ARCH = YES; 193 | SDKROOT = macosx; 194 | }; 195 | name = Debug; 196 | }; 197 | B82AAC2E1F61FC0C0015CDCF /* Release */ = { 198 | isa = XCBuildConfiguration; 199 | buildSettings = { 200 | ALWAYS_SEARCH_USER_PATHS = NO; 201 | CLANG_ANALYZER_NONNULL = YES; 202 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 203 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 204 | CLANG_CXX_LIBRARY = "libc++"; 205 | CLANG_ENABLE_MODULES = YES; 206 | CLANG_ENABLE_OBJC_ARC = YES; 207 | CLANG_WARN_BOOL_CONVERSION = YES; 208 | CLANG_WARN_CONSTANT_CONVERSION = YES; 209 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 210 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 211 | CLANG_WARN_EMPTY_BODY = YES; 212 | CLANG_WARN_ENUM_CONVERSION = YES; 213 | CLANG_WARN_INFINITE_RECURSION = YES; 214 | CLANG_WARN_INT_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 217 | CLANG_WARN_UNREACHABLE_CODE = YES; 218 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 219 | CODE_SIGN_IDENTITY = "-"; 220 | COPY_PHASE_STRIP = NO; 221 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 222 | ENABLE_NS_ASSERTIONS = NO; 223 | ENABLE_STRICT_OBJC_MSGSEND = YES; 224 | GCC_C_LANGUAGE_STANDARD = gnu99; 225 | GCC_NO_COMMON_BLOCKS = YES; 226 | GCC_PREPROCESSOR_DEFINITIONS = ""; 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | MACOSX_DEPLOYMENT_TARGET = 10.12; 234 | MTL_ENABLE_DEBUG_INFO = NO; 235 | SDKROOT = macosx; 236 | }; 237 | name = Release; 238 | }; 239 | B82AAC301F61FC0C0015CDCF /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; 243 | CLANG_X86_VECTOR_INSTRUCTIONS = avx; 244 | GCC_PREPROCESSOR_DEFINITIONS = ( 245 | "NO_NATIVE_FFT=1", 246 | "$(inherited)", 247 | ); 248 | PRODUCT_NAME = "$(TARGET_NAME)"; 249 | }; 250 | name = Debug; 251 | }; 252 | B82AAC311F61FC0C0015CDCF /* Release */ = { 253 | isa = XCBuildConfiguration; 254 | buildSettings = { 255 | CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; 256 | CLANG_X86_VECTOR_INSTRUCTIONS = avx; 257 | GCC_PREPROCESSOR_DEFINITIONS = ( 258 | "$(inherited)", 259 | "NO_NATIVE_FFT=1", 260 | ); 261 | PRODUCT_NAME = "$(TARGET_NAME)"; 262 | }; 263 | name = Release; 264 | }; 265 | /* End XCBuildConfiguration section */ 266 | 267 | /* Begin XCConfigurationList section */ 268 | B82AAC231F61FC0C0015CDCF /* Build configuration list for PBXProject "FFT_Tester" */ = { 269 | isa = XCConfigurationList; 270 | buildConfigurations = ( 271 | B82AAC2D1F61FC0C0015CDCF /* Debug */, 272 | B82AAC2E1F61FC0C0015CDCF /* Release */, 273 | ); 274 | defaultConfigurationIsVisible = 0; 275 | defaultConfigurationName = Release; 276 | }; 277 | B82AAC2F1F61FC0C0015CDCF /* Build configuration list for PBXNativeTarget "FFT_Tester" */ = { 278 | isa = XCConfigurationList; 279 | buildConfigurations = ( 280 | B82AAC301F61FC0C0015CDCF /* Debug */, 281 | B82AAC311F61FC0C0015CDCF /* Release */, 282 | ); 283 | defaultConfigurationIsVisible = 0; 284 | defaultConfigurationName = Release; 285 | }; 286 | /* End XCConfigurationList section */ 287 | }; 288 | rootObject = B82AAC201F61FC0C0015CDCF /* Project object */; 289 | } 290 | -------------------------------------------------------------------------------- /- Test/FFT_Tester/FFT_Tester.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /- Test/FFT_Tester/FFT_Tester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /- Test/FFT_Tester/FFT_Tester.xcodeproj/xcshareddata/xcschemes/FFT_Tester.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /- Test/FFT_Tester/FFT_Tester/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "HISSTools_FFT.h" 9 | 10 | // Output 11 | 12 | void tabbedOut(const std::string& name, const std::string& text, int tab = 25) 13 | { 14 | std::cout << std::setw(tab) << std::setfill(' '); 15 | std::cout.setf(std::ios::left); 16 | std::cout.unsetf(std::ios::right); 17 | std::cout << name; 18 | std::cout.unsetf(std::ios::left); 19 | std::cout << text << "\n"; 20 | } 21 | 22 | template 23 | std::string to_string_with_precision(const T a_value, const int n = 4, bool fixed = true) 24 | { 25 | std::ostringstream out; 26 | if (fixed) 27 | out << std::setprecision(n) << std::fixed << a_value; 28 | else 29 | out << std::setprecision(n) << a_value; 30 | 31 | return out.str(); 32 | } 33 | 34 | // Timing 35 | 36 | class Timer 37 | { 38 | 39 | public: 40 | 41 | Timer() : mStart(0), mStore(0) {} 42 | 43 | void start() 44 | { 45 | mStart = mach_absolute_time(); 46 | }; 47 | 48 | void stop() 49 | { 50 | uint64_t end = mach_absolute_time(); 51 | 52 | mach_timebase_info_data_t info; 53 | mach_timebase_info(&info); 54 | 55 | uint64_t elapsed = ((end - mStart) * info.numer) / info.denom; 56 | 57 | mStore += elapsed; 58 | } 59 | 60 | uint64_t finish(const std::string& msg) 61 | { 62 | tabbedOut(msg + " Elapsed ", to_string_with_precision(mStore / 1000000.0, 2), 35); 63 | 64 | uint64_t elapsed = mStore1; 65 | 66 | mStore = 0; 67 | 68 | return elapsed; 69 | }; 70 | 71 | private: 72 | 73 | uint64_t mStart; 74 | uint64_t mStore; 75 | }; 76 | 77 | template 78 | void fillSplit(SPLIT split, int max_log2) 79 | { 80 | for (long i =0; i < (1 << max_log2); i++) 81 | { 82 | split.realp[i] = 1.0 - 2.0 * std::rand() / RAND_MAX; 83 | split.imagp[i] = 1.0 - 2.0 * std::rand() / RAND_MAX; 84 | } 85 | } 86 | 87 | template 88 | uint64_t crash_test(int min_log2, int max_log2) 89 | { 90 | SETUP setup; 91 | SPLIT split; 92 | 93 | split.realp = (T *) malloc(sizeof(T) * 1 << max_log2); 94 | split.imagp = (T *) malloc(sizeof(T) * 1 << max_log2); 95 | 96 | hisstools_create_setup(&setup, max_log2); 97 | 98 | Timer timer; 99 | 100 | for (int i = min_log2; i < max_log2; i++) 101 | { 102 | fillSplit(split, i); 103 | timer.start(); 104 | hisstools_fft(setup, &split, i); 105 | timer.stop(); 106 | } 107 | 108 | 109 | for (int i = min_log2; i < max_log2; i++) 110 | { 111 | fillSplit(split, i); 112 | timer.start(); 113 | hisstools_ifft(setup, &split, i); 114 | timer.stop(); 115 | } 116 | 117 | for (int i = min_log2; i < max_log2; i++) 118 | { 119 | fillSplit(split, i); 120 | timer.start(); 121 | hisstools_rfft(setup, &split, i); 122 | timer.stop(); 123 | } 124 | 125 | for (int i = min_log2; i < max_log2; i++) 126 | { 127 | fillSplit(split, i); 128 | timer.start(); 129 | hisstools_rifft(setup, &split, i); 130 | timer.stop(); 131 | } 132 | 133 | uint64_t time = timer.finish("FFT Multiple Tests"); 134 | 135 | free(split.realp); 136 | free(split.imagp); 137 | hisstools_destroy_setup(setup); 138 | 139 | return time; 140 | } 141 | 142 | template 143 | uint64_t single_test(int size, void (*Fn)(SETUP, SPLIT *, uintptr_t)) 144 | { 145 | SETUP setup; 146 | SPLIT split; 147 | 148 | split.realp = (T *) malloc(sizeof(T) * 1 << size); 149 | split.imagp = (T *) malloc(sizeof(T) * 1 << size); 150 | 151 | hisstools_create_setup(&setup, size); 152 | 153 | Timer timer; 154 | 155 | for (int i = 0; i < 10000; i++) 156 | { 157 | fillSplit(split, size); 158 | 159 | timer.start(); 160 | Fn(setup, &split, size); 161 | timer.stop(); 162 | } 163 | 164 | uint64_t time = timer.finish(std::string("FFT Single Tests ").append(std::to_string (1 << size))); 165 | 166 | free(split.realp); 167 | free(split.imagp); 168 | hisstools_destroy_setup(setup); 169 | 170 | return time; 171 | } 172 | 173 | template 174 | uint64_t matched_size_test(int min_log2, int max_log2) 175 | { 176 | uint64_t time = 0; 177 | 178 | std::cout << "---FFT---\n"; 179 | 180 | for (int i = min_log2; i < max_log2; i++) 181 | time += single_test(i, &hisstools_fft); 182 | 183 | std::cout << "---iFFT---\n"; 184 | 185 | for (int i = min_log2; i < max_log2; i++) 186 | time += single_test(i, &hisstools_ifft); 187 | 188 | std::cout << "---Real FFT---\n"; 189 | 190 | for (int i = min_log2; i < max_log2; i++) 191 | time += single_test(i, &hisstools_rfft); 192 | 193 | std::cout << "---Real iFFT---\n"; 194 | 195 | for (int i = min_log2; i < max_log2; i++) 196 | time += single_test(i, &hisstools_rifft); 197 | 198 | return time; 199 | } 200 | 201 | template 202 | bool zip_correctness_test(int min_log2, int max_log2) 203 | { 204 | SPLIT split; 205 | 206 | U *ptr = (U *) malloc(sizeof(U) * 1 << max_log2); 207 | split.realp = (T *) malloc(sizeof(T) * 1 << (max_log2 - 1)); 208 | split.imagp = (T *) malloc(sizeof(T) * 1 << (max_log2 - 1)); 209 | 210 | for (int i = min_log2; i < max_log2; i++) 211 | { 212 | for (int j = 0; j < (1 << i); j++) 213 | ptr[j] = j; 214 | 215 | hisstools_unzip(ptr, &split, i); 216 | 217 | for (int j = 0 ; j < (1 << (i - 1)); j++) 218 | { 219 | if (split.realp[j] != (j << 1)) 220 | { 221 | std::cout << "zip error\n"; 222 | return true; 223 | } 224 | if (i > 1 && split.imagp[j] != (j << 1) + 1) 225 | { 226 | std::cout << "zip error\n"; 227 | return true; 228 | } 229 | } 230 | 231 | hisstools_zip(&split, ptr, i); 232 | 233 | for (int j = 0 ; j < (1 << (i - 1)); j++) 234 | { 235 | if (ptr[j] != j) 236 | { 237 | std::cout << "unzip error\n"; 238 | return true; 239 | } 240 | } 241 | } 242 | 243 | free(ptr); 244 | free(split.realp); 245 | free(split.imagp); 246 | 247 | std::cout << "FFT Zip Tests Successful\n"; 248 | 249 | return false; 250 | } 251 | 252 | template 253 | uint64_t zip_test(int min_log2, int max_log2) 254 | { 255 | SPLIT split; 256 | 257 | U *ptr = (U *) malloc(sizeof(U) * 1 << max_log2); 258 | split.realp = (T *) malloc(sizeof(T) * 1 << (max_log2 - 1)); 259 | split.imagp = (T *) malloc(sizeof(T) * 1 << (max_log2 - 1)); 260 | 261 | Timer timer; 262 | timer.start(); 263 | 264 | for (int i = min_log2; i < max_log2; i++) 265 | hisstools_unzip(ptr, &split, i); 266 | 267 | for (int i = min_log2; i < max_log2; i++) 268 | hisstools_unzip_zero(ptr, &split, 1 << i, i); 269 | 270 | for (int i = min_log2; i < max_log2; i++) 271 | hisstools_zip(&split, ptr, i); 272 | 273 | timer.stop(); 274 | uint64_t time = timer.finish("Zip Tests"); 275 | 276 | free(ptr); 277 | free(split.realp); 278 | free(split.imagp); 279 | 280 | return time; 281 | } 282 | 283 | 284 | int main(int argc, const char * argv[]) 285 | { 286 | uint64_t time = 0; 287 | 288 | std::cout << "****** DOUBLE ******\n"; 289 | 290 | if (zip_correctness_test(1, 24)) 291 | { 292 | std::cout << "Errors - did not complete tests\n"; 293 | return -1; 294 | } 295 | 296 | std::cout << "****** FLOAT ******\n"; 297 | 298 | if (zip_correctness_test(1, 24)) 299 | { 300 | std::cout << "Errors - did not complete tests\n"; 301 | return -1; 302 | } 303 | 304 | std::cout << "****** DOUBLE ******\n"; 305 | 306 | time += crash_test(0, 22); 307 | 308 | std::cout << "****** FLOAT ******\n"; 309 | 310 | time += crash_test(0, 22); 311 | 312 | std::cout << "****** DOUBLE ******\n"; 313 | 314 | time += matched_size_test(6, 14); 315 | 316 | std::cout << "****** FLOAT ******\n"; 317 | 318 | time += matched_size_test(6, 14); 319 | 320 | std::cout << "****** DOUBLE ******\n"; 321 | 322 | time += zip_test(1, 24); 323 | 324 | std::cout << "****** FLOAT ******\n"; 325 | 326 | time += zip_test(1, 24); 327 | 328 | tabbedOut("FFT Tests Total ", to_string_with_precision(time / 1000000.0, 2), 35); 329 | 330 | std::cout << "Finished Running\n"; 331 | return 0; 332 | } 333 | -------------------------------------------------------------------------------- /- Test/IR_Manipulation_Tester/IR_Manipulation_Tester.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B822E66E22FB455100368AE7 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B822E66D22FB455100368AE7 /* main.cpp */; }; 11 | B822E67922FC184700368AE7 /* HISSTools_FFT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B822E67822FC184700368AE7 /* HISSTools_FFT.cpp */; }; 12 | B822E67C22FC185C00368AE7 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B822E67B22FC185C00368AE7 /* Accelerate.framework */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | B822E66822FB455100368AE7 /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | B822E66A22FB455100368AE7 /* IR_Manipulation_Tester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = IR_Manipulation_Tester; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | B822E66D22FB455100368AE7 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 30 | B822E67822FC184700368AE7 /* HISSTools_FFT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = HISSTools_FFT.cpp; path = ../../../HISSTools_FFT/HISSTools_FFT.cpp; sourceTree = ""; }; 31 | B822E67B22FC185C00368AE7 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; 32 | B82A03F42302B61C00F887E7 /* SpectralFunctions.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = SpectralFunctions.hpp; path = ../../../SpectralFunctions.hpp; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | B822E66722FB455100368AE7 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | B822E67C22FC185C00368AE7 /* Accelerate.framework in Frameworks */, 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | B822E66122FB455100368AE7 = { 48 | isa = PBXGroup; 49 | children = ( 50 | B822E66C22FB455100368AE7 /* IR_Manipulation_Tester */, 51 | B822E66B22FB455100368AE7 /* Products */, 52 | B822E67A22FC185B00368AE7 /* Frameworks */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | B822E66B22FB455100368AE7 /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | B822E66A22FB455100368AE7 /* IR_Manipulation_Tester */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | B822E66C22FB455100368AE7 /* IR_Manipulation_Tester */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | B822E66D22FB455100368AE7 /* main.cpp */, 68 | B82A03F42302B61C00F887E7 /* SpectralFunctions.hpp */, 69 | B822E67822FC184700368AE7 /* HISSTools_FFT.cpp */, 70 | ); 71 | path = IR_Manipulation_Tester; 72 | sourceTree = ""; 73 | }; 74 | B822E67A22FC185B00368AE7 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | B822E67B22FC185C00368AE7 /* Accelerate.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | /* End PBXGroup section */ 83 | 84 | /* Begin PBXNativeTarget section */ 85 | B822E66922FB455100368AE7 /* IR_Manipulation_Tester */ = { 86 | isa = PBXNativeTarget; 87 | buildConfigurationList = B822E67122FB455100368AE7 /* Build configuration list for PBXNativeTarget "IR_Manipulation_Tester" */; 88 | buildPhases = ( 89 | B822E66622FB455100368AE7 /* Sources */, 90 | B822E66722FB455100368AE7 /* Frameworks */, 91 | B822E66822FB455100368AE7 /* CopyFiles */, 92 | ); 93 | buildRules = ( 94 | ); 95 | dependencies = ( 96 | ); 97 | name = IR_Manipulation_Tester; 98 | productName = IR_Manipulation_Tester; 99 | productReference = B822E66A22FB455100368AE7 /* IR_Manipulation_Tester */; 100 | productType = "com.apple.product-type.tool"; 101 | }; 102 | /* End PBXNativeTarget section */ 103 | 104 | /* Begin PBXProject section */ 105 | B822E66222FB455100368AE7 /* Project object */ = { 106 | isa = PBXProject; 107 | attributes = { 108 | LastUpgradeCheck = 0920; 109 | ORGANIZATIONNAME = AHarker; 110 | TargetAttributes = { 111 | B822E66922FB455100368AE7 = { 112 | CreatedOnToolsVersion = 9.2; 113 | ProvisioningStyle = Automatic; 114 | }; 115 | }; 116 | }; 117 | buildConfigurationList = B822E66522FB455100368AE7 /* Build configuration list for PBXProject "IR_Manipulation_Tester" */; 118 | compatibilityVersion = "Xcode 8.0"; 119 | developmentRegion = en; 120 | hasScannedForEncodings = 0; 121 | knownRegions = ( 122 | en, 123 | ); 124 | mainGroup = B822E66122FB455100368AE7; 125 | productRefGroup = B822E66B22FB455100368AE7 /* Products */; 126 | projectDirPath = ""; 127 | projectRoot = ""; 128 | targets = ( 129 | B822E66922FB455100368AE7 /* IR_Manipulation_Tester */, 130 | ); 131 | }; 132 | /* End PBXProject section */ 133 | 134 | /* Begin PBXSourcesBuildPhase section */ 135 | B822E66622FB455100368AE7 /* Sources */ = { 136 | isa = PBXSourcesBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | B822E67922FC184700368AE7 /* HISSTools_FFT.cpp in Sources */, 140 | B822E66E22FB455100368AE7 /* main.cpp in Sources */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXSourcesBuildPhase section */ 145 | 146 | /* Begin XCBuildConfiguration section */ 147 | B822E66F22FB455100368AE7 /* Debug */ = { 148 | isa = XCBuildConfiguration; 149 | buildSettings = { 150 | ALWAYS_SEARCH_USER_PATHS = NO; 151 | CLANG_ANALYZER_NONNULL = YES; 152 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 153 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 154 | CLANG_CXX_LIBRARY = "libc++"; 155 | CLANG_ENABLE_MODULES = YES; 156 | CLANG_ENABLE_OBJC_ARC = YES; 157 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 158 | CLANG_WARN_BOOL_CONVERSION = YES; 159 | CLANG_WARN_COMMA = YES; 160 | CLANG_WARN_CONSTANT_CONVERSION = YES; 161 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 162 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 163 | CLANG_WARN_EMPTY_BODY = YES; 164 | CLANG_WARN_ENUM_CONVERSION = YES; 165 | CLANG_WARN_INFINITE_RECURSION = YES; 166 | CLANG_WARN_INT_CONVERSION = YES; 167 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 168 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 169 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 170 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 171 | CLANG_WARN_STRICT_PROTOTYPES = YES; 172 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 173 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 174 | CLANG_WARN_UNREACHABLE_CODE = YES; 175 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 176 | CLANG_X86_VECTOR_INSTRUCTIONS = default; 177 | CODE_SIGN_IDENTITY = "-"; 178 | COPY_PHASE_STRIP = NO; 179 | DEBUG_INFORMATION_FORMAT = dwarf; 180 | ENABLE_STRICT_OBJC_MSGSEND = YES; 181 | ENABLE_TESTABILITY = YES; 182 | GCC_C_LANGUAGE_STANDARD = gnu11; 183 | GCC_DYNAMIC_NO_PIC = NO; 184 | GCC_NO_COMMON_BLOCKS = YES; 185 | GCC_OPTIMIZATION_LEVEL = 0; 186 | GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; 187 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 188 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 189 | GCC_WARN_UNDECLARED_SELECTOR = YES; 190 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 191 | GCC_WARN_UNUSED_FUNCTION = YES; 192 | GCC_WARN_UNUSED_VARIABLE = YES; 193 | MACOSX_DEPLOYMENT_TARGET = 10.12; 194 | MTL_ENABLE_DEBUG_INFO = YES; 195 | ONLY_ACTIVE_ARCH = YES; 196 | SDKROOT = macosx; 197 | }; 198 | name = Debug; 199 | }; 200 | B822E67022FB455100368AE7 /* Release */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | ALWAYS_SEARCH_USER_PATHS = NO; 204 | CLANG_ANALYZER_NONNULL = YES; 205 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 206 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 207 | CLANG_CXX_LIBRARY = "libc++"; 208 | CLANG_ENABLE_MODULES = YES; 209 | CLANG_ENABLE_OBJC_ARC = YES; 210 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 211 | CLANG_WARN_BOOL_CONVERSION = YES; 212 | CLANG_WARN_COMMA = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 216 | CLANG_WARN_EMPTY_BODY = YES; 217 | CLANG_WARN_ENUM_CONVERSION = YES; 218 | CLANG_WARN_INFINITE_RECURSION = YES; 219 | CLANG_WARN_INT_CONVERSION = YES; 220 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 221 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 222 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 223 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 224 | CLANG_WARN_STRICT_PROTOTYPES = YES; 225 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 226 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 227 | CLANG_WARN_UNREACHABLE_CODE = YES; 228 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 229 | CLANG_X86_VECTOR_INSTRUCTIONS = default; 230 | CODE_SIGN_IDENTITY = "-"; 231 | COPY_PHASE_STRIP = NO; 232 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 233 | ENABLE_NS_ASSERTIONS = NO; 234 | ENABLE_STRICT_OBJC_MSGSEND = YES; 235 | GCC_C_LANGUAGE_STANDARD = gnu11; 236 | GCC_NO_COMMON_BLOCKS = YES; 237 | GCC_PREPROCESSOR_DEFINITIONS = ""; 238 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 239 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 240 | GCC_WARN_UNDECLARED_SELECTOR = YES; 241 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 242 | GCC_WARN_UNUSED_FUNCTION = YES; 243 | GCC_WARN_UNUSED_VARIABLE = YES; 244 | MACOSX_DEPLOYMENT_TARGET = 10.12; 245 | MTL_ENABLE_DEBUG_INFO = NO; 246 | SDKROOT = macosx; 247 | }; 248 | name = Release; 249 | }; 250 | B822E67222FB455100368AE7 /* Debug */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | CODE_SIGN_STYLE = Automatic; 254 | PRODUCT_NAME = "$(TARGET_NAME)"; 255 | }; 256 | name = Debug; 257 | }; 258 | B822E67322FB455100368AE7 /* Release */ = { 259 | isa = XCBuildConfiguration; 260 | buildSettings = { 261 | CODE_SIGN_STYLE = Automatic; 262 | PRODUCT_NAME = "$(TARGET_NAME)"; 263 | }; 264 | name = Release; 265 | }; 266 | /* End XCBuildConfiguration section */ 267 | 268 | /* Begin XCConfigurationList section */ 269 | B822E66522FB455100368AE7 /* Build configuration list for PBXProject "IR_Manipulation_Tester" */ = { 270 | isa = XCConfigurationList; 271 | buildConfigurations = ( 272 | B822E66F22FB455100368AE7 /* Debug */, 273 | B822E67022FB455100368AE7 /* Release */, 274 | ); 275 | defaultConfigurationIsVisible = 0; 276 | defaultConfigurationName = Release; 277 | }; 278 | B822E67122FB455100368AE7 /* Build configuration list for PBXNativeTarget "IR_Manipulation_Tester" */ = { 279 | isa = XCConfigurationList; 280 | buildConfigurations = ( 281 | B822E67222FB455100368AE7 /* Debug */, 282 | B822E67322FB455100368AE7 /* Release */, 283 | ); 284 | defaultConfigurationIsVisible = 0; 285 | defaultConfigurationName = Release; 286 | }; 287 | /* End XCConfigurationList section */ 288 | }; 289 | rootObject = B822E66222FB455100368AE7 /* Project object */; 290 | } 291 | -------------------------------------------------------------------------------- /- Test/IR_Manipulation_Tester/IR_Manipulation_Tester.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /- Test/IR_Manipulation_Tester/IR_Manipulation_Tester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /- Test/IR_Manipulation_Tester/IR_Manipulation_Tester.xcodeproj/xcshareddata/xcschemes/IR_Manipulation_Tester.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /- Test/IR_Manipulation_Tester/IR_Manipulation_Tester/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../../../SpectralFunctions.hpp" 9 | 10 | // Output 11 | 12 | void tabbedOut(const std::string& name, const std::string& text, int tab = 25) 13 | { 14 | std::cout << std::setw(tab) << std::setfill(' '); 15 | std::cout.setf(std::ios::left); 16 | std::cout.unsetf(std::ios::right); 17 | std::cout << name; 18 | std::cout.unsetf(std::ios::left); 19 | std::cout << text << "\n"; 20 | } 21 | 22 | template 23 | std::string to_string_with_precision(const T a_value, const int n = 4, bool fixed = true) 24 | { 25 | std::ostringstream out; 26 | if (fixed) 27 | out << std::setprecision(n) << std::fixed << a_value; 28 | else 29 | out << std::setprecision(n) << a_value; 30 | 31 | return out.str(); 32 | } 33 | 34 | // Timing 35 | 36 | class Timer 37 | { 38 | 39 | public: 40 | 41 | Timer() : mStart(0), mStore1(0), mStore2(0) {} 42 | 43 | void start() 44 | { 45 | mStart = mach_absolute_time(); 46 | }; 47 | 48 | void stop() 49 | { 50 | uint64_t end = mach_absolute_time(); 51 | 52 | mach_timebase_info_data_t info; 53 | mach_timebase_info(&info); 54 | 55 | uint64_t elapsed = ((end - mStart) * info.numer) / info.denom; 56 | 57 | mStore2 = mStore1; 58 | mStore1 += elapsed; 59 | } 60 | 61 | uint64_t finish(const std::string& msg) 62 | { 63 | tabbedOut(msg + " Elapsed ", to_string_with_precision(mStore1 / 1000000.0, 2), 35); 64 | 65 | uint64_t elapsed = mStore1; 66 | 67 | mStore2 = 0; 68 | mStore1 = 0; 69 | 70 | return elapsed; 71 | }; 72 | 73 | void relative(const std::string& msg) 74 | { 75 | tabbedOut(msg + " Comparison ", to_string_with_precision(((double) mStore1 / (double) mStore2), 2), 35); 76 | } 77 | 78 | private: 79 | 80 | uint64_t mStart; 81 | uint64_t mStore1; 82 | uint64_t mStore2; 83 | }; 84 | 85 | 86 | template 87 | void fillSplit(SPLIT split, uintptr_t fft_log2) 88 | { 89 | for (uintptr_t i =0; i < (1 << fft_log2); i++) 90 | { 91 | split.realp[i] = 1.0 - 2.0 * std::rand() / RAND_MAX; 92 | split.imagp[i] = 1.0 - 2.0 * std::rand() / RAND_MAX; 93 | } 94 | } 95 | 96 | template 97 | uint64_t timing_test(std::string test, uintptr_t fft_log2, double phase, bool zero, int testSize) 98 | { 99 | SETUP setup; 100 | SPLIT split; 101 | 102 | uintptr_t fft_size = 1 << fft_log2; 103 | 104 | split.realp = (T *) malloc(sizeof(T) * fft_size); 105 | split.imagp = (T *) malloc(sizeof(T) * fft_size); 106 | 107 | hisstools_create_setup(&setup, fft_log2); 108 | 109 | Timer timer; 110 | 111 | for (int i = 0; i < testSize; i++) 112 | { 113 | fillSplit(split, fft_log2); 114 | timer.start(); 115 | ir_phase(setup, &split, &split, fft_size, phase, zero); 116 | timer.stop(); 117 | } 118 | 119 | uint64_t time = timer.finish(test); 120 | 121 | free(split.realp); 122 | free(split.imagp); 123 | hisstools_destroy_setup(setup); 124 | 125 | return time; 126 | } 127 | 128 | int main(int argc, const char * argv[]) 129 | { 130 | // insert code here... 131 | std::cout << "Double vector size is " << SIMDLimits::max_size << "\n"; 132 | 133 | int fft_log2 = 14; 134 | int iter = 100; 135 | 136 | timing_test("Zero Mix", fft_log2, 0.1, true, iter); 137 | timing_test("Center Mix", fft_log2, 0.9, false, iter); 138 | timing_test("Zero Min", fft_log2, 0.0, true, iter); 139 | timing_test("Center Min", fft_log2, 0.0, false, iter); 140 | timing_test("Zero Max", fft_log2, 1.0, true, iter); 141 | timing_test("Center Max", fft_log2, 1.0, false, iter); 142 | timing_test("Zero Lin", fft_log2, 0.5, true, iter); 143 | timing_test("Center Lin", fft_log2, 0.5, false, iter); 144 | 145 | std::cout << "Float vector size is " << SIMDLimits::max_size << "\n"; 146 | 147 | timing_test("Zero Mix", fft_log2, 0.1, true, iter); 148 | timing_test("Center Mix", fft_log2, 0.9, false, iter); 149 | timing_test("Zero Min", fft_log2, 0.0, true, iter); 150 | timing_test("Center Min", fft_log2, 0.0, false, iter); 151 | timing_test("Zero Max", fft_log2, 1.0, true, iter); 152 | timing_test("Center Max", fft_log2, 1.0, false, iter); 153 | timing_test("Zero Lin", fft_log2, 0.5, true, iter); 154 | timing_test("Center Lin", fft_log2, 0.5, false, iter); 155 | 156 | return 0; 157 | } 158 | -------------------------------------------------------------------------------- /- Test/Peak_Tester/Peak_Tester.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B80A056B267E1C3000AB6A91 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B80A056A267E1C3000AB6A91 /* main.cpp */; }; 11 | /* End PBXBuildFile section */ 12 | 13 | /* Begin PBXCopyFilesBuildPhase section */ 14 | B80A0565267E1C3000AB6A91 /* CopyFiles */ = { 15 | isa = PBXCopyFilesBuildPhase; 16 | buildActionMask = 2147483647; 17 | dstPath = /usr/share/man/man1/; 18 | dstSubfolderSpec = 0; 19 | files = ( 20 | ); 21 | runOnlyForDeploymentPostprocessing = 1; 22 | }; 23 | /* End PBXCopyFilesBuildPhase section */ 24 | 25 | /* Begin PBXFileReference section */ 26 | B80A0567267E1C3000AB6A91 /* Peak_Tester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Peak_Tester; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | B80A056A267E1C3000AB6A91 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 28 | B8474420267E1C7100DA966D /* PartialTracker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = PartialTracker.hpp; path = ../../../PartialTracker.hpp; sourceTree = ""; }; 29 | /* End PBXFileReference section */ 30 | 31 | /* Begin PBXFrameworksBuildPhase section */ 32 | B80A0564267E1C3000AB6A91 /* Frameworks */ = { 33 | isa = PBXFrameworksBuildPhase; 34 | buildActionMask = 2147483647; 35 | files = ( 36 | ); 37 | runOnlyForDeploymentPostprocessing = 0; 38 | }; 39 | /* End PBXFrameworksBuildPhase section */ 40 | 41 | /* Begin PBXGroup section */ 42 | B80A055E267E1C3000AB6A91 = { 43 | isa = PBXGroup; 44 | children = ( 45 | B80A0569267E1C3000AB6A91 /* Peak_Tester */, 46 | B80A0568267E1C3000AB6A91 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | B80A0568267E1C3000AB6A91 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | B80A0567267E1C3000AB6A91 /* Peak_Tester */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | B80A0569267E1C3000AB6A91 /* Peak_Tester */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | B8474420267E1C7100DA966D /* PartialTracker.hpp */, 62 | B80A056A267E1C3000AB6A91 /* main.cpp */, 63 | ); 64 | path = Peak_Tester; 65 | sourceTree = ""; 66 | }; 67 | /* End PBXGroup section */ 68 | 69 | /* Begin PBXNativeTarget section */ 70 | B80A0566267E1C3000AB6A91 /* Peak_Tester */ = { 71 | isa = PBXNativeTarget; 72 | buildConfigurationList = B80A056E267E1C3000AB6A91 /* Build configuration list for PBXNativeTarget "Peak_Tester" */; 73 | buildPhases = ( 74 | B80A0563267E1C3000AB6A91 /* Sources */, 75 | B80A0564267E1C3000AB6A91 /* Frameworks */, 76 | B80A0565267E1C3000AB6A91 /* CopyFiles */, 77 | ); 78 | buildRules = ( 79 | ); 80 | dependencies = ( 81 | ); 82 | name = Peak_Tester; 83 | productName = Peak_Tester; 84 | productReference = B80A0567267E1C3000AB6A91 /* Peak_Tester */; 85 | productType = "com.apple.product-type.tool"; 86 | }; 87 | /* End PBXNativeTarget section */ 88 | 89 | /* Begin PBXProject section */ 90 | B80A055F267E1C3000AB6A91 /* Project object */ = { 91 | isa = PBXProject; 92 | attributes = { 93 | LastUpgradeCheck = 0940; 94 | ORGANIZATIONNAME = AHarker; 95 | TargetAttributes = { 96 | B80A0566267E1C3000AB6A91 = { 97 | CreatedOnToolsVersion = 9.4.1; 98 | }; 99 | }; 100 | }; 101 | buildConfigurationList = B80A0562267E1C3000AB6A91 /* Build configuration list for PBXProject "Peak_Tester" */; 102 | compatibilityVersion = "Xcode 9.3"; 103 | developmentRegion = en; 104 | hasScannedForEncodings = 0; 105 | knownRegions = ( 106 | en, 107 | ); 108 | mainGroup = B80A055E267E1C3000AB6A91; 109 | productRefGroup = B80A0568267E1C3000AB6A91 /* Products */; 110 | projectDirPath = ""; 111 | projectRoot = ""; 112 | targets = ( 113 | B80A0566267E1C3000AB6A91 /* Peak_Tester */, 114 | ); 115 | }; 116 | /* End PBXProject section */ 117 | 118 | /* Begin PBXSourcesBuildPhase section */ 119 | B80A0563267E1C3000AB6A91 /* Sources */ = { 120 | isa = PBXSourcesBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | B80A056B267E1C3000AB6A91 /* main.cpp in Sources */, 124 | ); 125 | runOnlyForDeploymentPostprocessing = 0; 126 | }; 127 | /* End PBXSourcesBuildPhase section */ 128 | 129 | /* Begin XCBuildConfiguration section */ 130 | B80A056C267E1C3000AB6A91 /* Debug */ = { 131 | isa = XCBuildConfiguration; 132 | buildSettings = { 133 | ALWAYS_SEARCH_USER_PATHS = NO; 134 | CLANG_ANALYZER_NONNULL = YES; 135 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 136 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 137 | CLANG_CXX_LIBRARY = "libc++"; 138 | CLANG_ENABLE_MODULES = YES; 139 | CLANG_ENABLE_OBJC_ARC = YES; 140 | CLANG_ENABLE_OBJC_WEAK = YES; 141 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 142 | CLANG_WARN_BOOL_CONVERSION = YES; 143 | CLANG_WARN_COMMA = YES; 144 | CLANG_WARN_CONSTANT_CONVERSION = YES; 145 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 146 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 147 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 148 | CLANG_WARN_EMPTY_BODY = YES; 149 | CLANG_WARN_ENUM_CONVERSION = YES; 150 | CLANG_WARN_INFINITE_RECURSION = YES; 151 | CLANG_WARN_INT_CONVERSION = YES; 152 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 153 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 154 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 155 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 156 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 157 | CLANG_WARN_STRICT_PROTOTYPES = YES; 158 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 159 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 160 | CLANG_WARN_UNREACHABLE_CODE = YES; 161 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 162 | CODE_SIGN_IDENTITY = "-"; 163 | COPY_PHASE_STRIP = NO; 164 | DEBUG_INFORMATION_FORMAT = dwarf; 165 | ENABLE_STRICT_OBJC_MSGSEND = YES; 166 | ENABLE_TESTABILITY = YES; 167 | GCC_C_LANGUAGE_STANDARD = gnu11; 168 | GCC_DYNAMIC_NO_PIC = NO; 169 | GCC_NO_COMMON_BLOCKS = YES; 170 | GCC_OPTIMIZATION_LEVEL = 0; 171 | GCC_PREPROCESSOR_DEFINITIONS = ( 172 | "DEBUG=1", 173 | "$(inherited)", 174 | ); 175 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 176 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 177 | GCC_WARN_UNDECLARED_SELECTOR = YES; 178 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 179 | GCC_WARN_UNUSED_FUNCTION = YES; 180 | GCC_WARN_UNUSED_VARIABLE = YES; 181 | MACOSX_DEPLOYMENT_TARGET = 10.13; 182 | MTL_ENABLE_DEBUG_INFO = YES; 183 | ONLY_ACTIVE_ARCH = YES; 184 | SDKROOT = macosx; 185 | }; 186 | name = Debug; 187 | }; 188 | B80A056D267E1C3000AB6A91 /* Release */ = { 189 | isa = XCBuildConfiguration; 190 | buildSettings = { 191 | ALWAYS_SEARCH_USER_PATHS = NO; 192 | CLANG_ANALYZER_NONNULL = YES; 193 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 194 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 195 | CLANG_CXX_LIBRARY = "libc++"; 196 | CLANG_ENABLE_MODULES = YES; 197 | CLANG_ENABLE_OBJC_ARC = YES; 198 | CLANG_ENABLE_OBJC_WEAK = YES; 199 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 200 | CLANG_WARN_BOOL_CONVERSION = YES; 201 | CLANG_WARN_COMMA = YES; 202 | CLANG_WARN_CONSTANT_CONVERSION = YES; 203 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 204 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 205 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 206 | CLANG_WARN_EMPTY_BODY = YES; 207 | CLANG_WARN_ENUM_CONVERSION = YES; 208 | CLANG_WARN_INFINITE_RECURSION = YES; 209 | CLANG_WARN_INT_CONVERSION = YES; 210 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 211 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 212 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 213 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 214 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 215 | CLANG_WARN_STRICT_PROTOTYPES = YES; 216 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 217 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 218 | CLANG_WARN_UNREACHABLE_CODE = YES; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | CODE_SIGN_IDENTITY = "-"; 221 | COPY_PHASE_STRIP = NO; 222 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 223 | ENABLE_NS_ASSERTIONS = NO; 224 | ENABLE_STRICT_OBJC_MSGSEND = YES; 225 | GCC_C_LANGUAGE_STANDARD = gnu11; 226 | GCC_NO_COMMON_BLOCKS = YES; 227 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 228 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 229 | GCC_WARN_UNDECLARED_SELECTOR = YES; 230 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 231 | GCC_WARN_UNUSED_FUNCTION = YES; 232 | GCC_WARN_UNUSED_VARIABLE = YES; 233 | MACOSX_DEPLOYMENT_TARGET = 10.13; 234 | MTL_ENABLE_DEBUG_INFO = NO; 235 | SDKROOT = macosx; 236 | }; 237 | name = Release; 238 | }; 239 | B80A056F267E1C3000AB6A91 /* Debug */ = { 240 | isa = XCBuildConfiguration; 241 | buildSettings = { 242 | CODE_SIGN_STYLE = Automatic; 243 | PRODUCT_NAME = "$(TARGET_NAME)"; 244 | }; 245 | name = Debug; 246 | }; 247 | B80A0570267E1C3000AB6A91 /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | CODE_SIGN_STYLE = Automatic; 251 | PRODUCT_NAME = "$(TARGET_NAME)"; 252 | }; 253 | name = Release; 254 | }; 255 | /* End XCBuildConfiguration section */ 256 | 257 | /* Begin XCConfigurationList section */ 258 | B80A0562267E1C3000AB6A91 /* Build configuration list for PBXProject "Peak_Tester" */ = { 259 | isa = XCConfigurationList; 260 | buildConfigurations = ( 261 | B80A056C267E1C3000AB6A91 /* Debug */, 262 | B80A056D267E1C3000AB6A91 /* Release */, 263 | ); 264 | defaultConfigurationIsVisible = 0; 265 | defaultConfigurationName = Release; 266 | }; 267 | B80A056E267E1C3000AB6A91 /* Build configuration list for PBXNativeTarget "Peak_Tester" */ = { 268 | isa = XCConfigurationList; 269 | buildConfigurations = ( 270 | B80A056F267E1C3000AB6A91 /* Debug */, 271 | B80A0570267E1C3000AB6A91 /* Release */, 272 | ); 273 | defaultConfigurationIsVisible = 0; 274 | defaultConfigurationName = Release; 275 | }; 276 | /* End XCConfigurationList section */ 277 | }; 278 | rootObject = B80A055F267E1C3000AB6A91 /* Project object */; 279 | } 280 | -------------------------------------------------------------------------------- /- Test/Peak_Tester/Peak_Tester.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /- Test/Peak_Tester/Peak_Tester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /- Test/Peak_Tester/Peak_Tester/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // Peak_Tester 4 | // 5 | // Created by Alex Harker on 19/06/2021. 6 | // Copyright © 2021 AHarker. All rights reserved. 7 | // 8 | 9 | #include 10 | 11 | #include "PartialTracker.hpp" 12 | 13 | using Tracker = partial_tracker; 14 | 15 | void post_tracks(Tracker& tracker, int N) 16 | { 17 | for (int i = 0; i < N; i++) 18 | { 19 | track t = tracker.get_track(i); 20 | 21 | std::cout << "index " << i << "\n"; 22 | std::cout << "active " << t.active() << "\n"; 23 | std::cout << "state " << static_cast(t.m_state) << "\n"; 24 | std::cout << "freq " << t.m_peak.freq() << "\n"; 25 | std::cout << "amp " << t.m_peak.amp() << "\n"; 26 | } 27 | } 28 | 29 | int main(int argc, const char * argv[]) 30 | { 31 | Tracker tracker(12, 12); 32 | peak peaks[10]; 33 | 34 | for (int i = 0; i < 10; i++) 35 | peaks[i] = peak((i + 1) * 10.0, 1.0); 36 | 37 | tracker.process(peaks, 10, 0.0); 38 | 39 | post_tracks(tracker, 12); 40 | 41 | for (int i = 0; i < 10; i++) 42 | peaks[i] = peak((10 - i) * 10.0 + 2, 1.0); 43 | 44 | tracker.process(peaks, 8, 0.0); 45 | 46 | post_tracks(tracker, 12); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /- Test/Window_Tester/Window_Tester.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | B8C2DD6F24F94B28000EF2D0 /* OAudioFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8C2DD6E24F94B27000EF2D0 /* OAudioFile.cpp */; }; 11 | B8C2DD7224F94C56000EF2D0 /* BaseAudioFile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8C2DD7124F94C56000EF2D0 /* BaseAudioFile.cpp */; }; 12 | B8E728FD24F8288B0041006F /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B8E728FC24F8288B0041006F /* main.cpp */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | B8E728F724F8288B0041006F /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | ); 23 | runOnlyForDeploymentPostprocessing = 1; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | B8C2DD6E24F94B27000EF2D0 /* OAudioFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OAudioFile.cpp; path = ../../../AudioFile/OAudioFile.cpp; sourceTree = ""; }; 29 | B8C2DD7024F94B30000EF2D0 /* WindowFunctions.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; name = WindowFunctions.hpp; path = ../../../WindowFunctions.hpp; sourceTree = ""; }; 30 | B8C2DD7124F94C56000EF2D0 /* BaseAudioFile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BaseAudioFile.cpp; path = ../../../AudioFile/BaseAudioFile.cpp; sourceTree = ""; }; 31 | B8E728F924F8288B0041006F /* Window_Tester */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Window_Tester; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | B8E728FC24F8288B0041006F /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | B8E728F624F8288B0041006F /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | B8E728F024F8288B0041006F = { 47 | isa = PBXGroup; 48 | children = ( 49 | B8E728FB24F8288B0041006F /* Window_Tester */, 50 | B8E728FA24F8288B0041006F /* Products */, 51 | ); 52 | sourceTree = ""; 53 | }; 54 | B8E728FA24F8288B0041006F /* Products */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | B8E728F924F8288B0041006F /* Window_Tester */, 58 | ); 59 | name = Products; 60 | sourceTree = ""; 61 | }; 62 | B8E728FB24F8288B0041006F /* Window_Tester */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | B8C2DD7024F94B30000EF2D0 /* WindowFunctions.hpp */, 66 | B8E728FC24F8288B0041006F /* main.cpp */, 67 | B8C2DD6E24F94B27000EF2D0 /* OAudioFile.cpp */, 68 | B8C2DD7124F94C56000EF2D0 /* BaseAudioFile.cpp */, 69 | ); 70 | path = Window_Tester; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | B8E728F824F8288B0041006F /* Window_Tester */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = B8E7290024F8288B0041006F /* Build configuration list for PBXNativeTarget "Window_Tester" */; 79 | buildPhases = ( 80 | B8E728F524F8288B0041006F /* Sources */, 81 | B8E728F624F8288B0041006F /* Frameworks */, 82 | B8E728F724F8288B0041006F /* CopyFiles */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = Window_Tester; 89 | productName = WindowTesting; 90 | productReference = B8E728F924F8288B0041006F /* Window_Tester */; 91 | productType = "com.apple.product-type.tool"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | B8E728F124F8288B0041006F /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastUpgradeCheck = 0940; 100 | ORGANIZATIONNAME = AHarker; 101 | TargetAttributes = { 102 | B8E728F824F8288B0041006F = { 103 | CreatedOnToolsVersion = 9.4.1; 104 | }; 105 | }; 106 | }; 107 | buildConfigurationList = B8E728F424F8288B0041006F /* Build configuration list for PBXProject "Window_Tester" */; 108 | compatibilityVersion = "Xcode 9.3"; 109 | developmentRegion = en; 110 | hasScannedForEncodings = 0; 111 | knownRegions = ( 112 | en, 113 | ); 114 | mainGroup = B8E728F024F8288B0041006F; 115 | productRefGroup = B8E728FA24F8288B0041006F /* Products */; 116 | projectDirPath = ""; 117 | projectRoot = ""; 118 | targets = ( 119 | B8E728F824F8288B0041006F /* Window_Tester */, 120 | ); 121 | }; 122 | /* End PBXProject section */ 123 | 124 | /* Begin PBXSourcesBuildPhase section */ 125 | B8E728F524F8288B0041006F /* Sources */ = { 126 | isa = PBXSourcesBuildPhase; 127 | buildActionMask = 2147483647; 128 | files = ( 129 | B8C2DD7224F94C56000EF2D0 /* BaseAudioFile.cpp in Sources */, 130 | B8C2DD6F24F94B28000EF2D0 /* OAudioFile.cpp in Sources */, 131 | B8E728FD24F8288B0041006F /* main.cpp in Sources */, 132 | ); 133 | runOnlyForDeploymentPostprocessing = 0; 134 | }; 135 | /* End PBXSourcesBuildPhase section */ 136 | 137 | /* Begin XCBuildConfiguration section */ 138 | B8E728FE24F8288B0041006F /* Debug */ = { 139 | isa = XCBuildConfiguration; 140 | buildSettings = { 141 | ALWAYS_SEARCH_USER_PATHS = NO; 142 | CLANG_ANALYZER_NONNULL = YES; 143 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 144 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 145 | CLANG_CXX_LIBRARY = "libc++"; 146 | CLANG_ENABLE_MODULES = YES; 147 | CLANG_ENABLE_OBJC_ARC = YES; 148 | CLANG_ENABLE_OBJC_WEAK = YES; 149 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 150 | CLANG_WARN_BOOL_CONVERSION = YES; 151 | CLANG_WARN_COMMA = YES; 152 | CLANG_WARN_CONSTANT_CONVERSION = YES; 153 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 154 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 155 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 156 | CLANG_WARN_EMPTY_BODY = YES; 157 | CLANG_WARN_ENUM_CONVERSION = YES; 158 | CLANG_WARN_INFINITE_RECURSION = YES; 159 | CLANG_WARN_INT_CONVERSION = YES; 160 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 161 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 162 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 163 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 164 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 165 | CLANG_WARN_STRICT_PROTOTYPES = YES; 166 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 167 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 168 | CLANG_WARN_UNREACHABLE_CODE = YES; 169 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 170 | CODE_SIGN_IDENTITY = "-"; 171 | COPY_PHASE_STRIP = NO; 172 | DEBUG_INFORMATION_FORMAT = dwarf; 173 | ENABLE_STRICT_OBJC_MSGSEND = YES; 174 | ENABLE_TESTABILITY = YES; 175 | GCC_C_LANGUAGE_STANDARD = gnu11; 176 | GCC_DYNAMIC_NO_PIC = NO; 177 | GCC_NO_COMMON_BLOCKS = YES; 178 | GCC_OPTIMIZATION_LEVEL = 0; 179 | GCC_PREPROCESSOR_DEFINITIONS = ( 180 | "DEBUG=1", 181 | "$(inherited)", 182 | ); 183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 185 | GCC_WARN_UNDECLARED_SELECTOR = YES; 186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 187 | GCC_WARN_UNUSED_FUNCTION = YES; 188 | GCC_WARN_UNUSED_VARIABLE = YES; 189 | MACOSX_DEPLOYMENT_TARGET = 10.13; 190 | MTL_ENABLE_DEBUG_INFO = YES; 191 | ONLY_ACTIVE_ARCH = YES; 192 | SDKROOT = macosx; 193 | }; 194 | name = Debug; 195 | }; 196 | B8E728FF24F8288B0041006F /* Release */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | ALWAYS_SEARCH_USER_PATHS = NO; 200 | CLANG_ANALYZER_NONNULL = YES; 201 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 202 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 203 | CLANG_CXX_LIBRARY = "libc++"; 204 | CLANG_ENABLE_MODULES = YES; 205 | CLANG_ENABLE_OBJC_ARC = YES; 206 | CLANG_ENABLE_OBJC_WEAK = YES; 207 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 208 | CLANG_WARN_BOOL_CONVERSION = YES; 209 | CLANG_WARN_COMMA = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 212 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 213 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 214 | CLANG_WARN_EMPTY_BODY = YES; 215 | CLANG_WARN_ENUM_CONVERSION = YES; 216 | CLANG_WARN_INFINITE_RECURSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 219 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 220 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 221 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 222 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 223 | CLANG_WARN_STRICT_PROTOTYPES = YES; 224 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 225 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 226 | CLANG_WARN_UNREACHABLE_CODE = YES; 227 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 228 | CODE_SIGN_IDENTITY = "-"; 229 | COPY_PHASE_STRIP = NO; 230 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 231 | ENABLE_NS_ASSERTIONS = NO; 232 | ENABLE_STRICT_OBJC_MSGSEND = YES; 233 | GCC_C_LANGUAGE_STANDARD = gnu11; 234 | GCC_NO_COMMON_BLOCKS = YES; 235 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 236 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 237 | GCC_WARN_UNDECLARED_SELECTOR = YES; 238 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 239 | GCC_WARN_UNUSED_FUNCTION = YES; 240 | GCC_WARN_UNUSED_VARIABLE = YES; 241 | MACOSX_DEPLOYMENT_TARGET = 10.13; 242 | MTL_ENABLE_DEBUG_INFO = NO; 243 | SDKROOT = macosx; 244 | }; 245 | name = Release; 246 | }; 247 | B8E7290124F8288B0041006F /* Debug */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | CODE_SIGN_STYLE = Automatic; 251 | PRODUCT_NAME = "$(TARGET_NAME)"; 252 | }; 253 | name = Debug; 254 | }; 255 | B8E7290224F8288B0041006F /* Release */ = { 256 | isa = XCBuildConfiguration; 257 | buildSettings = { 258 | CODE_SIGN_STYLE = Automatic; 259 | PRODUCT_NAME = "$(TARGET_NAME)"; 260 | }; 261 | name = Release; 262 | }; 263 | /* End XCBuildConfiguration section */ 264 | 265 | /* Begin XCConfigurationList section */ 266 | B8E728F424F8288B0041006F /* Build configuration list for PBXProject "Window_Tester" */ = { 267 | isa = XCConfigurationList; 268 | buildConfigurations = ( 269 | B8E728FE24F8288B0041006F /* Debug */, 270 | B8E728FF24F8288B0041006F /* Release */, 271 | ); 272 | defaultConfigurationIsVisible = 0; 273 | defaultConfigurationName = Release; 274 | }; 275 | B8E7290024F8288B0041006F /* Build configuration list for PBXNativeTarget "Window_Tester" */ = { 276 | isa = XCConfigurationList; 277 | buildConfigurations = ( 278 | B8E7290124F8288B0041006F /* Debug */, 279 | B8E7290224F8288B0041006F /* Release */, 280 | ); 281 | defaultConfigurationIsVisible = 0; 282 | defaultConfigurationName = Release; 283 | }; 284 | /* End XCConfigurationList section */ 285 | }; 286 | rootObject = B8E728F124F8288B0041006F /* Project object */; 287 | } 288 | -------------------------------------------------------------------------------- /- Test/Window_Tester/Window_Tester.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /- Test/Window_Tester/Window_Tester.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /- Test/Window_Tester/Window_Tester/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../../../WindowFunctions.hpp" 11 | #include "../../../AudioFile/OAudioFile.h" 12 | 13 | // Output 14 | 15 | void tabbedOut(const std::string& name, const std::string& text, int tab = 25) 16 | { 17 | std::cout << std::setw(tab) << std::setfill(' '); 18 | std::cout.setf(std::ios::left); 19 | std::cout.unsetf(std::ios::right); 20 | std::cout << name; 21 | std::cout.unsetf(std::ios::left); 22 | std::cout << text << "\n"; 23 | } 24 | 25 | template 26 | std::string to_string_with_precision(const T a_value, const int n = 4, bool fixed = true) 27 | { 28 | std::ostringstream out; 29 | if (fixed) 30 | out << std::setprecision(n) << std::fixed << a_value; 31 | else 32 | out << std::setprecision(n) << a_value; 33 | 34 | return out.str(); 35 | } 36 | 37 | // Timing 38 | 39 | class Timer 40 | { 41 | 42 | public: 43 | 44 | Timer() : mStart(0), mStore1(0), mStore2(0) {} 45 | 46 | void start() 47 | { 48 | mStart = mach_absolute_time(); 49 | }; 50 | 51 | void stop() 52 | { 53 | uint64_t end = mach_absolute_time(); 54 | 55 | mach_timebase_info_data_t info; 56 | mach_timebase_info(&info); 57 | 58 | uint64_t elapsed = ((end - mStart) * info.numer) / info.denom; 59 | 60 | mStore2 = mStore1; 61 | mStore1 = elapsed; 62 | } 63 | 64 | uint64_t finish(const std::string& msg) 65 | { 66 | tabbedOut(msg + " Elapsed ", to_string_with_precision(mStore1 / 1000000.0, 2), 35); 67 | 68 | uint64_t elapsed = mStore1; 69 | 70 | return elapsed; 71 | }; 72 | 73 | void relative(const std::string& msg) 74 | { 75 | tabbedOut(msg + " Comparison ", to_string_with_precision(((double) mStore2 / (double) mStore1), 2), 35); 76 | } 77 | 78 | private: 79 | 80 | uint64_t mStart; 81 | uint64_t mStore1; 82 | uint64_t mStore2; 83 | }; 84 | 85 | std::default_random_engine rand_engine; 86 | 87 | int random_integer(int min, int max) 88 | { 89 | return (rand_engine() % ((max + 1) - min)) + min; 90 | } 91 | 92 | bool check_symmetry() 93 | { 94 | const int size = random_integer(20, 300); 95 | int begin = random_integer(0, size / 2 + 2); 96 | int end = random_integer(size / 2 - 2, size); 97 | 98 | double window1[size]; 99 | double window2[size]; 100 | 101 | using namespace window_functions; 102 | 103 | triangle(window1, size, 0, size, params()); 104 | triangle(window2, size, begin, end, params()); 105 | 106 | for (int i = begin; i < end; i++) 107 | { 108 | if (window1[i] != window2[i - begin]) 109 | { 110 | double relative_error = exp(fabs(log(window1[i] / window2[i - begin]))); 111 | 112 | if (relative_error > 1.000000000001 || isnan(relative_error)) 113 | return false; 114 | } 115 | } 116 | 117 | return true; 118 | } 119 | 120 | void check_window(const char* wind, window_functions::window_generator f, const window_functions::params &p) 121 | { 122 | constexpr int size = 32768; 123 | double window[size]; 124 | 125 | f(window, size, 0, size, p); 126 | 127 | auto it = std::max_element(window, window + size); 128 | auto n = it - window; 129 | bool symmetry = true; 130 | 131 | for (int i = ((size >> 1) + 1); i < size; i++) 132 | { 133 | if (window[i] != window[size - i]) 134 | { 135 | symmetry = false; 136 | break; 137 | } 138 | } 139 | 140 | std::cout << "** test " << wind << " window\n"; 141 | std::cout << "element zero " << window[0] << "\n"; 142 | std::cout << "middle element " << window[size >> 1] << "\n"; 143 | std::cout << "max element " << *it << " [" << n << "]\n"; 144 | std::cout << "symmetry " << symmetry << "\n"; 145 | } 146 | 147 | int main(int argc, const char * argv[]) 148 | { 149 | constexpr int size = 32768; 150 | constexpr int iter = 1024; 151 | constexpr int sym_iter = 32768; 152 | double window[size]; 153 | 154 | rand_engine.seed(std::random_device()()); 155 | 156 | using namespace window_functions; 157 | 158 | params ep; 159 | params tp(0.1, 0.9); 160 | params typ(0.1); 161 | params p(0.5, 0.5); 162 | 163 | check_window("parzen", &parzen, ep); 164 | check_window("welch", &welch, ep); 165 | check_window("sine", &sine, ep); 166 | check_window("hann", &hann, ep); 167 | check_window("triangle", &triangle, ep); 168 | check_window("trapezoid", &trapezoid, tp); 169 | check_window("tukey", &tukey, typ); 170 | 171 | for (int i = 0; i < iter; i++) 172 | sine(window, size, 0, size, params()); 173 | 174 | Timer timer; 175 | 176 | timer.start(); 177 | for (int i = 0; i < iter; i++) 178 | cosine_2_term(window, size, 0, size, p); 179 | timer.stop(); 180 | timer.finish("Branch Speed Test"); 181 | 182 | timer.start(); 183 | for (int i = 0; i < iter; i++) 184 | hann(window, size, 0, size, params(0.2, 0.3)); 185 | timer.stop(); 186 | timer.finish("Non-branch Speed Test"); 187 | 188 | timer.relative("Window Speed Test"); 189 | 190 | for (int i = 0; i < sym_iter; i++) 191 | { 192 | if (!check_symmetry()) 193 | { 194 | std::cout << "Symmetry copying failed!\n"; 195 | break; 196 | } 197 | 198 | if (i == sym_iter - 1) 199 | std::cout << "Symmetry copying succeeded!\n"; 200 | } 201 | 202 | indexed_generator, hann> gen; 203 | 204 | gen(0, window, size, 0, size, params(4)); 205 | 206 | if (argc > 1) 207 | { 208 | HISSTools::OAudioFile file(argv[1], HISSTools::BaseAudioFile::kAudioFileWAVE, HISSTools::BaseAudioFile::kAudioFileFloat32, 1, 44100.0); 209 | 210 | if (file.isOpen()) 211 | file.writeChannel(window, size, 0); 212 | } 213 | return 0; 214 | } 215 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData/ 4 | 5 | ## Outmoded source tracking 6 | CVS/ 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1 12 | !default.mode1 13 | *.mode1v3 14 | !default.mode1v3 15 | *.mode2v3 16 | !default.mode2v3 17 | *.perspectivev3 18 | !default.perspectivev3 19 | xcuserdata/ 20 | _MaxSDK_/ 21 | 22 | ## Latex 23 | 24 | *.aux 25 | *.synctex.gz 26 | *.toc 27 | *.log 28 | *.out 29 | 30 | ## Binaries 31 | externals/ 32 | 33 | ## Other 34 | *.moved-aside 35 | *.xccheckout 36 | *.xcscmblueprint 37 | 38 | Icon? 39 | .DS_Stor* 40 | -------------------------------------------------------------------------------- /Allocator.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef ALLOCATOR_HPP 3 | #define ALLOCATOR_HPP 4 | 5 | #include 6 | 7 | #include "SIMDSupport.hpp" 8 | 9 | namespace impl 10 | { 11 | typedef void *(*allocate_function)(size_t); 12 | typedef void (*free_function)(void *); 13 | }; 14 | 15 | // A template for wrapping functions as an allocator 16 | 17 | template 18 | struct function_allocator 19 | { 20 | template 21 | T* allocate(size_t size) { return reinterpret_cast(alloc(size * sizeof(T))); } 22 | 23 | template 24 | void deallocate(T *ptr) { dealloc(ptr); } 25 | }; 26 | 27 | using malloc_allocator = function_allocator; 28 | 29 | // Aligned allocator 30 | 31 | struct aligned_allocator 32 | { 33 | template 34 | T* allocate(size_t size) { return allocate_aligned(size); } 35 | 36 | template 37 | void deallocate(T *ptr) { deallocate_aligned(ptr); } 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /AudioFile/BaseAudioFile.cpp: -------------------------------------------------------------------------------- 1 | #include "BaseAudioFile.h" 2 | 3 | namespace HISSTools 4 | { 5 | BaseAudioFile::BaseAudioFile() 6 | { 7 | close(); 8 | } 9 | 10 | BaseAudioFile::~BaseAudioFile() 11 | { 12 | } 13 | 14 | void BaseAudioFile::close() 15 | { 16 | setFileType(kAudioFileNone); 17 | BaseAudioFile::setPCMFormat(kAudioFileInt8); 18 | setHeaderEndianness(kAudioFileLittleEndian); 19 | setAudioEndianness(kAudioFileLittleEndian); 20 | setSamplingRate(0); 21 | setChannels(0); 22 | setFrames(0); 23 | setPCMOffset(0); 24 | clearErrorFlags(); 25 | } 26 | 27 | BaseAudioFile::FileType BaseAudioFile::getFileType() const 28 | { 29 | return mFileType; 30 | } 31 | 32 | BaseAudioFile::PCMFormat BaseAudioFile::getPCMFormat() const 33 | { 34 | return mPCMFormat; 35 | } 36 | 37 | BaseAudioFile::Endianness BaseAudioFile::getHeaderEndianness() const 38 | { 39 | return mHeaderEndianness; 40 | } 41 | 42 | BaseAudioFile::Endianness BaseAudioFile::getAudioEndianness() const 43 | { 44 | return mAudioEndianness; 45 | } 46 | 47 | double BaseAudioFile::getSamplingRate() const 48 | { 49 | return mSamplingRate; 50 | } 51 | 52 | uint16_t BaseAudioFile::getChannels() const 53 | { 54 | return mNumChannels; 55 | } 56 | 57 | BaseAudioFile::FrameCount BaseAudioFile::getFrames() const 58 | { 59 | return mNumFrames; 60 | } 61 | 62 | BaseAudioFile::ByteCount BaseAudioFile::getPCMOffset() const 63 | { 64 | return mPCMOffset; 65 | } 66 | 67 | uint16_t BaseAudioFile::getBitDepth() const 68 | { 69 | return findBitDepth(getPCMFormat()); 70 | } 71 | 72 | uint16_t BaseAudioFile::getByteDepth() const 73 | { 74 | return getBitDepth() / 8; 75 | } 76 | 77 | BaseAudioFile::ByteCount BaseAudioFile::getFrameByteCount() const 78 | { 79 | return getChannels() * getByteDepth(); 80 | } 81 | 82 | BaseAudioFile::NumberFormat BaseAudioFile::getNumberFormat() const 83 | { 84 | return findNumberFormat(getPCMFormat()); 85 | } 86 | 87 | int BaseAudioFile::getErrorFlags() const 88 | { 89 | return mErrorFlags; 90 | } 91 | 92 | std::string BaseAudioFile::getErrorString(Error error) 93 | { 94 | switch (error) 95 | { 96 | case ERR_MEM_COULD_NOT_ALLOCATE: 97 | return "mem could not allocate"; 98 | case ERR_FILE_ERROR: 99 | return "file error"; 100 | case ERR_FILE_COULDNT_OPEN: 101 | return "file couldn't open"; 102 | case ERR_FILE_BAD_FORMAT: 103 | return "file bad format"; 104 | case ERR_FILE_UNKNOWN_FORMAT: 105 | return "file unknown format"; 106 | case ERR_FILE_UNSUPPORTED_PCM_FORMAT: 107 | return "file unsupported pcm format"; 108 | case ERR_AIFC_WRONG_VERSION: 109 | return "aifc wrong version"; 110 | case ERR_AIFC_UNSUPPORTED_FORMAT: 111 | return "aifc unsupported format"; 112 | case ERR_WAVE_UNSUPPORTED_FORMAT: 113 | return "wave unsupported format"; 114 | case ERR_FILE_COULDNT_WRITE: 115 | return "file couldn't write"; 116 | default: 117 | return "no error"; 118 | } 119 | } 120 | std::vector BaseAudioFile::extractErrorsFromFlags(int flags) 121 | { 122 | std::vector ret; 123 | for (int i = 0; i != sizeof(int) * 4; i++) 124 | { 125 | if (flags & (1 << i)) 126 | ret.push_back(static_cast(i)); 127 | } 128 | return ret; 129 | } 130 | 131 | std::vector BaseAudioFile::getErrors() const 132 | { 133 | return extractErrorsFromFlags(getErrorFlags()); 134 | } 135 | 136 | bool BaseAudioFile::getIsError() const 137 | { 138 | return mErrorFlags != ERR_NONE; 139 | } 140 | 141 | void BaseAudioFile::clearErrorFlags() 142 | { 143 | setErrorFlags(ERR_NONE); 144 | } 145 | 146 | void BaseAudioFile::setFileType(FileType i) 147 | { 148 | mFileType = i; 149 | } 150 | 151 | void BaseAudioFile::setPCMFormat(PCMFormat i) 152 | { 153 | mPCMFormat = i; 154 | } 155 | 156 | void BaseAudioFile::setHeaderEndianness(Endianness i) 157 | { 158 | mHeaderEndianness = i; 159 | } 160 | 161 | void BaseAudioFile::setAudioEndianness(Endianness i) 162 | { 163 | mAudioEndianness = i; 164 | } 165 | 166 | void BaseAudioFile::setSamplingRate(double i) 167 | { 168 | mSamplingRate = i; 169 | } 170 | 171 | void BaseAudioFile::setChannels(uint16_t i) 172 | { 173 | mNumChannels = i; 174 | } 175 | 176 | void BaseAudioFile::setFrames(FrameCount i) 177 | { 178 | mNumFrames = i; 179 | } 180 | 181 | void BaseAudioFile::setPCMOffset(ByteCount i) 182 | { 183 | mPCMOffset = i; 184 | } 185 | 186 | void BaseAudioFile::setErrorFlags(int flags) 187 | { 188 | mErrorFlags = flags; 189 | } 190 | 191 | void BaseAudioFile::setErrorBit(Error error) 192 | { 193 | mErrorFlags |= error; 194 | } 195 | 196 | uint16_t BaseAudioFile::findBitDepth(PCMFormat i) 197 | { 198 | switch (i) 199 | { 200 | case kAudioFileInt8: 201 | return 8; 202 | case kAudioFileInt24: 203 | return 24; 204 | case kAudioFileInt32: 205 | return 32; 206 | case kAudioFileFloat32: 207 | return 32; 208 | case kAudioFileFloat64: 209 | return 64; 210 | case kAudioFileInt16: 211 | default: 212 | return 16; 213 | } 214 | } 215 | 216 | BaseAudioFile::NumberFormat BaseAudioFile::findNumberFormat(PCMFormat i) 217 | { 218 | switch (i) 219 | { 220 | case kAudioFileFloat32: 221 | case kAudioFileFloat64: 222 | return kAudioFileFloat; 223 | 224 | case kAudioFileInt8: 225 | case kAudioFileInt16: 226 | case kAudioFileInt24: 227 | case kAudioFileInt32: 228 | default: 229 | return kAudioFileInt; 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /AudioFile/BaseAudioFile.h: -------------------------------------------------------------------------------- 1 | #ifndef _HISSTOOLS_BASEAUDIOFILE_ 2 | #define _HISSTOOLS_BASEAUDIOFILE_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace HISSTools 10 | { 11 | class BaseAudioFile 12 | { 13 | 14 | public: 15 | 16 | typedef uint32_t FrameCount; 17 | typedef uintptr_t ByteCount; 18 | 19 | enum FileType 20 | { 21 | kAudioFileNone, 22 | kAudioFileAIFF, 23 | kAudioFileAIFC, 24 | kAudioFileWAVE 25 | }; 26 | enum PCMFormat 27 | { 28 | kAudioFileInt8, 29 | kAudioFileInt16, 30 | kAudioFileInt24, 31 | kAudioFileInt32, 32 | kAudioFileFloat32, 33 | kAudioFileFloat64 34 | }; 35 | enum Endianness 36 | { 37 | kAudioFileLittleEndian, 38 | kAudioFileBigEndian 39 | }; 40 | enum NumberFormat 41 | { 42 | kAudioFileInt, 43 | kAudioFileFloat 44 | }; 45 | 46 | enum Error 47 | { 48 | ERR_NONE = 0, 49 | 50 | ERR_MEM_COULD_NOT_ALLOCATE = 1 << 0, 51 | 52 | ERR_FILE_ERROR = 1 << 1, 53 | ERR_FILE_COULDNT_OPEN = 1 << 2, 54 | ERR_FILE_BAD_FORMAT = 1 << 3, 55 | ERR_FILE_UNKNOWN_FORMAT = 1 << 4, 56 | ERR_FILE_UNSUPPORTED_PCM_FORMAT = 1 << 5, 57 | 58 | ERR_AIFC_WRONG_VERSION = 1 << 6, 59 | ERR_AIFC_UNSUPPORTED_FORMAT = 1 << 7, 60 | 61 | ERR_WAVE_UNSUPPORTED_FORMAT = 1 << 8, 62 | 63 | ERR_FILE_COULDNT_WRITE = 1 << 9, 64 | }; 65 | 66 | enum AiffVersion 67 | { 68 | AIFC_CURRENT_SPECIFICATION = 0xA2805140 69 | }; 70 | 71 | BaseAudioFile(); 72 | virtual ~BaseAudioFile(); 73 | 74 | FileType getFileType() const; 75 | PCMFormat getPCMFormat() const; 76 | Endianness getHeaderEndianness() const; 77 | Endianness getAudioEndianness() const; 78 | double getSamplingRate() const; 79 | uint16_t getChannels() const; 80 | FrameCount getFrames() const; 81 | uint16_t getBitDepth() const; 82 | uint16_t getByteDepth() const; 83 | ByteCount getFrameByteCount() const; 84 | NumberFormat getNumberFormat() const; 85 | 86 | static std::string getErrorString(Error error); 87 | static std::vector extractErrorsFromFlags(int flags); 88 | std::vector getErrors() const; 89 | 90 | int getErrorFlags() const; 91 | bool getIsError() const; 92 | void clearErrorFlags(); 93 | 94 | static uint16_t findBitDepth(PCMFormat); 95 | static NumberFormat findNumberFormat(PCMFormat); 96 | 97 | virtual void close(); 98 | virtual bool isOpen() = 0; 99 | virtual void seek(FrameCount position) = 0; 100 | virtual FrameCount getPosition() = 0; 101 | 102 | protected: 103 | 104 | ByteCount getPCMOffset() const; 105 | 106 | void setFileType(FileType); 107 | void setPCMFormat(PCMFormat); 108 | void setHeaderEndianness(Endianness); 109 | void setAudioEndianness(Endianness); 110 | void setSamplingRate(double); 111 | void setChannels(uint16_t); 112 | void setFrames(FrameCount); 113 | void setPCMOffset(ByteCount); 114 | 115 | void setErrorFlags(int flags); 116 | void setErrorBit(Error error); 117 | 118 | private: 119 | FileType mFileType; 120 | PCMFormat mPCMFormat; 121 | Endianness mHeaderEndianness; 122 | Endianness mAudioEndianness; 123 | 124 | double mSamplingRate; 125 | uint16_t mNumChannels; 126 | FrameCount mNumFrames; 127 | size_t mPCMOffset; 128 | 129 | int mErrorFlags; 130 | }; 131 | } 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /AudioFile/IAudioFile.h: -------------------------------------------------------------------------------- 1 | #ifndef _HISSTOOLS_IAUDIOFILE_ 2 | #define _HISSTOOLS_IAUDIOFILE_ 3 | 4 | #include "BaseAudioFile.h" 5 | #include 6 | 7 | namespace HISSTools 8 | { 9 | // FIX - check types, errors and returns 10 | 11 | class IAudioFile : public BaseAudioFile 12 | { 13 | enum AiffTag 14 | { 15 | AIFC_TAG_UNKNOWN = 0x0, 16 | AIFC_TAG_VERSION = 0x1, 17 | AIFC_TAG_COMMON = 0x2, 18 | AIFC_TAG_AUDIO = 0x4 19 | }; 20 | enum AifcCompression 21 | { 22 | AIFC_COMPRESSION_UNKNOWN, 23 | AIFC_COMPRESSION_NONE, 24 | AIFC_COMPRESSION_LITTLE_ENDIAN, 25 | AIFC_COMPRESSION_FLOAT 26 | }; 27 | 28 | public: 29 | 30 | // Constructor and Destructor 31 | 32 | IAudioFile(const std::string& = std::string()); 33 | ~IAudioFile(); 34 | 35 | // File Open / Close 36 | 37 | void open(const std::string& i); 38 | void close(); 39 | bool isOpen(); 40 | 41 | // File Position 42 | 43 | void seek(FrameCount position = 0); 44 | FrameCount getPosition(); 45 | 46 | // File Reading 47 | 48 | void readRaw(void* output, FrameCount numFrames); 49 | 50 | void readInterleaved(double* output, FrameCount numFrames); 51 | void readInterleaved(float* output, FrameCount numFrames); 52 | 53 | void readChannel(double* output, FrameCount numFrames, uint16_t channel); 54 | void readChannel(float* output, FrameCount numFrames, uint16_t channel); 55 | 56 | private: 57 | 58 | // Internal File Handling 59 | 60 | bool readInternal(char* buffer, ByteCount numBytes); 61 | bool seekInternal(ByteCount position); 62 | bool advanceInternal(ByteCount offset); 63 | ByteCount positionInternal(); 64 | 65 | // Extracting Single Values 66 | 67 | uint64_t getU64(const char* bytes, Endianness fileEndianness) const; 68 | uint32_t getU32(const char* bytes, Endianness fileEndianness) const; 69 | uint32_t getU24(const char* bytes, Endianness fileEndianness) const; 70 | uint32_t getU16(const char* bytes, Endianness fileEndianness) const; 71 | 72 | // Conversion 73 | 74 | double extendedToDouble(const char* bytes) const; 75 | template void u32ToOutput(T* output, uint32_t value); 76 | template void u8ToOutput(T* output, uint8_t value); 77 | template void float32ToOutput(T* output, uint32_t value); 78 | template void float64ToOutput(T* output, uint64_t value); 79 | 80 | // Chunk Reading 81 | 82 | static bool matchTag(const char* a, const char* b); 83 | bool readChunkHeader(char* tag, uint32_t& chunkSize); 84 | bool findChunk(const char* searchTag, uint32_t& chunkSize); 85 | bool readChunk(char* data, uint32_t readSize, uint32_t chunkSize); 86 | 87 | // PCM Format Helpers 88 | 89 | static Error findPCMFormat(uint16_t, NumberFormat, PCMFormat&); 90 | void setPCMFormat(uint16_t bitDepth, NumberFormat format); 91 | 92 | // AIFF Helpers 93 | 94 | bool getAIFFChunkHeader(AiffTag& enumeratedTag, uint32_t& chunkSize); 95 | AifcCompression getAIFCCompression(const char* tag, uint16_t& bitDepth) const; 96 | 97 | // Parse Headers 98 | 99 | void parseHeader(); 100 | void parseAIFFHeader(const char* fileSubtype); 101 | void parseWaveHeader(const char* fileType); 102 | 103 | // Internal Typed Audio Read 104 | 105 | template 106 | void readAudio(T* output, FrameCount numFrames, int32_t channel = -1); 107 | 108 | // Data 109 | 110 | std::ifstream mFile; 111 | char *mBuffer; 112 | }; 113 | } 114 | 115 | #endif 116 | -------------------------------------------------------------------------------- /AudioFile/OAudioFile.h: -------------------------------------------------------------------------------- 1 | #ifndef _HISSTOOLS_OAUDIOFILE_ 2 | #define _HISSTOOLS_OAUDIOFILE_ 3 | 4 | #include "BaseAudioFile.h" 5 | #include 6 | 7 | namespace HISSTools 8 | { 9 | class OAudioFile : public BaseAudioFile 10 | { 11 | public: 12 | OAudioFile(); 13 | OAudioFile(const std::string&, FileType, PCMFormat, uint16_t channels, double sr); 14 | OAudioFile(const std::string&, FileType, PCMFormat, uint16_t channels, double sr, Endianness); 15 | 16 | ~OAudioFile(); 17 | 18 | void open(const std::string&, FileType, PCMFormat, uint16_t channels, double sr); 19 | void open(const std::string&, FileType, PCMFormat, uint16_t channels, double sr, Endianness); 20 | void close(); 21 | bool isOpen(); 22 | void seek(FrameCount position = 0); 23 | FrameCount getPosition(); 24 | 25 | void writeInterleaved(const double* input, FrameCount numFrames); 26 | void writeInterleaved(const float* input, FrameCount numFrames); 27 | 28 | void writeChannel(const double* input, FrameCount numFrames, uint16_t channel); 29 | void writeChannel(const float* input, FrameCount numFrames, uint16_t channel); 30 | void writeRaw(const char *input, FrameCount numFrames) { writePCMData(input, numFrames); } 31 | 32 | protected: 33 | 34 | ByteCount getHeaderSize() const; 35 | 36 | private: 37 | 38 | ByteCount positionInternal(); 39 | bool seekInternal(ByteCount position); 40 | bool seekRelativeInternal(ByteCount offset); 41 | bool writeInternal(const char* buffer, ByteCount bytes); 42 | 43 | bool putU64(uint64_t value, Endianness fileEndianness); 44 | bool putU32(uint32_t value, Endianness fileEndianness); 45 | bool putU24(uint32_t value, Endianness fileEndianness); 46 | bool putU16(uint32_t value, Endianness fileEndianness); 47 | bool putU08(uint32_t value); 48 | 49 | bool putPadByte(); 50 | 51 | bool putExtended(double); 52 | bool putPString(const char* string); 53 | 54 | bool putTag(const char* tag); 55 | bool putChunk(const char* tag, uint32_t size); 56 | 57 | void writeWaveHeader(); 58 | void writeAIFCHeader(); 59 | 60 | uint32_t inputToU32(double input, int32_t bitDepth); 61 | uint8_t inputToU8(double input); 62 | 63 | bool resize(FrameCount numFrames); 64 | bool updateHeader(); 65 | template void writeAudio(const T* input, FrameCount numFrames, int32_t channel = -1); 66 | bool writePCMData(const char* input, FrameCount numFrames); 67 | 68 | const char *getCompressionTag(); 69 | const char *getCompressionString(); 70 | 71 | // Data 72 | 73 | std::ofstream mFile; 74 | }; 75 | } 76 | 77 | #endif /* _HISSTOOLS_OAUDIOFILE_ */ 78 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/ConvolveErrors.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | enum ConvolveError 5 | { 6 | CONVOLVE_ERR_NONE = 0, 7 | CONVOLVE_ERR_IN_CHAN_OUT_OF_RANGE = 1, 8 | CONVOLVE_ERR_OUT_CHAN_OUT_OF_RANGE = 2, 9 | CONVOLVE_ERR_MEM_UNAVAILABLE = 3, 10 | CONVOLVE_ERR_MEM_ALLOC_TOO_SMALL = 4, 11 | CONVOLVE_ERR_TIME_IMPULSE_TOO_LONG = 5, 12 | CONVOLVE_ERR_TIME_LENGTH_OUT_OF_RANGE = 6, 13 | CONVOLVE_ERR_PARTITION_LENGTH_TOO_LARGE = 7, 14 | CONVOLVE_ERR_FFT_SIZE_MAX_TOO_SMALL = 8, 15 | CONVOLVE_ERR_FFT_SIZE_MAX_TOO_LARGE = 9, 16 | CONVOLVE_ERR_FFT_SIZE_MAX_NON_POWER_OF_TWO = 10, 17 | CONVOLVE_ERR_FFT_SIZE_OUT_OF_RANGE = 11, 18 | CONVOLVE_ERR_FFT_SIZE_NON_POWER_OF_TWO = 12, 19 | }; 20 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/ConvolveSIMD.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | template 5 | struct SIMDVector 6 | { 7 | static constexpr int size = vec_size; 8 | typedef T scalar_type; 9 | 10 | SIMDVector() {} 11 | SIMDVector(U a) : mVal(a) {} 12 | 13 | U mVal; 14 | }; 15 | 16 | #if defined __arm__ || defined __arm64__ 17 | 18 | #include 19 | 20 | struct ARMFloat : public SIMDVector 21 | { 22 | ARMFloat() {} 23 | ARMFloat(uint32x4_t a) : SIMDVector(a) {} 24 | ARMFloat(float a) : SIMDVector(vdupq_n_f32(a)) {} 25 | 26 | friend ARMFloat operator + (const ARMFloat& a, const ARMFloat& b) { return vaddq_f32(a.mVal, b.mVal); } 27 | friend ARMFloat operator - (const ARMFloat& a, const ARMFloat& b) { return vsubq_f32(a.mVal, b.mVal); } 28 | friend ARMFloat operator * (const ARMFloat& a, const ARMFloat& b) { return vmulq_f32(a.mVal, b.mVal); } 29 | 30 | ARMFloat operator += (const ARMFloat& a) 31 | { 32 | *this = *this + a; 33 | return *this; 34 | } 35 | 36 | static ARMFloat unaligned_load(const float* ptr) { return vld1q_f32(ptr); } 37 | 38 | void unaligned_store(float* ptr) 39 | { 40 | vst1q_f32(ptr, mVal); 41 | } 42 | 43 | float sum() 44 | { 45 | float values[4]; 46 | unaligned_store(values); 47 | return values[0] + values[1] + values[2] + values[3]; 48 | } 49 | }; 50 | 51 | typedef ARMFloat FloatVector; 52 | 53 | #else 54 | 55 | #ifdef __APPLE__ 56 | #include 57 | #else 58 | #include 59 | #include 60 | #endif 61 | 62 | struct SSEFloat : public SIMDVector 63 | { 64 | SSEFloat() {} 65 | SSEFloat(__m128 a) : SIMDVector(a) {} 66 | SSEFloat(float a) : SIMDVector(_mm_set1_ps(a)) {} 67 | 68 | friend SSEFloat operator + (const SSEFloat& a, const SSEFloat& b) { return _mm_add_ps(a.mVal, b.mVal); } 69 | friend SSEFloat operator - (const SSEFloat& a, const SSEFloat& b) { return _mm_sub_ps(a.mVal, b.mVal); } 70 | friend SSEFloat operator * (const SSEFloat& a, const SSEFloat& b) { return _mm_mul_ps(a.mVal, b.mVal); } 71 | 72 | SSEFloat operator += (const SSEFloat& a) 73 | { 74 | *this = *this + a; 75 | return *this; 76 | } 77 | 78 | static SSEFloat unaligned_load(const float* ptr) { return _mm_loadu_ps(ptr); } 79 | 80 | void unaligned_store(float* ptr) 81 | { 82 | _mm_storeu_ps(ptr, mVal); 83 | } 84 | 85 | float sum() 86 | { 87 | float values[4]; 88 | unaligned_store(values); 89 | return values[0] + values[1] + values[2] + values[3]; 90 | } 91 | }; 92 | 93 | typedef SSEFloat FloatVector; 94 | 95 | #endif 96 | 97 | #ifdef __APPLE__ 98 | #define ALIGNED_MALLOC malloc 99 | #define ALIGNED_FREE free 100 | #elif defined _WIN32 101 | #include 102 | #define ALIGNED_MALLOC(x) _aligned_malloc(x, 16) 103 | #define ALIGNED_FREE(x) _aligned_free(x) 104 | #else 105 | #define ALIGNED_MALLOC(x) aligned_alloc(16, x); 106 | #define ALIGNED_FREE free 107 | #endif 108 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/Convolver.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Convolver.h" 3 | #include "ConvolveSIMD.h" 4 | 5 | HISSTools::Convolver::Convolver(uint32_t numIns, uint32_t numOuts, LatencyMode latency) 6 | : mTemporaryMemory(0) 7 | { 8 | numIns = numIns < 1 ? 1 : numIns; 9 | 10 | mN2M = true; 11 | mNumIns = numIns; 12 | mNumOuts = numOuts; 13 | 14 | for (uint32_t i = 0; i < numIns; i++) 15 | mInTemps.push_back(nullptr); 16 | 17 | for (uint32_t i = 0; i < numOuts; i++) 18 | mConvolvers.push_back(new NToMonoConvolve(numIns, 16384, latency)); 19 | 20 | mTemp1 = nullptr; 21 | mTemp2 = nullptr; 22 | } 23 | 24 | HISSTools::Convolver::Convolver(uint32_t numIO, LatencyMode latency) 25 | : mTemporaryMemory(0) 26 | { 27 | numIO = numIO < 1 ? 1 : numIO; 28 | 29 | mN2M = false; 30 | mNumIns = numIO; 31 | mNumOuts = numIO; 32 | 33 | for (uint32_t i = 0; i < numIO; i++) 34 | { 35 | mConvolvers.push_back(new NToMonoConvolve(1, 16384, latency)); 36 | mInTemps.push_back(nullptr); 37 | } 38 | 39 | mTemp1 = nullptr; 40 | mTemp2 = nullptr; 41 | } 42 | 43 | HISSTools::Convolver::~Convolver() throw() 44 | { 45 | for (uint32_t i = 0; i < mNumOuts; i++) 46 | delete mConvolvers[i]; 47 | } 48 | 49 | // Clear IRs 50 | 51 | void HISSTools::Convolver::clear(bool resize) 52 | { 53 | if (mN2M) 54 | { 55 | for (uint32_t i = 0; i < mNumOuts; i++) 56 | for (uint32_t j = 0; j < mNumIns; j++) 57 | clear(j, i, resize); 58 | } 59 | else 60 | { 61 | for (uint32_t i = 0; i < mNumOuts; i++) 62 | clear(i, i, resize); 63 | } 64 | } 65 | 66 | void HISSTools::Convolver::clear(uint32_t inChan, uint32_t outChan, bool resize) 67 | { 68 | set(inChan, outChan, (float *)nullptr, 0, resize); 69 | } 70 | 71 | // DSP Engine Reset 72 | 73 | void HISSTools::Convolver::reset() 74 | { 75 | if (mN2M) 76 | { 77 | for (uint32_t i = 0; i < mNumOuts; i++) 78 | for (uint32_t j = 0; j < mNumIns; j++) 79 | reset(j, i); 80 | } 81 | else 82 | { 83 | for (uint32_t i = 0; i < mNumOuts; i++) 84 | reset(i, i); 85 | } 86 | } 87 | 88 | ConvolveError HISSTools::Convolver::reset(uint32_t inChan, uint32_t outChan) 89 | { 90 | // For Parallel operation you must pass the same in/out channel 91 | 92 | if (!mN2M) inChan -= outChan; 93 | 94 | if (outChan < mNumOuts) 95 | return mConvolvers[outChan]->reset(inChan); 96 | else 97 | return CONVOLVE_ERR_OUT_CHAN_OUT_OF_RANGE; 98 | } 99 | 100 | // Resize and set IR 101 | 102 | ConvolveError HISSTools::Convolver::resize(uint32_t inChan, uint32_t outChan, uintptr_t length) 103 | { 104 | // For Parallel operation you must pass the same in/out channel 105 | 106 | if (!mN2M) inChan -= outChan; 107 | 108 | if (outChan < mNumOuts) 109 | return mConvolvers[outChan]->resize(inChan, length); 110 | else 111 | return CONVOLVE_ERR_IN_CHAN_OUT_OF_RANGE; 112 | } 113 | 114 | ConvolveError HISSTools::Convolver::set(uint32_t inChan, uint32_t outChan, const float* input, uintptr_t length, bool resize) 115 | { 116 | // For Parallel operation you must pass the same in/out channel 117 | 118 | if (!mN2M) inChan -= outChan; 119 | 120 | if (outChan < mNumOuts) 121 | return mConvolvers[outChan]->set(inChan, input, length, resize); 122 | else 123 | return CONVOLVE_ERR_OUT_CHAN_OUT_OF_RANGE; 124 | } 125 | 126 | ConvolveError HISSTools::Convolver::set(uint32_t inChan, uint32_t outChan, const double* input, uintptr_t impulseLength, bool resize) 127 | { 128 | std::vector inputFloat(impulseLength); 129 | 130 | for (unsigned long i = 0; i < impulseLength; i++) 131 | inputFloat[i] = static_cast(input[i]); 132 | 133 | return set(inChan, outChan, inputFloat.data(), impulseLength, resize); 134 | } 135 | 136 | // DSP 137 | 138 | void HISSTools::Convolver::process(const float * const* ins, float** outs, size_t numIns, size_t numOuts, size_t numSamples) 139 | { 140 | auto memPointer = mTemporaryMemory.grow((mNumIns + 2) * numSamples); 141 | tempSetup(memPointer.get(), memPointer.getSize()); 142 | 143 | if (!memPointer.get()) 144 | numIns = numOuts = 0; 145 | 146 | SIMDSettings settings; 147 | 148 | for (size_t i = 0; i < numOuts; i++) 149 | { 150 | const float *n2nIn[1] = { ins[i] }; 151 | 152 | mConvolvers[i]->process(mN2M ? ins : n2nIn, outs[i], mTemp1, numSamples, mN2M ? numIns : 1); 153 | } 154 | } 155 | 156 | void HISSTools::Convolver::process(const double * const* ins, double** outs, size_t numIns, size_t numOuts, size_t numSamples) 157 | { 158 | auto memPointer = mTemporaryMemory.grow((mNumIns + 2) * numSamples); 159 | tempSetup(memPointer.get(), memPointer.getSize()); 160 | 161 | if (!memPointer.get()) 162 | numIns = numOuts = 0; 163 | 164 | numIns = numIns > mNumIns ? mNumIns : numIns; 165 | numOuts = numOuts > mNumOuts ? mNumOuts : numOuts; 166 | 167 | for (uintptr_t i = 0; i < numIns; i++) 168 | for (uintptr_t j = 0; j < numSamples; j++) 169 | mInTemps[i][j] = static_cast(ins[i][j]); 170 | 171 | SIMDSettings settings; 172 | 173 | for (uintptr_t i = 0; i < numOuts; i++) 174 | { 175 | const float *n2nIn[1] = { mInTemps[i] }; 176 | const float * const *inTemps = mInTemps.data(); 177 | 178 | mConvolvers[i]->process(mN2M ? inTemps : n2nIn, mTemp2, mTemp1, numSamples, mN2M ? numIns : 1); 179 | 180 | for (uintptr_t j = 0; j < numSamples; j++) 181 | outs[i][j] = mTemp2[j]; 182 | } 183 | } 184 | 185 | void HISSTools::Convolver::tempSetup(float* memPointer, uintptr_t maxFrameSize) 186 | { 187 | maxFrameSize /= (mNumIns + 2); 188 | mInTemps[0] = memPointer; 189 | 190 | for (uint32_t i = 1; i < mNumIns; i++) 191 | mInTemps[i] = mInTemps[0] + (i * maxFrameSize); 192 | 193 | mTemp1 = mInTemps[mNumIns - 1] + maxFrameSize; 194 | mTemp2 = mTemp1 + maxFrameSize; 195 | } 196 | 197 | HISSTools::Convolver::SIMDSettings::SIMDSettings() 198 | { 199 | #if defined(__i386__) || defined(__x86_64__) 200 | mOldMXCSR = _mm_getcsr(); 201 | _mm_setcsr(mOldMXCSR | 0x8040); 202 | #endif 203 | } 204 | 205 | HISSTools::Convolver::SIMDSettings::~SIMDSettings() 206 | { 207 | #if defined(__i386__) || defined(__x86_64__) 208 | _mm_setcsr(mOldMXCSR); 209 | #endif 210 | } 211 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/Convolver.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "MemorySwap.h" 5 | #include "NToMonoConvolve.h" 6 | #include "ConvolveErrors.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace HISSTools 12 | { 13 | class Convolver 14 | { 15 | struct SIMDSettings 16 | { 17 | SIMDSettings(); 18 | ~SIMDSettings(); 19 | 20 | unsigned int mOldMXCSR; 21 | }; 22 | 23 | public: 24 | 25 | Convolver(uint32_t numIns, uint32_t numOuts, LatencyMode latency); 26 | Convolver(uint32_t numIO, LatencyMode latency); 27 | 28 | virtual ~Convolver() throw(); 29 | 30 | // Clear IRs 31 | 32 | void clear(bool resize); 33 | void clear(uint32_t inChan, uint32_t outChan, bool resize); 34 | 35 | // DSP Engine Reset 36 | 37 | void reset(); 38 | ConvolveError reset(uint32_t inChan, uint32_t outChan); 39 | 40 | // Resize and set IR 41 | 42 | ConvolveError resize(uint32_t inChan, uint32_t outChan, uintptr_t impulseLength); 43 | 44 | ConvolveError set(uint32_t inChan, uint32_t outChan, const float* input, uintptr_t length, bool resize); 45 | ConvolveError set(uint32_t inChan, uint32_t outChan, const double* input, uintptr_t length, bool resize); 46 | 47 | // DSP 48 | 49 | void process(const double * const* ins, double** outs, size_t numIns, size_t numOuts, size_t numSamples); 50 | void process(const float * const* ins, float** outs, size_t numIns, size_t numOuts, size_t numSamples); 51 | 52 | private: 53 | 54 | void tempSetup(float* memPointer, uintptr_t maxFrameSize); 55 | 56 | // Data 57 | 58 | uint32_t mNumIns; 59 | uint32_t mNumOuts; 60 | bool mN2M; 61 | 62 | std::vector mInTemps; 63 | float* mTemp1; 64 | float* mTemp2; 65 | 66 | MemorySwap mTemporaryMemory; 67 | 68 | std::vector mConvolvers; 69 | }; 70 | } 71 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/MemorySwap.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../ThreadLocks.hpp" 10 | 11 | #ifdef _WIN32 12 | #include 13 | #endif 14 | 15 | // FIX - locks around memory assignment - can be made more efficient by avoiding this at which point spinlocks will be justified.... 16 | // Follow the HISSTools C++ design for this.... use separate freeing locks so the memory is always freed in the assignment thread 17 | // All memory assignments are aligned in order that the memory is suitable for vector ops etc. 18 | 19 | template 20 | class MemorySwap 21 | { 22 | 23 | public: 24 | 25 | // Alloc and free routine prototypes 26 | 27 | typedef std::function AllocFunc; 28 | typedef std::function FreeFunc; 29 | 30 | class Ptr 31 | { 32 | friend MemorySwap; 33 | 34 | public: 35 | 36 | Ptr(Ptr&& p) 37 | : mOwner(p.mOwner), mPtr(p.mPtr), mSize(p.mSize) 38 | { 39 | p.mOwner = nullptr; 40 | p.mPtr = nullptr; 41 | p.mSize = 0; 42 | } 43 | 44 | ~Ptr() { clear(); } 45 | 46 | void clear() 47 | { 48 | if (mOwner) mOwner->unlock(); 49 | mOwner = nullptr; 50 | mPtr = nullptr; 51 | mSize = 0; 52 | } 53 | 54 | void swap(T *ptr, uintptr_t size) 55 | { 56 | update(&MemorySwap::set, ptr, size, nullptr); 57 | } 58 | 59 | void grow(uintptr_t size) 60 | { 61 | grow(&allocate, &deallocate, size); 62 | } 63 | 64 | void equal(uintptr_t size) 65 | { 66 | equal(&allocate, &deallocate, size); 67 | } 68 | 69 | void grow(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size) 70 | { 71 | updateAllocateIf(allocFunction, freeFunction, size, std::greater()); 72 | } 73 | 74 | void equal(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size) 75 | { 76 | updateAllocateIf(allocFunction, freeFunction, size, std::not_equal_to()); 77 | } 78 | 79 | T *get() { return mPtr; } 80 | uintptr_t getSize() { return mSize; } 81 | 82 | private: 83 | 84 | Ptr() 85 | : mOwner(nullptr), mPtr(nullptr), mSize(0) 86 | {} 87 | 88 | Ptr(MemorySwap *owner) 89 | : mOwner(owner), mPtr(mOwner ? mOwner->mPtr : nullptr), mSize(mOwner ? mOwner->mSize : 0) 90 | {} 91 | 92 | Ptr(const Ptr& p) = delete; 93 | Ptr operator = (const Ptr& p) = delete; 94 | 95 | template 96 | void updateAllocateIf(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size, Op op) 97 | { 98 | update(&MemorySwap::allocateIfLockHeld, allocFunction, freeFunction, size, op); 99 | } 100 | 101 | template 102 | void update(Op op, Args...args) 103 | { 104 | if (mOwner) 105 | { 106 | (mOwner->*op)(args...); 107 | mPtr = mOwner->mPtr; 108 | mSize = mOwner->mSize; 109 | } 110 | } 111 | 112 | MemorySwap *mOwner; 113 | T *mPtr; 114 | uintptr_t mSize; 115 | }; 116 | 117 | // Constructor (standard allocation) 118 | 119 | MemorySwap(uintptr_t size) 120 | : mPtr(nullptr), mSize(0), mFreeFunction(nullptr) 121 | { 122 | if (size) 123 | set(allocate(size), size, &deallocate); 124 | } 125 | 126 | // Constructor (custom allocation) 127 | 128 | MemorySwap(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size) 129 | : mPtr(nullptr), mSize(0), mFreeFunction(nullptr) 130 | { 131 | if (size) 132 | set(allocFunction(size), size, freeFunction); 133 | } 134 | 135 | ~MemorySwap() 136 | { 137 | clear(); 138 | } 139 | 140 | MemorySwap(const MemorySwap&) = delete; 141 | MemorySwap& operator = (const MemorySwap&) = delete; 142 | 143 | MemorySwap(MemorySwap&& obj) 144 | : mPtr(nullptr), mSize(0), mFreeFunction(nullptr) 145 | { 146 | *this = std::move(obj); 147 | obj.mPtr = nullptr; 148 | obj.mFreeFunction = nullptr; 149 | } 150 | 151 | MemorySwap& operator = (MemorySwap&& obj) 152 | { 153 | clear(); 154 | obj.lock(); 155 | mPtr = obj.mPtr; 156 | mSize = obj.mSize; 157 | mFreeFunction = obj.mFreeFunction; 158 | obj.mPtr = nullptr; 159 | obj.mFreeFunction = nullptr; 160 | obj.unlock(); 161 | 162 | return *this; 163 | } 164 | 165 | // frees the memory immediately 166 | 167 | void clear() 168 | { 169 | swap(nullptr, 0); 170 | } 171 | 172 | // lock to get access to the memory struct and safely return the pointer 173 | 174 | Ptr access() 175 | { 176 | lock(); 177 | return Ptr(this); 178 | } 179 | 180 | // This non-blocking routine will attempt to get the pointer but fail if the pointer is being accessed in another thread 181 | 182 | Ptr attempt() 183 | { 184 | return tryLock() ? Ptr(this) : Ptr(); 185 | } 186 | 187 | Ptr swap(T *ptr, uintptr_t size) 188 | { 189 | lock(); 190 | set(ptr, size, nullptr); 191 | return Ptr(this); 192 | } 193 | 194 | Ptr grow(uintptr_t size) 195 | { 196 | return grow(&allocate, &deallocate, size); 197 | } 198 | 199 | Ptr equal(uintptr_t size) 200 | { 201 | return equal(&allocate, &deallocate, size); 202 | } 203 | 204 | Ptr grow(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size) 205 | { 206 | return allocateIf(allocFunction, freeFunction, size, std::greater()); 207 | } 208 | 209 | Ptr equal(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size) 210 | { 211 | return allocateIf(allocFunction, freeFunction, size, std::not_equal_to()); 212 | } 213 | 214 | private: 215 | 216 | template 217 | Ptr allocateIf(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size, Op op) 218 | { 219 | lock(); 220 | allocateIfLockHeld(allocFunction, freeFunction, size, op); 221 | return Ptr(this); 222 | } 223 | 224 | template 225 | void allocateIfLockHeld(AllocFunc allocFunction, FreeFunc freeFunction, uintptr_t size, Op op) 226 | { 227 | if (op(size, mSize)) 228 | set(allocFunction(size), size, freeFunction); 229 | } 230 | 231 | void set(T *ptr, uintptr_t size, FreeFunc freeFunction) 232 | { 233 | if (mFreeFunction) 234 | mFreeFunction(mPtr); 235 | 236 | mPtr = ptr; 237 | mSize = ptr ? size : 0; 238 | mFreeFunction = freeFunction; 239 | } 240 | 241 | bool tryLock() 242 | { 243 | return mLock.attempt(); 244 | } 245 | 246 | void lock() 247 | { 248 | mLock.acquire(); 249 | } 250 | 251 | void unlock() 252 | { 253 | mLock.release(); 254 | } 255 | 256 | #ifdef _WIN32 257 | static T* allocate(size_t size) 258 | { 259 | return static_cast(_aligned_malloc(size * sizeof(T), 16)); 260 | } 261 | 262 | static void deallocate(T *ptr) 263 | { 264 | _aligned_free(ptr); 265 | } 266 | #else 267 | #ifdef __APPLE__ 268 | static T* allocate(size_t size) 269 | { 270 | return reinterpret_cast(malloc(size * sizeof(T))); 271 | } 272 | #else 273 | static T* allocate(size_t size) 274 | { 275 | return reinterpret_cast(aligned_alloc(16, size * sizeof(T))); 276 | } 277 | #endif 278 | 279 | static void deallocate(T *ptr) 280 | { 281 | free(ptr); 282 | } 283 | #endif 284 | 285 | thread_lock mLock; 286 | 287 | T *mPtr; 288 | uintptr_t mSize; 289 | FreeFunc mFreeFunction; 290 | }; 291 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/MonoConvolve.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "MonoConvolve.h" 3 | #include "ConvolveSIMD.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | // Free Utility 10 | 11 | void largeFree(HISSTools::PartitionedConvolve *largePartition) 12 | { 13 | delete largePartition; 14 | } 15 | 16 | // Standard Constructor 17 | 18 | HISSTools::MonoConvolve::MonoConvolve(uintptr_t maxLength, LatencyMode latency) 19 | : mAllocator(nullptr) 20 | , mPart4(0) 21 | , mLength(0) 22 | , mPart4ResetOffset(0) 23 | , mReset(false) 24 | , mRandGenerator(std::random_device()()) 25 | { 26 | switch (latency) 27 | { 28 | case kLatencyZero: setPartitions(maxLength, true, 256, 1024, 4096, 16384); break; 29 | case kLatencyShort: setPartitions(maxLength, false, 256, 1024, 4096, 16384); break; 30 | case kLatencyMedium: setPartitions(maxLength, false, 1024, 4096, 16384); break; 31 | } 32 | } 33 | 34 | // Constructor (custom partitioning) 35 | 36 | HISSTools::MonoConvolve::MonoConvolve(uintptr_t maxLength, bool zeroLatency, uint32_t A, uint32_t B, uint32_t C, uint32_t D) 37 | : mAllocator(nullptr) 38 | , mPart4(0) 39 | , mLength(0) 40 | , mPart4ResetOffset(0) 41 | , mReset(false) 42 | , mRandGenerator(std::random_device()()) 43 | { 44 | setPartitions(maxLength, zeroLatency, A, B, C, D); 45 | } 46 | 47 | // Move Constructor 48 | 49 | HISSTools::MonoConvolve::MonoConvolve(MonoConvolve&& obj) 50 | : mAllocator(obj.mAllocator) 51 | , mSizes(std::move(obj.mSizes)) 52 | , mTime1(std::move(obj.mTime1)) 53 | , mPart1(std::move(obj.mPart1)) 54 | , mPart2(std::move(obj.mPart2)) 55 | , mPart3(std::move(obj.mPart3)) 56 | , mPart4(std::move(obj.mPart4)) 57 | , mLength(obj.mLength) 58 | , mPart4ResetOffset(obj.mPart4ResetOffset) 59 | , mReset(true) 60 | {} 61 | 62 | // Move Assignment 63 | 64 | HISSTools::MonoConvolve& HISSTools::MonoConvolve::operator = (MonoConvolve&& obj) 65 | { 66 | mAllocator = obj.mAllocator; 67 | mSizes = std::move(obj.mSizes); 68 | mTime1 = std::move(obj.mTime1); 69 | mPart1 = std::move(obj.mPart1); 70 | mPart2 = std::move(obj.mPart2); 71 | mPart3 = std::move(obj.mPart3); 72 | mPart4 = std::move(obj.mPart4); 73 | mLength = obj.mLength; 74 | mPart4ResetOffset = obj.mPart4ResetOffset; 75 | mReset = true; 76 | 77 | return *this; 78 | } 79 | 80 | void HISSTools::MonoConvolve::setResetOffset(intptr_t offset) 81 | { 82 | PartPtr part4 = mPart4.access(); 83 | 84 | setResetOffset(part4, offset); 85 | } 86 | 87 | void HISSTools::MonoConvolve::setResetOffset(PartPtr& part4, intptr_t offset) 88 | { 89 | if (offset < 0) 90 | offset = mRandDistribution(mRandGenerator); 91 | 92 | if (mPart1) mPart1.get()->setResetOffset(offset + (mSizes[numSizes() - 3] >> 3)); 93 | if (mPart2) mPart2.get()->setResetOffset(offset + (mSizes[numSizes() - 2] >> 3)); 94 | if (mPart3) mPart3.get()->setResetOffset(offset + (mSizes[numSizes() - 1] >> 3)); 95 | 96 | if (part4.get()) part4.get()->setResetOffset(offset); 97 | 98 | mPart4ResetOffset = offset; 99 | } 100 | 101 | ConvolveError HISSTools::MonoConvolve::resize(uintptr_t length) 102 | { 103 | mLength = 0; 104 | PartPtr part4 = mPart4.equal(mAllocator, largeFree, length); 105 | 106 | if (part4.get()) 107 | part4.get()->setResetOffset(mPart4ResetOffset); 108 | 109 | return part4.getSize() == length ? CONVOLVE_ERR_NONE : CONVOLVE_ERR_MEM_UNAVAILABLE; 110 | } 111 | 112 | template 113 | void setPart(T *obj, const float *input, uintptr_t length) 114 | { 115 | if (obj) obj->set(input, length); 116 | } 117 | 118 | ConvolveError HISSTools::MonoConvolve::set(const float *input, uintptr_t length, bool requestResize) 119 | { 120 | // Lock or resize first to ensure that audio finishes processing before we replace 121 | 122 | mLength = 0; 123 | PartPtr part4 = requestResize ? mPart4.equal(mAllocator, largeFree, length) : mPart4.access(); 124 | 125 | if (part4.get()) 126 | { 127 | setPart(mTime1.get(), input, length); 128 | setPart(mPart1.get(), input, length); 129 | setPart(mPart2.get(), input, length); 130 | setPart(mPart3.get(), input, length); 131 | setPart(part4.get(), input, length); 132 | 133 | part4.get()->setResetOffset(mPart4ResetOffset); 134 | 135 | mLength = length; 136 | reset(); 137 | } 138 | 139 | return (length && !part4.get()) ? CONVOLVE_ERR_MEM_UNAVAILABLE : (length > part4.getSize()) ? CONVOLVE_ERR_MEM_ALLOC_TOO_SMALL : CONVOLVE_ERR_NONE; 140 | } 141 | 142 | template 143 | void resetPart(T *obj) 144 | { 145 | if (obj) obj->reset(); 146 | } 147 | 148 | ConvolveError HISSTools::MonoConvolve::reset() 149 | { 150 | mReset = true; 151 | return CONVOLVE_ERR_NONE; 152 | } 153 | 154 | template 155 | bool isUnaligned(const T* ptr) 156 | { 157 | return reinterpret_cast(ptr) % 16; 158 | } 159 | 160 | template 161 | void sum(T *temp, T *out, uintptr_t numItems) 162 | { 163 | for (uintptr_t i = 0; i < numItems; i++, out++, temp++) 164 | *out = *out + *temp; 165 | } 166 | 167 | template 168 | void processAndSum(T *obj, const float *in, float *temp, float *out, uintptr_t numSamples, bool accumulate) 169 | { 170 | if (obj && obj->process(in, accumulate ? temp : out, numSamples) && accumulate) 171 | { 172 | if ((numSamples % 4) || isUnaligned(out) || isUnaligned(temp)) 173 | sum(temp, out, numSamples); 174 | else 175 | sum(reinterpret_cast(temp), reinterpret_cast(out), numSamples / FloatVector::size); 176 | } 177 | } 178 | 179 | void HISSTools::MonoConvolve::process(const float *in, float *temp, float *out, uintptr_t numSamples, bool accumulate) 180 | { 181 | PartPtr part4 = mPart4.attempt(); 182 | 183 | if (mLength && mLength <= part4.getSize()) 184 | { 185 | if (mReset) 186 | { 187 | resetPart(mTime1.get()); 188 | resetPart(mPart1.get()); 189 | resetPart(mPart2.get()); 190 | resetPart(mPart3.get()); 191 | resetPart(part4.get()); 192 | mReset = false; 193 | } 194 | 195 | processAndSum(mTime1.get(), in, temp, out, numSamples, accumulate); 196 | processAndSum(mPart1.get(), in, temp, out, numSamples, accumulate || mTime1); 197 | processAndSum(mPart2.get(), in, temp, out, numSamples, accumulate || mPart1); 198 | processAndSum(mPart3.get(), in, temp, out, numSamples, accumulate || mPart2); 199 | processAndSum(part4.get(), in, temp, out, numSamples, accumulate || mPart3); 200 | } 201 | } 202 | 203 | void HISSTools::MonoConvolve::setPartitions(uintptr_t maxLength, bool zeroLatency, uint32_t A, uint32_t B, uint32_t C, uint32_t D) 204 | { 205 | // Utilities 206 | 207 | auto checkAndStoreFFTSize = [this](int size, int prev) 208 | { 209 | if ((size >= (1 << 5)) && (size <= (1 << 20)) && size > prev) 210 | mSizes.push_back(size); 211 | else if (size) 212 | throw std::runtime_error("invalid FFT size or order"); 213 | }; 214 | 215 | auto createPart = [](PartUniquePtr& obj, uint32_t& offset, uint32_t size, uint32_t next) 216 | { 217 | obj.reset(new PartitionedConvolve(size, (next - size) >> 1, offset, (next - size) >> 1)); 218 | offset += (next - size) >> 1; 219 | }; 220 | 221 | // Sanity checks 222 | 223 | checkAndStoreFFTSize(A, 0); 224 | checkAndStoreFFTSize(B, A); 225 | checkAndStoreFFTSize(C, B); 226 | checkAndStoreFFTSize(D, C); 227 | 228 | if (!numSizes()) 229 | throw std::runtime_error("no valid FFT sizes given"); 230 | 231 | // Lock to ensure we have exclusive access 232 | 233 | PartPtr part4 = mPart4.access(); 234 | 235 | uint32_t offset = zeroLatency ? mSizes[0] >> 1 : 0; 236 | uint32_t largestSize = mSizes[numSizes() - 1]; 237 | 238 | // Allocate paritions in unique pointers 239 | 240 | if (zeroLatency) mTime1.reset(new TimeDomainConvolve(0, mSizes[0] >> 1)); 241 | if (numSizes() == 4) createPart(mPart1, offset, mSizes[0], mSizes[1]); 242 | if (numSizes() > 2) createPart(mPart2, offset, mSizes[numSizes() - 3], mSizes[numSizes() - 2]); 243 | if (numSizes() > 1) createPart(mPart3, offset, mSizes[numSizes() - 2], mSizes[numSizes() - 1]); 244 | 245 | // Allocate the final resizeable partition 246 | 247 | mAllocator = [offset, largestSize](uintptr_t size) 248 | { 249 | return new HISSTools::PartitionedConvolve(largestSize, std::max(size, uintptr_t(largestSize)) - offset, offset, 0); 250 | }; 251 | 252 | part4.equal(mAllocator, largeFree, maxLength); 253 | 254 | // Set offsets 255 | 256 | mRandDistribution = std::uniform_int_distribution(0, (mSizes.back() >> 1) - 1); 257 | setResetOffset(part4); 258 | } 259 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/MonoConvolve.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "PartitionedConvolve.h" 5 | #include "TimeDomainConvolve.h" 6 | #include "ConvolveErrors.h" 7 | #include "MemorySwap.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | enum LatencyMode 15 | { 16 | kLatencyZero, 17 | kLatencyShort, 18 | kLatencyMedium, 19 | } ; 20 | 21 | namespace HISSTools 22 | { 23 | class MonoConvolve 24 | { 25 | typedef MemorySwap::Ptr PartPtr; 26 | typedef std::unique_ptr PartUniquePtr; 27 | 28 | public: 29 | 30 | MonoConvolve(uintptr_t maxLength, LatencyMode latency); 31 | MonoConvolve(uintptr_t maxLength, bool zeroLatency, uint32_t A, uint32_t B = 0, uint32_t C = 0, uint32_t D = 0); 32 | 33 | // Moveable but not copyable 34 | 35 | MonoConvolve(MonoConvolve& obj) = delete; 36 | MonoConvolve& operator = (MonoConvolve& obj) = delete; 37 | MonoConvolve(MonoConvolve&& obj); 38 | MonoConvolve& operator = (MonoConvolve&& obj); 39 | 40 | void setResetOffset(intptr_t offset = -1); 41 | 42 | ConvolveError resize(uintptr_t length); 43 | ConvolveError set(const float *input, uintptr_t length, bool requestResize); 44 | ConvolveError reset(); 45 | 46 | void process(const float *in, float *temp, float *out, uintptr_t numSamples, bool accumulate = false); 47 | 48 | void setPartitions(uintptr_t maxLength, bool zeroLatency, uint32_t A, uint32_t B = 0, uint32_t C = 0, uint32_t D = 0); 49 | 50 | private: 51 | 52 | void setResetOffset(PartPtr &part4, intptr_t offset = -1); 53 | 54 | size_t numSizes() { return mSizes.size(); } 55 | 56 | MemorySwap::AllocFunc mAllocator; 57 | 58 | std::vector mSizes; 59 | 60 | std::unique_ptr mTime1; 61 | std::unique_ptr mPart1; 62 | std::unique_ptr mPart2; 63 | std::unique_ptr mPart3; 64 | 65 | MemorySwap mPart4; 66 | 67 | uintptr_t mLength; 68 | 69 | intptr_t mPart4ResetOffset; 70 | bool mReset; 71 | 72 | // Random Number Generation 73 | 74 | std::default_random_engine mRandGenerator; 75 | std::uniform_int_distribution mRandDistribution; 76 | }; 77 | } 78 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/NToMonoConvolve.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "NToMonoConvolve.h" 3 | 4 | HISSTools::NToMonoConvolve::NToMonoConvolve(uint32_t inChans, uintptr_t maxLength, LatencyMode latency) 5 | : mNumInChans(inChans) 6 | { 7 | for (uint32_t i = 0; i < mNumInChans; i++) 8 | mConvolvers.emplace_back(maxLength, latency); 9 | } 10 | 11 | template 12 | ConvolveError HISSTools::NToMonoConvolve::doChannel(Method method, uint32_t inChan, Args...args) 13 | { 14 | if (inChan < mNumInChans) 15 | return (mConvolvers[inChan].*method)(args...); 16 | else 17 | return CONVOLVE_ERR_IN_CHAN_OUT_OF_RANGE; 18 | } 19 | 20 | ConvolveError HISSTools::NToMonoConvolve::resize(uint32_t inChan, uintptr_t impulse_length) 21 | { 22 | return doChannel(&MonoConvolve::resize, inChan, impulse_length); 23 | } 24 | 25 | ConvolveError HISSTools::NToMonoConvolve::set(uint32_t inChan, const float *input, uintptr_t impulse_length, bool resize) 26 | { 27 | return doChannel(&MonoConvolve::set, inChan, input, impulse_length, resize); 28 | } 29 | 30 | ConvolveError HISSTools::NToMonoConvolve::reset(uint32_t inChan) 31 | { 32 | return doChannel(&MonoConvolve::reset, inChan); 33 | } 34 | 35 | void HISSTools::NToMonoConvolve::process(const float * const* ins, float *out, float *temp, size_t numSamples, size_t activeInChans) 36 | { 37 | // Zero output then convolve 38 | 39 | std::fill_n(out, numSamples, 0.f); 40 | 41 | for (uint32_t i = 0; i < mNumInChans && i < activeInChans ; i++) 42 | mConvolvers[i].process(ins[i], temp, out, numSamples, true); 43 | } 44 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/NToMonoConvolve.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "MonoConvolve.h" 5 | #include "ConvolveErrors.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | namespace HISSTools 12 | { 13 | class NToMonoConvolve 14 | { 15 | 16 | public: 17 | 18 | NToMonoConvolve(uint32_t input_chans, uintptr_t maxLength, LatencyMode latency); 19 | 20 | ConvolveError resize(uint32_t inChan, uintptr_t impulse_length); 21 | ConvolveError set(uint32_t inChan, const float *input, uintptr_t impulse_length, bool resize); 22 | ConvolveError reset(uint32_t inChan); 23 | 24 | void process(const float * const* ins, float *out, float *temp, size_t numSamples, size_t active_in_chans); 25 | 26 | private: 27 | 28 | template 29 | ConvolveError doChannel(Method method, uint32_t inChan, Args...args); 30 | 31 | std::vector mConvolvers; 32 | 33 | uint32_t mNumInChans; 34 | }; 35 | } 36 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/PartitionedConvolve.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "../HISSTools_FFT/HISSTools_FFT.h" 5 | 6 | #include "ConvolveErrors.h" 7 | 8 | #include 9 | #include 10 | 11 | namespace HISSTools 12 | { 13 | class PartitionedConvolve 14 | { 15 | // N.B. MIN_FFT_SIZE_LOG2 should never be smaller than 4, as below code assumes loop unroll of vectors (4 vals) by 4 (== 16 or 2^4) 16 | // MAX_FFT_SIZE_LOG2 is perhaps conservative right now, but it is easy to increase this if necessary 17 | 18 | static constexpr int MIN_FFT_SIZE_LOG2 = 5; 19 | static constexpr int MAX_FFT_SIZE_LOG2 = 20; 20 | 21 | public: 22 | 23 | PartitionedConvolve(uintptr_t maxFFTSize, uintptr_t maxLength, uintptr_t offset, uintptr_t length); 24 | ~PartitionedConvolve(); 25 | 26 | // Non-moveable and copyable 27 | 28 | PartitionedConvolve(PartitionedConvolve& obj) = delete; 29 | PartitionedConvolve& operator = (PartitionedConvolve& obj) = delete; 30 | PartitionedConvolve(PartitionedConvolve&& obj) = delete; 31 | PartitionedConvolve& operator = (PartitionedConvolve&& obj) = delete; 32 | 33 | ConvolveError setFFTSize(uintptr_t FFTSize); 34 | ConvolveError setLength(uintptr_t length); 35 | void setOffset(uintptr_t offset); 36 | void setResetOffset(intptr_t offset = -1); 37 | 38 | ConvolveError set(const float *input, uintptr_t length); 39 | void reset(); 40 | 41 | bool process(const float *in, float *out, uintptr_t numSamples); 42 | 43 | private: 44 | 45 | uintptr_t getFFTSize() { return uintptr_t(1) << mFFTSizeLog2; } 46 | uintptr_t getMaxFFTSize() { return uintptr_t(1) << mMaxFFTSizeLog2; } 47 | 48 | void processPartition(FFT_SPLIT_COMPLEX_F in1, FFT_SPLIT_COMPLEX_F in2, FFT_SPLIT_COMPLEX_F out, uintptr_t numBins); 49 | ConvolveError setMaxFFTSize(uintptr_t max_fft_size); 50 | uintptr_t log2(uintptr_t value); 51 | 52 | // Parameters 53 | 54 | uintptr_t mOffset; 55 | uintptr_t mLength; 56 | uintptr_t mMaxImpulseLength; 57 | 58 | // FFT variables 59 | 60 | FFT_SETUP_F mFFTSetup; 61 | 62 | uintptr_t mMaxFFTSizeLog2; 63 | uintptr_t mFFTSizeLog2; 64 | uintptr_t mRWCounter; 65 | 66 | // Scheduling variables 67 | 68 | uintptr_t mInputPosition; 69 | uintptr_t mPartitionsDone; 70 | uintptr_t mLastPartition; 71 | uintptr_t mNumPartitions; 72 | uintptr_t mValidPartitions; 73 | 74 | // Internal buffers 75 | 76 | float *mFFTBuffers[4]; 77 | 78 | FFT_SPLIT_COMPLEX_F mImpulseBuffer; 79 | FFT_SPLIT_COMPLEX_F mInputBuffer; 80 | FFT_SPLIT_COMPLEX_F mAccumBuffer; 81 | FFT_SPLIT_COMPLEX_F mPartitionTemp; 82 | 83 | // Flags 84 | 85 | intptr_t mResetOffset; 86 | bool mResetFlag; 87 | 88 | // Random number generation 89 | 90 | std::default_random_engine mRandGenerator; 91 | std::uniform_int_distribution mRandDistribution; 92 | }; 93 | } 94 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/TimeDomainConvolve.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * TimeDomainConvolve 4 | * 5 | * TimeDomainConvolve performs real-time zero latency time-based convolution. 6 | * 7 | * Typically TimeDomainConvolve is suitable for use in conjunction with PartitionedConvolve for zero-latency convolution with longer impulses (time_domain_convolve use apple's vDSP and the IR length is limited to 2044 samples). 8 | * Note that in fact the algorithms process correlation with reversed impulse response coeffients - which is equivalent to convolution. 9 | * 10 | * Copyright 2012 Alex Harker. All rights reserved. 11 | * 12 | */ 13 | 14 | #include "TimeDomainConvolve.h" 15 | #include "ConvolveSIMD.h" 16 | 17 | #include 18 | 19 | #ifdef __APPLE__ 20 | #include 21 | 22 | uintptr_t padded_length(uintptr_t length) 23 | { 24 | return length; 25 | } 26 | #else 27 | uintptr_t padded_length(uintptr_t length) 28 | { 29 | return ((length + 15) >> 4) << 4; 30 | } 31 | #endif 32 | 33 | HISSTools::TimeDomainConvolve::TimeDomainConvolve(uintptr_t offset, uintptr_t length) : mInputPosition(0), mImpulseLength(0) 34 | { 35 | // Set default initial variables 36 | 37 | setOffset(offset); 38 | setLength(length); 39 | 40 | // Allocate impulse buffer and input bufferr 41 | 42 | mImpulseBuffer = (float *) ALIGNED_MALLOC(sizeof(float) * 2048); 43 | mInputBuffer = (float *) ALIGNED_MALLOC(sizeof(float) * 8192); 44 | 45 | // Zero buffers 46 | 47 | std::fill_n(mImpulseBuffer, 2048, 0.f); 48 | std::fill_n(mInputBuffer, 8192, 0.f); 49 | } 50 | 51 | HISSTools::TimeDomainConvolve::~TimeDomainConvolve() 52 | { 53 | ALIGNED_FREE(mImpulseBuffer); 54 | ALIGNED_FREE(mInputBuffer); 55 | } 56 | 57 | void HISSTools::TimeDomainConvolve::setOffset(uintptr_t offset) 58 | { 59 | mOffset = offset; 60 | } 61 | 62 | ConvolveError HISSTools::TimeDomainConvolve::setLength(uintptr_t length) 63 | { 64 | mLength = std::min(length, uintptr_t(2044)); 65 | 66 | return length > 2044 ? CONVOLVE_ERR_TIME_LENGTH_OUT_OF_RANGE : CONVOLVE_ERR_NONE; 67 | } 68 | 69 | ConvolveError HISSTools::TimeDomainConvolve::set(const float *input, uintptr_t length) 70 | { 71 | mImpulseLength = 0; 72 | 73 | if (input && length > mOffset) 74 | { 75 | // Calculate impulse length 76 | 77 | mImpulseLength = std::min(length - mOffset, (mLength ? mLength : 2044)); 78 | 79 | uintptr_t pad = padded_length(mImpulseLength) - mImpulseLength; 80 | std::fill_n(mImpulseBuffer, pad, 0.f); 81 | std::reverse_copy(input + mOffset, input + mOffset + mImpulseLength, mImpulseBuffer + pad); 82 | } 83 | 84 | reset(); 85 | 86 | return (!mLength && (length - mOffset) > 2044) ? CONVOLVE_ERR_TIME_IMPULSE_TOO_LONG : CONVOLVE_ERR_NONE; 87 | } 88 | 89 | void HISSTools::TimeDomainConvolve::reset() 90 | { 91 | mReset = true; 92 | } 93 | 94 | #ifdef __APPLE__ 95 | void convolve(const float *in, const float *impulse, float *output, uintptr_t N, uintptr_t L) 96 | { 97 | vDSP_conv(in + 1 - L, 1, impulse, 1, output, 1, N, L); 98 | } 99 | #else 100 | void convolve(const float *in, const float *impulse, float *output, uintptr_t N, uintptr_t L) 101 | { 102 | constexpr int size = FloatVector::size; 103 | 104 | L = padded_length(L); 105 | 106 | const FloatVector *impulse_vector = reinterpret_cast(impulse); 107 | 108 | for (uintptr_t i = 0; i < N; i++) 109 | { 110 | FloatVector outputAccum(0.f); 111 | const float *input = in - L + 1 + i - size; 112 | 113 | for (uintptr_t j = 0; j < L >> 2; j += 4) 114 | { 115 | // Load vals 116 | 117 | outputAccum += (impulse_vector[j + 0] * FloatVector::unaligned_load(input += size)); 118 | outputAccum += (impulse_vector[j + 1] * FloatVector::unaligned_load(input += size)); 119 | outputAccum += (impulse_vector[j + 2] * FloatVector::unaligned_load(input += size)); 120 | outputAccum += (impulse_vector[j + 3] * FloatVector::unaligned_load(input += size)); 121 | } 122 | 123 | *output++ = outputAccum.sum(); 124 | } 125 | } 126 | #endif 127 | 128 | bool HISSTools::TimeDomainConvolve::process(const float *in, float *out, uintptr_t numSamples) 129 | { 130 | if (mReset) 131 | { 132 | std::fill_n(mInputBuffer, 8192, 0.f); 133 | mReset = false; 134 | } 135 | 136 | uintptr_t currentLoop; 137 | 138 | while ((currentLoop = (mInputPosition + numSamples) > 4096 ? (4096 - mInputPosition) : ((numSamples > 2048) ? 2048 : numSamples))) 139 | { 140 | // Copy input twice (allows us to read input out in one go) 141 | 142 | std::copy_n(in, currentLoop, mInputBuffer + mInputPosition); 143 | std::copy_n(in, currentLoop, mInputBuffer + mInputPosition + 4096); 144 | 145 | // Advance pointer 146 | 147 | mInputPosition += currentLoop; 148 | if (mInputPosition >= 4096) 149 | mInputPosition -= 4096; 150 | 151 | // Do convolution 152 | 153 | convolve(mInputBuffer + 4096 + (mInputPosition - currentLoop), mImpulseBuffer, out, currentLoop, mImpulseLength); 154 | 155 | // Updates 156 | 157 | in += currentLoop; 158 | out += currentLoop; 159 | numSamples -= currentLoop; 160 | } 161 | 162 | return mImpulseLength; 163 | } 164 | -------------------------------------------------------------------------------- /HIRT_Multichannel_Convolution/TimeDomainConvolve.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include "ConvolveErrors.h" 5 | 6 | #include 7 | 8 | namespace HISSTools 9 | { 10 | class TimeDomainConvolve 11 | { 12 | 13 | public: 14 | 15 | TimeDomainConvolve(uintptr_t offset, uintptr_t length); 16 | ~TimeDomainConvolve(); 17 | 18 | // Non-moveable and copyable 19 | 20 | TimeDomainConvolve(TimeDomainConvolve& obj) = delete; 21 | TimeDomainConvolve& operator = (TimeDomainConvolve& obj) = delete; 22 | TimeDomainConvolve(TimeDomainConvolve&& obj) = delete; 23 | TimeDomainConvolve& operator = (TimeDomainConvolve&& obj) = delete; 24 | 25 | ConvolveError setLength(uintptr_t length); 26 | void setOffset(uintptr_t offset); 27 | 28 | ConvolveError set(const float *input, uintptr_t length); 29 | void reset(); 30 | 31 | bool process(const float *in, float *out, uintptr_t numSamples); 32 | 33 | private: 34 | 35 | // Internal buffers 36 | 37 | float *mImpulseBuffer; 38 | float *mInputBuffer; 39 | 40 | uintptr_t mInputPosition; 41 | uintptr_t mImpulseLength; 42 | 43 | uintptr_t mOffset; 44 | uintptr_t mLength; 45 | 46 | // Flags 47 | 48 | bool mReset; 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /HISSTools_FFT/HISSTools_FFT.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HISSTools_FFT.h" 3 | #include "HISSTools_FFT_Core.h" 4 | 5 | #if defined(USE_APPLE_FFT) 6 | 7 | // This file provides bindings to the relevant Apple or HISSTools template routines. 8 | 9 | // User FFT Routines 10 | 11 | void hisstools_fft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 12 | { 13 | vDSP_fft_zipD(setup, input, (vDSP_Stride) 1, log2n, FFT_FORWARD); 14 | } 15 | 16 | void hisstools_fft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 17 | { 18 | vDSP_fft_zip(setup, input, (vDSP_Stride) 1, log2n, FFT_FORWARD); 19 | } 20 | 21 | void hisstools_rfft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 22 | { 23 | vDSP_fft_zripD(setup, input, (vDSP_Stride) 1, log2n, FFT_FORWARD); 24 | } 25 | 26 | void hisstools_rfft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 27 | { 28 | vDSP_fft_zrip(setup, input, (vDSP_Stride) 1, log2n, FFT_FORWARD); 29 | } 30 | 31 | void hisstools_ifft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 32 | { 33 | vDSP_fft_zipD(setup, input, (vDSP_Stride) 1, log2n, FFT_INVERSE); 34 | } 35 | 36 | void hisstools_ifft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 37 | { 38 | vDSP_fft_zip(setup, input, (vDSP_Stride) 1, log2n, FFT_INVERSE); 39 | } 40 | 41 | void hisstools_rifft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 42 | { 43 | vDSP_fft_zripD(setup, input, (vDSP_Stride) 1, log2n, FFT_INVERSE); 44 | } 45 | 46 | void hisstools_rifft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 47 | { 48 | vDSP_fft_zrip(setup, input, (vDSP_Stride) 1, log2n, FFT_INVERSE); 49 | } 50 | 51 | // Zip and Unzip 52 | 53 | void hisstools_unzip(const double *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t log2n) 54 | { 55 | vDSP_ctozD((DOUBLE_COMPLEX *) input, (vDSP_Stride) 2, output, (vDSP_Stride) 1, (vDSP_Length) (1 << (log2n - 1))); 56 | } 57 | 58 | void hisstools_unzip(const float *input, FFT_SPLIT_COMPLEX_F *output, uintptr_t log2n) 59 | { 60 | vDSP_ctoz((COMPLEX *) input, (vDSP_Stride) 2, output, (vDSP_Stride) 1, (vDSP_Length) (1 << (log2n - 1))); 61 | } 62 | 63 | void hisstools_zip(const FFT_SPLIT_COMPLEX_D *input, double *output, uintptr_t log2n) 64 | { 65 | vDSP_ztocD(input, (vDSP_Stride) 1, (DOUBLE_COMPLEX *) output, (vDSP_Stride) 2, (vDSP_Length) (1 << (log2n - 1))); 66 | } 67 | 68 | void hisstools_zip(const FFT_SPLIT_COMPLEX_F *input, float *output, uintptr_t log2n) 69 | { 70 | vDSP_ztoc(input, (vDSP_Stride) 1, (COMPLEX *) output, (vDSP_Stride) 2, (vDSP_Length) (1 << (log2n - 1))); 71 | } 72 | 73 | // Setup Create / Destroy 74 | 75 | void hisstools_create_setup(FFT_SETUP_D *setup, uintptr_t max_fft_log_2) 76 | { 77 | *setup = vDSP_create_fftsetupD(max_fft_log_2, FFT_RADIX2); 78 | } 79 | 80 | void hisstools_create_setup(FFT_SETUP_F *setup, uintptr_t max_fft_log_2) 81 | { 82 | *setup = vDSP_create_fftsetup(max_fft_log_2, FFT_RADIX2); 83 | } 84 | 85 | void hisstools_destroy_setup(FFT_SETUP_D setup) 86 | { 87 | if (setup) 88 | vDSP_destroy_fftsetupD(setup); 89 | } 90 | 91 | void hisstools_destroy_setup(FFT_SETUP_F setup) 92 | { 93 | if (setup) 94 | vDSP_destroy_fftsetup(setup); 95 | } 96 | 97 | // Zip and Unzip 98 | 99 | template void unzipComplex(const float *input, FFT_SPLIT_COMPLEX_F *output, uintptr_t half_length) 100 | { 101 | vDSP_ctoz((COMPLEX *) input, (vDSP_Stride) 2, output, (vDSP_Stride) 1, half_length); 102 | } 103 | 104 | template void unzipComplex(const double *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t half_length) 105 | { 106 | vDSP_ctozD((DOUBLE_COMPLEX *) input, (vDSP_Stride) 2, output, (vDSP_Stride) 1, half_length); 107 | } 108 | 109 | #else 110 | 111 | // User FFT Routines 112 | 113 | void hisstools_fft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 114 | { 115 | hisstools_fft_impl::hisstools_fft(input, setup, log2n); 116 | } 117 | 118 | void hisstools_fft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 119 | { 120 | hisstools_fft_impl::hisstools_fft(input, setup, log2n); 121 | } 122 | 123 | void hisstools_rfft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 124 | { 125 | hisstools_fft_impl::hisstools_rfft(input, setup, log2n); 126 | } 127 | 128 | void hisstools_rfft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 129 | { 130 | hisstools_fft_impl::hisstools_rfft(input, setup, log2n); 131 | } 132 | 133 | void hisstools_ifft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 134 | { 135 | hisstools_fft_impl::hisstools_ifft(input, setup, log2n); 136 | } 137 | 138 | void hisstools_ifft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 139 | { 140 | hisstools_fft_impl::hisstools_ifft(input, setup, log2n); 141 | } 142 | 143 | void hisstools_rifft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, uintptr_t log2n) 144 | { 145 | hisstools_fft_impl::hisstools_rifft(input, setup, log2n); 146 | } 147 | 148 | void hisstools_rifft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, uintptr_t log2n) 149 | { 150 | hisstools_fft_impl::hisstools_rifft(input, setup, log2n); 151 | } 152 | 153 | // Zip and Unzip 154 | 155 | void hisstools_unzip(const double *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t log2n) 156 | { 157 | hisstools_fft_impl::unzip_complex(input, output, (uintptr_t) 1 << (log2n - (uintptr_t) 1)); 158 | } 159 | 160 | void hisstools_unzip(const float *input, FFT_SPLIT_COMPLEX_F *output, uintptr_t log2n) 161 | { 162 | hisstools_fft_impl::unzip_complex(input, output, (uintptr_t) 1 << (log2n - (uintptr_t) 1)); 163 | } 164 | 165 | void hisstools_zip(const FFT_SPLIT_COMPLEX_D *input, double *output, uintptr_t log2n) 166 | { 167 | hisstools_fft_impl::zip_complex(input, output, (uintptr_t) 1 << (log2n - (uintptr_t) 1)); 168 | } 169 | 170 | void hisstools_zip(const FFT_SPLIT_COMPLEX_F *input, float *output, uintptr_t log2n) 171 | { 172 | hisstools_fft_impl::zip_complex(input, output, (uintptr_t) 1 << (log2n - (uintptr_t) 1)); 173 | } 174 | 175 | // Setup Create / Destroy 176 | 177 | void hisstools_create_setup(FFT_SETUP_D *setup, uintptr_t max_fft_log_2) 178 | { 179 | *setup = static_cast(hisstools_fft_impl::create_setup(max_fft_log_2)); 180 | } 181 | 182 | void hisstools_create_setup(FFT_SETUP_F *setup, uintptr_t max_fft_log_2) 183 | { 184 | *setup = static_cast(hisstools_fft_impl::create_setup(max_fft_log_2)); 185 | } 186 | 187 | void hisstools_destroy_setup(FFT_SETUP_D setup) 188 | { 189 | hisstools_fft_impl::destroy_setup(setup); 190 | } 191 | 192 | void hisstools_destroy_setup(FFT_SETUP_F setup) 193 | { 194 | hisstools_fft_impl::destroy_setup(setup); 195 | } 196 | 197 | #endif 198 | 199 | // Unzip incorporating zero padding 200 | 201 | void hisstools_unzip_zero(const double *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t in_length, uintptr_t log2n) 202 | { 203 | hisstools_fft_impl::unzip_zero(input, output, in_length, log2n); 204 | } 205 | 206 | void hisstools_unzip_zero(const float *input, FFT_SPLIT_COMPLEX_F *output, uintptr_t in_length, uintptr_t log2n) 207 | { 208 | hisstools_fft_impl::unzip_zero(input, output, in_length, log2n); 209 | } 210 | 211 | // N.B This routine specifically deals with unzipping float data into a double precision complex split format 212 | 213 | void hisstools_unzip_zero(const float *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t in_length, uintptr_t log2n) 214 | { 215 | hisstools_fft_impl::unzip_zero(input, output, in_length, log2n); 216 | } 217 | 218 | // Convenience Real FFT Functions 219 | 220 | void hisstools_rfft(FFT_SETUP_D setup, const double *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t in_length, uintptr_t log2n) 221 | { 222 | hisstools_unzip_zero(input, output, in_length, log2n); 223 | hisstools_rfft(setup, output, log2n); 224 | } 225 | 226 | void hisstools_rfft(FFT_SETUP_F setup, const float *input, FFT_SPLIT_COMPLEX_F *output, uintptr_t in_length, uintptr_t log2n) 227 | { 228 | hisstools_unzip_zero(input, output, in_length, log2n); 229 | hisstools_rfft(setup, output, log2n); 230 | } 231 | 232 | void hisstools_rfft(FFT_SETUP_D setup, const float *input, FFT_SPLIT_COMPLEX_D *output, uintptr_t in_length, uintptr_t log2n) 233 | { 234 | hisstools_unzip_zero(input, output, in_length, log2n); 235 | hisstools_rfft(setup, output, log2n); 236 | } 237 | 238 | void hisstools_rifft(FFT_SETUP_D setup, FFT_SPLIT_COMPLEX_D *input, double *output, uintptr_t log2n) 239 | { 240 | hisstools_rifft(setup, input, log2n); 241 | hisstools_zip(input, output, log2n); 242 | } 243 | 244 | void hisstools_rifft(FFT_SETUP_F setup, FFT_SPLIT_COMPLEX_F *input, float *output, uintptr_t log2n) 245 | { 246 | hisstools_rifft(setup, input, log2n); 247 | hisstools_zip(input, output, log2n); 248 | } 249 | -------------------------------------------------------------------------------- /Interpolation.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef INTERPOLATION_HPP 3 | #define INTERPOLATION_HPP 4 | 5 | // Enumeration of interpolation types 6 | 7 | enum class InterpType { None, Linear, CubicHermite, CubicLagrange, CubicBSpline }; 8 | 9 | // Linear 10 | 11 | template 12 | struct linear_interp 13 | { 14 | T operator()(const T& x, const T& y0, const T& y1) { return (y0 + x * ((y1 - y0))); } 15 | }; 16 | 17 | // Cubic Hermite 18 | 19 | template 20 | struct cubic_hermite_interp 21 | { 22 | cubic_hermite_interp() : _5div2(2.5), _3div2(1.5), _1div2(0.5) {} 23 | 24 | T operator()(const T& x, const T& y0, const T& y1, const T& y2, const T& y3) 25 | { 26 | const T c0 = y1; 27 | const T c1 = _1div2 * (y2 - y0); 28 | const T c2 = y0 - _5div2 * y1 + y2 + y2 - _1div2 * y3; 29 | const T c3 = _1div2 * (y3 - y0) + _3div2 * (y1 - y2); 30 | 31 | return (((c3 * x + c2) * x + c1) * x + c0); 32 | } 33 | 34 | private: 35 | 36 | const T _5div2; 37 | const T _3div2; 38 | const T _1div2; 39 | }; 40 | 41 | // Cubic Lagrange 42 | 43 | template 44 | struct cubic_lagrange_interp 45 | { 46 | cubic_lagrange_interp() : _1div3(T(1)/T(3)), _1div6(T(1)/T(6)), _1div2(0.5) {} 47 | 48 | T operator()(const T& x, const T& y0, const T& y1, const T& y2, const T& y3) 49 | { 50 | const T c0 = y1; 51 | const T c1 = y2 - _1div3 * y0 - _1div2 * y1 - _1div6 * y3; 52 | const T c2 = _1div2 * (y0 + y2) - y1; 53 | const T c3 = _1div6 * (y3 - y0) + _1div2 * (y1 - y2); 54 | 55 | return (((c3 * x + c2) * x + c1) * x + c0); 56 | } 57 | 58 | private: 59 | 60 | const T _1div3; 61 | const T _1div6; 62 | const T _1div2; 63 | }; 64 | 65 | // Cubic B-spline 66 | 67 | template 68 | struct cubic_bspline_interp 69 | { 70 | cubic_bspline_interp() : _2div3(T(2)/T(3)), _1div6(T(1)/T(6)), _1div2(0.5) {} 71 | 72 | T operator()(const T& x, const T& y0, const T& y1, const T& y2, const T& y3) 73 | { 74 | const T y0py2 = y0 + y2; 75 | const T c0 = _1div6 * y0py2 + _2div3 * y1; 76 | const T c1 = _1div2 * (y2 - y0); 77 | const T c2 = _1div2 * y0py2 - y1; 78 | const T c3 = _1div2 * (y1 - y2) + _1div6 * (y3 - y0); 79 | 80 | return (((c3 * x + c2) * x + c1) * x + c0); 81 | } 82 | 83 | private: 84 | 85 | const T _2div3; 86 | const T _1div6; 87 | const T _1div2; 88 | }; 89 | 90 | #endif /* Interpolation_h */ 91 | -------------------------------------------------------------------------------- /KernelSmoother.hpp: -------------------------------------------------------------------------------- 1 | 2 | #ifndef KERNELSMOOTHER_HPP 3 | #define KERNELSMOOTHER_HPP 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Allocator.hpp" 13 | #include "SIMDSupport.hpp" 14 | #include "SpectralProcessor.hpp" 15 | #include "TableReader.hpp" 16 | 17 | template 18 | class kernel_smoother : private spectral_processor 19 | { 20 | using processor = spectral_processor; 21 | using op_sizes = typename processor::op_sizes; 22 | using zipped_pointer = typename processor::zipped_pointer; 23 | using in_ptr = typename processor::in_ptr; 24 | 25 | using Split = typename FFTTypes::Split; 26 | 27 | template 28 | using enable_if_t = typename std::enable_if::type; 29 | 30 | enum class Ends { Zero, NonZero, SymZero, SymNonZero }; 31 | 32 | public: 33 | 34 | enum class EdgeMode { ZeroPad, Extend, Wrap, Fold, Mirror }; 35 | 36 | template ::value> = 0> 37 | kernel_smoother(uintptr_t max_fft_size = 1 << 18) 38 | : spectral_processor(max_fft_size) 39 | {} 40 | 41 | template ::value> = 0> 42 | kernel_smoother(const Allocator& allocator, uintptr_t max_fft_size = 1 << 18) 43 | : spectral_processor(allocator, max_fft_size) 44 | {} 45 | 46 | template ::value> = 0> 47 | kernel_smoother(const Allocator&& allocator, uintptr_t max_fft_size = 1 << 18) 48 | : spectral_processor(allocator, max_fft_size) 49 | {} 50 | 51 | void set_max_fft_size(uintptr_t size) { processor::set_max_fft_size(size); } 52 | 53 | uintptr_t max_fft_size() { return processor::max_fft_size(); } 54 | 55 | void smooth(T *out, const T *in, const T *kernel, uintptr_t length, uintptr_t kernel_length, double width_lo, double width_hi, bool symmetric, EdgeMode edges) 56 | { 57 | if (!length || !kernel_length) 58 | return; 59 | 60 | Allocator& allocator = processor::m_allocator; 61 | 62 | const int N = SIMDLimits::max_size; 63 | 64 | width_lo = std::min(static_cast(length), std::max(1.0, width_lo)); 65 | width_hi = std::min(static_cast(length), std::max(1.0, width_hi)); 66 | 67 | double width_mul = (width_hi - width_lo) / (length - 1); 68 | 69 | auto half_width_calc = [&](uintptr_t a) 70 | { 71 | return static_cast(std::round((width_lo + a * width_mul) * 0.5)); 72 | }; 73 | 74 | uintptr_t filter_size = static_cast(std::ceil(std::max(width_lo, width_hi) * 0.5)); 75 | uintptr_t filter_full = filter_size * 2 - 1; 76 | uintptr_t max_per_filter = static_cast(width_mul ? (2.0 / std::abs(width_mul)) + 1.0 : length); 77 | uintptr_t data_width = max_per_filter + (filter_full - 1); 78 | 79 | op_sizes sizes(data_width, filter_full, processor::EdgeMode::Linear); 80 | 81 | if (auto_resize_fft && processor::max_fft_size() < sizes.fft()) 82 | set_max_fft_size(sizes.fft()); 83 | 84 | uintptr_t fft_size = processor::max_fft_size() >= sizes.fft() ? sizes.fft() : 0; 85 | 86 | T *ptr = allocator.template allocate(fft_size * 2 + filter_full + length + filter_size * 2); 87 | Split io { ptr, ptr + (fft_size >> 1) }; 88 | Split st { io.realp + fft_size, io.imagp + fft_size }; 89 | T *filter = ptr + (fft_size << 1); 90 | T *padded = filter + filter_full; 91 | 92 | Ends ends = Ends::NonZero; 93 | 94 | if (kernel_length) 95 | { 96 | const T max_value = *std::max_element(kernel, kernel + kernel_length); 97 | const T test_value_1 = kernel[0] / max_value; 98 | const T test_value_2 = kernel[kernel_length - 1] / max_value; 99 | const T epsilon = std::numeric_limits::epsilon(); 100 | 101 | if ((symmetric || test_value_1 < epsilon) && test_value_2 < epsilon) 102 | ends = symmetric ? Ends::SymZero : Ends::Zero; 103 | } 104 | 105 | // Copy data 106 | 107 | switch (edges) 108 | { 109 | case EdgeMode::ZeroPad: 110 | std::fill_n(padded, filter_size, 0.0); 111 | std::copy_n(in, length, padded + filter_size); 112 | std::fill_n(padded + filter_size + length, filter_size, 0.0); 113 | break; 114 | 115 | case EdgeMode::Extend: 116 | std::fill_n(padded, filter_size, in[0]); 117 | std::copy_n(in, length, padded + filter_size); 118 | std::fill_n(padded + filter_size + length, filter_size, in[length - 1]); 119 | break; 120 | 121 | case EdgeMode::Wrap: 122 | copy_edges(in, padded, length, filter_size); 123 | break; 124 | 125 | case EdgeMode::Fold: 126 | copy_edges(in, padded, length, filter_size); 127 | break; 128 | 129 | case EdgeMode::Mirror: 130 | copy_edges(in, padded, length, filter_size); 131 | break; 132 | } 133 | 134 | if (symmetric) 135 | { 136 | // Offsets into the data and the filter 137 | 138 | const T *data = padded + filter_size; 139 | filter += filter_size - 1; 140 | 141 | // Symmetric filtering 142 | 143 | for (uintptr_t i = 0, j = 0; i < length; i = j) 144 | { 145 | const uintptr_t half_width = static_cast(half_width_calc(i)); 146 | const uintptr_t width = half_width * 2 - 1; 147 | const T filter_half_sum = make_filter(filter, kernel, kernel_length, half_width, ends); 148 | const T filter_sum = (filter_half_sum * T(2) - filter[0]); 149 | const T gain = filter_sum ? T(1) / filter_sum : 1.0; 150 | 151 | for (j = i; (j < length) && half_width == half_width_calc(j); j++); 152 | 153 | uintptr_t n = j - i; 154 | uintptr_t m = use_fft_n(n, half_width, fft_size); 155 | uintptr_t k = 0; 156 | 157 | const double *data_fft = data - (half_width - 1); 158 | const double *filter_fft = filter - (half_width - 1); 159 | 160 | // Mirror the filter if required for the FFT processing 161 | 162 | if (m) 163 | { 164 | for (intptr_t i = 1; i < static_cast(half_width); i++) 165 | filter[-i] = filter[i]; 166 | } 167 | 168 | for (; k + (m - 1) < n; k += m) 169 | apply_filter_fft(out + i + k, data_fft + i + k, filter_fft, io, st, width, m, gain); 170 | 171 | for (; k + (N - 1) < n; k += N) 172 | apply_filter_symmetric(out + i + k, data + i + k, filter, half_width, gain); 173 | 174 | for (; k < n; k++) 175 | apply_filter_symmetric<1>(out + i + k, data + i + k, filter, half_width, gain); 176 | } 177 | } 178 | else 179 | { 180 | // Non-symmetric filtering 181 | 182 | for (uintptr_t i = 0, j = 0; i < length; i = j) 183 | { 184 | const uintptr_t half_width = static_cast(half_width_calc(i)); 185 | const uintptr_t width = half_width * 2 - 1; 186 | const T filter_sum = make_filter(filter, kernel, kernel_length, width, ends); 187 | const T gain = filter_sum ? T(1) / filter_sum : 1.0; 188 | 189 | const T *data = padded + filter_size - (half_width - 1); 190 | 191 | for (j = i; (j < length) && half_width == half_width_calc(j); j++); 192 | 193 | uintptr_t n = j - i; 194 | uintptr_t m = use_fft_n(n, half_width, fft_size); 195 | uintptr_t k = 0; 196 | 197 | for (; k + (m - 1) < n; k += m) 198 | apply_filter_fft(out + i + k, data + i + k, filter, io, st, width, m, gain); 199 | 200 | for (; k + (N - 1) < n; k += N) 201 | apply_filter(out + i + k, data + i + k, filter, width, gain); 202 | 203 | for (; k < n; k++) 204 | apply_filter<1>(out + i + k, data + i + k, filter, width, gain); 205 | } 206 | } 207 | 208 | allocator.deallocate(ptr); 209 | } 210 | 211 | private: 212 | 213 | struct fetcher : table_fetcher 214 | { 215 | fetcher(const T *in, intptr_t size) 216 | : table_fetcher(size, 1.0), data(in) {} 217 | 218 | T operator()(intptr_t idx) { return data[idx]; } 219 | 220 | const T *data; 221 | }; 222 | 223 | template