【问题标题】:How to play raw audio data from socket in Swift如何在 Swift 中从套接字播放原始音频数据
【发布时间】:2017-11-10 22:41:44
【问题描述】:

我需要小块播放来自套接字的原始音频数据。我读过我想使用循环缓冲区,并在 Objective C 中找到了一些解决方案,但无法使它们中的任何一个工作,尤其是在 Swift 3 中。
谁能帮帮我?

【问题讨论】:

  • 你知道传入的音频数据的格式吗?
  • @Dave 我得到的只是字节,所以格式应该是 PCM

标签: ios swift sockets avfoundation pcm


【解决方案1】:

首先你像这样实现环形缓冲区。

public struct RingBuffer<T> {
  private var array: [T?]
  private var readIndex = 0
  private var writeIndex = 0

  public init(count: Int) {
    array = [T?](repeating: nil, count: count)
  }

  /* Returns false if out of space. */
  @discardableResult public mutating func write(element: T) -> Bool {
    if !isFull {
      array[writeIndex % array.count] = element
      writeIndex += 1
      return true
    } else {
      return false
    }
  }

  /* Returns nil if the buffer is empty. */
  public mutating func read() -> T? {
    if !isEmpty {
      let element = array[readIndex % array.count]
      readIndex += 1
      return element
    } else {
      return nil
    }
  }

  fileprivate var availableSpaceForReading: Int {
    return writeIndex - readIndex
  }

  public var isEmpty: Bool {
    return availableSpaceForReading == 0
  }

  fileprivate var availableSpaceForWriting: Int {
    return array.count - availableSpaceForReading
  }

  public var isFull: Bool {
    return availableSpaceForWriting == 0
  }
}

之后,您可以像这样实现音频单元。 (必要时修改)

class ToneGenerator {
    fileprivate var toneUnit: AudioUnit? = nil

    init() {
        setupAudioUnit()
    }

    deinit {
        stop()
    }

    func setupAudioUnit() {

        // Configure the description of the output audio component we want to find:
        let componentSubtype: OSType
        #if os(OSX)
            componentSubtype = kAudioUnitSubType_DefaultOutput
        #else
            componentSubtype = kAudioUnitSubType_RemoteIO
        #endif
        var defaultOutputDescription = AudioComponentDescription(componentType: kAudioUnitType_Output,
                                                                 componentSubType: componentSubtype,
                                                                 componentManufacturer: kAudioUnitManufacturer_Apple,
                                                                 componentFlags: 0,
                                                                 componentFlagsMask: 0)
        let defaultOutput = AudioComponentFindNext(nil, &defaultOutputDescription)

        var err: OSStatus

        // Create a new instance of it in the form of our audio unit:
        err = AudioComponentInstanceNew(defaultOutput!, &toneUnit)
        assert(err == noErr, "AudioComponentInstanceNew failed")

        // Set the render callback as the input for our audio unit:
        var renderCallbackStruct = AURenderCallbackStruct(inputProc: renderCallback as? AURenderCallback,
                                                          inputProcRefCon: nil)
        err = AudioUnitSetProperty(toneUnit!,
                                   kAudioUnitProperty_SetRenderCallback,
                                   kAudioUnitScope_Input,
                                   0,
                                   &renderCallbackStruct,
                                   UInt32(MemoryLayout<AURenderCallbackStruct>.size))
        assert(err == noErr, "AudioUnitSetProperty SetRenderCallback failed")

        // Set the stream format for the audio unit. That is, the format of the data that our render callback will provide.
        var streamFormat = AudioStreamBasicDescription(mSampleRate: Float64(sampleRate),
                                                       mFormatID: kAudioFormatLinearPCM,
                                                       mFormatFlags: kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved,
                                                       mBytesPerPacket: 4 /*four bytes per float*/,
            mFramesPerPacket: 1,
            mBytesPerFrame: 4,
            mChannelsPerFrame: 1,
            mBitsPerChannel: 4*8,
            mReserved: 0)
        err = AudioUnitSetProperty(toneUnit!,
                                   kAudioUnitProperty_StreamFormat,
                                   kAudioUnitScope_Input,
                                   0,
                                   &streamFormat,
                                   UInt32(MemoryLayout<AudioStreamBasicDescription>.size))
        assert(err == noErr, "AudioUnitSetProperty StreamFormat failed")

    }

    func start() {
        var status: OSStatus
        status = AudioUnitInitialize(toneUnit!)
        status = AudioOutputUnitStart(toneUnit!)
        assert(status == noErr)
    }

    func stop() {
        AudioOutputUnitStop(toneUnit!)
        AudioUnitUninitialize(toneUnit!)
    }

}

这是固定值

private let sampleRate = 16000
private let amplitude: Float = 1.0
private let frequency: Float = 440

/// Theta is changed over time as each sample is provided.
private var theta: Float = 0.0


private func renderCallback(_ inRefCon: UnsafeMutableRawPointer,
                            ioActionFlags: UnsafeMutablePointer<AudioUnitRenderActionFlags>,
                            inTimeStamp: UnsafePointer<AudioTimeStamp>,
                            inBusNumber: UInt32,
                            inNumberFrames: UInt32,
                            ioData: UnsafeMutablePointer<AudioBufferList>) -> OSStatus {
    let abl = UnsafeMutableAudioBufferListPointer(ioData)
    let buffer = abl[0]
    let pointer: UnsafeMutableBufferPointer<Float32> = UnsafeMutableBufferPointer(buffer)
    for frame in 0..<inNumberFrames {
        let pointerIndex = pointer.startIndex.advanced(by: Int(frame))
        pointer[pointerIndex] = sin(theta) * amplitude
        theta += 2.0 * Float(M_PI) * frequency / Float(sampleRate)
    }
    return noErr
}

您需要将数据放入循环缓冲区中,然后播放声音。

【讨论】:

  • 附注这是来自 udp PCM 16000 频率 440 的原始音频代码
  • 你如何在这里读取缓冲区并将其发送播放?我现在正在尝试理解您的代码
  • 我有 ADTS 格式的原始数据,这些数据来自作为主机的 windows 机器到作为客户端的 Mac。但无法播放音频。我已将 TPCircularBuffer 用于环形缓冲区。有什么帮助吗?
猜你喜欢
  • 1970-01-01
  • 2016-12-14
  • 1970-01-01
  • 1970-01-01
  • 2015-05-01
  • 1970-01-01
  • 2020-09-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多