質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.52%

  • Swift

    7221questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • iOS

    3983questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

ipad同士でマイクから入力された音声で通話したい

受付中

回答 0

投稿

  • 評価
  • クリップ 0
  • VIEW 233

nu-

score 2

 実現したいこと

作成しているアプリの機能の一部で、ipadのマイクから入力された音声で、リアルタイム通話をする
機能を作成しています。
マイクからの音声は、AVAudioEngineを使用しています。
音声データはData型に変換後、UDP通信を使用し送信しています。

現在、マイクから入力された音声をUDP通信で送信し受信するところまではできていると思うのですが、
受け取ったデータを再生する方法がわかりません。

 試したこと

受信したData型をAVAudioPCMBufferに変換しAVAudioEngineで再生しようとしてみたのですが
下記のようなエラーがでてしまいアプリが落ちてしまいます。
また、この方法で試してはみたのですが、データを受信するたびにAngineを止めているため音声をうまく再生できた場合でも
音が途切れてしまうような気がします。

ソースコードの一部を貼らせていただきます。
長くなり申し訳ございませんが、
再生するための方法やアドバイスをいただけませんでしょうか。

環境

言語:Swift4
Xcode:9.2
プログラムの実行は、シュミレータを使用しています。

 エラーメッセージ

2018-05-02 15:07:35.508183+0900 AudioConnectTest[3614:125779] AUBase.cpp:832:DispatchSetProperty:  ca_require: ValidFormat(inScope, inElement, newDesc) InvalidFormat
2018-05-02 15:07:35.508687+0900 AudioConnectTest[3614:125779] [avae] AVAEInternal.h:124:_AVAE_CheckSuccessAndNoNSError: [AUInterface.mm:512:SetFormat: ([[busArray objectAtIndexedSubscript:(NSUInteger)element] setFormat:format error:&nsErr])] returned false, error Error Domain=NSOSStatusErrorDomain Code=-10868 "(null)"
2018-05-02 15:07:35.512176+0900 AudioConnectTest[3614:125779] *** Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: '[[busArray objectAtIndexedSubscript:(NSUInteger)element] setFormat:format error:&nsErr]: returned false, error Error Domain=NSOSStatusErrorDomain Code=-10868 "(null)"'
*** First throw call stack:
(
    0   CoreFoundation                      0x000000010530312b __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x0000000103efef41 objc_exception_throw + 48
    2   CoreFoundation                      0x00000001053082f2 +[NSException raise:format:arguments:] + 98
    3   AVFAudio                            0x00000001142384fe _Z19AVAE_RaiseExceptionP8NSStringz + 158
    4   AVFAudio                            0x000000011429b596 _Z30_AVAE_CheckSuccessAndNoNSErrorPKciS0_S0_bP7NSError + 472
    5   AVFAudio                            0x000000011429b3b1 _ZN17AUInterfaceBaseV39SetFormatEbjP13AVAudioFormat + 147
    6   AVFAudio                            0x0000000114293913 _ZN21AVAudioPlayerNodeImpl15SetOutputFormatEmP13AVAudioFormat + 25
    7   AVFAudio                            0x000000011422d790 _ZN18AVAudioEngineGraph8_ConnectEP19AVAudioNodeImplBaseS1_jjP13AVAudioFormat + 1664
    8   AVFAudio                            0x0000000114297814 _ZN17AVAudioEngineImpl7ConnectEP11AVAudioNodeS1_mmP13AVAudioFormat + 194
    9   AVFAudio                            0x000000011429787b -[AVAudioEngine connect:to:format:] + 83
    10  AudioConnectTest                    0x000000010344012b _T016AudioConnectTest0A5UnittC13playSineWave3ySo16AVAudioPCMBufferC8audiobuf_tF + 1211
    11  AudioConnectTest                    0x000000010343d72a _T016AudioConnectTest0A5UnittC9udpSocketySo011GCDAsyncUdpF0C_10Foundation4DataV10didReceiveAI11fromAddressypSg17withFilterContexttF + 202
    12  AudioConnectTest                    0x000000010343d95f _T016AudioConnectTest0A5UnittC9udpSocketySo011GCDAsyncUdpF0C_10Foundation4DataV10didReceiveAI11fromAddressypSg17withFilterContexttFTo + 383
    13  CocoaAsyncSocket                    0x00000001037826cc __72-[GCDAsyncUdpSocket notifyDidReceiveData:fromAddress:withFilterContext:]_block_invoke + 76
    14  libdispatch.dylib                   0x000000010987e2f7 _dispatch_call_block_and_release + 12
    15  libdispatch.dylib                   0x000000010987f33d _dispatch_client_callout + 8
    16  libdispatch.dylib                   0x000000010988a5f9 _dispatch_main_queue_callback_4CF + 628
    17  CoreFoundation                      0x00000001052c5e39 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    18  CoreFoundation                      0x000000010528a462 __CFRunLoopRun + 2402
    19  CoreFoundation                      0x0000000105289889 CFRunLoopRunSpecific + 409
    20  GraphicsServices                    0x000000010aec49c6 GSEventRunModal + 62
    21  UIKit                               0x00000001057815d6 UIApplicationMain + 159
    22  AudioConnectTest                    0x000000010344a647 main + 55
    23  libdyld.dylib                       0x00000001098fbd81 start + 1
    24  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

 該当のソースコード

宣言部分

    var audioEngine : AVAudioEngine!
    var audioFile : AVAudioFile!
    var audioPlayer : AVAudioPlayerNode!
    var outref: ExtAudioFileRef?
    var audioFilePlayer: AVAudioPlayerNode!
    var mixer : AVAudioMixerNode!
    var filePath : String? = nil
    var isPlay = false
    var isRec = false
    var captureSession = AVCaptureSession()
    var audioData:AVCaptureAudioDataOutput!

    var audioEngine1 : AVAudioEngine!
    var audioFilePlayer1: AVAudioPlayerNode!
    var mixer1 : AVAudioMixerNode!

    let audioEngine2 = AVAudioEngine()
    let player = AVAudioPlayerNode()
    var sinFlg = false


音声データ受信と再生部分

//音声データ受信
    func udpSocket(_ sock: GCDAsyncUdpSocket, didReceive data: Data, fromAddress address: Data, withFilterContext filterContext: Any?){
        let convertPcmData = toPCMBuffer(data: data as NSData)//受信したデータをPCMBufferに変換
       audioPlay(audiobuf: convertPcmData)//再生
    }

//再生
    func audioPlay(audiobuf:AVAudioPCMBuffer) {
        let format2 = AVAudioFormat(
            commonFormat: AVAudioCommonFormat.pcmFormatInt16,
            sampleRate: 8000.0,
            channels: 1,
            interleaved: false)

        if (audioEngine2.isRunning) {//2回目以降受信した場合は一度止めてから再生する。
            audioEngine2.stop()
            audioEngine2.reset()
        }       
        if(!sinFlg){
            sinFlg = true
            // オーディオエンジンにプレイヤーをアタッチ
            audioEngine2.attach(player)
        }
        let mixer = audioEngine2.mainMixerNode
        audioEngine2.connect(player, to: mixer, format: format2)//☆ここで上記のエラーが出てアプリが落ちてしまいます。
        player.scheduleBuffer(audiobuf) {
            print("Play completed")
        }      
        do {
            // エンジンを開始
            try audioEngine2.start()
            // 再生
            player.play()
        } catch let error {
            print(error)
        }
    }


マイクからの音声データ取得部分

    func startRecord() {

        let audioF = audioFilePlayer.outputFormat(forBus: 0)
        let asdf = audioF.channelCount
        let fasd = audioF.channelLayout
        let wre =  audioF.sampleRate

        self.filePath = nil

        self.isRec = true
        try! AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
        try! AVAudioSession.sharedInstance().setActive(true)
        let format = AVAudioFormat(
            commonFormat: AVAudioCommonFormat.pcmFormatInt16,
            sampleRate: 44100.0,
            channels: 1,
            interleaved: true)
        self.audioEngine.connect(self.audioEngine.inputNode, to: self.mixer, format: format)

        //データ変換用フォーマット
        let downFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16,
                                       sampleRate: 8000.0,
                                       channels: 1,
                                       interleaved: true)
        audioEngine.inputNode.installTap(onBus: 0, bufferSize: AVAudioFrameCount((format?.sampleRate)! * 0.1), format: format, block: { (buffer: AVAudioPCMBuffer!, time: AVAudioTime!) -> Void in
            //音声データを取得してサンプリングレートを8000kHに変換
            let converter = AVAudioConverter.init(from: format!, to: downFormat!)
            let newbuffer = AVAudioPCMBuffer(pcmFormat: downFormat!,
                                             frameCapacity: AVAudioFrameCount((downFormat?.sampleRate)! * 0.1))
            let inputBlock : AVAudioConverterInputBlock = { (inNumPackets, outStatus) -> AVAudioBuffer? in
                outStatus.pointee = AVAudioConverterInputStatus.haveData
                let audioBuffer : AVAudioBuffer = buffer
                return audioBuffer
            }
            var error : NSError?
            converter?.convert(to: newbuffer!, error: &error, withInputFrom: inputBlock)
            _ = ExtAudioFileWrite(self.outref!, (newbuffer?.frameLength)!, (newbuffer?.audioBufferList)!)

            let test = self.toNSData(PCMBuffer: newbuffer!)//PCMBufferからNSData型に変換
            self.sendData(data: test as Data)//NSDataからData型に変換してUDP通信で送信
        })

        try! self.audioEngine.start()
    }


