iOS play audio success on simulator but fail on device - ios

I use audio recorder to record and create file in the document directory, then I play it using audio player. I am able to record it on both simulator and device, but I can't play it on device only.
Notice that
1. I don't play sound from files, I play from NSData (I store music into database, and retrieve them to play)
2. It works fine on simulator but failed on device.
Here is my code for playing audio
- (void)playAudioWithData:(NSData *)data {
[self stopPlayingAudio];
[self stopRecordingAudio];
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
[audioSession setActive:YES error:nil];
self.audioPlayer = [[AVAudioPlayer alloc] initWithData:data error:nil];
[self.audioPlayer play];
}
this is the setting for recording:
NSDictionary *audioSettings = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithFloat:44100],AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatLinearPCM],AVFormatIDKey,
[NSNumber numberWithInt: 1],AVNumberOfChannelsKey,
[NSNumber numberWithInt:AVAudioQualityMedium],AVEncoderAudioQualityKey,nil];

Instead of using AVAudioSessionCategoryPlayAndRecord try setting the audio session to AVAudioSessionCategoryRecord when recording and then to AVAudioSessionCategoryPlayback when you want to play back what you just recorded.
I experienced the same thing in my app when recording and playing back audio and this resolved the issue.

Related

Why does the volume of my sound go down?

