2016-09-15 9 views
5

Apple Multipeer Connectivity 프레임 워크를 통해 마이크에서 다른 iPhone으로 오디오를 스트리밍하려고합니다. 오디오 캡처 및 재생을 수행하려면 AVAudioEngine을 사용하고 있습니다 (Rhythmic Fistman' 대답 덕분에 here).멀티 페어 연결을 통해 마이크에서 다른 전화기로 오디오 스트리밍하려고 시도합니다.

입력에 탭을 설치하여 마이크에서 데이터를받습니다. 그런 다음 AVAudioPCMBuffer를 가져 와서 UInt8의 배열로 변환 한 다음 다른 휴대 전화로 스트리밍합니다.

그러나 배열을 다시 AVAudioPCMBuffer로 변환 할 때 바이트 배열을 AVAudioPCMBuffer로 다시 변환하는 메서드를 가리키는 컴파일러에서 EXC_BAD_ACCESS 예외가 발생합니다. 여기

내가 복용 변환 입력 스트리밍하고있어 경우에 대한 코드입니다 :

func binarytotype <T> (_ value: [UInt8], _: T.Type) -> T { 
    return value.withUnsafeBufferPointer { 
     UnsafeRawPointer($0.baseAddress!).load(as: T.self) 
    } 

} 

func typetobinary<T>(_ value: T) -> [UInt8] { 
    var data = [UInt8](repeating: 0, count: MemoryLayout<T>.size) 
    data.withUnsafeMutableBufferPointer { 
     UnsafeMutableRawPointer($0.baseAddress!).storeBytes(of: value, as: T.self) 
    } 
    return data 
} 
: 데이터를 변환

input.installTap(onBus: 0, bufferSize: 2048, format: input.inputFormat(forBus: 0), block: { 
       (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in 

       let audioBuffer = self.typetobinary(buffer) 
       stream.write(audioBuffer, maxLength: audioBuffer.count) 
      }) 

내 두 기능을 (Martin.R의 대답 here에서 촬영)

그리고 수신 측에 :

func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { 
    if streamName == "voice" { 

     stream.schedule(in: RunLoop.current, forMode: .defaultRunLoopMode) 
     stream.open() 

     var bytes = [UInt8](repeating: 0, count: 8) 
     stream.read(&bytes, maxLength: bytes.count) 

     let audioBuffer = self.binarytotype(bytes, AVAudioPCMBuffer.self) //Here is where the app crashes 

     do { 
      try engine.start() 

      audioPlayer.scheduleBuffer(audioBuffer, completionHandler: nil) 
      audioPlayer.play() 
     }catch let error { 
      print(error.localizedDescription) 

     } 
    } 
} 

것은 내가 바이트 배열을 앞뒤로 변환하고 (동일한 전화에서) 스트리밍하기 전에 그것으로부터 사운드를 재생할 수 있지만 수신 끝에서 AVAudioPCMBuffer를 생성하지 않는다는 것입니다. 왜 변환이 수신 측에서 작동하지 않는지 아는 사람이 있습니까? 이것이 올바른 방법인가요?

모든 도움, 생각/의견을 보내 주시면 감사하겠습니다.

+0

오디오 대기열 또는 샘플 프로젝트를 사용하는 방법에 대한 참조를 줄 수 있습니까? –

+0

아니, 두렵지 않아. – nullforlife

답변

4

AVAudioPCMBuffer serialization/deserialisation이 잘못되었습니다.

Swift3의 캐스팅이 많이 변경되었습니다. &은 Swift2보다 더 많은 복사가 필요합니다. 여기

[UInt8]AVAudioPCMBuffer의 사이의 변환 방법은 다음과 같습니다

N.B :이 코드는 44.1kHz의에서 모노 부동 소수점 데이터를 가정합니다.
변경할 수 있습니다.

func copyAudioBufferBytes(_ audioBuffer: AVAudioPCMBuffer) -> [UInt8] { 
    let srcLeft = audioBuffer.floatChannelData![0] 
    let bytesPerFrame = audioBuffer.format.streamDescription.pointee.mBytesPerFrame 
    let numBytes = Int(bytesPerFrame * audioBuffer.frameLength) 

    // initialize bytes to 0 (how to avoid?) 
    var audioByteArray = [UInt8](repeating: 0, count: numBytes) 

    // copy data from buffer 
    srcLeft.withMemoryRebound(to: UInt8.self, capacity: numBytes) { srcByteData in 
     audioByteArray.withUnsafeMutableBufferPointer { 
      $0.baseAddress!.initialize(from: srcByteData, count: numBytes) 
     } 
    } 

    return audioByteArray 
} 

func bytesToAudioBuffer(_ buf: [UInt8]) -> AVAudioPCMBuffer { 
    // format assumption! make this part of your protocol? 
    let fmt = AVAudioFormat(commonFormat: .pcmFormatFloat32, sampleRate: 44100, channels: 1, interleaved: true) 
    let frameLength = UInt32(buf.count)/fmt.streamDescription.pointee.mBytesPerFrame 

    let audioBuffer = AVAudioPCMBuffer(pcmFormat: fmt, frameCapacity: frameLength) 
    audioBuffer.frameLength = frameLength 

    let dstLeft = audioBuffer.floatChannelData![0] 
    // for stereo 
    // let dstRight = audioBuffer.floatChannelData![1] 

    buf.withUnsafeBufferPointer { 
     let src = UnsafeRawPointer($0.baseAddress!).bindMemory(to: Float.self, capacity: Int(frameLength)) 
     dstLeft.initialize(from: src, count: Int(frameLength)) 
    } 

    return audioBuffer 
} 
+0

다시 한 번 감사의 인사 Fistman! 이것은 작동하는 것, 나는 몇 가지 문제가있어. iPhone 5에서 녹음 할 때 녹음은 8kHz에서 이루어지며 iPhone 6에서는 16kHz입니다. 이것은 16kHz 스트림을 보내고 (8kHz 스트림을 원할 때)이 방법을 사용하면 프로그램이 멈추게 만듭니다. 스트림을 보내기 전에 스트림을 변환하는 방법에 대한 조언이 있습니까? 또한 5s에서 6s로 스트림을 보내고 8kHz 스트림을 수신하는 방법을 설정하면 소리가 나오지 않습니다. 그것은 단지 몇 밀리 초, 일시 중지 및 반복에 대한 흰색 noice 재생합니다. 이견있는 사람? – nullforlife

+1

하나의 샘플 속도를 선택하고 어디서나 사용하는 것이 좋습니다. 'AVAudioMixer'를 엔진에 추가하거나'AVAudioConverter'를 사용하여 변환 할 수 있습니다. –

+0

안녕하세요 @ 리듬 미스터 맨, 나는 당신이 대답 할 수 있기를 희망하는 질문을 가지고있었습니다. 위 알고리즘을 사용하고 있으며 numBytes 변수가 무엇인지 궁금합니다. 이 변수를 기반으로,이 데이터를 스트림에 쓰려고하면 쓰기 당 17000 바이트 이상이 보내지 만 엄청나게 높습니다.결국 다른 장치의 스트림 버퍼가 가득 차서 쓰기가 실패합니다 ... – Logan