Detecting if headphones are plugged into iPhone - iphone

Does anyone know if you can detect if headphones are plugged into the iPhone, and if they aren't - disable sound from your application.
I think I could manage disabling sound, but the detection part I have yet to find anything on.
Thanks

With this code you can detect the changes between:
MicrophoneWired
Headphone
LineOut
Speaker
Detecting when an iOS Device connector was plugged/unplugged
Note: Since iOS 5 part of the "audioRouteChangeListenerCallback(...)" behavior is deprecated but you can update it with:
// kAudioSession_AudioRouteChangeKey_PreviousRouteDescription -> Previous route
// kAudioSession_AudioRouteChangeKey_CurrentRouteDescription -> Current route
CFDictionaryRef newRouteRef = CFDictionaryGetValue(routeChangeDictionary, kAudioSession_AudioRouteChangeKey_CurrentRouteDescription);
NSDictionary *newRouteDict = (NSDictionary *)newRouteRef;
// RouteDetailedDescription_Outputs -> Output
// RouteDetailedDescription_Outputs -> Input
NSArray * paths = [[newRouteDict objectForKey: #"RouteDetailedDescription_Outputs"] count] ? [newRouteDict objectForKey: #"RouteDetailedDescription_Outputs"] : [newRouteDict objectForKey: #"RouteDetailedDescription_Inputs"];
NSString * newRouteString = [[paths objectAtIndex: 0] objectForKey: #"RouteDetailedDescription_PortType"];
// newRouteString -> MicrophoneWired, Speaker, LineOut, Headphone
Greetings

http://developer.apple.com/iphone/library/samplecode/SpeakHere/Introduction/Intro.html
In this project there is a code-snippet where it pauses recording if the headphones is unpluged. Maybe you can use it to achieve your result.
Good luck!
(edit)
You will have to study the SpeakHereController.mm file.
I found this code in the awakeFromNib method
// we do not want to allow recording if input is not available
error = AudioSessionGetProperty(kAudioSessionProperty_AudioInputAvailable, &size, &inputAvailable);
if (error) printf("ERROR GETTING INPUT AVAILABILITY! %d\n", error);
btn_record.enabled = (inputAvailable) ? YES : NO;
// we also need to listen to see if input availability changes
error = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioInputAvailable, propListener, self);
if (error) printf("ERROR ADDING AUDIO SESSION PROP LISTENER! %d\n", error);

Here is the solution, you may like it or it is helpful to you.
Before using below method please write this two line also
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_None; AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
(void)isHeadsetPluggedIn {
UInt32 routeSize = sizeof (CFStringRef); CFStringRef route;
AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &routeSize, &route);
//NSLog(#"Error >>>>>>>>>> :%#", error);
/* Known values of route:
"Headset"
"Headphone"
"Speaker"
"SpeakerAndMicrophone"
"HeadphonesAndMicrophone"
"HeadsetInOut"
"ReceiverAndMicrophone"
"Lineout" */
NSString* routeStr = (NSString*)route;
NSRange headsetRange = [routeStr rangeOfString : #"Headset"]; NSRange receiverRange = [routeStr rangeOfString : #"Receiver"];
if(headsetRange.location != NSNotFound) {
// Don't change the route if the headset is plugged in.
NSLog(#"headphone is plugged in ");
} else
if (receiverRange.location != NSNotFound) {
// Change to play on the speaker
NSLog(#"play on the speaker");
} else {
NSLog(#"Unknown audio route.");
}
}

To perform a one-off check to determine if headphones are plugged in (rather than setting a callback when they're unplugged) I found the following works in iOS5 and above:
- (BOOL) isAudioJackPlugged
{
// initialise the audio session - this should only be done once - so move this line to your AppDelegate
AudioSessionInitialize(NULL, NULL, NULL, NULL);
UInt32 routeSize;
// oddly, without calling this method caused an error.
AudioSessionGetPropertySize(kAudioSessionProperty_AudioRouteDescription, &routeSize);
CFDictionaryRef desc; // this is the dictionary to contain descriptions
// make the call to get the audio description and populate the desc dictionary
AudioSessionGetProperty (kAudioSessionProperty_AudioRouteDescription, &routeSize, &desc);
// the dictionary contains 2 keys, for input and output. Get output array
CFArrayRef outputs = CFDictionaryGetValue(desc, kAudioSession_AudioRouteKey_Outputs);
// the output array contains 1 element - a dictionary
CFDictionaryRef dict = CFArrayGetValueAtIndex(outputs, 0);
// get the output description from the dictionary
CFStringRef output = CFDictionaryGetValue(dict, kAudioSession_AudioRouteKey_Type);
/**
These are the possible output types:
kAudioSessionOutputRoute_LineOut
kAudioSessionOutputRoute_Headphones
kAudioSessionOutputRoute_BluetoothHFP
kAudioSessionOutputRoute_BluetoothA2DP
kAudioSessionOutputRoute_BuiltInReceiver
kAudioSessionOutputRoute_BuiltInSpeaker
kAudioSessionOutputRoute_USBAudio
kAudioSessionOutputRoute_HDMI
kAudioSessionOutputRoute_AirPlay
*/
return CFStringCompare(output, kAudioSessionOutputRoute_Headphones, 0) == kCFCompareEqualTo;
}
For those keeping score at home, that's a string in a dictionary in an array in a dictionary.

Related

Id UUID static over sessions?

I want to use an alternative to the UDID and found this:
+ (NSString *)GetUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
but in the simulator the method gives me different results every session?
Is this only in simulator?
I need to be sure that on actual devices the method returns me always the same string
to identify a user.
Is it true or not?
Mirza
CFUUIDRef will create different values at each session.
Solution 1:
Save the value in NSUserDefaults and next time onwards use it from the NSUserDefaults.
Solution 2:
You can use identifierForVendor for doing this.
NSString *udidVendor = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
According to UIDevice Class Reference:
The value of this property is the same for apps that come from the
same vendor running on the same device. A different value is returned
for apps on the same device that come from different vendors, and for
apps on different devices regardless of vendor.
Please check Unique Identifier In iOS 6
CFUUIDCreate gives you a Universally Unique Identifier every time you call that function, so each time you will get a different result (by definition).
What you can do is persist this in between sessions using, for example, NSUserDefaults, to uniquely identify a particular user (or bunch of user's settings).
CFUUID is not persisted at all.
Every time you call CFUUIDCreate the system will return to you a brand new unique identifier.
If you want to persist this identifier you will need to do that yourself using NSUserDefaults, Keychain, Pasteboard or some other means.
Read the code one line at a time and try to understand what it does. The CFUUIDCreate function creates a new UUID every time you call it. That would explain your finding. You need to save the value in NSUserDefaults* the first time and use that value the next time you launch the app:
+ (NSString *)GetUUID
{
NSString *string = [[NSUserDefaults standardUserDefaults] objectForKey: #"UUID"];
if (!string)
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
string = (NSString*)[CFUUIDCreateString(NULL, theUUID) autorelease];
CFRelease(theUUID);
[[NSUserDefaults standardUserDefaults] setObject: string forKey: #"UUID"];
}
return string;
}
*There are one small caveat of using NSUserDefaults - UUID will be created again if user uninstalls and reinstalls the app again. If you can't live with this, look into saving it in Keychain. Alternatively, you might want to look at OpenUDID.
The method will always return a unique string. If the app will only ever have a single user, run this method once when the user first launches the app and persist that string in a plist, or NSUserDefaults, or core data if you've already using it.
The link below may help with this UUID persistence logic:
UUID for app on iOS5
However, if the user then uninstalls and reinstalls the app, this persisted UUID will still be lost and need will be generated again.
Device IDs are also no longer allowed by Apple.
Assuming the UUID is required because the app connects to a server, as far as I know, you need the user log in to the server with a user name and password.
It is always different. UUID includes timestamps, so every time you call this function, you will get a different (random) one.
I have followed this approach in IDManager class,
This is a collection from different solutions. KeyChainUtil is a wrapper to read from keychain. A similar keychain util is found in github.
// IDManager.m
/*
A replacement for deprecated uniqueIdentifier API. Apple restrict using this from 1st May, 2013.
We have to consider,
* iOS <6 have not the ASIIdentifer API
* When the user upgrade from iOS < 6 to >6
- Check if there is a UUID already stored in keychain. Then use that.
- In that case, this UUID is constant for whole device lifetime. Keychain item is not deleted with application deletion.
*/
#import "IDManager.h"
#import "KeychainUtils.h"
#import "CommonUtil.h"
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
#import <AdSupport/AdSupport.h>
#endif
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <net/if.h>
#include <net/if_dl.h>
/* Apple confirmed this bug in their system in response to a Technical Support Incident request. They said that identifierForVendor and advertisingIdentifier sometimes returning all zeros can be seen both in development builds and apps downloaded over the air from the App Store. They have no work around and can't say when the problem will be fixed. */
#define kBuggyASIID #"00000000-0000-0000-0000-000000000000"
#pragma mark
#pragma mark
#implementation IDManager
+ (NSString *) getUniqueID {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
if (NSClassFromString(#"ASIdentifierManager")) {
NSString * asiID = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
if ([asiID compare:kBuggyASIID] == NSOrderedSame) {
NSLog(#"Error: This device return buggy advertisingIdentifier.");
return [IDManager getUniqueUUID];
} else {
return asiID;
}
} else {
#endif
return [IDManager getUniqueUUID];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 60000
}
#endif
}
+ (NSString *) getUniqueUUID
{
NSError * error;
NSString * uuid = [KeychainUtils getPasswordForUsername:#"UserName" andServiceName:#"YourServiceName" error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
if (!uuid) {
DLog(#"No UUID found. Creating a new one.");
uuid = [IDManager getUUID];
uuid = [CommonUtil md5String:uuid]; // create md5 hash for security reason
[KeychainUtils storeUsername:#"UserName" andPassword:uuid forServiceName:#"YourServiceName" updateExisting:YES error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
}
return uuid;
}
+ (NSString *) readUUIDFromKeyChain {
NSError * error;
NSString * uuid = [KeychainUtils getPasswordForUsername:#"UserName" andServiceName:#"YourServiceName" error:&error];
if (error) {
NSLog(#"Error geting unique UUID for this device! %#", [error localizedDescription]);
return nil;
}
return uuid;
}
/* NSUUID is after iOS 6. So we are using CFUUID for compatibility with iOS 4.3 */
+ (NSString *)getUUID
{
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
return [(NSString *)string autorelease];
}
#pragma mark - MAC address
/* THIS WILL NOT WORK IN iOS 7. IT WILL RETURN A CONSTANT MAC ADDRESS ALL THE TIME.
SEE - https://developer.apple.com/news/?id=8222013a
*/
// Return the local MAC address
// Courtesy of FreeBSD hackers email list
// Last fallback for unique identifier
+ (NSString *) getMACAddress
{
int mib[6];
size_t len;
char *buf;
unsigned char *ptr;
struct if_msghdr *ifm;
struct sockaddr_dl *sdl;
mib[0] = CTL_NET;
mib[1] = AF_ROUTE;
mib[2] = 0;
mib[3] = AF_LINK;
mib[4] = NET_RT_IFLIST;
if ((mib[5] = if_nametoindex("en0")) == 0) {
printf("Error: if_nametoindex error\n");
return NULL;
}
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
printf("Error: sysctl, take 1\n");
return NULL;
}
if ((buf = malloc(len)) == NULL) {
printf("Error: Memory allocation error\n");
return NULL;
}
if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
printf("Error: sysctl, take 2\n");
free(buf); // Thanks, Remy "Psy" Demerest
return NULL;
}
ifm = (struct if_msghdr *)buf;
sdl = (struct sockaddr_dl *)(ifm + 1);
ptr = (unsigned char *)LLADDR(sdl);
NSString *outstring = [NSString stringWithFormat:#"%02X:%02X:%02X:%02X:%02X:%02X",
*ptr, *(ptr+1), *(ptr+2), *(ptr+3), *(ptr+4), *(ptr+5)];
free(buf);
return outstring;
}
+ (NSString *) getHashedMACAddress
{
NSString * mac = [IDManager getMACAddress];
return [CommonUtil md5String:mac];
}
#end

How to determine sound recording source

While recording a sound using my iPad app, how can I know if the source of the sound is from the built-in microphone or headphone microphone?
Additional information: iOS ver 4.2 and above.
The way to determine this is to poll the hardware and query the current audio route.
Use the AudioSessionGetProperty object to get back the route of the audio.
This example by #TPoschel should set you on the right track.
- (void)playSound:(id) sender
{
if(player){
CFStringRef route;
UInt32 propertySize = sizeof(CFStringRef);
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &route);
if((route == NULL) || (CFStringGetLength(route) == 0)){
// Silent Mode
NSLog(#"AudioRoute: SILENT");
} else {
NSString* routeStr = (NSString*)route;
NSLog(#"AudioRoute: %#", routeStr);
/* Known values of route:
* "Headset"
* "Headphone"
* "Speaker"
* "SpeakerAndMicrophone"
* "HeadphonesAndMicrophone"
* "HeadsetInOut"
* "ReceiverAndMicrophone"
* "Lineout"
*/
NSRange headphoneRange = [routeStr rangeOfString : #"Headphone"];
NSRange headsetRange = [routeStr rangeOfString : #"Headset"];
NSRange receiverRange = [routeStr rangeOfString : #"Receiver"];
NSRange speakerRange = [routeStr rangeOfString : #"Speaker"];
NSRange lineoutRange = [routeStr rangeOfString : #"Lineout"];
if (headphoneRange.location != NSNotFound) {
// Don't change the route if the headphone is plugged in.
} else if(headsetRange.location != NSNotFound) {
// Don't change the route if the headset is plugged in.
} else if (receiverRange.location != NSNotFound) {
// Change to play on the speaker
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
} else if (speakerRange.location != NSNotFound) {
// Don't change the route if the speaker is currently playing.
} else if (lineoutRange.location != NSNotFound) {
// Don't change the route if the lineout is plugged in.
} else {
NSLog(#"Unknown audio route.");
}
}
[player play];
}
}

Redirecting audio output to phone speaker and mic input to headphones

Is it possible to redirect audio output to the phone speaker and still use the microphone headphone input?
If i redirect the audio route to the phone speaker instead of the headphones it also redirects the mic. This makes sense but I can't seem to just be able to just redirect the mic input? Any ideas?
Here is the code I'm using to redirect audio to the speaker:
UInt32 doChangeDefaultRoute = true;
propertySetError = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryDefaultToSpeaker, sizeof(doChangeDefaultRoute), &doChangeDefaultRoute);
NSAssert(propertySetError == 0, #"Failed to set audio session property: OverrideCategoryDefaultToSpeaker");
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
This is possible, but it's picky about how you set it up.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
It's very important to use AVAudioSessionCategoryPlayAndRecord or the route will fail to go to the speaker. Once you've set the override route for the audio session, you can use an AVAudioPlayer instance and send some output to the speaker.
Hope that works for others like it did for me. The documentation on this is scattered, but the Skype app proves it's possible. Persevere, my friends! :)
Some Apple documentation here: http://developer.apple.com/library/ios/#documentation/AudioToolbox/Reference/AudioSessionServicesReference/Reference/reference.html
Do a search on the page for kAudioSessionProperty_OverrideAudioRoute
It doesn't look like it's possible, I'm afraid.
From the Audio Session Programming Guide - kAudioSessionProperty_OverrideAudioRoute
If a headset is plugged in at the time you set this property’s value
to kAudioSessionOverrideAudioRoute_Speaker, the system changes the
audio routing for input as well as for output: input comes from the
built-in microphone; output goes to the built-in speaker.
Possible duplicate of this question
What you can do is to force audio output to speakers in any case:
From UI Hacker - iOS: Force audio output to speakers while headphones are plugged in
#interface AudioRouter : NSObject
+ (void) initAudioSessionRouting;
+ (void) switchToDefaultHardware;
+ (void) forceOutputToBuiltInSpeakers;
#end
and
#import "AudioRouter.h"
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#implementation AudioRouter
#define IS_DEBUGGING NO
#define IS_DEBUGGING_EXTRA_INFO NO
+ (void) initAudioSessionRouting {
// Called once to route all audio through speakers, even if something's plugged into the headphone jack
static BOOL audioSessionSetup = NO;
if (audioSessionSetup == NO) {
// set category to accept properties assigned below
NSError *sessionError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error: &sessionError];
// Doubly force audio to come out of speaker
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
// fix issue with audio interrupting video recording - allow audio to mix on top of other media
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
// set active
[[AVAudioSession sharedInstance] setDelegate:self];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
// add listener for audio input changes
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, onAudioRouteChange, nil );
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioInputAvailable, onAudioRouteChange, nil );
}
// Force audio to come out of speaker
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
// set flag
audioSessionSetup = YES;
}
+ (void) switchToDefaultHardware {
// Remove forcing to built-in speaker
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_None;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
}
+ (void) forceOutputToBuiltInSpeakers {
// Re-force audio to come out of speaker
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
}
void onAudioRouteChange (void* clientData, AudioSessionPropertyID inID, UInt32 dataSize, const void* inData) {
if( IS_DEBUGGING == YES ) {
NSLog(#"==== Audio Harware Status ====");
NSLog(#"Current Input: %#", [AudioRouter getAudioSessionInput]);
NSLog(#"Current Output: %#", [AudioRouter getAudioSessionOutput]);
NSLog(#"Current hardware route: %#", [AudioRouter getAudioSessionRoute]);
NSLog(#"==============================");
}
if( IS_DEBUGGING_EXTRA_INFO == YES ) {
NSLog(#"==== Audio Harware Status (EXTENDED) ====");
CFDictionaryRef dict = (CFDictionaryRef)inData;
CFNumberRef reason = CFDictionaryGetValue(dict, kAudioSession_RouteChangeKey_Reason);
CFDictionaryRef oldRoute = CFDictionaryGetValue(dict, kAudioSession_AudioRouteChangeKey_PreviousRouteDescription);
CFDictionaryRef newRoute = CFDictionaryGetValue(dict, kAudioSession_AudioRouteChangeKey_CurrentRouteDescription);
NSLog(#"Audio old route: %#", oldRoute);
NSLog(#"Audio new route: %#", newRoute);
NSLog(#"=========================================");
}
}
+ (NSString*) getAudioSessionInput {
UInt32 routeSize;
AudioSessionGetPropertySize(kAudioSessionProperty_AudioRouteDescription, &routeSize);
CFDictionaryRef desc; // this is the dictionary to contain descriptions
// make the call to get the audio description and populate the desc dictionary
AudioSessionGetProperty (kAudioSessionProperty_AudioRouteDescription, &routeSize, &desc);
// the dictionary contains 2 keys, for input and output. Get output array
CFArrayRef outputs = CFDictionaryGetValue(desc, kAudioSession_AudioRouteKey_Inputs);
// the output array contains 1 element - a dictionary
CFDictionaryRef diction = CFArrayGetValueAtIndex(outputs, 0);
// get the output description from the dictionary
CFStringRef input = CFDictionaryGetValue(diction, kAudioSession_AudioRouteKey_Type);
return [NSString stringWithFormat:#"%#", input];
}
+ (NSString*) getAudioSessionOutput {
UInt32 routeSize;
AudioSessionGetPropertySize(kAudioSessionProperty_AudioRouteDescription, &routeSize);
CFDictionaryRef desc; // this is the dictionary to contain descriptions
// make the call to get the audio description and populate the desc dictionary
AudioSessionGetProperty (kAudioSessionProperty_AudioRouteDescription, &routeSize, &desc);
// the dictionary contains 2 keys, for input and output. Get output array
CFArrayRef outputs = CFDictionaryGetValue(desc, kAudioSession_AudioRouteKey_Outputs);
// the output array contains 1 element - a dictionary
CFDictionaryRef diction = CFArrayGetValueAtIndex(outputs, 0);
// get the output description from the dictionary
CFStringRef output = CFDictionaryGetValue(diction, kAudioSession_AudioRouteKey_Type);
return [NSString stringWithFormat:#"%#", output];
}
+ (NSString*) getAudioSessionRoute {
/*
returns the current session route:
* ReceiverAndMicrophone
* HeadsetInOut
* Headset
* HeadphonesAndMicrophone
* Headphone
* SpeakerAndMicrophone
* Speaker
* HeadsetBT
* LineInOut
* Lineout
* Default
*/
UInt32 rSize = sizeof (CFStringRef);
CFStringRef route;
AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &rSize, &route);
if (route == NULL) {
NSLog(#"Silent switch is currently on");
return #"None";
}
return [NSString stringWithFormat:#"%#", route];
}
#end

Detect if headphones (not microphone) are plugged in to an iOS device [duplicate]

This question already has answers here:
Are headphones plugged in? iOS7
(8 answers)
Closed 6 years ago.
I need to change my audio depending on whether or not headphones are plugged in. I'm aware of kAudioSessionProperty_AudioInputAvailable, which will tell me if there's a microphone, but I'd like to test for any headphones, not just headphones with a built-in microphone. Is this possible?
Here is a method of my own which is a slightly modified version of one found on this site : http://www.iphonedevsdk.com/forum/iphone-sdk-development/9982-play-record-same-time.html
- (BOOL)isHeadsetPluggedIn {
UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
OSStatus error = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
&routeSize,
&route);
/* Known values of route:
* "Headset"
* "Headphone"
* "Speaker"
* "SpeakerAndMicrophone"
* "HeadphonesAndMicrophone"
* "HeadsetInOut"
* "ReceiverAndMicrophone"
* "Lineout"
*/
if (!error && (route != NULL)) {
NSString* routeStr = (NSString*)route;
NSRange headphoneRange = [routeStr rangeOfString : #"Head"];
if (headphoneRange.location != NSNotFound) return YES;
}
return NO;
}
Here's a solution based on rob mayoff's comment:
- (BOOL)isHeadsetPluggedIn
{
AVAudioSessionRouteDescription *route = [[AVAudioSession sharedInstance] currentRoute];
BOOL headphonesLocated = NO;
for( AVAudioSessionPortDescription *portDescription in route.outputs )
{
headphonesLocated |= ( [portDescription.portType isEqualToString:AVAudioSessionPortHeadphones] );
}
return headphonesLocated;
}
Simply link to the AVFoundation framework.
Just a heads up for any future readers of this post.
Most of the AVToolbox methods have been deprecated with the release of iOS 7 without alternative so audio listeners are now largely redundancy
I started with the code given above by jpsetung, but there were a few issues with it for my use case:
No evidence of something called kAudioSessionProperty_AudioRoute in the docs
Leaks route
No audio session check
String check for headphones instead of logical awareness of categories
I was more interested in whether the iPhone was using its speakers, with "headphones" meaning "anything other than speakers". I feel that leaving out options like "bluetooth", "airplay", or "lineout" was dangerous.
This implementation broadens the check to allow for any type of specified output:
BOOL isAudioRouteAvailable(CFStringRef routeType)
{
/*
As of iOS 5:
kAudioSessionOutputRoute_LineOut;
kAudioSessionOutputRoute_Headphones;
kAudioSessionOutputRoute_BluetoothHFP;
kAudioSessionOutputRoute_BluetoothA2DP;
kAudioSessionOutputRoute_BuiltInReceiver;
kAudioSessionOutputRoute_BuiltInSpeaker;
kAudioSessionOutputRoute_USBAudio;
kAudioSessionOutputRoute_HDMI;
kAudioSessionOutputRoute_AirPlay;
*/
//Prep
BOOL foundRoute = NO;
CFDictionaryRef description = NULL;
//Session
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
AudioSessionInitialize(NULL, NULL, NULL, NULL);
});
//Property
UInt32 propertySize;
AudioSessionGetPropertySize(kAudioSessionProperty_AudioRouteDescription, &propertySize);
OSStatus error = AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &propertySize, &description);
if ( !error && description ) {
CFArrayRef outputs = CFDictionaryGetValue(description, kAudioSession_AudioRouteKey_Outputs);
CFIndex count = CFArrayGetCount(outputs);
if ( outputs && count ) {
for (CFIndex i = 0; i < count; i++) {
CFDictionaryRef route = CFArrayGetValueAtIndex(outputs, i);
CFStringRef type = CFDictionaryGetValue(route, kAudioSession_AudioRouteKey_Type);
NSLog(#"Got audio route %#", type);
//Audio route type
if ( CFStringCompare(type, routeType, 0) == kCFCompareEqualTo ) {
foundRoute = YES;
break;
}
}
}
} else if ( error ) {
NSLog(#"Audio route error %ld", error);
}
//Cleanup
if ( description ) {
CFRelease(description);
}
//Done
return foundRoute;
}
Used like so:
if ( isAudioRouteAvailable(kAudioSessionOutputRoute_BuiltInSpeaker) ) {
//Do great things...
}

Why does this Audio Unit RemoteIO initialisation work on iPhone but not in simulator?

I am using the Audio Unit services to set up an output rendering callback so I can mix together synthesized audio. The code I have seems to work perfectly on the devices I have (iPod Touch, iPhone 3G, and iPad) but fails to work on the simulator.
On the simulator, the AudioUnitInitialise function fails and returns a value of -10851 (kAudioUnitErr_InvalidPropertyValue according to Apple documentation).
Here is my initialisation code.. anyone with more experience with this API than I see anything I'm doing incorrect here?
#define kOutputBus 0
#define kInputBus 1
...
static OSStatus playbackCallback(void *inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
// Mix audio here - but it never gets here on the simulator
return noErr;
}
...
{
OSStatus status;
// Describe audio component
AudioComponentDescription desc;
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
// Get component
AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
// Get audio units
status = AudioComponentInstanceNew(inputComponent, &m_audio_unit);
if(status != noErr) {
NSLog(#"Failed to get audio component instance: %d", status);
}
// Enable IO for playback
UInt32 flag = 1;
status = AudioUnitSetProperty(m_audio_unit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kOutputBus,
&flag,
sizeof(flag));
if(status != noErr) {
NSLog(#"Failed to enable audio i/o for playback: %d", status);
}
// Describe format
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 2;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBytesPerFrame = 4;
// Apply format
status = AudioUnitSetProperty(m_audio_unit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kOutputBus,
&audioFormat,
sizeof(audioFormat));
if(status != noErr) {
NSLog(#"Failed to set format descriptor: %d", status);
}
// Set output callback
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = playbackCallback;
callbackStruct.inputProcRefCon = self;
status = AudioUnitSetProperty(m_audio_unit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Global,
kOutputBus,
&callbackStruct,
sizeof(callbackStruct));
if(status != noErr) {
NSLog(#"Failed to set output callback: %d", status);
}
// Initialize (This is where it fails on the simulator)
status = AudioUnitInitialize(m_audio_unit);
if(status != noErr) {
NSLog(#"Failed to initialise audio unit: %d", status);
}
}
My XCode version is 3.2.2 (64 bit)
My Simulator version is 3.2 (Though the same issue occurs in 3.1.3 Debug or Release)
Thanks, I appreciate it!
compiling for a device and for a simulator is totally different. Most common things have the same expected result. For example loading a view switch between them playing sounds and so on. However when it comes to other things like playing sound with OpenAL loading 10 buffers and then switching between them the simulator cannot handle that but the devices can.
The way i see it is as long as it works on the device that's all I care about. Try not t pull your hair out just to make an application work on a simulator when it works fine on the device.
hope that helps
Pk
Did you configure and enable an Audio Session prior to calling your RemoteIO initialization code?
When you are setting the stream properties to the input bus, you are using kOutputBus for your input scope. That's probably not good. Also, you probably don't need to apply the render callback to the global scope, as you only need it for output. Furthermore, I think that your definitions of kOutputBus and kInputBus are wrong... when I look at working iPhone Audio code, it uses 0 for the input bus and 1 for the output bus.
I can also think of a few minor things in regards to the AudioStreamBasicDescription, though I don't think these will make much of a difference:
Add the kAudioFormatFlagsNativeEndian property to your format flags
Explicitly set the mReserved field to 0.

Categories