From f658557c6a708584b8c21987659f374c271adc69 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Tue, 11 Oct 2016 10:08:09 +0200 Subject: [PATCH 1/9] add YIN algorithm http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf http://matthiasmauch.de/_pdf/mauch_pyin_2014.pdf https://code.soundsoftware.ac.uk/projects/pyin/repository Signed-off-by: Guillaume Laurent --- Beethoven.xcodeproj/project.pbxproj | 8 + Source/Estimation/YINUtil.swift | 184 ++++++++++++++++++ .../Transform/Strategies/YINTransformer.swift | 19 ++ 3 files changed, 211 insertions(+) create mode 100644 Source/Estimation/YINUtil.swift create mode 100644 Source/Transform/Strategies/YINTransformer.swift diff --git a/Beethoven.xcodeproj/project.pbxproj b/Beethoven.xcodeproj/project.pbxproj index 96fd258..471496e 100644 --- a/Beethoven.xcodeproj/project.pbxproj +++ b/Beethoven.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + AF3190521DABE01600C897CA /* YINTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3190511DABE01600C897CA /* YINTransformer.swift */; }; + AF4AFD9A1DAA3462002B6DC9 /* YINUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4AFD991DAA3462002B6DC9 /* YINUtil.swift */; }; D51575B61C343B77006F8E75 /* Beethoven.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D51575AB1C343B77006F8E75 /* Beethoven.framework */; }; D59CB1571C345E0A00290B63 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59CB1371C345E0A00290B63 /* Config.swift */; }; D59CB1581C345E0A00290B63 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59CB1391C345E0A00290B63 /* Buffer.swift */; }; @@ -55,6 +57,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + AF3190511DABE01600C897CA /* YINTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YINTransformer.swift; sourceTree = ""; }; + AF4AFD991DAA3462002B6DC9 /* YINUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YINUtil.swift; sourceTree = ""; tabWidth = 4; }; D51575AB1C343B77006F8E75 /* Beethoven.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Beethoven.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D51575B51C343B77006F8E75 /* Beethoven-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Beethoven-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D59CB1371C345E0A00290B63 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; @@ -172,6 +176,7 @@ D59CB13D1C345E0A00290B63 /* EstimationStrategy.swift */, D59CB13E1C345E0A00290B63 /* Estimator.swift */, D59CB13F1C345E0A00290B63 /* LocationEstimator.swift */, + AF4AFD991DAA3462002B6DC9 /* YINUtil.swift */, ); path = Estimation; sourceTree = ""; @@ -232,6 +237,7 @@ children = ( D59CB1521C345E0A00290B63 /* FFTTransformer.swift */, D59CB1531C345E0A00290B63 /* SimpleTransformer.swift */, + AF3190511DABE01600C897CA /* YINTransformer.swift */, ); path = Strategies; sourceTree = ""; @@ -495,6 +501,7 @@ D59CB16D1C345E0A00290B63 /* TransformFactory.swift in Sources */, D59CB1631C345E0A00290B63 /* QuinnsFirstEstimator.swift in Sources */, D59CB15B1C345E0A00290B63 /* EstimationStrategy.swift in Sources */, + AF4AFD9A1DAA3462002B6DC9 /* YINUtil.swift in Sources */, D59CB15E1C345E0A00290B63 /* BarycentricEstimator.swift in Sources */, D59CB1621C345E0A00290B63 /* QuadradicEstimator.swift in Sources */, D59CB1591C345E0A00290B63 /* EstimationError.swift in Sources */, @@ -508,6 +515,7 @@ D59CB1571C345E0A00290B63 /* Config.swift in Sources */, D59CB1681C345E0A00290B63 /* InputSignalTracker.swift in Sources */, D59CB1581C345E0A00290B63 /* Buffer.swift in Sources */, + AF3190521DABE01600C897CA /* YINTransformer.swift in Sources */, D59CB16A1C345E0A00290B63 /* FFTTransformer.swift in Sources */, D59CB15A1C345E0A00290B63 /* EstimationFactory.swift in Sources */, ); diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift new file mode 100644 index 0000000..44fa61c --- /dev/null +++ b/Source/Estimation/YINUtil.swift @@ -0,0 +1,184 @@ +// +// YINUtil.swift +// Beethoven +// +// Created by Guillaume Laurent on 09/10/16. +// Adapted from https://code.soundsoftware.ac.uk/projects/pyin/repository +// by Matthias Mauch, Centre for Digital Music, Queen Mary, University of London. +// +// + +import UIKit +import Accelerate + +class YINUtil { + + + func difference(buffer:[Float]) -> [Float] { + + let frameSize = buffer.count + let yinBufferSize = frameSize / 2 + + var powerTerms = [Float](repeating:0, count:yinBufferSize) + + let addSquare = { (res:Float, element:Float) -> Float in + res + element * element + } + + // power terms calculation + // + let powerTermFirstElement = buffer.reduce(0.0, addSquare) + + powerTerms.append(powerTermFirstElement) + + for tau in 1 ..< buffer.count { + let v = powerTerms[tau - 1] + let v1 = buffer[tau - 1] * buffer[tau - 1] + let v2 = buffer[tau + yinBufferSize] * buffer[tau + yinBufferSize] + let newV = v - v1 + v2 + + powerTerms[tau] = newV + } + + let log2n = UInt(round(log2(Double(buffer.count)))) + let bufferSizePOT = Int(1 << log2n) + let inputCount = bufferSizePOT / 2 + let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2)) + + var audioRealp = [Float](repeating: 0, count: inputCount) + var audioImagp = [Float](repeating: 0, count: inputCount) + var audioTransformedComplex = DSPSplitComplex(realp: &audioRealp, imagp: &audioImagp) + + let temp = UnsafePointer(buffer) + + temp.withMemoryRebound(to: DSPComplex.self, capacity: buffer.count) { (typeConvertedTransferBuffer) -> Void in + vDSP_ctoz(typeConvertedTransferBuffer, 2, &audioTransformedComplex, 1, vDSP_Length(inputCount)) + } + + // YIN-STYLE AUTOCORRELATION via FFT + // 1. data + vDSP_fft_zrip(fftSetup!, &audioTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) + + var kernel = [Float](repeating: 0, count: frameSize) + + // 2. half of the data, disguised as a convolution kernel + // + for j in 0 ..< yinBufferSize { + kernel[j] = buffer[yinBufferSize - 1 - j] + } +// for j in yinBufferSize ..< frameSize { +// kernel[j] = 0.0 +// } + + var kernelRealp = [Float](repeating: 0, count: inputCount) + var kernelImagp = [Float](repeating: 0, count: inputCount) + var kernelTransformedComplex = DSPSplitComplex(realp: &kernelRealp, imagp: &kernelImagp) + + let ktemp = UnsafePointer(kernel) + + ktemp.withMemoryRebound(to: DSPComplex.self, capacity: kernel.count) { (typeConvertedTransferBuffer) -> Void in + vDSP_ctoz(typeConvertedTransferBuffer, 2, &kernelTransformedComplex, 1, vDSP_Length(inputCount)) + } + + vDSP_fft_zrip(fftSetup!, &kernelTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) + + + var yinStyleACFRealp = [Float](repeating: 0, count: inputCount) + var yinStyleACFImagp = [Float](repeating: 0, count: inputCount) + var yinStyleACFComplex = DSPSplitComplex(realp: &yinStyleACFRealp, imagp: &yinStyleACFImagp) + + for j in 0 ..< inputCount { + yinStyleACFRealp[j] = audioRealp[j] * kernelRealp[j] - audioImagp[j] * kernelImagp[j]; + yinStyleACFImagp[j] = audioRealp[j] * kernelImagp[j] + audioImagp[j] * kernelRealp[j]; + } + + vDSP_fft_zrip(fftSetup!, &yinStyleACFComplex, 1, log2n, FFTDirection(FFT_INVERSE)) + + + return yinStyleACFRealp + } + + func cumulativeDifference(yinBuffer: inout [Float]) { + yinBuffer[0] = 1.0 + + var runningSum:Float = 0.0 + + for tau in 1 ..< yinBuffer.count { + runningSum += yinBuffer[tau] + if runningSum == 0 { + yinBuffer[tau] = 1 + } else { + yinBuffer[tau] *= Float(tau) / runningSum + } + } + } + + func absoluteThreshold(yinBuffer:[Float], withThreshold threshold: Float) -> Int { + + var tau = 2 + var minTau = 0 + var minVal:Float = 1000.0 + + while tau < yinBuffer.count { + + if yinBuffer[tau] < threshold { + while (tau + 1) < yinBuffer.count && yinBuffer[tau + 1] < yinBuffer[tau] { + tau += 1 + } + return tau + } else { + if yinBuffer[tau] < minVal { + minVal = yinBuffer[tau] + minTau = tau + } + } + tau += 1 + } + + if minTau > 0 { + return -minTau + } + + return 0 + } + + func parabolicInterpolation(yinBuffer:[Float], tau:Int) -> Float { + + guard tau != yinBuffer.count else { return Float(tau) } + + var betterTau:Float = 0.0 + + if tau > 0 && tau < yinBuffer.count - 1 { + + let s0 = yinBuffer[tau - 1] + let s1 = yinBuffer[tau] + let s2 = yinBuffer[tau + 1] + + var adjustment = (s2 - s0) / (2.0 * (2.0 * s1 - s2 - s0)) + + if abs(adjustment) > 1 { + adjustment = 0 + } + + betterTau = Float(tau) + adjustment + + } else { + + betterTau = Float(tau) + + } + + return betterTau + } + + func sumSquare(yinBuffer:[Float], start:Int, end:Int) -> Float { + var out:Float = 0.0 + + for i in start ..< end { + out += yinBuffer[i] * yinBuffer[i] + } + + return out + } + +} diff --git a/Source/Transform/Strategies/YINTransformer.swift b/Source/Transform/Strategies/YINTransformer.swift new file mode 100644 index 0000000..3d678f2 --- /dev/null +++ b/Source/Transform/Strategies/YINTransformer.swift @@ -0,0 +1,19 @@ +// +// YINTransformer.swift +// Beethoven +// +// Created by Guillaume Laurent on 10/10/16. +// Adapted from https://code.soundsoftware.ac.uk/projects/pyin/repository +// by Matthias Mauch, Centre for Digital Music, Queen Mary, University of London. +// + +import Foundation +import AVFoundation + +public struct YINTransformer : Transformer { + + public func transformBuffer(_ buffer: AVAudioPCMBuffer) -> Buffer { + + + } +} From 40c800051c3cfc88ff32450f5061660918f6bb1d Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Tue, 18 Oct 2016 13:11:33 +0200 Subject: [PATCH 2/9] Finished implementation of YIN algorithm - not tested yet Signed-off-by: Guillaume Laurent --- Beethoven.xcodeproj/project.pbxproj | 12 +++-- Source/Estimation/EstimationFactory.swift | 2 + Source/Estimation/EstimationStrategy.swift | 1 + .../Estimation/Strategies/YINEstimator.swift | 35 ++++++++++++++ Source/Estimation/YINUtil.swift | 46 +++++++++---------- .../Transform/Strategies/YINTransformer.swift | 5 ++ Source/Transform/TransformFactory.swift | 10 ++-- Source/Transform/TransformStrategy.swift | 1 + 8 files changed, 82 insertions(+), 30 deletions(-) create mode 100644 Source/Estimation/Strategies/YINEstimator.swift diff --git a/Beethoven.xcodeproj/project.pbxproj b/Beethoven.xcodeproj/project.pbxproj index 471496e..1063562 100644 --- a/Beethoven.xcodeproj/project.pbxproj +++ b/Beethoven.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ AF3190521DABE01600C897CA /* YINTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF3190511DABE01600C897CA /* YINTransformer.swift */; }; AF4AFD9A1DAA3462002B6DC9 /* YINUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF4AFD991DAA3462002B6DC9 /* YINUtil.swift */; }; + AFFF3FF11DB633B4004AD9D9 /* YINEstimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = AFFF3FF01DB633B4004AD9D9 /* YINEstimator.swift */; }; D51575B61C343B77006F8E75 /* Beethoven.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D51575AB1C343B77006F8E75 /* Beethoven.framework */; }; D59CB1571C345E0A00290B63 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59CB1371C345E0A00290B63 /* Config.swift */; }; D59CB1581C345E0A00290B63 /* Buffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59CB1391C345E0A00290B63 /* Buffer.swift */; }; @@ -57,14 +58,15 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - AF3190511DABE01600C897CA /* YINTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YINTransformer.swift; sourceTree = ""; }; - AF4AFD991DAA3462002B6DC9 /* YINUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YINUtil.swift; sourceTree = ""; tabWidth = 4; }; + AF3190511DABE01600C897CA /* YINTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = YINTransformer.swift; sourceTree = ""; tabWidth = 4; }; + AF4AFD991DAA3462002B6DC9 /* YINUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = YINUtil.swift; sourceTree = ""; tabWidth = 4; }; + AFFF3FF01DB633B4004AD9D9 /* YINEstimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YINEstimator.swift; sourceTree = ""; }; D51575AB1C343B77006F8E75 /* Beethoven.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Beethoven.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D51575B51C343B77006F8E75 /* Beethoven-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Beethoven-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; D59CB1371C345E0A00290B63 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = ""; }; D59CB1391C345E0A00290B63 /* Buffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Buffer.swift; sourceTree = ""; }; D59CB13B1C345E0A00290B63 /* EstimationError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EstimationError.swift; sourceTree = ""; }; - D59CB13C1C345E0A00290B63 /* EstimationFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EstimationFactory.swift; sourceTree = ""; }; + D59CB13C1C345E0A00290B63 /* EstimationFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.swift; path = EstimationFactory.swift; sourceTree = ""; tabWidth = 2; }; D59CB13D1C345E0A00290B63 /* EstimationStrategy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EstimationStrategy.swift; sourceTree = ""; }; D59CB13E1C345E0A00290B63 /* Estimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Estimator.swift; sourceTree = ""; }; D59CB13F1C345E0A00290B63 /* LocationEstimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationEstimator.swift; sourceTree = ""; }; @@ -134,7 +136,9 @@ D5A49CCE1C343FD700427BF8 /* BeethovenTests */, D51575AC1C343B77006F8E75 /* Products */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; D51575AC1C343B77006F8E75 /* Products */ = { isa = PBXGroup; @@ -191,6 +195,7 @@ D59CB1451C345E0A00290B63 /* QuadradicEstimator.swift */, D59CB1461C345E0A00290B63 /* QuinnsFirstEstimator.swift */, D59CB1471C345E0A00290B63 /* QuinnsSecondEstimator.swift */, + AFFF3FF01DB633B4004AD9D9 /* YINEstimator.swift */, ); path = Strategies; sourceTree = ""; @@ -505,6 +510,7 @@ D59CB15E1C345E0A00290B63 /* BarycentricEstimator.swift in Sources */, D59CB1621C345E0A00290B63 /* QuadradicEstimator.swift in Sources */, D59CB1591C345E0A00290B63 /* EstimationError.swift in Sources */, + AFFF3FF11DB633B4004AD9D9 /* YINEstimator.swift in Sources */, D59CB1671C345E0A00290B63 /* SignalTracker.swift in Sources */, D59CB16C1C345E0A00290B63 /* Transformer.swift in Sources */, D59CB1661C345E0A00290B63 /* PitchEngine.swift in Sources */, diff --git a/Source/Estimation/EstimationFactory.swift b/Source/Estimation/EstimationFactory.swift index 39f4db5..cb1af52 100644 --- a/Source/Estimation/EstimationFactory.swift +++ b/Source/Estimation/EstimationFactory.swift @@ -16,6 +16,8 @@ struct EstimationFactory { estimator = JainsEstimator() case .hps: estimator = HPSEstimator() + case .yin: + estimator = YINEstimator() default: estimator = MaxValueEstimator() } diff --git a/Source/Estimation/EstimationStrategy.swift b/Source/Estimation/EstimationStrategy.swift index 7ef7072..6b81850 100644 --- a/Source/Estimation/EstimationStrategy.swift +++ b/Source/Estimation/EstimationStrategy.swift @@ -6,4 +6,5 @@ public enum EstimationStrategy { case quinnsSecond case jains case hps + case yin } diff --git a/Source/Estimation/Strategies/YINEstimator.swift b/Source/Estimation/Strategies/YINEstimator.swift new file mode 100644 index 0000000..06f91c4 --- /dev/null +++ b/Source/Estimation/Strategies/YINEstimator.swift @@ -0,0 +1,35 @@ +// +// YINEstimator.swift +// Beethoven +// +// Created by Guillaume Laurent on 18/10/16. +// Copyright © 2016 Vadym Markov. All rights reserved. +// + +import UIKit + +class YINEstimator: Estimator { + + var threshold:Float = 0.0 + + func estimateFrequency(_ sampleRate: Float, buffer: Buffer) throws -> Float { + + var elements = buffer.elements + + YINUtil.cumulativeDifference(yinBuffer: &elements) + + let tau = YINUtil.absoluteThreshold(yinBuffer: elements, withThreshold: threshold) + + var f0:Float + + if tau != 0 { + let interpolatedTau = YINUtil.parabolicInterpolation(yinBuffer: elements, tau: tau) + f0 = sampleRate / interpolatedTau + } else { + f0 = 0.0 + } + + return f0 + } + +} diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift index 44fa61c..264c934 100644 --- a/Source/Estimation/YINUtil.swift +++ b/Source/Estimation/YINUtil.swift @@ -14,7 +14,7 @@ import Accelerate class YINUtil { - func difference(buffer:[Float]) -> [Float] { + class func difference(buffer:[Float]) -> [Float] { let frameSize = buffer.count let yinBufferSize = frameSize / 2 @@ -66,9 +66,9 @@ class YINUtil { for j in 0 ..< yinBufferSize { kernel[j] = buffer[yinBufferSize - 1 - j] } -// for j in yinBufferSize ..< frameSize { -// kernel[j] = 0.0 -// } + // for j in yinBufferSize ..< frameSize { + // kernel[j] = 0.0 + // } var kernelRealp = [Float](repeating: 0, count: inputCount) var kernelImagp = [Float](repeating: 0, count: inputCount) @@ -98,7 +98,7 @@ class YINUtil { return yinStyleACFRealp } - func cumulativeDifference(yinBuffer: inout [Float]) { + class func cumulativeDifference(yinBuffer: inout [Float]) { yinBuffer[0] = 1.0 var runningSum:Float = 0.0 @@ -113,7 +113,7 @@ class YINUtil { } } - func absoluteThreshold(yinBuffer:[Float], withThreshold threshold: Float) -> Int { + class func absoluteThreshold(yinBuffer:[Float], withThreshold threshold: Float) -> Int { var tau = 2 var minTau = 0 @@ -142,43 +142,43 @@ class YINUtil { return 0 } - func parabolicInterpolation(yinBuffer:[Float], tau:Int) -> Float { - + class func parabolicInterpolation(yinBuffer:[Float], tau:Int) -> Float { + guard tau != yinBuffer.count else { return Float(tau) } - + var betterTau:Float = 0.0 - + if tau > 0 && tau < yinBuffer.count - 1 { - + let s0 = yinBuffer[tau - 1] let s1 = yinBuffer[tau] let s2 = yinBuffer[tau + 1] - + var adjustment = (s2 - s0) / (2.0 * (2.0 * s1 - s2 - s0)) - + if abs(adjustment) > 1 { adjustment = 0 } - + betterTau = Float(tau) + adjustment - + } else { - + betterTau = Float(tau) - + } - + return betterTau } - - func sumSquare(yinBuffer:[Float], start:Int, end:Int) -> Float { + + class func sumSquare(yinBuffer:[Float], start:Int, end:Int) -> Float { var out:Float = 0.0 - + for i in start ..< end { out += yinBuffer[i] * yinBuffer[i] } - + return out } - + } diff --git a/Source/Transform/Strategies/YINTransformer.swift b/Source/Transform/Strategies/YINTransformer.swift index 3d678f2..0ffef33 100644 --- a/Source/Transform/Strategies/YINTransformer.swift +++ b/Source/Transform/Strategies/YINTransformer.swift @@ -14,6 +14,11 @@ public struct YINTransformer : Transformer { public func transformBuffer(_ buffer: AVAudioPCMBuffer) -> Buffer { + let pointer = buffer.floatChannelData + let elements = Array.fromUnsafePointer((pointer?.pointee)!, count:Int(buffer.frameLength)) + let diffElements = YINUtil.difference(buffer: elements) + + return Buffer(elements: diffElements) } } diff --git a/Source/Transform/TransformFactory.swift b/Source/Transform/TransformFactory.swift index 60d6ecb..4b62d00 100644 --- a/Source/Transform/TransformFactory.swift +++ b/Source/Transform/TransformFactory.swift @@ -1,15 +1,17 @@ struct TransformFactory { static func create(_ strategy: TransformStrategy) -> Transformer { - let estimator: Transformer + let transformer: Transformer switch strategy { case .fft: - estimator = FFTTransformer() + transformer = FFTTransformer() + case .yin: + transformer = YINTransformer() default: - estimator = SimpleTransformer() + transformer = SimpleTransformer() } - return estimator + return transformer } } diff --git a/Source/Transform/TransformStrategy.swift b/Source/Transform/TransformStrategy.swift index 0c959e3..6007e2a 100644 --- a/Source/Transform/TransformStrategy.swift +++ b/Source/Transform/TransformStrategy.swift @@ -1,4 +1,5 @@ public enum TransformStrategy { case simple case fft + case yin } From 9afec12ba855c92cb3d59d238ebc238a109d79e4 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Tue, 18 Oct 2016 16:34:28 +0200 Subject: [PATCH 3/9] Port GuitarTuner example to Swift 3, iOS 10 Signed-off-by: Guillaume Laurent --- .../GuitarTuner.xcodeproj/project.pbxproj | 25 ++++++-- Example/GuitarTuner/GuitarTuner/Info.plist | 2 + .../GuitarTuner/Source/AppDelegate.swift | 12 ++-- .../GuitarTuner/Source/ViewController.swift | 63 ++++++++++--------- Example/GuitarTuner/Podfile | 9 +++ Example/GuitarTuner/Podfile.lock | 24 +++---- 6 files changed, 84 insertions(+), 51 deletions(-) diff --git a/Example/GuitarTuner/GuitarTuner.xcodeproj/project.pbxproj b/Example/GuitarTuner/GuitarTuner.xcodeproj/project.pbxproj index a8f7795..2856e62 100644 --- a/Example/GuitarTuner/GuitarTuner.xcodeproj/project.pbxproj +++ b/Example/GuitarTuner/GuitarTuner.xcodeproj/project.pbxproj @@ -101,7 +101,9 @@ 7FCBD55DDFAE042D34748647 /* Frameworks */, 28B4E9F09A7A47F3AF3538A7 /* Pods */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; D57D40FE1BE95A350026B005 /* Products */ = { isa = PBXGroup; @@ -118,7 +120,7 @@ isa = PBXNativeTarget; buildConfigurationList = D57D410F1BE95A350026B005 /* Build configuration list for PBXNativeTarget "GuitarTuner" */; buildPhases = ( - 0E47FCD898C9F86D6AB5429F /* 📦 Check Pods Manifest.lock */, + 0E47FCD898C9F86D6AB5429F /* [CP] Check Pods Manifest.lock */, 1962EBAD1F8E34314E982594 /* [CP] Check Pods Manifest.lock */, D57D40F91BE95A350026B005 /* Sources */, D57D40FA1BE95A350026B005 /* Frameworks */, @@ -143,11 +145,13 @@ D57D40F51BE95A350026B005 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0700; + LastUpgradeCheck = 0800; ORGANIZATIONNAME = "Vadym Markov"; TargetAttributes = { D57D40FC1BE95A350026B005 = { CreatedOnToolsVersion = 7.0.1; + DevelopmentTeam = 6YMA3EN78R; + LastSwiftMigration = 0800; }; }; }; @@ -182,14 +186,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 0E47FCD898C9F86D6AB5429F /* 📦 Check Pods Manifest.lock */ = { + 0E47FCD898C9F86D6AB5429F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "📦 Check Pods Manifest.lock"; + name = "[CP] Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -311,8 +315,10 @@ 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_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -339,6 +345,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -355,8 +362,10 @@ 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_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -375,6 +384,8 @@ IPHONEOS_DEPLOYMENT_TARGET = 9.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 3.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -383,11 +394,14 @@ isa = XCBuildConfiguration; baseConfigurationReference = 1B8EABAEB6DB7CDA44CCC1AD /* Pods-GuitarTuner.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = 6YMA3EN78R; INFOPLIST_FILE = "$(SRCROOT)/GuitarTuner/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.vadymmarkov.GuitarTuner; PRODUCT_NAME = GuitarTuner; + SWIFT_VERSION = 3.0; }; name = Debug; }; @@ -395,11 +409,14 @@ isa = XCBuildConfiguration; baseConfigurationReference = 00C49D4F07D54BCD11781B94 /* Pods-GuitarTuner.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = 6YMA3EN78R; INFOPLIST_FILE = "$(SRCROOT)/GuitarTuner/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.vadymmarkov.GuitarTuner; PRODUCT_NAME = GuitarTuner; + SWIFT_VERSION = 3.0; }; name = Release; }; diff --git a/Example/GuitarTuner/GuitarTuner/Info.plist b/Example/GuitarTuner/GuitarTuner/Info.plist index b071347..c64a840 100644 --- a/Example/GuitarTuner/GuitarTuner/Info.plist +++ b/Example/GuitarTuner/GuitarTuner/Info.plist @@ -22,6 +22,8 @@ 1 LSRequiresIPhoneOS + NSMicrophoneUsageDescription + app needs microphone access UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities diff --git a/Example/GuitarTuner/GuitarTuner/Source/AppDelegate.swift b/Example/GuitarTuner/GuitarTuner/Source/AppDelegate.swift index 9ad6cce..cfb1649 100644 --- a/Example/GuitarTuner/GuitarTuner/Source/AppDelegate.swift +++ b/Example/GuitarTuner/GuitarTuner/Source/AppDelegate.swift @@ -16,8 +16,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return controller }() - func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { - window = UIWindow(frame: UIScreen.mainScreen().bounds) + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = navigationController applyStyles() @@ -29,12 +29,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applyStyles() { let navigationBar = UINavigationBar.appearance() - navigationBar.barStyle = .Black - navigationBar.barTintColor = UIColor.hex("111011") - navigationBar.translucent = false + navigationBar.barStyle = .black + navigationBar.barTintColor = UIColor(hex: "111011") + navigationBar.isTranslucent = false navigationBar.shadowImage = UIImage() navigationBar.titleTextAttributes = [ - NSForegroundColorAttributeName: UIColor.whiteColor() + NSForegroundColorAttributeName: UIColor.white ] } } diff --git a/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift b/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift index 68844ee..15ddb9c 100644 --- a/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift +++ b/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift @@ -9,9 +9,9 @@ class ViewController: UIViewController { lazy var noteLabel: UILabel = { let label = UILabel() label.text = "--" - label.font = UIFont.boldSystemFontOfSize(65) - label.textColor = UIColor.hex("DCD9DB") - label.textAlignment = .Center + label.font = UIFont.boldSystemFont(ofSize: 65) + label.textColor = UIColor(hex: "DCD9DB") + label.textAlignment = .center label.numberOfLines = 0 label.sizeToFit() @@ -20,9 +20,9 @@ class ViewController: UIViewController { lazy var offsetLabel: UILabel = { [unowned self] in let label = UILabel() - label.font = UIFont.systemFontOfSize(28) - label.textColor = UIColor.whiteColor() - label.textAlignment = .Center + label.font = UIFont.systemFont(ofSize: 28) + label.textColor = UIColor.white + label.textAlignment = .center label.numberOfLines = 0 label.sizeToFit() @@ -30,15 +30,15 @@ class ViewController: UIViewController { }() lazy var actionButton: UIButton = { [unowned self] in - let button = UIButton(type: .System) + let button = UIButton(type: .system) button.layer.cornerRadius = 20 - button.backgroundColor = UIColor.hex("3DAFAE") - button.titleLabel?.font = UIFont.systemFontOfSize(20) - button.setTitleColor(UIColor.whiteColor(), forState: .Normal) + button.backgroundColor = UIColor(hex: "3DAFAE") + button.titleLabel?.font = UIFont.systemFont(ofSize: 20) + button.setTitleColor(UIColor.white, for: UIControlState()) button.addTarget(self, action: #selector(ViewController.actionButtonDidPress(_:)), - forControlEvents: .TouchUpInside) - button.setTitle("Start".uppercaseString, forState: .Normal) + for: .touchUpInside) + button.setTitle("Start".uppercased(), for: UIControlState()) return button }() @@ -55,8 +55,8 @@ class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - title = "Tuner".uppercaseString - view.backgroundColor = UIColor.hex("111011") + title = "Tuner".uppercased() + view.backgroundColor = UIColor(hex: "111011") [noteLabel, actionButton, offsetLabel].forEach { view.addSubview($0) @@ -67,25 +67,25 @@ class ViewController: UIViewController { // MARK: - Action methods - func actionButtonDidPress(button: UIButton) { + func actionButtonDidPress(_ button: UIButton) { let text = pitchEngine.active - ? NSLocalizedString("Start", comment: "").uppercaseString - : NSLocalizedString("Stop", comment: "").uppercaseString + ? NSLocalizedString("Start", comment: "").uppercased() + : NSLocalizedString("Stop", comment: "").uppercased() - button.setTitle(text, forState: .Normal) + button.setTitle(text, for: .normal) button.backgroundColor = pitchEngine.active - ? UIColor.hex("3DAFAE") - : UIColor.hex("E13C6C") + ? UIColor(hex: "3DAFAE") + : UIColor(hex: "E13C6C") noteLabel.text = "--" pitchEngine.active ? pitchEngine.stop() : pitchEngine.start() - offsetLabel.hidden = !pitchEngine.active + offsetLabel.isHidden = !pitchEngine.active } // MARK: - Constrains func setupLayout() { - let totalSize = UIScreen.mainScreen().bounds + let totalSize = UIScreen.main.bounds constrain(actionButton, noteLabel, offsetLabel) { actionButton, noteLabel, offsetLabel in @@ -111,16 +111,16 @@ class ViewController: UIViewController { // MARK: - UI - func offsetColor(offsetPercentage: Double) -> UIColor { + func offsetColor(_ offsetPercentage: Double) -> UIColor { let color: UIColor switch abs(offsetPercentage) { case 0...5: - color = UIColor.hex("3DAFAE") + color = UIColor(hex: "3DAFAE") case 6...25: - color = UIColor.hex("FDFFB1") + color = UIColor(hex: "FDFFB1") default: - color = UIColor.hex("E13C6C") + color = UIColor(hex: "E13C6C") } return color @@ -131,7 +131,7 @@ class ViewController: UIViewController { extension ViewController: PitchEngineDelegate { - func pitchEngineDidRecievePitch(pitchEngine: PitchEngine, pitch: Pitch) { + func pitchEngineDidReceivePitch(_ pitchEngine: PitchEngine, pitch: Pitch) { noteLabel.text = pitch.note.string let offsetPercentage = pitch.closestOffset.percentage @@ -148,10 +148,15 @@ extension ViewController: PitchEngineDelegate { offsetLabel.text = "\(prefix)" + String(format:"%.2f", absOffsetPercentage) + "%" offsetLabel.textColor = color - offsetLabel.hidden = false + offsetLabel.isHidden = false } - func pitchEngineDidRecieveError(pitchEngine: PitchEngine, error: ErrorType) { + func pitchEngineDidReceiveError(_ pitchEngine: PitchEngine, error: Error) { print(error) } + + public func pitchEngineWentBelowLevelThreshold(_ pitchEngine: PitchEngine) { + + } + } diff --git a/Example/GuitarTuner/Podfile b/Example/GuitarTuner/Podfile index 7c2ccb8..86f50a5 100644 --- a/Example/GuitarTuner/Podfile +++ b/Example/GuitarTuner/Podfile @@ -7,3 +7,12 @@ target 'GuitarTuner' do pod 'Hue', git: 'https://github.com/hyperoslo/Hue' pod 'Cartography' end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['SWIFT_VERSION'] = '3.0' + end + end +end + diff --git a/Example/GuitarTuner/Podfile.lock b/Example/GuitarTuner/Podfile.lock index 8863af0..388ea65 100644 --- a/Example/GuitarTuner/Podfile.lock +++ b/Example/GuitarTuner/Podfile.lock @@ -1,9 +1,9 @@ PODS: - - Beethoven (1.0.0): + - Beethoven (2.0.0): - Pitchy - - Cartography (0.6.0) - - Hue (1.1.1) - - Pitchy (1.0.0) + - Cartography (1.0.1) + - Hue (2.0.0) + - Pitchy (2.0.1) DEPENDENCIES: - Beethoven (from `../../`) @@ -12,21 +12,21 @@ DEPENDENCIES: EXTERNAL SOURCES: Beethoven: - :path: "../../" + :path: ../../ Hue: :git: https://github.com/hyperoslo/Hue CHECKOUT OPTIONS: Hue: - :commit: 66e5c25d9ef9a95840c23467dc79f12ded8af0c8 + :commit: 9588169ba4201e8a1038a3d2c1750843bf90d7e0 :git: https://github.com/hyperoslo/Hue SPEC CHECKSUMS: - Beethoven: 33b9add13299ea95e84bce44900055ac3c232fe0 - Cartography: 70af183a248547694a8cfaa6b989cb7e04589985 - Hue: 931aeeadcb29ce5de5b97589aa936be4638a1501 - Pitchy: 4e8302612409d07a27c5083f0bbc2a4bd376f1cc + Beethoven: 8dedb982e34b53a1a2c5c79ee879929d65710869 + Cartography: c1460e99395b824d9d75360b0382faeb0b33dcd7 + Hue: 2b317616a04cc5d7cccdb024c88e6d143e2b9cf1 + Pitchy: 00dbe7cb7ac87fd9c32cb2aa6ce50d11f931289f -PODFILE CHECKSUM: 63470e9ecb94f73e46132580d5ace67162bce27b +PODFILE CHECKSUM: 1e153b4e2c75610a8ffdfdf508f1f60b13da109d -COCOAPODS: 1.0.0 +COCOAPODS: 1.0.1 From 8d17738c5ec76d86b243fb30ff1d0d01932da68f Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Tue, 18 Oct 2016 17:16:49 +0200 Subject: [PATCH 4/9] Fix crash in YINUtil:difference() function Signed-off-by: Guillaume Laurent --- Source/Estimation/YINUtil.swift | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift index 264c934..a766df4 100644 --- a/Source/Estimation/YINUtil.swift +++ b/Source/Estimation/YINUtil.swift @@ -19,19 +19,20 @@ class YINUtil { let frameSize = buffer.count let yinBufferSize = frameSize / 2 + + // power terms calculation + // var powerTerms = [Float](repeating:0, count:yinBufferSize) let addSquare = { (res:Float, element:Float) -> Float in res + element * element } - // power terms calculation - // let powerTermFirstElement = buffer.reduce(0.0, addSquare) - powerTerms.append(powerTermFirstElement) + powerTerms[0] = powerTermFirstElement - for tau in 1 ..< buffer.count { + for tau in 1 ..< yinBufferSize { let v = powerTerms[tau - 1] let v1 = buffer[tau - 1] * buffer[tau - 1] let v2 = buffer[tau + yinBufferSize] * buffer[tau + yinBufferSize] From cd81393e343cdd26897946eb7ddbb2a63c1dcec8 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Tue, 18 Oct 2016 17:17:19 +0200 Subject: [PATCH 5/9] Test YIN estimator in GuitarTuner Poor results so far :-( Signed-off-by: Guillaume Laurent --- Example/GuitarTuner/GuitarTuner/Source/ViewController.swift | 5 ++++- Source/Estimation/Strategies/YINEstimator.swift | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift b/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift index 15ddb9c..839b161 100644 --- a/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift +++ b/Example/GuitarTuner/GuitarTuner/Source/ViewController.swift @@ -44,7 +44,10 @@ class ViewController: UIViewController { }() lazy var pitchEngine: PitchEngine = { [unowned self] in - let pitchEngine = PitchEngine(delegate: self) + var config = Config() + config.transformStrategy = .yin + config.estimationStrategy = .yin + let pitchEngine = PitchEngine(config: config, delegate: self) pitchEngine.levelThreshold = -30.0 return pitchEngine diff --git a/Source/Estimation/Strategies/YINEstimator.swift b/Source/Estimation/Strategies/YINEstimator.swift index 06f91c4..98c6db8 100644 --- a/Source/Estimation/Strategies/YINEstimator.swift +++ b/Source/Estimation/Strategies/YINEstimator.swift @@ -29,6 +29,8 @@ class YINEstimator: Estimator { f0 = 0.0 } + NSLog("YINEstimator : f0 = \(f0)") + return f0 } From 8252cd286d77bf8e05b1529cf99232ae317c7252 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Wed, 19 Oct 2016 17:50:38 +0200 Subject: [PATCH 6/9] Fixes for the YIN algorithm - much better results Apparently the problem is the YINUtil:difference() function. The simpler (but slower) difference2() gives good results. Setting the threshold to an appropriate value seems to have helped too. Signed-off-by: Guillaume Laurent --- Source/Estimation/Strategies/YINEstimator.swift | 2 +- Source/Estimation/YINUtil.swift | 15 +++++++++++++++ Source/Transform/Strategies/YINTransformer.swift | 2 +- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Source/Estimation/Strategies/YINEstimator.swift b/Source/Estimation/Strategies/YINEstimator.swift index 98c6db8..5fab083 100644 --- a/Source/Estimation/Strategies/YINEstimator.swift +++ b/Source/Estimation/Strategies/YINEstimator.swift @@ -10,7 +10,7 @@ import UIKit class YINEstimator: Estimator { - var threshold:Float = 0.0 + var threshold:Float = 0.05 func estimateFrequency(_ sampleRate: Float, buffer: Buffer) throws -> Float { diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift index a766df4..25fdb4f 100644 --- a/Source/Estimation/YINUtil.swift +++ b/Source/Estimation/YINUtil.swift @@ -13,6 +13,21 @@ import Accelerate class YINUtil { + class func difference2(buffer:[Float]) -> [Float] { + + let bufferHalfCount = buffer.count / 2 + + var resultBuffer = [Float](repeating:0.0, count:bufferHalfCount) + + for tau in 0 ..< bufferHalfCount { + for i in 0 ..< bufferHalfCount { + let delta = buffer[i] - buffer[i + tau] + resultBuffer[tau] += delta * delta + } + } + + return resultBuffer + } class func difference(buffer:[Float]) -> [Float] { diff --git a/Source/Transform/Strategies/YINTransformer.swift b/Source/Transform/Strategies/YINTransformer.swift index 0ffef33..861a751 100644 --- a/Source/Transform/Strategies/YINTransformer.swift +++ b/Source/Transform/Strategies/YINTransformer.swift @@ -17,7 +17,7 @@ public struct YINTransformer : Transformer { let pointer = buffer.floatChannelData let elements = Array.fromUnsafePointer((pointer?.pointee)!, count:Int(buffer.frameLength)) - let diffElements = YINUtil.difference(buffer: elements) + let diffElements = YINUtil.difference2(buffer: elements) return Buffer(elements: diffElements) } From 2b4ca26c3e1313835bd2ffb1ddb9786bb7615bc6 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Sat, 22 Oct 2016 19:35:31 +0200 Subject: [PATCH 7/9] More fixes on the YIN algorith - now giving good results but too CPU consuming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instruments shows that the difference2() function eats more than 50% of CPU. I need to find a faster way to do this, and I can’t seem to get the difference() function working. Signed-off-by: Guillaume Laurent --- Source/Estimation/YINUtil.swift | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift index 25fdb4f..133fa39 100644 --- a/Source/Estimation/YINUtil.swift +++ b/Source/Estimation/YINUtil.swift @@ -13,6 +13,8 @@ import Accelerate class YINUtil { + // slow and eats a lot of CPU, but working + // class func difference2(buffer:[Float]) -> [Float] { let bufferHalfCount = buffer.count / 2 @@ -29,6 +31,8 @@ class YINUtil { return resultBuffer } + // supposedly faster and less CPU consuming, but doesn't work + // class func difference(buffer:[Float]) -> [Float] { let frameSize = buffer.count @@ -43,7 +47,10 @@ class YINUtil { res + element * element } - let powerTermFirstElement = buffer.reduce(0.0, addSquare) + var powerTermFirstElement:Float = 0.0 + for j in 0 ..< yinBufferSize { + powerTermFirstElement += buffer[j] * buffer[j] + } powerTerms[0] = powerTermFirstElement @@ -61,6 +68,9 @@ class YINUtil { let inputCount = bufferSizePOT / 2 let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2)) + // buffer.count : 4410 - inputCount : 2048 - yinBufferSize : 2205 + NSLog("buffer.count : \(buffer.count) - inputCount : \(inputCount) - yinBufferSize : \(yinBufferSize)") + var audioRealp = [Float](repeating: 0, count: inputCount) var audioImagp = [Float](repeating: 0, count: inputCount) var audioTransformedComplex = DSPSplitComplex(realp: &audioRealp, imagp: &audioImagp) @@ -86,8 +96,8 @@ class YINUtil { // kernel[j] = 0.0 // } - var kernelRealp = [Float](repeating: 0, count: inputCount) - var kernelImagp = [Float](repeating: 0, count: inputCount) + var kernelRealp = [Float](repeating: 0, count: frameSize) + var kernelImagp = [Float](repeating: 0, count: frameSize) var kernelTransformedComplex = DSPSplitComplex(realp: &kernelRealp, imagp: &kernelImagp) let ktemp = UnsafePointer(kernel) @@ -99,8 +109,8 @@ class YINUtil { vDSP_fft_zrip(fftSetup!, &kernelTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) - var yinStyleACFRealp = [Float](repeating: 0, count: inputCount) - var yinStyleACFImagp = [Float](repeating: 0, count: inputCount) + var yinStyleACFRealp = [Float](repeating: 0, count: frameSize) + var yinStyleACFImagp = [Float](repeating: 0, count: frameSize) var yinStyleACFComplex = DSPSplitComplex(realp: &yinStyleACFRealp, imagp: &yinStyleACFImagp) for j in 0 ..< inputCount { @@ -110,8 +120,13 @@ class YINUtil { vDSP_fft_zrip(fftSetup!, &yinStyleACFComplex, 1, log2n, FFTDirection(FFT_INVERSE)) + var resultYinBuffer = [Float](repeating:0.0, count: yinBufferSize) + + for j in 0 ..< yinBufferSize { + resultYinBuffer[j] = powerTerms[0] + powerTerms[j] - 2 * yinStyleACFRealp[j + yinBufferSize - 1] + } - return yinStyleACFRealp + return resultYinBuffer } class func cumulativeDifference(yinBuffer: inout [Float]) { @@ -183,8 +198,8 @@ class YINUtil { betterTau = Float(tau) } - - return betterTau + + return fabs(betterTau) } class func sumSquare(yinBuffer:[Float], start:Int, end:Int) -> Float { From 7d1b90bbbecb773b567e2a3aa4712cd66019d248 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Sat, 22 Oct 2016 20:38:58 +0200 Subject: [PATCH 8/9] YIN algo : faster, Accelerate-based version of difference() function Drops CPU usage from 95% to 25%. Signed-off-by: Guillaume Laurent --- Source/Estimation/YINUtil.swift | 36 +++++++++++++++++-- .../Transform/Strategies/YINTransformer.swift | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift index 133fa39..a0b1e56 100644 --- a/Source/Estimation/YINUtil.swift +++ b/Source/Estimation/YINUtil.swift @@ -31,9 +31,41 @@ class YINUtil { return resultBuffer } - // supposedly faster and less CPU consuming, but doesn't work + // accelerated version of difference2 - Instruments shows roughly around 22% CPU usage, compared to 95% for difference2 // - class func difference(buffer:[Float]) -> [Float] { + class func differenceA(buffer:[Float]) -> [Float] { + let bufferHalfCount = buffer.count / 2 + + var resultBuffer = [Float](repeating:0.0, count:bufferHalfCount) + + var tempBuffer = [Float](repeating:0.0, count:bufferHalfCount) + var tempBufferSq = [Float](repeating:0.0, count:bufferHalfCount) + + let len = vDSP_Length(bufferHalfCount) + var vSum:Float = 0.0 + + for tau in 0 ..< bufferHalfCount { + + let bufferTau = UnsafePointer(buffer).advanced(by: tau) + // do a diff of buffer with itself at tau offset + vDSP_vsub(buffer, 1, bufferTau, 1, &tempBuffer, 1, len) + // square each value of the diff vector + vDSP_vsq(tempBuffer, 1, &tempBufferSq, 1, len) + // sum the squared values into vSum + vDSP_sve(tempBufferSq, 1, &vSum, len) + // store that in the result buffer + resultBuffer[tau] = vSum + } + + return resultBuffer + } + + // supposedly faster and less CPU consuming, but doesn't work, must be because I missed something when porting it from + // https://code.soundsoftware.ac.uk/projects/pyin/repository but I don't know what + // + // kept for reference only + // + class func difference_broken_do_not_use(buffer:[Float]) -> [Float] { let frameSize = buffer.count let yinBufferSize = frameSize / 2 diff --git a/Source/Transform/Strategies/YINTransformer.swift b/Source/Transform/Strategies/YINTransformer.swift index 861a751..d0345cc 100644 --- a/Source/Transform/Strategies/YINTransformer.swift +++ b/Source/Transform/Strategies/YINTransformer.swift @@ -17,7 +17,7 @@ public struct YINTransformer : Transformer { let pointer = buffer.floatChannelData let elements = Array.fromUnsafePointer((pointer?.pointee)!, count:Int(buffer.frameLength)) - let diffElements = YINUtil.difference2(buffer: elements) + let diffElements = YINUtil.differenceA(buffer: elements) return Buffer(elements: diffElements) } From 152610de7f06218a049cbc37052f9f37dbc16813 Mon Sep 17 00:00:00 2001 From: Guillaume Laurent Date: Sat, 22 Oct 2016 20:40:20 +0200 Subject: [PATCH 9/9] Re-indent all my files to 2 spaces according to the rest of the project Signed-off-by: Guillaume Laurent --- .../Estimation/Strategies/YINEstimator.swift | 34 +- Source/Estimation/YINUtil.swift | 356 +++++++++--------- .../Transform/Strategies/YINTransformer.swift | 12 +- 3 files changed, 201 insertions(+), 201 deletions(-) diff --git a/Source/Estimation/Strategies/YINEstimator.swift b/Source/Estimation/Strategies/YINEstimator.swift index 5fab083..0968880 100644 --- a/Source/Estimation/Strategies/YINEstimator.swift +++ b/Source/Estimation/Strategies/YINEstimator.swift @@ -10,28 +10,28 @@ import UIKit class YINEstimator: Estimator { - var threshold:Float = 0.05 + var threshold:Float = 0.05 - func estimateFrequency(_ sampleRate: Float, buffer: Buffer) throws -> Float { + func estimateFrequency(_ sampleRate: Float, buffer: Buffer) throws -> Float { - var elements = buffer.elements + var elements = buffer.elements - YINUtil.cumulativeDifference(yinBuffer: &elements) + YINUtil.cumulativeDifference(yinBuffer: &elements) - let tau = YINUtil.absoluteThreshold(yinBuffer: elements, withThreshold: threshold) + let tau = YINUtil.absoluteThreshold(yinBuffer: elements, withThreshold: threshold) - var f0:Float + var f0:Float - if tau != 0 { - let interpolatedTau = YINUtil.parabolicInterpolation(yinBuffer: elements, tau: tau) - f0 = sampleRate / interpolatedTau - } else { - f0 = 0.0 - } - - NSLog("YINEstimator : f0 = \(f0)") - - return f0 + if tau != 0 { + let interpolatedTau = YINUtil.parabolicInterpolation(yinBuffer: elements, tau: tau) + f0 = sampleRate / interpolatedTau + } else { + f0 = 0.0 } - + + NSLog("YINEstimator : f0 = \(f0)") + + return f0 + } + } diff --git a/Source/Estimation/YINUtil.swift b/Source/Estimation/YINUtil.swift index a0b1e56..a88c16b 100644 --- a/Source/Estimation/YINUtil.swift +++ b/Source/Estimation/YINUtil.swift @@ -13,235 +13,235 @@ import Accelerate class YINUtil { - // slow and eats a lot of CPU, but working - // - class func difference2(buffer:[Float]) -> [Float] { - - let bufferHalfCount = buffer.count / 2 + // slow and eats a lot of CPU, but working + // + class func difference2(buffer:[Float]) -> [Float] { - var resultBuffer = [Float](repeating:0.0, count:bufferHalfCount) + let bufferHalfCount = buffer.count / 2 - for tau in 0 ..< bufferHalfCount { - for i in 0 ..< bufferHalfCount { - let delta = buffer[i] - buffer[i + tau] - resultBuffer[tau] += delta * delta - } - } + var resultBuffer = [Float](repeating:0.0, count:bufferHalfCount) - return resultBuffer + for tau in 0 ..< bufferHalfCount { + for i in 0 ..< bufferHalfCount { + let delta = buffer[i] - buffer[i + tau] + resultBuffer[tau] += delta * delta + } } - // accelerated version of difference2 - Instruments shows roughly around 22% CPU usage, compared to 95% for difference2 - // - class func differenceA(buffer:[Float]) -> [Float] { - let bufferHalfCount = buffer.count / 2 + return resultBuffer + } - var resultBuffer = [Float](repeating:0.0, count:bufferHalfCount) + // accelerated version of difference2 - Instruments shows roughly around 22% CPU usage, compared to 95% for difference2 + // + class func differenceA(buffer:[Float]) -> [Float] { + let bufferHalfCount = buffer.count / 2 - var tempBuffer = [Float](repeating:0.0, count:bufferHalfCount) - var tempBufferSq = [Float](repeating:0.0, count:bufferHalfCount) + var resultBuffer = [Float](repeating:0.0, count:bufferHalfCount) - let len = vDSP_Length(bufferHalfCount) - var vSum:Float = 0.0 + var tempBuffer = [Float](repeating:0.0, count:bufferHalfCount) + var tempBufferSq = [Float](repeating:0.0, count:bufferHalfCount) - for tau in 0 ..< bufferHalfCount { + let len = vDSP_Length(bufferHalfCount) + var vSum:Float = 0.0 - let bufferTau = UnsafePointer(buffer).advanced(by: tau) - // do a diff of buffer with itself at tau offset - vDSP_vsub(buffer, 1, bufferTau, 1, &tempBuffer, 1, len) - // square each value of the diff vector - vDSP_vsq(tempBuffer, 1, &tempBufferSq, 1, len) - // sum the squared values into vSum - vDSP_sve(tempBufferSq, 1, &vSum, len) - // store that in the result buffer - resultBuffer[tau] = vSum - } - - return resultBuffer + for tau in 0 ..< bufferHalfCount { + + let bufferTau = UnsafePointer(buffer).advanced(by: tau) + // do a diff of buffer with itself at tau offset + vDSP_vsub(buffer, 1, bufferTau, 1, &tempBuffer, 1, len) + // square each value of the diff vector + vDSP_vsq(tempBuffer, 1, &tempBufferSq, 1, len) + // sum the squared values into vSum + vDSP_sve(tempBufferSq, 1, &vSum, len) + // store that in the result buffer + resultBuffer[tau] = vSum } - // supposedly faster and less CPU consuming, but doesn't work, must be because I missed something when porting it from - // https://code.soundsoftware.ac.uk/projects/pyin/repository but I don't know what - // - // kept for reference only - // - class func difference_broken_do_not_use(buffer:[Float]) -> [Float] { + return resultBuffer + } - let frameSize = buffer.count - let yinBufferSize = frameSize / 2 + // supposedly faster and less CPU consuming, but doesn't work, must be because I missed something when porting it from + // https://code.soundsoftware.ac.uk/projects/pyin/repository but I don't know what + // + // kept for reference only + // + class func difference_broken_do_not_use(buffer:[Float]) -> [Float] { + let frameSize = buffer.count + let yinBufferSize = frameSize / 2 - // power terms calculation - // - var powerTerms = [Float](repeating:0, count:yinBufferSize) - let addSquare = { (res:Float, element:Float) -> Float in - res + element * element - } + // power terms calculation + // + var powerTerms = [Float](repeating:0, count:yinBufferSize) - var powerTermFirstElement:Float = 0.0 - for j in 0 ..< yinBufferSize { - powerTermFirstElement += buffer[j] * buffer[j] - } + let addSquare = { (res:Float, element:Float) -> Float in + res + element * element + } - powerTerms[0] = powerTermFirstElement + var powerTermFirstElement:Float = 0.0 + for j in 0 ..< yinBufferSize { + powerTermFirstElement += buffer[j] * buffer[j] + } - for tau in 1 ..< yinBufferSize { - let v = powerTerms[tau - 1] - let v1 = buffer[tau - 1] * buffer[tau - 1] - let v2 = buffer[tau + yinBufferSize] * buffer[tau + yinBufferSize] - let newV = v - v1 + v2 + powerTerms[0] = powerTermFirstElement - powerTerms[tau] = newV - } + for tau in 1 ..< yinBufferSize { + let v = powerTerms[tau - 1] + let v1 = buffer[tau - 1] * buffer[tau - 1] + let v2 = buffer[tau + yinBufferSize] * buffer[tau + yinBufferSize] + let newV = v - v1 + v2 - let log2n = UInt(round(log2(Double(buffer.count)))) - let bufferSizePOT = Int(1 << log2n) - let inputCount = bufferSizePOT / 2 - let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2)) + powerTerms[tau] = newV + } - // buffer.count : 4410 - inputCount : 2048 - yinBufferSize : 2205 - NSLog("buffer.count : \(buffer.count) - inputCount : \(inputCount) - yinBufferSize : \(yinBufferSize)") + let log2n = UInt(round(log2(Double(buffer.count)))) + let bufferSizePOT = Int(1 << log2n) + let inputCount = bufferSizePOT / 2 + let fftSetup = vDSP_create_fftsetup(log2n, Int32(kFFTRadix2)) - var audioRealp = [Float](repeating: 0, count: inputCount) - var audioImagp = [Float](repeating: 0, count: inputCount) - var audioTransformedComplex = DSPSplitComplex(realp: &audioRealp, imagp: &audioImagp) + // buffer.count : 4410 - inputCount : 2048 - yinBufferSize : 2205 + NSLog("buffer.count : \(buffer.count) - inputCount : \(inputCount) - yinBufferSize : \(yinBufferSize)") - let temp = UnsafePointer(buffer) + var audioRealp = [Float](repeating: 0, count: inputCount) + var audioImagp = [Float](repeating: 0, count: inputCount) + var audioTransformedComplex = DSPSplitComplex(realp: &audioRealp, imagp: &audioImagp) - temp.withMemoryRebound(to: DSPComplex.self, capacity: buffer.count) { (typeConvertedTransferBuffer) -> Void in - vDSP_ctoz(typeConvertedTransferBuffer, 2, &audioTransformedComplex, 1, vDSP_Length(inputCount)) - } + let temp = UnsafePointer(buffer) - // YIN-STYLE AUTOCORRELATION via FFT - // 1. data - vDSP_fft_zrip(fftSetup!, &audioTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) + temp.withMemoryRebound(to: DSPComplex.self, capacity: buffer.count) { (typeConvertedTransferBuffer) -> Void in + vDSP_ctoz(typeConvertedTransferBuffer, 2, &audioTransformedComplex, 1, vDSP_Length(inputCount)) + } - var kernel = [Float](repeating: 0, count: frameSize) + // YIN-STYLE AUTOCORRELATION via FFT + // 1. data + vDSP_fft_zrip(fftSetup!, &audioTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) - // 2. half of the data, disguised as a convolution kernel - // - for j in 0 ..< yinBufferSize { - kernel[j] = buffer[yinBufferSize - 1 - j] - } - // for j in yinBufferSize ..< frameSize { - // kernel[j] = 0.0 - // } + var kernel = [Float](repeating: 0, count: frameSize) - var kernelRealp = [Float](repeating: 0, count: frameSize) - var kernelImagp = [Float](repeating: 0, count: frameSize) - var kernelTransformedComplex = DSPSplitComplex(realp: &kernelRealp, imagp: &kernelImagp) + // 2. half of the data, disguised as a convolution kernel + // + for j in 0 ..< yinBufferSize { + kernel[j] = buffer[yinBufferSize - 1 - j] + } + // for j in yinBufferSize ..< frameSize { + // kernel[j] = 0.0 + // } - let ktemp = UnsafePointer(kernel) + var kernelRealp = [Float](repeating: 0, count: frameSize) + var kernelImagp = [Float](repeating: 0, count: frameSize) + var kernelTransformedComplex = DSPSplitComplex(realp: &kernelRealp, imagp: &kernelImagp) - ktemp.withMemoryRebound(to: DSPComplex.self, capacity: kernel.count) { (typeConvertedTransferBuffer) -> Void in - vDSP_ctoz(typeConvertedTransferBuffer, 2, &kernelTransformedComplex, 1, vDSP_Length(inputCount)) - } + let ktemp = UnsafePointer(kernel) - vDSP_fft_zrip(fftSetup!, &kernelTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) + ktemp.withMemoryRebound(to: DSPComplex.self, capacity: kernel.count) { (typeConvertedTransferBuffer) -> Void in + vDSP_ctoz(typeConvertedTransferBuffer, 2, &kernelTransformedComplex, 1, vDSP_Length(inputCount)) + } + vDSP_fft_zrip(fftSetup!, &kernelTransformedComplex, 1, log2n, FFTDirection(FFT_FORWARD)) - var yinStyleACFRealp = [Float](repeating: 0, count: frameSize) - var yinStyleACFImagp = [Float](repeating: 0, count: frameSize) - var yinStyleACFComplex = DSPSplitComplex(realp: &yinStyleACFRealp, imagp: &yinStyleACFImagp) - for j in 0 ..< inputCount { - yinStyleACFRealp[j] = audioRealp[j] * kernelRealp[j] - audioImagp[j] * kernelImagp[j]; - yinStyleACFImagp[j] = audioRealp[j] * kernelImagp[j] + audioImagp[j] * kernelRealp[j]; - } + var yinStyleACFRealp = [Float](repeating: 0, count: frameSize) + var yinStyleACFImagp = [Float](repeating: 0, count: frameSize) + var yinStyleACFComplex = DSPSplitComplex(realp: &yinStyleACFRealp, imagp: &yinStyleACFImagp) - vDSP_fft_zrip(fftSetup!, &yinStyleACFComplex, 1, log2n, FFTDirection(FFT_INVERSE)) + for j in 0 ..< inputCount { + yinStyleACFRealp[j] = audioRealp[j] * kernelRealp[j] - audioImagp[j] * kernelImagp[j]; + yinStyleACFImagp[j] = audioRealp[j] * kernelImagp[j] + audioImagp[j] * kernelRealp[j]; + } - var resultYinBuffer = [Float](repeating:0.0, count: yinBufferSize) + vDSP_fft_zrip(fftSetup!, &yinStyleACFComplex, 1, log2n, FFTDirection(FFT_INVERSE)) - for j in 0 ..< yinBufferSize { - resultYinBuffer[j] = powerTerms[0] + powerTerms[j] - 2 * yinStyleACFRealp[j + yinBufferSize - 1] - } + var resultYinBuffer = [Float](repeating:0.0, count: yinBufferSize) - return resultYinBuffer + for j in 0 ..< yinBufferSize { + resultYinBuffer[j] = powerTerms[0] + powerTerms[j] - 2 * yinStyleACFRealp[j + yinBufferSize - 1] } - class func cumulativeDifference(yinBuffer: inout [Float]) { - yinBuffer[0] = 1.0 + return resultYinBuffer + } - var runningSum:Float = 0.0 + class func cumulativeDifference(yinBuffer: inout [Float]) { + yinBuffer[0] = 1.0 - for tau in 1 ..< yinBuffer.count { - runningSum += yinBuffer[tau] - if runningSum == 0 { - yinBuffer[tau] = 1 - } else { - yinBuffer[tau] *= Float(tau) / runningSum - } - } + var runningSum:Float = 0.0 + + for tau in 1 ..< yinBuffer.count { + runningSum += yinBuffer[tau] + if runningSum == 0 { + yinBuffer[tau] = 1 + } else { + yinBuffer[tau] *= Float(tau) / runningSum + } } + } - class func absoluteThreshold(yinBuffer:[Float], withThreshold threshold: Float) -> Int { - - var tau = 2 - var minTau = 0 - var minVal:Float = 1000.0 - - while tau < yinBuffer.count { - - if yinBuffer[tau] < threshold { - while (tau + 1) < yinBuffer.count && yinBuffer[tau + 1] < yinBuffer[tau] { - tau += 1 - } - return tau - } else { - if yinBuffer[tau] < minVal { - minVal = yinBuffer[tau] - minTau = tau - } - } - tau += 1 - } + class func absoluteThreshold(yinBuffer:[Float], withThreshold threshold: Float) -> Int { - if minTau > 0 { - return -minTau + var tau = 2 + var minTau = 0 + var minVal:Float = 1000.0 + + while tau < yinBuffer.count { + + if yinBuffer[tau] < threshold { + while (tau + 1) < yinBuffer.count && yinBuffer[tau + 1] < yinBuffer[tau] { + tau += 1 + } + return tau + } else { + if yinBuffer[tau] < minVal { + minVal = yinBuffer[tau] + minTau = tau } + } + tau += 1 + } - return 0 + if minTau > 0 { + return -minTau } - class func parabolicInterpolation(yinBuffer:[Float], tau:Int) -> Float { - - guard tau != yinBuffer.count else { return Float(tau) } - - var betterTau:Float = 0.0 - - if tau > 0 && tau < yinBuffer.count - 1 { - - let s0 = yinBuffer[tau - 1] - let s1 = yinBuffer[tau] - let s2 = yinBuffer[tau + 1] - - var adjustment = (s2 - s0) / (2.0 * (2.0 * s1 - s2 - s0)) - - if abs(adjustment) > 1 { - adjustment = 0 - } - - betterTau = Float(tau) + adjustment - - } else { - - betterTau = Float(tau) - - } + return 0 + } + + class func parabolicInterpolation(yinBuffer:[Float], tau:Int) -> Float { + + guard tau != yinBuffer.count else { return Float(tau) } + + var betterTau:Float = 0.0 + + if tau > 0 && tau < yinBuffer.count - 1 { + + let s0 = yinBuffer[tau - 1] + let s1 = yinBuffer[tau] + let s2 = yinBuffer[tau + 1] + + var adjustment = (s2 - s0) / (2.0 * (2.0 * s1 - s2 - s0)) + + if abs(adjustment) > 1 { + adjustment = 0 + } + + betterTau = Float(tau) + adjustment + + } else { + + betterTau = Float(tau) - return fabs(betterTau) } - - class func sumSquare(yinBuffer:[Float], start:Int, end:Int) -> Float { - var out:Float = 0.0 - - for i in start ..< end { - out += yinBuffer[i] * yinBuffer[i] - } - - return out + + return fabs(betterTau) + } + + class func sumSquare(yinBuffer:[Float], start:Int, end:Int) -> Float { + var out:Float = 0.0 + + for i in start ..< end { + out += yinBuffer[i] * yinBuffer[i] } - + + return out + } + } diff --git a/Source/Transform/Strategies/YINTransformer.swift b/Source/Transform/Strategies/YINTransformer.swift index d0345cc..fc26a72 100644 --- a/Source/Transform/Strategies/YINTransformer.swift +++ b/Source/Transform/Strategies/YINTransformer.swift @@ -12,13 +12,13 @@ import AVFoundation public struct YINTransformer : Transformer { - public func transformBuffer(_ buffer: AVAudioPCMBuffer) -> Buffer { + public func transformBuffer(_ buffer: AVAudioPCMBuffer) -> Buffer { - let pointer = buffer.floatChannelData - let elements = Array.fromUnsafePointer((pointer?.pointee)!, count:Int(buffer.frameLength)) + let pointer = buffer.floatChannelData + let elements = Array.fromUnsafePointer((pointer?.pointee)!, count:Int(buffer.frameLength)) - let diffElements = YINUtil.differenceA(buffer: elements) + let diffElements = YINUtil.differenceA(buffer: elements) - return Buffer(elements: diffElements) - } + return Buffer(elements: diffElements) + } }