12

I am trying to perform a vibration in an app similar to Snapchat, that uses both audio output and input as well as supports audio mixing from other apps, but this seems to be a harder task that I initially thought it would be. Important to know is that I am not trying to vibrate during playback or recording. From reading all the documentation I could find on the subject, this is what I have come to understand:

  • In order to support both playback and recording (output and input), I need to use AVAudioSessionCategoryPlayAndRecord
  • Making the phone vibrate through AudioServicesPlaySystemSound (kSystemSoundID_Vibrate) is not supported in any of the recording categories, including AVAudioSessionCategoryPlayAndRecord.
  • Enabling other apps to play audio can be done by adding the option AVAudioSessionCategoryOptionMixWithOthers.

Therefore, I do this in my app delegate:

NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:&error];

The possible solutions to doing the vibration that I have tried but failed at are:

  • Deactivating the shared AVAudioSession before vibrating, and then activate it straight after.

    [[AVAudioSession sharedInstance] setActive:NO error:nil];         
    AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    

    This successfully performs the vibration, but afterwards, when I try to record a movie, the audio is ducked (or something else is causing it to be very silent). It also gives me an error saying that I am not allowed to deactivate a session without removing its I/O devices first.

  • Changing category before vibrating, and then changing it back.

    [[AVAudioSession sharedInstance] setActive:NO error:nil];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    
    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
    
    [[AVAudioSession sharedInstance] setActive:NO error:nil];
    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers error:nil];
    [[AVAudioSession sharedInstance] setActive:YES error:nil];
    

    This solution comes up every now and then, but does not seem to work for me. No vibration occurs, even though the categories seems to be set. This might still be a valid solution if I set usesApplicationAudioSession = YES on my AVCaptureSession, but I haven't made it work yet.

Sources:

Daniel Larsson
  • 6,278
  • 5
  • 44
  • 82

4 Answers4

3

So, I've been recently trying to play a simple beep in my app and one of the methods of doing so I stumbled upon is:

AudioServicesPlaySystemSound(SystemSoundID inSystemSoundID);

It's a function that plays a system sound from /System/Library/Audio/UISounds/ it's supported on all iOS versions.

The sound 1350 equals to RingerVibeChanged (vibration). So..

AudioServicesPlaySystemSound(1350);

...vibrates for about 0.5 seconds.

Incase you're interested in more sounds here is a link to all the playable sounds and what iOS versions they've been added in: http://iphonedevwiki.net/index.php/AudioServices

Community
  • 1
  • 1
Adam Svestka
  • 112
  • 2
  • 8
  • Using AudioServicesPlaySystemSound to perform a vibration is the problem I am having. Using 1350 instead of kSystemSoundID_Vibrate (which is just an alias for 4095) doesn't make a difference. – Daniel Larsson Oct 02 '16 at 03:51
1

You didn't say what your supported device requirements are, but for newer devices, you can use the new taptic engine APIs, such as:

UIImpactFeedbackGenerator

UISelectionFeedbackGenerator

UINotificationFeedbackGenerator

https://developer.apple.com/reference/uikit/uifeedbackgenerator#2555399

atomkirk
  • 3,701
  • 27
  • 30
1

For reference, I'm not using AVCaptureSession but an AVAudioEngine to record something, and for my case I pinpointed it down to having to pause the input in order to play the vibration properly with AudioServicesPlaySystemSound during a record session.

So in my case, I did something like this

audioEngine?.pause()
AudioServicesPlaySystemSound(1519)
do {
  try audioEngine.start()
} catch let error {
  print("An error occurred starting audio engine. \(error.localizedDescription)")
}

I'm not sure if it would also work with doing something similar for AVCaptureSession (i.e., stopRunning() and startRunning()), but leaving this here in case someone wants to give it a try.

jayOrange
  • 123
  • 1
  • 9
0

You may try setting the allowHapticsAndSystemSoundsDuringRecording flag on AVAudioSession to true (the flag defaults to false) by calling

sessionInstance.setAllowHapticsAndSystemSoundsDuringRecording(true)

It worked for me.

Note the system prevents haptic and sound feedback (such as those from a UIPickerView or UIDatePicker) from playing, to avoid unexpected system noise while recording.

leo
  • 360
  • 2
  • 7