import AVFoundation @testable import HaishinKit enum CMAudioSampleBufferFactory { static func makeSilence(_ sampleRate: Double = 44100, numSamples: Int = 1024, channels: UInt32 = 1, presentaionTimeStamp: CMTime = .zero) -> CMSampleBuffer? { var asbd = AudioStreamBasicDescription( mSampleRate: sampleRate, mFormatID: kAudioFormatLinearPCM, mFormatFlags: 0xc, mBytesPerPacket: 2 * channels, mFramesPerPacket: 1, mBytesPerFrame: 2 * channels, mChannelsPerFrame: channels, mBitsPerChannel: 16, mReserved: 0 ) var formatDescription: CMAudioFormatDescription? var status: OSStatus = noErr var blockBuffer: CMBlockBuffer? let blockSize = numSamples * Int(asbd.mBytesPerPacket) status = CMBlockBufferCreateWithMemoryBlock( allocator: nil, memoryBlock: nil, blockLength: blockSize, blockAllocator: nil, customBlockSource: nil, offsetToData: 0, dataLength: blockSize, flags: 0, blockBufferOut: &blockBuffer ) status = CMAudioFormatDescriptionCreate( allocator: kCFAllocatorDefault, asbd: &asbd, layoutSize: 0, layout: nil, magicCookieSize: 0, magicCookie: nil, extensions: nil, formatDescriptionOut: &formatDescription ) guard let blockBuffer, status == noErr else { return nil } status = CMBlockBufferFillDataBytes( with: 0, blockBuffer: blockBuffer, offsetIntoDestination: 0, dataLength: blockSize ) guard status == noErr else { return nil } var sampleBuffer: CMSampleBuffer? status = CMAudioSampleBufferCreateWithPacketDescriptions( allocator: nil, dataBuffer: blockBuffer, dataReady: true, makeDataReadyCallback: nil, refcon: nil, formatDescription: formatDescription!, sampleCount: numSamples, presentationTimeStamp: presentaionTimeStamp, packetDescriptions: nil, sampleBufferOut: &sampleBuffer ) guard let sampleBuffer, status == noErr else { return nil } return sampleBuffer } static func makeSinWave(_ sampleRate: Double = 44100, numSamples: Int = 1024, channels: UInt32 = 1) -> CMSampleBuffer? { var status: OSStatus = noErr var sampleBuffer: CMSampleBuffer? var timing = CMSampleTimingInfo( duration: CMTime(value: 1, timescale: Int32(sampleRate)), presentationTimeStamp: CMTime.zero, decodeTimeStamp: CMTime.invalid ) var streamDescription = AudioStreamBasicDescription( mSampleRate: sampleRate, mFormatID: kAudioFormatLinearPCM, mFormatFlags: 0xc, mBytesPerPacket: 2 * channels, mFramesPerPacket: 1, mBytesPerFrame: 2 * channels, mChannelsPerFrame: channels, mBitsPerChannel: 16, mReserved: 0 ) guard let format = AVAudioFormat(streamDescription: &streamDescription, channelLayout: AVAudioUtil.makeChannelLayout(channels)) else { return nil } status = CMSampleBufferCreate( allocator: kCFAllocatorDefault, dataBuffer: nil, dataReady: false, makeDataReadyCallback: nil, refcon: nil, formatDescription: format.formatDescription, sampleCount: numSamples, sampleTimingEntryCount: 1, sampleTimingArray: &timing, sampleSizeEntryCount: 0, sampleSizeArray: nil, sampleBufferOut: &sampleBuffer ) guard status == noErr else { return nil } let buffer = AVAudioPCMBuffer(pcmFormat: format, frameCapacity: AVAudioFrameCount(numSamples))! buffer.frameLength = buffer.frameCapacity let channels = Int(format.channelCount) let samples = buffer.int16ChannelData![0] for n in 0..