AVAudioPlayer don't work in AVAudioSessionCategoryAmbient mode in iOS 6.0.1? - avaudioplayer

In my app, I use a MPMusicPlayerController play .mp3 as background music,
and an AVAudioPlayer play sound-effect, just like button press, and so on.
The code is like this:
// Initialization code here.
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AVAudioSession *session =[AVAudioSession sharedInstance];
//The background music and the sound-effect can play simultaneously
[session setCategory: AVAudioSessionCategoryAmbient error:nil];
[session setActive: YES error: nil];
m_sharedPlayer = [[MPMusicPlayerController applicationMusicPlayer] retain];
[m_sharedPlayer setShuffleMode: MPMusicShuffleModeSongs];
[m_sharedPlayer setRepeatMode: MPMusicRepeatModeAll];
[m_sharedPlayer setVolume:0.2];
// choose the first song
[m_sharedPlayer setQueueWithQuery: [MPMediaQuery songsQuery]];
[m_sharedPlayer play];
...
//when need play sound-effect, soundfilename is a NSString
NSData *data =[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:soundfilename ofType:nil]];
AVAudioPlayer *audioplayer = [[AVAudioPlayer alloc] initWithData:data error:nil];
audioplayer =1.0;
audioplayer.delegate =self;
[audioplayer prepareToPlay];
[audioplayer play];
...
audioplayer is release after it has finished play.
The code is work in iOS 5.0. But in iOS 6.0, everything changed.
AVAudioPlayer don't play sound anyway.
If I change this line:
[session setCategory: AVAudioSessionCategoryAmbient error:nil];
To:
[session setCategory: AVAudioSessionCategoryPlayback error: nil];
The AVAudioPlayer will play sound, but it will break the play session of MPMusicPlayerController...
How can I find a way to play AVAudioPlayer but without break the background music? Thanks a lot for your help.

OK. I find the solution finally. In iOS 6.0, apple provide a new function call setCategory:withOptions:. It work.
So the code is just like this:
AVAudioSession *session =[AVAudioSession sharedInstance];
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
if (version <6.0) {
[session setCategory:AVAudioSessionCategoryAmbient error:nil];
}
else {
[session setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error: nil];
}
Thanks.

Related

NSCocoaErrorDomain Code=257 when trying to access "My Photo Stream" media in iOS 13

