user1871869
user1871869

Reputation: 3367

Music is softer after Audio Ducking?

I have some code for implementing audio ducking in my application. When a user is listening to some music, then enters a certain location, a particular clip of music will play. When that occurs, the original music that is playing "ducks" (or becomes much more silent) and the new music clip that my application sets out to play plays. After that music finishes playing however, the original music that was playing plays again but is much softer than it was before. Any ideas on how to fix this? Below is my code:

audioPlayer = AVAudioPlayer(data: NSData(contentsOfMappedFile: musicFilePath), error: nil)

//Before music is played, make sure background music is off and audio ducking is turned on
AVAudioSession.sharedInstance().setActive(false, withOptions: AVAudioSessionSetActiveOptions.allZeros, error: nil)
        AVAudioSession.sharedInstance().setCategory("AVAudioSessionCategoryPlayback", withOptions: AVAudioSessionCategoryOptions.DuckOthers, error: nil)

audioPlayer.prepareToPlay()
audioPlayer.play()

//Allows audio player to play in the background & turn back on previously played music.
AVAudioSession.sharedInstance().setCategory("AVAudioSessionCategoryAmbient", withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil) 

I have tried to look at the documentation but haven't found much. Any help would be appreciated. Thanks!

EDIT: How code looks like when audio ducking based on answer below:

       audioPlayer = AVAudioPlayer(data: NSData(contentsOfMappedFile: musicFilePath), error: nil)

        AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
        AVAudioSession.sharedInstance().setActive(true, error: nil)

        audioPlayer.prepareToPlay()
        audioPlayer.play()

        AVAudioSession.sharedInstance().setActive(false, error: nil)
        AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
        AVAudioSession.sharedInstance().setActive(true, error: nil)

EDIT 2:

audioPlayer = AVAudioPlayer(data: NSData(contentsOfMappedFile: musicFilePath), error: nil)
audioPlayer.delegate = self
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)

audioPlayer.prepareToPlay()
audioPlayer.play()

Then in in the audioPlayerDidFinishPlaying method, I placed the code to duck the audio:

func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool){
    //Prepare to play after Sound finished playing
    AVAudioSession.sharedInstance().setActive(false, error: nil)
    AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
    AVAudioSession.sharedInstance().setActive(true, error: nil)
}

Upvotes: 2

Views: 2630

Answers (3)

chings228
chings228

Reputation: 1889

set audiosession active :YES when read , after finish read , set back to NO at didfinish delegate

-(void)createAVSpeech : (NSString*) readString{


    audioSession_ = [AVAudioSession sharedInstance];

    NSError *setCategoryError = nil;

    [audioSession_ setCategory:AVAudioSessionCategoryPlayback
                   withOptions:AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError];

    NSError *activationError = nil;
    [audioSession_ setActive:YES error:&activationError];

    speech_ = [[AVSpeechSynthesizer alloc] init];
    speech_.delegate = self;

    AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:readString];
    NSString *readLang = @"en-US";
    float speedrate = 1;
    utterance.rate = AVSpeechUtteranceDefaultSpeechRate * speedrate;
    utterance.pitchMultiplier =0.8;

    utterance.voice = [AVSpeechSynthesisVoice voiceWithLanguage:readLang];

    [speech_ speakUtterance:utterance];
}


-(void)speechSynthesizer:(AVSpeechSynthesizer *)synthesizer didFinishSpeechUtterance:(AVSpeechUtterance *)utterance{

    NSLog(@"did finish speech");
        [audioSession_ setActive:NO error:nil];
}

Upvotes: 2

RemyNL
RemyNL

Reputation: 596

I run into the same problem and this post really helped me, but I had to temper with the code to get it to work. I feel I should post my code to help people....

import AVFoundation

class TextToSpeechService: NSObject {
  let synth = AVSpeechSynthesizer()
  let audioSession = AVAudioSession.sharedInstance()
  override init() {
    super.init()
    synth.delegate = self
    do {
      try audioSession.setCategory(AVAudioSessionCategoryPlayback, withOptions: .DuckOthers)
    } catch {
      print("AVAudioSession: cannot set category")
    }
  }
  func saySomething(text: String) {
    let myUtterance = AVSpeechUtterance(string: text)       
    do {
      try audioSession.setActive(true)
    } catch {
      print("AVAudioSession: cannot activate")
    }
    synth.speakUtterance(myUtterance)
  }
}

extension TextToSpeechService: AVSpeechSynthesizerDelegate {
  func speechSynthesizer(synthesizer: AVSpeechSynthesizer, didFinishSpeechUtterance utterance: AVSpeechUtterance) {
    do {
      try audioSession.setActive(false)
    } catch {
      print("AVAudioSession: cannot deactivate")
    }
  }
}

I this code I use the AVSpeechSynthesizer, but it is equal to using AVAudioPlayer. I start audio that is being ducked and unducked with

let tts = TextToSpeechService()
tts.saySomething("Hello StackOverflow")

The difference with previous answers and the OP's code is the fact that the audioSession is activated only when the app is actually playing sound and is immediately deactivated when finished playing sound. The other examples contain all sorts of extra code (might be useful when your app is indeed also playing ambient sounds, but I don't see any reference to that in the OP's question.)

(code tested in XCode 7.3.1 with Swift 2.2 on iOS 9.3.2)

Upvotes: 0

Gordon Childs
Gordon Childs

Reputation: 36159

You're not activating your ducked session, so I'm not sure why it's working at all. Can you duck by doing this?

AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)

To unduck it should be enough to deactive the session:

AVAudioSession.sharedInstance().setActive(false, error: nil)

Otherwise try deactivating your session and reactivating it after turning off ducking?

AVAudioSession.sharedInstance().setActive(false, error: nil)
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, withOptions: AVAudioSessionCategoryOptions.allZeros, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)

p.s. use the category symbols instead of raw strings!

Upvotes: 0

Related Questions