stereo recording on iPhone - ios

The iPhone 5 has three microphones - top front, top back, and bottom. I would like to record on all of them at the same time to do some signal processing. I've tried for several days unsuccessfully.
Using AVAudioSession, I can see the microphones:
NSLog(#"%#", [AVAudioSession sharedInstance].availableInputs);
"<AVAudioSessionPortDescription: 0x14554400, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = Back>"
NSLog(#"%#", [AVAudioSession sharedInstance].availableInputs[0].inputDataSources);
"<AVAudioSessionDataSourceDescription: 0x145afb00, ID = 1835216945; name = Bottom>",
"<AVAudioSessionDataSourceDescription: 0x145b1870, ID = 1835216946; name = Front>",
"<AVAudioSessionDataSourceDescription: 0x145b3650, ID = 1835216947; name = Back>"
I can use AVAudioSessionPortDescription -setPreferredDataSource:error: to record from one of the three. But I cannot record on more than one simultaneously. If I set the number of input channels to 2, I get two identical tracks from the same microphone.
AVAudioRecorder has a property channelAssignments which seems like it should work, but AVAudioSession inputNumberOfChannels and maximumInputNumberOfChannels are both 1. The property channelAssignments is designed for auxiliary microphones which have multiple channels.
I tried using the low-level AudioUnit, but I get the same result. I could not find any properties on AudioUnit to change the input source.
Any help would be appreciated.

My understanding, after all my research trying to do the same thing, is just what you've described - you can't prefer multiple data sources for the one device, thus you can't record from multiple built-in mics at once. If anyone can prove me wrong, I'd VERY much love to hear it!
Sidenote: I can't seem to run your code. As written, I get
Property availableInputs not found on object of type 'id'
Even after massaging what you've got into a format that doesn't require any explicit casts:
NSLog(#"%#", [[[AVAudioSession sharedInstance] availableInputs][0] inputDataSources]);
I get SIGABRT:
-[AVAudioSessionPortDescription inputDataSources]: unrecognized selector sent to instance 0xd59dbe0'
what SDK are you using that your code actually compiles, much less runs?

Related

Question about AVAudioSession channel UID

I'm using AVAudioSession to see which route I'm on at the moment.
Now, looking at the output response I see something like this:
<AVAudioSessionPortDescription: 0x28263c1c0, type = BluetoothA2DPOutput; name = FreeBuds Lite; UID = E4:34:93:3C:4B:5F-tacl; selectedDataSource = (null)>
According to Apple's documentation the UID is A system-assigned unique identifier (UID) for the port. I wanted to know if it means that for each different device the number will be different (so far its seems it is) and also that for the same device the number will be presistent an won't change over time?

AVPlayer Audio Output

Through AVCaptureSession I record a video and then immediately play it back via an AVPlayer once recording has stopped.
My problem is that the audio from the video sometimes plays out of the ear speaker at a really low volume and other times plays out of the bottom speaker.
How can I default the audio to output to the bottom speaker?
I've looked at other related posts with instances of the below code, which I tried, but to no avail..Any guidance would be appreciated.
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(.playAndRecord)
try session.overrideOutputAudioPort(AVAudioSession.PortOverride.none)
try session.setActive(true)
} catch {
print ("error")
}
You're explicitly turning that off here:
try session.overrideOutputAudioPort(AVAudioSession.PortOverride.none)
If you want to prefer the speaker, you'd use:
try session.overrideOutputAudioPort(.speaker)
AVAudioSession is very complicated, and many parts of it are not intuitive. Do not copy code you find on the internet without reading the docs on each command. The docs are pretty good, but you have to read them.
That said, rather than doing this, I'd probably switch your category and options when you switch to playback. You can do that at any time:
try session.setCategory(.playback, options: [.defaultToSpeaker])
It is generally best to keep your category aligned what you're doing. If you set .playback here as the category, you may not even need .defaultToSpeaker, depending on what precisely you're trying to achieve.
Be certain to read all the relevant docs on .defaultToSpeaker, setCategory, overrideOutputAudioPort, etc. Don't just copy my suggestions. These settings have many subtle (and documented) interactions, you need to configure it based on your actual use case, not just copy something that "seems to work." You may be very surprised at what happens when the user switches to Bluetooth, or plugs headphones, or switches to CarPlay.
You can change the audio output device for a given AVPlayer instance by setting the instance property 'audioOutputDeviceUniqueID' to the UniqueID of the desired device.
I can confirm that this works as expected in MacOS 10.11.6, using Key-Value coding ( setValue:forKey:)
Apple's doc on this:
Instance Property
audioOutputDeviceUniqueID
Specifies the unique ID of the Core Audio output device used to play audio.
Declaration
#property(nonatomic, copy) NSString *audioOutputDeviceUniqueID;
Discussion
The default value of this property is nil, indicating that the default audio output device is used. Otherwise the value of this property is a string containing the unique ID of the Core Audio output device to be used for audio output.
Core Audio's kAudioDevicePropertyDeviceUID is a suitable source of audio output device unique IDs.

AVAudioSession availableInputs returning nil with and without external microphone

I am using AVAudioSession to detect whether an external mic is attached to the device I'm using (an iPad 2 in this case). However, the below call is returning nil when I have an external mic attached, and when I don't.
NSArray *availableInputs = [[AVAudioSession sharedInstance] availableInputs];
AVAudioSessionPortDescription *port = [availableInputs objectAtIndex:0];
I would have assumed that when the mic is not attached this would return a list including the internal mic alone, with the external microphone attached it would return a list including the internal mic and the external mic.
This thread indicates that it should be returning these sort of results (with an error in this case, but that seems irrelevant), so I'm confused as to why I'm not getting the correct output. Perhaps there's a flag that needs to be set to show that I'm using multi-route audio.
Any help would be appreciated.

AVAudioSessionManager availableInputs "Unknown selected data source for port iPhone Microphone"

I've noticed this error in my console log for a while. Though it does not affect the execution of my application, I find it really annoying. Thus, I started to trace where this error came from. It turns out when I call availableInputs
NSArray *inputs = [[AVAudioSession sharedInstance] availableInputs];
It will give me the log message:
ERROR: [0x3d61318c] AVAudioSessionPortImpl.mm:50: ValidateRequiredFields: Unknown selected data source for Port iPhone Microphone (type: MicrophoneBuiltIn)
I tried to print out the inputs..
Printing description of inputs:
<__NSArrayI 0x188c4610>(
<AVAudioSessionPortDescription: 0x188c4580, type = MicrophoneBuiltIn; name = iPhone Microphone; UID = Built-In Microphone; selectedDataSource = (null)>,
<AVAudioSessionPortDescription: 0x18835d90, type = BluetoothHFP; name = Valore-BTi22; UID = 00:23:01:10:38:77-tsco; selectedDataSource = (null)>
So selectedDataSource is (null). I don't know what should I do to make it not null? iPhone Microphone is a built-in input... I suppose it's set by Apple already?
This problem seems not just happen to me... I will just share my understanding here..
My situation is.. I'm using pjsip library, which has a lower level control of audio resources. I've noticed that, the sound device has been closed before I call [[AVAudioSession sharedInstance] availableInputs];
Thus, (I guess) AVAudioSession, as a higher level control, couldn't find corresponding audio data source for its input - as the error indicated...
To further investigate the problem, you'd better check somewhere in your code that modify the audio source.. and make sure the audio source is activated before you call AVAudioSession
I can only go this far for now... Deeper understanding and better explanation of audio control is always appreciated!!
Regarding the error in your console, I can also confirm that I sometimes receive this message when using my iPhone 5S, but I've never seen it on my 4S. It could just be some core audio dump, but it doesn't seem to affect actual performance (at least for me).
Regarding the available inputs, what your actually printing out is the available input ports and their descriptions. This bit is more confusing and I don't understand why the selectedDataSource field is null for each one.
I will say that the iPhone is definitely defaulting to one of those sources (probably the Built-in Mic) regardless of what the selectedDataSource is saying.
Now if you wanted to explicitly select one of the port descriptions you could do something like this:
NSArray *availableInputs = [[AVAudioSession sharedInstance] availableInputs];
AVAudioSessionPortDescription *port = [availableInputs objectAtIndex:0]; //built in mic for your case
NSError *portErr = nil;
[[AVAudioSession sharedInstance] setPreferredInput:port error:&portErr];
and I would check portErr afterwards to make sure there's no error in setting the preferredInput.
Its worth noting that you can also cycle through the available dataSources for a particular Port Description as well and select one using
[port setPreferredDataSource:source error:&sourceErr];
then follow that with:
[[AVAudioSession sharedInstance] setPreferredInput:port error:&portErr];
These are some handy iOS7 only features that take advantage of hardware with multiple built-in mice.

Detect if headset is plugged in - iOS 5

I'm aware that this is a question already asked, I've found possible duplicates:
Detecting if headphones are plugged into iPhone
headphone plug-in plug-out event when audio route doesn't change - iOS
Detect if headphones (not microphone) are plugged in to an iOS device
...and more info on the WWW. But I've tried out every solution given and everytime I have problem, probably because they are old threads and are referring to iOS 4.
How can I detect it on iOS 5.0?
Thanks
If you're okay with an iOS 6-only solution, Apple added several new AVAudioSession properties that let you detect audio routes in just a few lines (and without the use of C).
Use this method to check for headphones (or adjust it to check for other outputs - "Speaker", "Headset", etc.):
- (BOOL)isHeadsetPluggedIn
{
// Get array of current audio outputs (there should only be one)
NSArray *outputs = [[AVAudioSession sharedInstance] currentRoute].outputs;
NSString *portName = [[outputs objectAtIndex:0] portName];
if ([portName isEqualToString:#"Headphones"]) {
return YES;
}
return NO;
}
If you want to respond to audio route changes passively, you can do this with the new NSNotification, AVAudioSessionRouteChangeNotification. Unfortunately, this notification doesn't tell you what the new route is, just the previous route that it switched from. But, you can just call some variation of the method above to get the current route.
Wes seems to have a great solution. Alas it is not international-proof. This code only works for the English language. In Dutch, for instance, the headset is called 'Koptelefoon' and
*portName
contains indeed 'Koptelefoon' which makes the test fail.
This will do the job internationally correct:
if ([portDescription.portType isEqualToString:AVAudioSessionPortHeadphones])
;

Resources