I have a voip app and for an incoming call I redirect to a separate view where the user can accept or reject the call. I want a sound to be played while in this view, so I load a sound file:
NSString *ringtoneSoundPath = [[NSBundle mainBundle] pathForResource:#"ringtone" ofType:#"wav"];
NSURL *ringtoneSoundUrl = [NSURL fileURLWithPath:ringtoneSoundPath];
ringtone = [[AVAudioPlayer alloc] initWithContentsOfURL:ringtoneSoundUrl error:nil];
ringtone.numberOfLoops = -1;
and then in the viewcontroller where I want the sound to be played I have these methods:
- (void)viewWillAppear:(BOOL)animated {
[ringtone play];
}
- (void)viewWillDisappear:(BOOL)animated {
[ringtone stop];
}
This works, but when the call is finished and I call a second time the volume of the sound is suddenly very low. I checked the device's volume settings, but it's still at max. Why does the volume go down when stopping the sound and then playing it again? It doesn't repeat, so the third time, it's just as low as the second time.
We had the similar issue. If you have voip app it means that you use setCategory: in AVAudioSession.
So, you have the code like:
AVAudioSession *aSession = [AVAudioSession sharedInstance];
if ([aSession setCategory:AVAudioSessionCategoryPlayAndRecord error:nil]) {
[aSession setMode:AVAudioSessionModeVoiceChat error:nil];
[aSession setActive: YES error:nil];
}
But when you use AVAudioSessionCategoryPlayAndRecord the volume becomes low.
So, before rington play you should set e.g.:
if ([aSession setCategory:AVAudioSessionCategoryPlayback error:nil]) {
[aSession setMode:AVAudioSessionModeDefault error:nil];
[aSession setActive:YES error:nil];
}
Hope this helps

AVAudioSession record while playing weird issue

For part of an app that I'm making I need to record audio from the user. I have chosen to use AVAudioRecorder to do this.
The problem is that when I start to record audio, all audio playing on the device is paused. I then looked in the Apple docs for AVAudioSession and set the recording options to be: AVAudioSessionCategoryOptionMixWithOthers.
The problem is that whenever I start recording, any audio playing on the device switches from the main external speaker to the small phone (call) speaker.
My goal is to record audio while still allowing all other audio from the device to be outputted through the main speaker.
How can I achieve this?
Here's my code currently:
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
[audioSession setActive:YES error:&error];
recorder = [[AVAudioRecorder alloc] initWithURL:url settings:settings error:&error];
if(recorder != nil){
[recorder prepareToRecord];
recorder.meteringEnabled = YES;
[recorder record];
levelTimer = [NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(levelTimerCallback:) userInfo:nil repeats:YES];
}else{
NSLog(#"Error: %#",[error description]);
}//end if
Thanks!
Maybe you can try using AVAudioSessionCategoryOptionDefaultToSpeaker. According to the AVAudioSession reference:
When using this option and no other audio route (such as a headset) is available, session audio will play through the device’s built-in speaker. When not using this option, and no other audio output is available or selected, audio will play through the receiver (a speaker intended to be held to the ear).

iOS - Unable to play a sound in background

I'm currently trying to implement a system like the alarm one to alert the phone when an event occurred (in my case a bluetooth event). I want this alert to occur even if the phone is in silent and in background.
I create a local notification but i can't get sound played if the phone is in silent mode (which seems to be normal since we put the phone in silent).
So i tried to manage the sound by myself and i'm struggling with playing sound in background. So far i implement the "App plays audio or streams audio/video using AirPlay" key in my plist and i'm using this code.
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *error = nil;
BOOL result = [audioSession setActive:YES error:&error];
if ( ! result && error) {
NSLog(#"Error For AudioSession Activation: %#", error);
}
error = nil;
result = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
if ( ! result && error) {
NSLog(#"Error For AudioSession Category: %#", error);
}
if (player == nil) {
NSString *path = [[NSBundle mainBundle] pathForResource:#"bell" ofType:#"wav"];
NSURL *url = [NSURL fileURLWithPath:path];
NSError *err = nil;
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&err];
if(err)
{
NSLog(#"Error Player == %#", err);
}
else {
NSLog(#"Play ready");
}
}
[player prepareToPlay];
[player setVolume:1.0];
if([player play])
{
NSLog(#"YAY sound");
}
else {
NSLog(#"Error sound");
}
The sound works great in foreground, even in silent mode, but i got no sound at all in background. Any ideas ?
Thanks
EDIT:
Finally i got it working with the above code. The only missing point is that i was trying to play the sound in somewhat appeared to be a different thread, when i play it right in my bluetooth event and not my function call it's working.
An app that plays audio continuously (even while the app is running in the background) can register as a background audio app by including the UIBackgroundModes key (with the value audio) in its Info.plist file. Apps that include this key must play audible content to the user while in the background.
Apple reference "Playing Background Audio"
Ensuring That Audio Continues When the Screen Locks
Found here
I'm not sure if your problem is that you have not configure correctly the audio session output. I had similar problem while playing .wav in my app. I only listened them with earphones. This method helped me:
- (void) configureAVAudioSession {
//get your app's audioSession singleton object
AVAudioSession *session = [AVAudioSession sharedInstance];
//error handling
BOOL success;
NSError* error;
//set the audioSession category.
//Needs to be Record or PlayAndRecord to use audioRouteOverride:
success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
error:&error];
if (!success) NSLog(#"AVAudioSession error setting category:%#",error);
//set the audioSession override
success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker
error:&error];
if (!success) NSLog(#"AVAudioSession error overrideOutputAudioPort:%#",error);
//activate the audio session
success = [session setActive:YES error:&error];
if (!success) NSLog(#"AVAudioSession error activating: %#",error);
else NSLog(#"audioSession active"); }
Try it out!
You need to make couple of changes in plist file.
for enabling sound when the app enter's Background.
1) Set Required background mode to App plays audio
2) set Application does not run in background to YES.
NSError *setCategoryErr = nil;
NSError *activationErr = nil;
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error:&setCategoryErr];
[[AVAudioSession sharedInstance] setActive:YES error:&activationErr];
Then, you need to write these much code in AppDelegate
Now, you can easily run audio while phone screen locks or in background.
It works fine for me. :)
for more please refer to Playing Audio in background mode and Audio Session Programming Guide

iOS - Sending one audio stream to the built-in speaker and a different stream to a Bluetooth HFP speaker

I am building an alarm clock app which works in conjunction with a physical Bluetooth device. I need to send one audio file (a song selected from the iTunes Library) to the built-in speaker and a separate audio file to the Bluetooth device (which is, effectively, a Bluetooth HFP speaker) at the same time.
My first thought in completing this would be to use AVAudioSession's new AVAudioSessionCategoryMultiRoute, but iOS 7 does not detect my speaker as a possible route; it will detect either the built-in speaker or the HFP speaker, but it will not detect both simultaneously. Detecting both is required to send two files at the same time.
My second thought was to use AVAudioSessionCategoryPlayAndRecord (even though the app has no need for a microphone) and to use overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker and overrideOutputAudioPort:AVAudioSessionPortOverrideNone like so:
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:#"alarm"
ofType:#"m4a"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
self.bluetoothAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL
error:nil];
self.bluetoothAudioPlayer.delegate = self;
[self.bluetoothAudioPlayer play];
NSError *error = nil;
NSURL *selectedSongURL = [self.selectedSong valueForProperty:MPMediaItemPropertyAssetURL];
NSLog(#"selectedSongURL: %#", selectedSongURL);
if (selectedSongURL) {
if (![[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]) {
NSLog(#"overrideOutput error: %#", error);
}
self.internalAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:selectedSongURL error:nil];
self.internalAudioPlayer.delegate = self;
[self.internalAudioPlayer play];
}
After this alarm is silenced, I run [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil]; to reset the override call.
However, this doesn't work because iOS just puts both audio signals on the same speaker.
Is there a way to send one audio stream to a Bluetooth HFP speaker and a different audio stream to the built-in speaker?

iOS avaudiosession not starting on certain iOS hardware

I have a strange situation with an AVAudioRecorder used to record audio in my iOS app. I have found some rare iPad2 devices where the audio session will not start recording. The iOS app code does record fine for all other types of iPod Touches, iPhones, and iPads as well as in the simulator. It only seems to not be able to record in a very few cases. Does anyone know why this might be happening?
Here is the coding information.
In my .h file I have the AVAudioRecorder set up...
RVC.h
#interface RVC : UIViewController <AVAudioRecorderDelegate, AVAudioPlayerDelegate>{
AVAudioRecorder *audioRecorder;
}
#property (nonatomic, retain) AVAudioRecorder *audioRecorder;
RVC.m
#implementation RVC
#synthesize audioRecorder;
// Set up the file path and name, where the path is to the temporary directory
soundFile = [NSURL fileURLWithPath:[tempDir stringByAppendingFormat:#"theSoundFile.m4a"]];
// Set up the settings for the recording
soundSetting = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithFloat: 44100.0],AVSampleRateKey,
[NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey,
[NSNumber numberWithInt: 2], AVNumberOfChannelsKey,
[NSNumber numberWithInt: AVAudioQualityHigh], AVEncoderAudioQualityKey, nil];
// Make the AVAudioRecorder with the filename and the sound settings.
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFile settings:soundSetting error:&error];
// Check to make sure the iOS device has audio hardware that can make a recording
BOOL audioHWAvailable = [[AVAudioSession sharedInstance] inputIsAvailable];
if (! audioHWAvailable) {
NSLog(#"No audio hardware is available.");
return;
}
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
// Make sure any output sound is played through the speakers.
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute,sizeof (audioRouteOverride),&audioRouteOverride);
[[AVAudioSession sharedInstance] setActive:YES error:nil];
// If the audiorecorder is properly set up, then start recording.
if (audioRecorder) {
[audioRecorder prepareToRecord];
// Start the recording
isRecording = [audioRecorder record];
}
else {
isRecording = FALSE;
NSLog(#"The audio recorder was not properly set up.");
}
if (isRecording) {
// The AVAudioSession is recording.
}
else {
// The AVAudioSession did not start so make some kind of log file.
NSLog(#"The recording was not started properly.");
// This is where THE PROBLEM SHOWS UP where it is having trouble with some versions of the iPad2, which I can not replicate in the simulators.
return;
}
You are passing in an error in the init call:
self.audioRecorder = [[AVAudioRecorder alloc] initWithURL:soundFile settings:soundSetting error:&error];
but you are never reading it. I would bet that on the failed cases, you are getting an error here, causing the audioRecorder to be nil.

Resources