In iOS 13, I get an error when I try to access to "My Photo Stream" media, which doesn't exist in device.
Error Domain=NSCocoaErrorDomain Code=257 "ファイル“IMG_0010.JPG”を表示するためのアクセス権がないため、開けませんでした。" UserInfo={NSFilePath=/var/mobile/Media/PhotoStreamsData/8281221100/100APPLE/IMG_0010.JPG, NSUnderlyingError=0x283452820 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
But in iOS 12 and earlier, I don't get the error.
My actual code is like below.
// asset is Photo Stream media.
PHContentEditingInputRequestOptions *editOptions = [PHContentEditingInputRequestOptions new];
editOptions.networkAccessAllowed = YES;
editOptions.progressHandler = ^void(double progress, BOOL *stop) {};
[asset requestContentEditingInputWithOptions:editOptions
completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:contentEditingInput.fullSizeImageURL
options:(NSDataReadingMappedIfSafe)
error:&error];
NSLog(#"%#", data); // "Error Domain=NSCocoaErrorDomain Code=257"
I found some people who are struggling with similar issues.
NSCocoaErrorDomain Code=257 file couldn’t be opened because you don’t have permission to view it : FileManager attributesOfItem returns nil in iOS13
I know Apple is putting so much effort into privacy.
So anything changed around it? And I'd like to know the way to solve my problem.

How to upload an image file in a background session (iOS)?

I am unable to upload an image from my device's Photos in a background session. When I call [session uploadTaskWithRequest:req fromFile:nsurl] the system immediately complains by sending
Failed to issue sandbox extension for file file:///var/mobile/Media/DCIM/103APPLE/IMG_3984.JPG, errno = 1
to the console. (A similar Stack Overflow issue is here)
However, if I create my NSURLSessionConfiguration with [NSURLSessionConfiguration defaultSessionConfiguration] (instead of [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:id], which I need) and if I construct an NSData object out of the NSURL and upload that instead of uploading straight from a file (which is required by a background session) then the upload succeeds. Btw I'm uploading files into our Rackspace Cloud account, and am able to do this successfully with a simple Postman PUT.
The problem occurs in my uploadObject method, which looks like:
-(void) uploadObject:(NSURL*)urlToBeUploadedIn
{
NSDictionary *headers = #{ #"x-auth-token": state.tokenID,
#"content-type": #"image/jpeg",
#"cache-control": #"no-cache" };
// create destination url for Rackspace cloud upload
NSString *sURL = [NSString stringWithFormat:#"%#/testing_folder/%#.jpg", state.publicURL, [state generateImageName]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:sURL]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:#"PUT"];
[request setAllHTTPHeaderFields:headers];
self.sessionIdentifier = [NSString stringWithFormat:#"my-background-session"];
// NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; // when I use this instead of the line below the sandbox error goes away
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:self.sessionIdentifier];
self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [self.session uploadTaskWithRequest:request fromFile:urlToBeUploadedIn];
[uploadTask resume];
}
My call that invokes uploadObject: looks like:
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithLocalIdentifiers:state.arrImagesToBeUploaded options:nil];
PHAsset *phAsset = [fetchResult objectAtIndex:0]; // yes the 0th item in array is guaranteed to exist, above.
[phAsset requestContentEditingInputWithOptions:nil
completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
NSURL *imageURL = contentEditingInput.fullSizeImageURL;
[self uploadObject:imageURL];
}];
Btw I first validate the NSURL I send to uploadObject: with a call to fileExistsAtPath: so I know my reference to the file is good. Finally, my delegate calls
(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)dataIn
(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error does get invoked, although the data
do get called by the server (although the response won't parse) so, I am getting something back from the server which never correctly receives the image.
A solution is to first copy the image to be uploaded into the app's sandbox. I used:
NSError *err;
BOOL bVal = [myNSDataOb writeToURL:myDestinationURL options:0 error:&err];
and copied into my app's 'Documents' directory, but one might also use:
NSError *err;
BOOL bVal = [[NSFileManager defaultManager] copyItemAtURL:myImageURL toURL:myDestinationURL error:&err];

Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us

We need Get Data form BluetooothDevices to IOS devices.We are using core_bluetooth.frameworks. didDisconnectPeripheral call after didConnectPeripheral ever time.We got error is
error is Error Domain=CBErrorDomain Code=7 "The specified device has disconnected from us." UserInfo={NSLocalizedDescription=The specified device has disconnected from us.}
We tried code is:
//in ViewDidLoad
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO], CBCentralManagerOptionShowPowerAlertKey, nil];
self.central=[[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
//self.central=[[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0)];
self.discoveredPeripherals=[NSMutableArray new];
//Bluetooth on/off
-(void) centralManagerDidUpdateState:(CBCentralManager *)central {
NSString *stateString = nil;
switch(central.state)
{
case CBCentralManagerStateResetting:
stateString = #"The connection with the system service was momentarily lost, update imminent.";
break;
case CBCentralManagerStateUnsupported:
stateString = #"The platform doesn't support Bluetooth Low Energy.";
break;
case CBCentralManagerStateUnauthorized:
stateString = #"The app is not authorized to use Bluetooth Low Energy.";
break;
case CBCentralManagerStatePoweredOff:
stateString = #"Bluetooth is currently powered off.";
break;
case CBCentralManagerStatePoweredOn:
stateString = #"Bluetooth is currently powered on and available to use.";
}
}
//Discover
NSLog(#"Discovered peripheral %# (%#)",peripheral.name,peripheral.identifier.UUIDString);
if (![self.discoveredPeripherals containsObject:peripheral] ) {
dispatch_async(dispatch_get_main_queue(), ^{
[self.discoveredPeripherals addObject:peripheral];
[self.tableview insertRowsAtIndexPaths:#[[NSIndexPath indexPathForRow:self.discoveredPeripherals.count-1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
});
}
//didConnectPeripheral
[self.activePeripheral discoverServices:#[[CBUUID UUIDWithString:#"00001c00-d102-11e1-9b23-00025b00a5a5"]]];
//didDisconnectPeripheral
NSLog(#"error is %#",error.description);
We are not understand why it's ever time call didDisconnection after didConnectPeripheral. Please tell me what wrong in my code.

AVAssetReader kills playback (in AVAudioPlayer)

I am using AVAssetReader to read ipod library asset audio data and render a waveform image. this takes place using code I have described in my answer to this question
this sometimes takes place while audio is being played by an instance of AVAudioPlayer.
regardless of wether the audio being played is the same asset that is being read, the moment i hit
[reader startReading];
the audio being played "fades out". (as if the AVAudioPlayer has somehow been told to stop playback). This is odd, as I am not actually playing the audio, just reading it.
I did a search on SO and found this possible solution however i have found that this does not appear to solve the problem.
note - I am able to have several instances of AVAudioPlayer playing, and starting these do not seem to interfere with each other - however
[reader startReading];
will even kill multiple simultaneous instances of AVAudioPlayer, causing them all to synchronously fade out.
any ideas?
answering my own question....
further searching on SO led me to implementing this alternate solution:
- (void)setupAudio {
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
[[AVAudioSession sharedInstance] setActive: YES error: nil];
}
this was gleaned from here
**EDIT **UPDATED****
I have since made this into a class that also pre-initialises the audio queue (useful in both simulator and device as it eliminates the startup lag from the playback of the first audio file.
you can find the point1sec.mp3 here: http://www.xamuel.com/blank-mp3s/
#import <AVFoundation/AVFoundation.h>
#import "AudioToolbox/AudioServices.h"
#interface sw_AVAudioPlayerSetup : NSObject
<AVAudioPlayerDelegate> {
}
+ (void)setupAudio ;
+ (void)setupSharedSession ;
#end
#implementation sw_AVAudioPlayerSetup
+ (void)setupSharedSession {
static BOOL audioSessionSetup = NO;
if (audioSessionSetup) {
return;
}
[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayback error: nil];
UInt32 doSetProperty = 1;
AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryMixWithOthers, sizeof(doSetProperty), &doSetProperty);
[[AVAudioSession sharedInstance] setActive: YES error: nil];
audioSessionSetup = YES;
}
+ (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{
// delegate callback to release player
[player release];
}
+ (void)setupAudio {
[self setupSharedSession];
NSString *filepath = [[NSBundle mainBundle]
pathForResource:#"point1sec"
ofType:#"mp3"];
if ([[NSFileManager defaultManager] fileExistsAtPath:filepath]) {
AVAudioPlayer* player = [[AVAudioPlayer alloc]
initWithContentsOfURL:
[NSURL fileURLWithPath:filepath]
error:nil];
player.delegate = (id <AVAudioPlayerDelegate>) self;
[player play];
}
}

About MPMusicPlayerController and does AVAudioPlayer works in iPhoneSimulator?

From (iPhone) Can I do AudioServicesPlaySystemSound Looping with scheduler?
MPMusicPlayerController doesn't work because it is about iPod music library and I can't add a music file to it since I'm doing an iPhone game and not interacting with the outside iPod library.
Does AVAudioPlayer works in iPhoneSimulator?
Check out following code its a simple AVAudioPlayer example and its work on iPhone simulator too ..
AVAudioPlayer *audioPlayer;
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/azan1.mp3", [[NSBundle mainBundle] resourcePath]]];
NSError *error;
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = -1;
[audioPlayer play];

Resources