diff --git a/ffttest.xcodeproj/project.pbxproj b/ffttest.xcodeproj/project.pbxproj index 3ae623b..650a77e 100644 --- a/ffttest.xcodeproj/project.pbxproj +++ b/ffttest.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 50; objects = { /* Begin PBXBuildFile section */ @@ -47,6 +47,7 @@ 9D149E461B82226B00FF68B2 /* Products */, ); sourceTree = ""; + usesTabs = 0; }; 9D149E461B82226B00FF68B2 /* Products */ = { isa = PBXGroup; @@ -91,7 +92,7 @@ 9D149E3D1B82226B00FF68B2 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0830; + LastUpgradeCheck = 1240; ORGANIZATIONNAME = "Christopher Helf"; TargetAttributes = { 9D149E441B82226B00FF68B2 = { @@ -101,10 +102,11 @@ }; }; buildConfigurationList = 9D149E401B82226B00FF68B2 /* Build configuration list for PBXProject "ffttest" */; - compatibilityVersion = "Xcode 5.2"; + compatibilityVersion = "Xcode 9.3"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 9D149E3C1B82226B00FF68B2; @@ -134,18 +136,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -180,18 +192,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -210,13 +232,15 @@ MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; 9D149E4D1B82226B00FF68B2 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "-"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.2; }; @@ -225,6 +249,7 @@ 9D149E4E1B82226B00FF68B2 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = "-"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.2; }; diff --git a/ffttest/fft.swift b/ffttest/fft.swift index 0d0bbef..4998f53 100755 --- a/ffttest/fft.swift +++ b/ffttest/fft.swift @@ -73,125 +73,136 @@ class FFT { var tempSplitComplexReal : [Double] = [Double](repeating: 0.0, count: N/2) var tempSplitComplexImag : [Double] = [Double](repeating: 0.0, count: N/2) - var tempSplitComplex : DSPDoubleSplitComplex = DSPDoubleSplitComplex(realp: &tempSplitComplexReal, imagp: &tempSplitComplexImag) - - // For polar coordinates - var mag : [Double] = [Double](repeating: 0.0, count: N/2) - var phase : [Double] = [Double](repeating: 0.0, count: N/2) - - // ---------------------------------------------------------------- - // Forward FFT - // ---------------------------------------------------------------- - - var valuesAsComplex : UnsafeMutablePointer? = nil - - values.withUnsafeMutableBytes { - valuesAsComplex = $0.baseAddress?.bindMemory(to: DSPDoubleComplex.self, capacity: values1.count) - } - - // Scramble-pack the real data into complex buffer in just the way that's - // required by the real-to-complex FFT function that follows. - vDSP_ctozD(valuesAsComplex!, 2, &tempSplitComplex, 1, N2); - - // Do real->complex forward FFT - vDSP_fft_zripD(fftSetup, &tempSplitComplex, 1, LOG_N, FFTDirection(FFT_FORWARD)); - - // ---------------------------------------------------------------- - // Get the Frequency Spectrum - // ---------------------------------------------------------------- - - var fftMagnitudes = [Double](repeating: 0.0, count: N/2) - vDSP_zvmagsD(&tempSplitComplex, 1, &fftMagnitudes, 1, N2); - - // vDSP_zvmagsD returns squares of the FFT magnitudes, so take the root here - let roots = sqrt(fftMagnitudes) - - // Normalize the Amplitudes - var fullSpectrum = [Double](repeating: 0.0, count: N/2) - vDSP_vsmulD(roots, vDSP_Stride(1), [1.0 / Double(N)], &fullSpectrum, 1, N2) - - // ---------------------------------------------------------------- - // Convert from complex/rectangular (real, imaginary) coordinates - // to polar (magnitude and phase) coordinates. - // ---------------------------------------------------------------- - - vDSP_zvabsD(&tempSplitComplex, 1, &mag, 1, N2); - - // Beware: Outputted phase here between -PI and +PI - // https://developer.apple.com/library/prerelease/ios/documentation/Accelerate/Reference/vDSPRef/index.html#//apple_ref/c/func/vDSP_zvphasD - vDSP_zvphasD(&tempSplitComplex, 1, &phase, 1, N2); + + // For polar coordinates + var mag : [Double] = [Double](repeating: 0.0, count: N/2) + var phase : [Double] = [Double](repeating: 0.0, count: N/2) + + tempSplitComplexReal.withUnsafeMutableBufferPointer { tempSplitComplexRealPtr in + tempSplitComplexImag.withUnsafeMutableBufferPointer { tempSplitComplexImagPtr in + var tempSplitComplex = DSPDoubleSplitComplex(realp: tempSplitComplexRealPtr.baseAddress!, + imagp: tempSplitComplexImagPtr.baseAddress!) - // ---------------------------------------------------------------- - // Bandpass Filtering - // ---------------------------------------------------------------- - - // Get the Frequencies for the current Framerate - let freqs = getFrequencies(N,fps: fps) - // Get a Bandpass Filter - let bandPassFilter = generateBandPassFilter(freqs) - - // Multiply phase and magnitude with the bandpass filter - mag = mul(mag, y: bandPassFilter.0) - phase = mul(phase, y: bandPassFilter.0) - - // Output Variables - let filteredSpectrum = mul(fullSpectrum, y: bandPassFilter.0) - var filteredPhase = phase - - // ---------------------------------------------------------------- - // Determine Maximum Frequency - // ---------------------------------------------------------------- - let maxFrequencyResult = max(filteredSpectrum) - let maxFrequency = freqs[maxFrequencyResult.1] - let maxPhase = filteredPhase[maxFrequencyResult.1] - - print("Amplitude: \(maxFrequencyResult.0)") - print("Frequency: \(maxFrequency)") - print("Phase: \(maxPhase + .pi / 2)") - - // ---------------------------------------------------------------- - // Convert from polar coordinates back to rectangular coordinates. - // ---------------------------------------------------------------- - - tempSplitComplex = DSPDoubleSplitComplex(realp: &mag, imagp: &phase) - - var complexAsValue : UnsafeMutablePointer? = nil - - tempComplex.withUnsafeMutableBytes { - complexAsValue = $0.baseAddress?.bindMemory(to: Double.self, capacity: values.count) - } - - vDSP_ztocD(&tempSplitComplex, 1, &tempComplex, 2, N2); - vDSP_rectD(complexAsValue!, 2, complexAsValue!, 2, N2); - vDSP_ctozD(&tempComplex, 2, &tempSplitComplex, 1, N2); - - // ---------------------------------------------------------------- - // Do Inverse FFT - // ---------------------------------------------------------------- - - // Create result - var result : [Double] = [Double](repeating: 0.0, count: N) - var resultAsComplex : UnsafeMutablePointer? = nil - - result.withUnsafeMutableBytes { - resultAsComplex = $0.baseAddress?.bindMemory(to: DSPDoubleComplex.self, capacity: values.count) + // ---------------------------------------------------------------- + // Forward FFT + // ---------------------------------------------------------------- + + var valuesAsComplex : UnsafeMutablePointer? = nil + + values.withUnsafeMutableBytes { + valuesAsComplex = $0.baseAddress?.bindMemory(to: DSPDoubleComplex.self, capacity: values1.count) + } + + // Scramble-pack the real data into complex buffer in just the way that's + // required by the real-to-complex FFT function that follows. + vDSP_ctozD(valuesAsComplex!, 2, &tempSplitComplex, 1, N2); + + // Do real->complex forward FFT + vDSP_fft_zripD(fftSetup, &tempSplitComplex, 1, LOG_N, FFTDirection(FFT_FORWARD)); + + // ---------------------------------------------------------------- + // Get the Frequency Spectrum + // ---------------------------------------------------------------- + + var fftMagnitudes = [Double](repeating: 0.0, count: N/2) + vDSP_zvmagsD(&tempSplitComplex, 1, &fftMagnitudes, 1, N2); + + // vDSP_zvmagsD returns squares of the FFT magnitudes, so take the root here + let roots = sqrt(fftMagnitudes) + + // Normalize the Amplitudes + var fullSpectrum = [Double](repeating: 0.0, count: N/2) + vDSP_vsmulD(roots, vDSP_Stride(1), [1.0 / Double(N)], &fullSpectrum, 1, N2) + + // ---------------------------------------------------------------- + // Convert from complex/rectangular (real, imaginary) coordinates + // to polar (magnitude and phase) coordinates. + // ---------------------------------------------------------------- + + vDSP_zvabsD(&tempSplitComplex, 1, &mag, 1, N2); + + // Beware: Outputted phase here between -PI and +PI + // https://developer.apple.com/library/prerelease/ios/documentation/Accelerate/Reference/vDSPRef/index.html#//apple_ref/c/func/vDSP_zvphasD + vDSP_zvphasD(&tempSplitComplex, 1, &phase, 1, N2); + + // ---------------------------------------------------------------- + // Bandpass Filtering + // ---------------------------------------------------------------- + + // Get the Frequencies for the current Framerate + let freqs = getFrequencies(N,fps: fps) + // Get a Bandpass Filter + let bandPassFilter = generateBandPassFilter(freqs) + + // Multiply phase and magnitude with the bandpass filter + mag = mul(mag, y: bandPassFilter.0) + phase = mul(phase, y: bandPassFilter.0) + + // Output Variables + let filteredSpectrum = mul(fullSpectrum, y: bandPassFilter.0) + let filteredPhase = phase + + // ---------------------------------------------------------------- + // Determine Maximum Frequency + // ---------------------------------------------------------------- + let maxFrequencyResult = max(filteredSpectrum) + let maxFrequency = freqs[maxFrequencyResult.1] + let maxPhase = filteredPhase[maxFrequencyResult.1] + + print("Amplitude: \(maxFrequencyResult.0)") + print("Frequency: \(maxFrequency)") + print("Phase: \(maxPhase + .pi / 2)") + } } - - // Do complex->real inverse FFT. - vDSP_fft_zripD(fftSetup, &tempSplitComplex, 1, LOG_N, FFTDirection(FFT_INVERSE)); - - // This leaves result in packed format. Here we unpack it into a real vector. - vDSP_ztocD(&tempSplitComplex, 1, resultAsComplex!, 2, N2); - // Neither the forward nor inverse FFT does any scaling. Here we compensate for that. - var scale : Double = 0.5/Double(N); - var copyOfResult = result; - vDSP_vsmulD(&result, 1, &scale, ©OfResult, 1, vDSP_Length(N)); - result = copyOfResult - - // Print Result - for k in 0 ..< N { - print("\(k) \(values[k]) \(result[k])") + mag.withUnsafeMutableBufferPointer { magPtr in + phase.withUnsafeMutableBufferPointer { phasePtr in + // ---------------------------------------------------------------- + // Convert from polar coordinates back to rectangular coordinates. + // ---------------------------------------------------------------- + + var tempSplitComplex = DSPDoubleSplitComplex(realp: magPtr.baseAddress!, + imagp: phasePtr.baseAddress!) + + var complexAsValue : UnsafeMutablePointer? = nil + + tempComplex.withUnsafeMutableBytes { + complexAsValue = $0.baseAddress?.bindMemory(to: Double.self, capacity: values.count) + } + + vDSP_ztocD(&tempSplitComplex, 1, &tempComplex, 2, N2); + vDSP_rectD(complexAsValue!, 2, complexAsValue!, 2, N2); + vDSP_ctozD(&tempComplex, 2, &tempSplitComplex, 1, N2); + + // ---------------------------------------------------------------- + // Do Inverse FFT + // ---------------------------------------------------------------- + + // Create result + var result : [Double] = [Double](repeating: 0.0, count: N) + var resultAsComplex : UnsafeMutablePointer? = nil + + result.withUnsafeMutableBytes { + resultAsComplex = $0.baseAddress?.bindMemory(to: DSPDoubleComplex.self, capacity: values.count) + } + + // Do complex->real inverse FFT. + vDSP_fft_zripD(fftSetup, &tempSplitComplex, 1, LOG_N, FFTDirection(FFT_INVERSE)); + + // This leaves result in packed format. Here we unpack it into a real vector. + vDSP_ztocD(&tempSplitComplex, 1, resultAsComplex!, 2, N2); + + // Neither the forward nor inverse FFT does any scaling. Here we compensate for that. + var scale : Double = 0.5/Double(N); + var copyOfResult = result; + vDSP_vsmulD(&result, 1, &scale, ©OfResult, 1, vDSP_Length(N)); + result = copyOfResult + + // Print Result + for k in 0 ..< N { + print("\(k) \(values[k]) \(result[k])") + } + } } }