Files
swift-mirror/stdlib/public/Darwin/Accelerate/vDSP_FFT.swift
Stephen Canon e6406d878d Drop @inline(__always) from Accelerate overlay; it doesn't do what you want. (#24641)
@inline(__always) does not imply inlinable, which means that it effectively does nothing in the context of the Accelerate overlay. I have replaced all of these with @inlinable where that can be done as a one-line change. Functions that switch over open enums and more complex API (DCT, DFT, FFT) will require more sophisticated corrections, which we can undertake in later commits. For now, they have been rolled back to simply being normal public API.
2019-05-09 17:10:34 -04:00

445 lines
18 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
//
// 1D and 2D Fast Fourier Transform
//
//===----------------------------------------------------------------------===//
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
extension vDSP {
/// An enumeration that defines the size of the FFT decomposition.
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public enum Radix {
case radix2
case radix3
case radix5
public var fftRadix: FFTRadix {
switch self {
case .radix2:
return FFTRadix(kFFTRadix2)
case .radix3:
return FFTRadix(kFFTRadix3)
case .radix5:
return FFTRadix(kFFTRadix5)
}
}
}
/// A class that provides forward and inverse FFT on `DSPSplitComplex` or `DSPDoubleSplitComplex` structure.
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public class FFT<T: vDSP_FourierTransformable> {
let log2n: vDSP_Length
let radix: Radix
fileprivate let fftSetup: OpaquePointer
/// Initializes a new fast Fourier transform structure.
///
/// - Parameter log2n: The base-two logarithm of the maximum number of elements to be transformed.
/// - Parameter radix: Specifies radix options.
public init?(log2n: vDSP_Length,
radix: Radix,
ofType: T.Type) {
self.log2n = log2n
self.radix = radix
guard let setup = T.FFTFunctions.makeFFTSetup(log2n: log2n,
radix: radix) else {
return nil
}
fftSetup = setup
}
/// Computes an out-of-place single-precision real forward or inverse fast Fourier transform.
///
/// - Parameter input: Complex input vector.
/// - Parameter output: Complex output vector.
/// - Parameter direction: The transform direction.
public func transform<T: vDSP_FourierTransformable>(input: T,
output: inout T,
direction: vDSP.FourierTransformDirection) {
vDSP_FFTFunctions.fftTransform(fftSetup: fftSetup,
log2n: log2n,
source: input,
destination: &output,
direction: direction)
}
/// Computes an out-of-place single-precision real forward fast Fourier transform.
///
/// - Parameter input: Complex input vector.
/// - Parameter output: Complex output vector.
public func forward(input: DSPSplitComplex,
output: inout DSPSplitComplex) {
transform(input: input,
output: &output,
direction: .forward)
}
/// Computes an out-of-place single-precision real inverse fast Fourier transform.
///
/// - Parameter input: Complex input vector.
/// - Parameter output: Complex output vector.
public func inverse(input: DSPSplitComplex,
output: inout DSPSplitComplex) {
transform(input: input,
output: &output,
direction: .inverse)
}
/// Frees memory associated with this `FFT` struct.
deinit {
T.FFTFunctions.destroySetup(fftSetup)
}
}
// MARK: 2D FFT
/// A class that provides forward and inverse 2D FFT on `DSPSplitComplex` or `DSPDoubleSplitComplex` structure.
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public class FFT2D<T: vDSP_FourierTransformable>: FFT<T> {
let width: Int
let height: Int
/// Initializes a new fast Fourier transform structure for 2D FFT.
///
/// - Parameter width: The width of the matrix to be transformed.
/// - Parameter height: The width of the matrix to be transformed.
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
required public init?(width: Int,
height: Int,
ofType: T.Type) {
self.width = width
self.height = height
let log2n = vDSP_Length(log2(Float(width * height)))
super.init(log2n: log2n,
radix: .radix2,
ofType: ofType)
}
/// Computes an out-of-place 2D fast Fourier transform.
///
/// - Parameter input: Complex input vector.
/// - Parameter output: Complex output vector.
/// - Parameter direction: Specifies transform direction.
override public func transform<T: vDSP_FourierTransformable>(input: T,
output: inout T,
direction: vDSP.FourierTransformDirection) {
vDSP_FFTFunctions.fftTransform2D(fftSetup: fftSetup,
width: width,
height: height,
source: input,
destination: &output,
direction: direction)
}
}
}
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public protocol vDSP_FourierTransformFunctions {
associatedtype SplitComplex
static func makeFFTSetup(log2n: vDSP_Length,
radix: vDSP.Radix) -> OpaquePointer?
static func transform(fftSetup: OpaquePointer,
log2n: vDSP_Length,
source: UnsafePointer<SplitComplex>,
destination: UnsafeMutablePointer<SplitComplex>,
direction: vDSP.FourierTransformDirection)
static func transform2D(fftSetup: OpaquePointer,
width: Int,
height: Int,
source: UnsafePointer<SplitComplex>,
destination: UnsafeMutablePointer<SplitComplex>,
direction: vDSP.FourierTransformDirection)
static func destroySetup(_ setup: OpaquePointer)
}
//===----------------------------------------------------------------------===//
//
// Type-specific FFT function implementations
//
//===----------------------------------------------------------------------===//
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public struct vDSP_SplitComplexFloat: vDSP_FourierTransformFunctions {
public typealias SplitComplex = DSPSplitComplex
/// Returns a setup structure to perform a fast Fourier transform.
public static func makeFFTSetup(log2n: vDSP_Length,
radix: vDSP.Radix) -> OpaquePointer? {
return vDSP_create_fftsetup(
log2n,
radix.fftRadix)
}
/// Performs a 1D fast Fourier transform.
public static func transform(fftSetup: OpaquePointer,
log2n: vDSP_Length,
source: UnsafePointer<SplitComplex>,
destination: UnsafeMutablePointer<SplitComplex>,
direction: vDSP.FourierTransformDirection) {
vDSP_fft_zrop(fftSetup,
source, 1,
destination, 1,
log2n,
direction.fftDirection)
}
/// Performs a 2D fast Fourier transform.
public static func transform2D(fftSetup: OpaquePointer,
width: Int,
height: Int,
source: UnsafePointer<SplitComplex>,
destination: UnsafeMutablePointer<SplitComplex>,
direction: vDSP.FourierTransformDirection) {
vDSP_fft2d_zrop(fftSetup,
source, 1, 0,
destination, 1, 0,
vDSP_Length(log2(Float(width))),
vDSP_Length(log2(Float(height))),
direction.fftDirection)
}
/// Releases an FFT setup object.
public static func destroySetup(_ setup: OpaquePointer) {
vDSP_destroy_fftsetup(setup)
}
}
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public struct vDSP_SplitComplexDouble: vDSP_FourierTransformFunctions {
public typealias SplitComplex = DSPDoubleSplitComplex
/// Returns a setup structure to perform a fast Fourier transform.
public static func makeFFTSetup(log2n: vDSP_Length,
radix: vDSP.Radix) -> OpaquePointer? {
return vDSP_create_fftsetupD(
log2n,
radix.fftRadix)
}
/// Performs a 1D fast Fourier transform.
public static func transform(fftSetup: OpaquePointer,
log2n: vDSP_Length,
source: UnsafePointer<SplitComplex>,
destination: UnsafeMutablePointer<SplitComplex>,
direction: vDSP.FourierTransformDirection) {
vDSP_fft_zropD(fftSetup,
source, 1,
destination, 1,
log2n,
direction.fftDirection)
}
/// Performs a 2D fast Fourier transform.
public static func transform2D(fftSetup: OpaquePointer,
width: Int,
height: Int,
source: UnsafePointer<SplitComplex>,
destination: UnsafeMutablePointer<SplitComplex>,
direction: vDSP.FourierTransformDirection) {
vDSP_fft2d_zropD(fftSetup,
source, 1, 0,
destination, 1, 0,
vDSP_Length(log2(Float(width))),
vDSP_Length(log2(Float(height))),
direction.fftDirection)
}
/// Releases an FFT setup object.
public static func destroySetup(_ setup: OpaquePointer) {
vDSP_destroy_fftsetupD(setup)
}
}
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
public protocol vDSP_FourierTransformable {
associatedtype FFTFunctions: vDSP_FourierTransformFunctions where FFTFunctions.SplitComplex == Self
}
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
extension DSPSplitComplex: vDSP_FourierTransformable {
public typealias FFTFunctions = vDSP_SplitComplexFloat
}
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
extension DSPDoubleSplitComplex: vDSP_FourierTransformable {
public typealias FFTFunctions = vDSP_SplitComplexDouble
}
@available(iOS 9999, OSX 9999, tvOS 9999, watchOS 9999, *)
struct vDSP_FFTFunctions {
/// Performs a 1D fast Fourier transform.
@inlinable
static func fftTransform<SplitComplex>(fftSetup: OpaquePointer,
log2n: vDSP_Length,
source: SplitComplex,
destination: inout SplitComplex,
direction: vDSP.FourierTransformDirection) where SplitComplex: vDSP_FourierTransformable {
withUnsafePointer(to: source) { sourcePointer in
SplitComplex.FFTFunctions.transform(fftSetup: fftSetup,
log2n: log2n,
source: sourcePointer,
destination: &destination,
direction: direction)
}
}
/// Performs a 2D fast Fourier transform.
@inlinable
static func fftTransform2D<SplitComplex>(fftSetup: OpaquePointer,
width: Int,
height: Int,
source: SplitComplex,
destination: inout SplitComplex,
direction: vDSP.FourierTransformDirection) where SplitComplex: vDSP_FourierTransformable {
withUnsafePointer(to: source) { sourcePointer in
SplitComplex.FFTFunctions.transform2D(fftSetup: fftSetup,
width: width,
height: height,
source: sourcePointer,
destination: &destination,
direction: direction)
}
}
}
extension DSPSplitComplex {
/// Creates a new `DSPSplitComplex` structure from a real array not in even-odd split configuration.
///
/// - Parameter inputArray: The source array of contiguous values.
/// - Parameter realParts: An array of real parts of the complex numbers.
/// - Parameter imaginaryParts: An array of imaginary parts of the complex numbers.
public init(fromInputArray inputArray: [Float],
realParts: inout [Float],
imaginaryParts: inout [Float]) {
self.init(realp: &realParts,
imagp: &imaginaryParts)
inputArray.withUnsafeBytes{
vDSP_ctoz([DSPComplex]($0.bindMemory(to: DSPComplex.self)), 2,
&self, 1,
vDSP_Length(inputArray.count / 2))
}
}
}
extension DSPDoubleSplitComplex {
/// Creates a new `DSPDoubleSplitComplex` structure from a real array not in even-odd split configuration.
///
/// - Parameter inputArray: The source array of contiguous values.
/// - Parameter realParts: An array of real parts of the complex numbers.
/// - Parameter imaginaryParts: An array of imaginary parts of the complex numbers.
public init(fromInputArray inputArray: [Double],
realParts: inout [Double],
imaginaryParts: inout [Double]) {
self.init(realp: &realParts,
imagp: &imaginaryParts)
inputArray.withUnsafeBytes{
vDSP_ctozD([DSPDoubleComplex]($0.bindMemory(to: DSPDoubleComplex.self)), 2,
&self, 1,
vDSP_Length(inputArray.count / 2))
}
}
}
extension Array where Element == Float {
/// Creates a new array of single-precision values from a `DSPSplitComplex` structure.
///
/// - Parameter scale: A multiplier to apply during conversion.
/// - Parameter count: The length of the required resulting array (typically half the count of either the real or imaginary parts of the `DSPSplitComplex`.
public init(fromSplitComplex splitComplex: DSPSplitComplex,
scale: Float,
count: Int) {
var complexPairs = [DSPComplex](repeating: DSPComplex(real: 0, imag: 0),
count: count / 2)
withUnsafePointer(to: splitComplex) {
vDSP_ztoc($0, 1,
&complexPairs, 2,
vDSP_Length(count / 2))
}
self = [Float](repeating: 0, count: count)
complexPairs.withUnsafeBytes {
guard let complexPairsUnsafePointer = $0.bindMemory(to: Float.self).baseAddress else {
fatalError("Internal error")
}
vDSP_vsmul(complexPairsUnsafePointer, 1,
[scale],
&self, 1,
vDSP_Length(count))
}
}
}
extension Array where Element == Double {
/// Creates a new array of single-precision values from a `DSPDoubleSplitComplex` structure.
///
/// - Parameter scale: A multiplier to apply during conversion.
/// - Parameter count: The length of the required resulting array (typically half the count of either the real or imaginary parts of the `DSPSplitComplex`.
public init(fromSplitComplex splitComplex: DSPDoubleSplitComplex,
scale: Double,
count: Int) {
var complexPairs = [DSPDoubleComplex](repeating: DSPDoubleComplex(real: 0, imag: 0),
count: count / 2)
withUnsafePointer(to: splitComplex) {
vDSP_ztocD($0, 1,
&complexPairs, 2,
vDSP_Length(count / 2))
}
self = [Double](repeating: 0, count: count)
complexPairs.withUnsafeBytes {
guard let complexPairsUnsafePointer = $0.bindMemory(to: Double.self).baseAddress else {
fatalError("Internal error")
}
vDSP_vsmulD(complexPairsUnsafePointer, 1,
[scale],
&self, 1,
vDSP_Length(count))
}
}
}