3

I currently have a pair of Bluetooth earbuds. From this post, I have the code needed to retrieve audio from the Bluetooth earbuds' microphone and then playback the audio through the Bluetooth earbuds. However, I want to modify the code so that I can retrieve audio from the Bluetooth earbuds' microphone and then playback the audio through the phone's INTERNAL speaker / through any other pair of earbuds that may be physically connected to the phone. How would I go about doing that? This is my current code:

import UIKit
import AVFoundation

class PlayRecordVC: UIViewController, AVAudioRecorderDelegate {

    let audioSession = AVAudioSession.sharedInstance()
    let player = AVAudioPlayerNode()
    let engine = AVAudioEngine()

    override func viewDidLoad() {
        super.viewDidLoad()

        do{
            try audioSession.setCategory(.playAndRecord, mode: .default, options: [.allowBluetooth])
            try audioSession.overrideOutputAudioPort(.speaker)
            try audioSession.setActive(true)
        } catch{
            print(error.localizedDescription)
        }
        let input = engine.inputNode

        engine.attach(player)

        let bus = 0
        let inputFormat = input.inputFormat(forBus: bus)

        engine.connect(player, to: engine.mainMixerNode, format: inputFormat)

        input.installTap(onBus: bus, bufferSize: 512, format: inputFormat) { (buffer, time) -> Void in
            self.player.scheduleBuffer(buffer)
            print(buffer)
        }
    }

    @IBAction func start(_ sender: UIButton) {
        try! engine.start()
        player.play()
    }

    @IBAction func stop(_ sender: UIButton) {
        engine.stop()
        player.stop()
    }

}

UPDATE:

When I add the line audioSession.overrideOutputAudioPort(.speaker) no audio plays at all (neither from the Bluetooth earbuds nor the phone's internal speaker)

Nikhil Sridhar
  • 1,670
  • 5
  • 20
  • 39

2 Answers2

1

I haven't tested if this actually causes microphone to still be used by the headphones, but by default to route the audio to the speakers instead of the headphones this should work:

try! audioSession.overrideOutputAudioPort(.speaker)  

Here is the documentation of the method.

There is also this category option you could give to the audio session to have that same effect but also avoid it to be reset by gestures (like attaching new headphones).

If after your test you find out this actually moves also the recording to use the internal microphone, I think there is no other way (at least no other way that I found of).

Enricoza
  • 1,101
  • 6
  • 18
  • I tried both. The first resulted in no audio playing anywhere. The second resulted in the same thing as just having [.allowBluetooth]. However, I think something is wrong. When the only option I pass is [.defaultToSpeaker], no audio plays anywhere too. Any idea what's happening? – Nikhil Sridhar Apr 05 '20 at 19:18
1
let audioSession = AVAudioSession.sharedInstance()
do {

    try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, with: .allowBluetooth)
    try audioSession.setMode(AVAudioSessionModeDefault)
    try audioSession.setActive(true)
} catch {
print(error)
}

need to define the correct mode I believe. Not exactly sure which mode will work best in your case. https://developer.apple.com/documentation/avfoundation/avaudiosession/mode

This link below has extension for checking if microphone is plugged in. https://stackoverflow.com/a/52460651/8272698

Julian Silvestri
  • 1,970
  • 1
  • 15
  • 33