13

I'd like to know how to properly let my iPhone speak one sentence while my app is in background but then return to whatever was playing before.

My question is quite similar to AVSpeechSynthesizer in background mode but again with the difference that I want to be able to "say something" while in background without having to stop Music that is playing. So while my AVSpeechSynthesizer is speaking, music should pause (or be a bit less loud) but then it should resume. Even when my app is currently in background.

What I am trying to archive is a spoken summary of tracking-stats while GPS-Tracking in my fitness app. And chances are that you are listening to music is quite high, and I don't want to disturb the user...

Georg
  • 3,664
  • 3
  • 34
  • 75
  • Have you already looked at AVAudioSessionCategoryOptionMixWithOthers and similar options? – kvr Sep 05 '17 at 03:29

5 Answers5

14

I found the answer myself...

The important part ist to configure the AVAudioSession with the .duckOthers option:

let audioSession = AVAudioSession.sharedInstance()
try audioSession.setCategory(AVAudioSessionCategoryPlayback, with: .duckOthers)

This will make playback of f.e. music less loud but this would make it stay less loud even when speech is done. This is why you need to set a delegate for the AVSpeechSynthesizer and then handle it like so:

func speechSynthesizer(_ synthesizer: AVSpeechSynthesizer, didFinish utterance: AVSpeechUtterance) {
    guard !synthesizer.isSpeaking else { return }

    let audioSession = AVAudioSession.sharedInstance()
    try? audioSession.setActive(false)
}

That way, music will continue with normal volume after speech is done.

Also, right before speaking, I activate my audioSession just to make sure (not sure if that would really be necessary, but since I do so, I have no more problems...)

Georg
  • 3,664
  • 3
  • 34
  • 75
  • 1
    Use this: [.duckOthers, .interruptSpokenAudioAndMixWithOthers]. It will restore volume (of Music app, etc) to normal immediately after the spoken part is done. – Roboris Jan 06 '19 at 10:00
3

For swift 3, import AVKit then add

try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
Jayprakash Singh
  • 1,343
  • 3
  • 15
  • 28
  • You have to configure the audio session in your AppDelegate – Jayprakash Singh Sep 05 '17 at 05:42
  • I already found the solution... I have to set the category but also set the option to `.duckOthers`. Then I need a delegate for `AVSpeechSynthesizer` and set the session not active once it's done speaking... – Georg Sep 05 '17 at 06:33
  • AVKit has to replaced with AVFoundation and set "Audio and AirPlay" in background modes is obviously still needed – cristallo Sep 11 '17 at 10:21
2

for swift 4.2 / Xcode 10

unfortunately .duckOthers is no longer available; I managed to make it work like that:

    let audioSession = AVAudioSession.sharedInstance()
    try! audioSession.setCategory(AVAudioSession.Category.ambient, mode: .default)
mindeon
  • 41
  • 4
2

Swift 5.0

let synthesizer = AVSpeechSynthesizer()
let synthesizerVoice = AVSpeechSynthesisVoice(language: "en-US")

let str = "String"
let utterance = AVSpeechUtterance(string: str)

let audioSession = AVAudioSession.sharedInstance()
try! audioSession.setCategory(
    AVAudioSession.Category.playback,
    options: AVAudioSession.CategoryOptions.mixWithOthers
)
        
utterance.rate = 0.5
utterance.voice = synthesizerVoice

synthesizer.speak(utterance)
XY L
  • 25,431
  • 14
  • 84
  • 143
0

Background music don't stop and your word sound play even phone in silent mode by this two line of code before usual text to speech codes :

let audioSession = AVAudioSession.sharedInstance()
try!audioSession.setCategory(AVAudioSessionCategoryPlayback, with: AVAudioSessionCategoryOptions.mixWithOthers)
Hamid Reza Ansari
  • 1,107
  • 13
  • 16