         // free the FFT setup
         vDSP_destroy_fftsetup(fftSetup)
         
+        // free the memory used to store the samples
+        samplesWindowed.destroy()
+        samplesWindowed.dealloc(length)
+        
         // free the window
         window.destroy()
         window.dealloc(length)

        // get half length
        let halfLength = length / 2
        
        // temporary holding (for windowing)
        let samplesCur = UnsafeMutablePointer<Float>.alloc(length)
        defer {
            samplesCur.destroy()
            samplesCur.dealloc(length)
        }
        
        // prepare output
        var output = [Float](count: halfLength, repeatedValue: 0.0)
        
        // window the samples
        vDSP_vmul(samples, 1, window, 1, samplesCur, 1, UInt(length))
        
        // pack samples into complex values (use stride 2 to fill just reals
        vDSP_ctoz(UnsafePointer<DSPComplex>(samplesCur), 2, &complexBufferA, 1, UInt(halfLength))
        
        // perform FFT
        // TODO: potentially use vDSP_fftm_zrip

        var scale: Float = 4.0 // 4.0 * Float(length)
        vDSP_vsdiv(&output, 1, &scale, &output, 1, UInt(halfLength))
        
        return output
    }
    