データ変換部分

    func toNSData(PCMBuffer: AVAudioPCMBuffer) -> NSData {//PCMBufferをDataに変換
        let channelCount = 1  // given PCMBuffer channel count is 1
        var channels = UnsafeBufferPointer(start: PCMBuffer.int16ChannelData, count: channelCount)
        var ch0Data = NSData(bytes: channels[0], length:Int(PCMBuffer.frameCapacity * PCMBuffer.format.streamDescription.pointee.mBytesPerFrame))
        return ch0Data
    }
    func toPCMBuffer(data: NSData) -> AVAudioPCMBuffer {//データをPCMBufferに変換
        let audioFormat = AVAudioFormat(commonFormat: AVAudioCommonFormat.pcmFormatInt16, sampleRate: 8000, channels: 1, interleaved: true)  // given NSData audio format
        var PCMBuffer = AVAudioPCMBuffer(pcmFormat: audioFormat!, frameCapacity: UInt32(data.length) / (audioFormat?.streamDescription.pointee.mBytesPerFrame)!)
        PCMBuffer?.frameLength = (PCMBuffer?.frameCapacity)!
        let channels = UnsafeBufferPointer(start: PCMBuffer?.int16ChannelData, count: Int(PCMBuffer!.format.channelCount))
        data.getBytes(UnsafeMutableRawPointer(channels[0]) , length: data.length)
        return PCMBuffer!
    }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

まだ回答がついていません

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Swift

    7221questions

    Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

  • iOS

    3983questions

    iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。