I am wanting to loop a local audio file in my Apple Watch App. Currently I am using AVAudioPlayerNode and AVAudioEngine which works well but I cannot figure out how to loop the sound.
I noticed that I can use AVAudioPlayer, which has the handy "numberOfLoops" but, for some reason AVAudioPlayer is not working on the watch. I have no idea why.
Here is my current code to play a sound:
_audioPlayer = [[AVAudioPlayerNode alloc] init];
_audioEngine = [[AVAudioEngine alloc] init];
[_audioEngine attachNode:_audioPlayer];
AVAudioFormat *stereoFormat = [[AVAudioFormat alloc] initStandardFormatWithSampleRate:44100 channels:2];
[_audioEngine connect:_audioPlayer to:_audioEngine.mainMixerNode format:stereoFormat];
if (!_audioEngine.isRunning) {
NSError* error;
[_audioEngine startAndReturnError:&error];
}
NSError *error;
NSBundle* appBundle = [NSBundle mainBundle];
NSURL *url = [NSURL fileURLWithPath:[appBundle pathForResource:#"FILE_NAME" ofType:#"mp3"]];
AVAudioFile *asset = [[AVAudioFile alloc] initForReading:url error:&error];
[_audioPlayer scheduleFile:asset atTime:nil completionHandler:nil];
[_audioPlayer play];
Here is the code i've tried to use for AVAudioPlayer, but does not work:
NSError *audioError;
AVAudioPlayer* player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"FILE_NAME" ofType:#"mp3"]] error:&audioError];
player.numberOfLoops = MAXFLOAT;
player.delegate = self;
[player play];
I am using WatchKit 5.0(+).
You can loop your AVAudioFile by recursively scheduling it:
__block __weak void (^weakSheduleFile)(void);
void (^scheduleFile)(void);
weakSheduleFile = scheduleFile = ^{ [self->_audioPlayer scheduleFile:asset atTime:nil completionHandler:weakSheduleFile]; };
scheduleFile();
I'm not sure if this will be a seamless loop. If it's not, you can try always having two files scheduled:
scheduleFile();
scheduleFile();
If your audio file fits into memory, you could schedule playback as an AVAudioBuffer with the AVAudioPlayerNodeBufferLoops option (N.B. only tested on simulator!):
AVAudioFormat *outputFormat = [_audioPlayer outputFormatForBus:0];
__block AVAudioPCMBuffer *srcBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:asset.processingFormat frameCapacity:(AVAudioFrameCount)asset.length];
if (![asset readIntoBuffer:srcBuffer error:&error]) {
NSLog(#"Read error: %#", error);
abort();
}
AVAudioPCMBuffer *dstBuffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:outputFormat frameCapacity:(AVAudioFrameCount)asset.length];
AVAudioConverter *converter = [[AVAudioConverter alloc] initFromFormat:srcBuffer.format toFormat:dstBuffer.format];
AVAudioConverterOutputStatus status = [converter convertToBuffer:dstBuffer error:&error withInputFromBlock:^AVAudioBuffer * _Nullable(AVAudioPacketCount inNumberOfPackets, AVAudioConverterInputStatus * _Nonnull outStatus) {
if (srcBuffer) {
AVAudioBuffer *result = srcBuffer;
srcBuffer = NULL;
*outStatus = AVAudioConverterInputStatus_HaveData;
return result;
} else {
*outStatus = AVAudioConverterInputStatus_EndOfStream;
return NULL;
}
}];
assert(status != AVAudioConverterOutputStatus_Error);
[_audioPlayer scheduleBuffer:dstBuffer atTime:nil options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
[_audioPlayer play];
Related
How to resolve this issue.
" (entity: EventDetails; id: 0x155ebe90 ; data: )".
I am using below piece of code to fetch events from my entity.
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] parentContext];
writerObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = managedObjectContext;
[temporaryContext performBlockAndWait:^{
if (temporaryContext == nil) {
managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] parentContext];
writerObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
NSManagedObjectContext *temporaryContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
temporaryContext.parentContext = managedObjectContext;
}
NSError *error = nil;
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:#"EventDetails"];
NSArray *arr = [temporaryContext executeFetchRequest:request error:&error];
NSLog(#" %#", arr);
if (![temporaryContext save:&error]) {
NSLog(#"Error in getsize - error:%#",[error userInfo]);
}
[managedObjectContext performBlockAndWait:^{
NSError *error = nil;
if(![managedObjectContext save:&error])
{
NSLog(#"error");
}
[writerObjectContext performBlockAndWait:^{
NSError *error = nil;
if(![writerObjectContext save:&error])
{
NSLog(#"error");
}
}]; // writer
}]; // main
}]; // parent
I runned above code both on my iphone and simulator.But getting same result. How to get rid of this?
data: <fault> (presumably from a log statement) simply means that Core Data did not fetch all the associated data from the database but will make future trips to the store automatically, should the information be needed.
In other words, this is normal behavior. You do not need to worry about it.
I'm trying to have an app play audio files added via itunes file-sharing. I've managed the app to retrieve the app's sandbox folder's content, but I'm not able to load such files into a C4Sample by specifying the complete path.
NSString *documentsFolder;
NSString *clickAudioPath;
C4Sample *clicksample;
-(void)setup {
// Retrieve the app's Documents folder
documentsFolder = [self applicationDocumentsDirectory];
clickAudioPath = [NSString stringWithFormat:#"%#/click.mp3", documentsFolder];
// Add test click audio
clicksample = [C4Sample sampleNamed:clickAudioPath];
[clicksample prepareToPlay];
[clicksample play];
}
// Get Documents folder
- (NSString *) applicationDocumentsDirectory{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
return basePath;
}
The above code doesn't play any sound, although I've doubled checked that clicksample actually refers to an existing file. How can I specify a complete path instead of just a name to load the audio?
Add a new method as below.
-(id) initWithURL:(NSURL *) soundFileURL
{
self = [super init];
if(self != nil) {
_player = [[AVAudioPlayer alloc] initWithContentsOfURL:soundFileURL error:nil];
self.enableRate = YES;
self.player.delegate = self;
[self setup];
}
return self;
}
I have this 2 sounds playing in a scrollview using AVAudioPlayer.
The problem is that the sleep() is creating lags and I need an alternative to playing two sounds one after the other more easily without the sleep function.
PS: the name of the file is given by the page in the scrollView in which the user is.
if( nextPage!=6 ) {
[scrMain scrollRectToVisible:CGRectMake(0, nextPage*250, scrMain.frame.size.width, scrMain.frame.size.height) animated:YES];
pgCtr.currentPage=nextPage;
NSString *path;
NSError *error;
path = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"pas%02i",nextPage+1] ofType:#"m4a"];
if ([[NSFileManager defaultManager] fileExistsAtPath:path])
{
player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error];
player.volume = 0.5f;
[player prepareToPlay];
[player setNumberOfLoops:0];
[player play];
}
sleep(1);
NSString *path2;
NSError *error2;
path2 = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:#"passun%02i",nextPage+1] ofType:#"m4a"];
if ([[NSFileManager defaultManager] fileExistsAtPath:path2])
{
player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path2] error:&error2];
player.volume = 0.5f;
[player prepareToPlay];
[player setNumberOfLoops:0];
[player play];
}
How can I do this?
In this app, I am using an sqlite database that was created by another app. I can query the database using the Firefox SQLite Manager and see that what I am searching for does exist in the database. I have reduced my query to something very simple, but still get nothing returned in my NSFetchedResultsController.
Here is my code:
- (NSFetchedResultsController*) frc {
if (!frc_) {
#autoreleasepool {
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription * entity = [NSEntityDescription entityForName:#"INDEX" inManagedObjectContext:[ManagedObjectContext moc]];
[fetchRequest setEntity:entity];
[fetchRequest setFetchBatchSize:15];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"lemma = 'dog'"];
[NSFetchedResultsController deleteCacheWithName:nil];
[fetchRequest setPredicate:predicate];
NSSortDescriptor *sortDescriptor1 = [[NSSortDescriptor alloc] initWithKey:#"lemma" ascending:YES];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor1, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
NSFetchedResultsController *aFetchedResultsController =
[[NSFetchedResultsController alloc]
initWithFetchRequest:fetchRequest
managedObjectContext:[ManagedObjectContext moc]
sectionNameKeyPath:#"lemma"
cacheName:nil];
aFetchedResultsController.delegate = (id<NSFetchedResultsControllerDelegate>)self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error]) {
NSLog(#"Unresolved Error %#, %#", error, [error userInfo]);
abort();
}
self.frc = aFetchedResultsController;
}
}
return frc_;
}
There is an entity called "INDEX" in the data model. To confirm that the entity has the lemma property, I show the content of its lemma property, getting this:
po [[entity propertiesByName] objectForKey:#"lemma"]
(id) $3 = 0x1104f740 (<NSAttributeDescription: 0x1104f740>), name lemma, isOptional 1, isTransient 0, entity INDEX, renamingIdentifier lemma, validation predicates (
), warnings (
), versionHashModifier (null)
userInfo {
}, attributeType 700 , attributeValueClassName NSString, defaultValue (null)
Examining the contents of the aFetchedResultsController immediately after the fetch (where it is assigned to self.frc) gives this:
po aFetchedResultsController
(NSFetchedResultsController *) $4 = 0x1105c5c0 <NSFetchedResultsController: 0x1105c5c0>
po [[aFetchedResultsController fetchedObjects] count]
(id) $1 = 0x00000000 <nil>
I suppose the problem here is something very basic, but I don't know what I am overlooking.
I found the answer at the Ray Wenderlich site. The problem is that the sqlite file must be moved from the application bundle to the application documents directory.
This is done by copying it form the bundle into the documents directory if it is not already there.
Here is the code for creating the persistent store coordinator:
- (NSPersistentStoreCoordinator *)pstore {
if (pstore_ != nil) {
return pstore_;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: #"words.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath: storePath];
// THIS IS THE KEY PIECE TO IMPORTING AN EXISTING SQLITE FILE:
// Put down default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle]
pathForResource:#"words" ofType:#"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSError *error = nil;
pstore_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:self.mom];
if(![pstore_ addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:storeUrl options:options error:&error]) {
// Error for store creation should be handled in here
}
return pstore_;
}
This code was working fine, now for some reason I'm getting an NSException error that's generating a SIGABRT at the following line of code...
self.popoverController = [[[UIPopoverController alloc] initWithContentViewController:wbPopOverController] autorelease];
Here's the method it's located in...
-(IBAction)showPop:(id)sender {
//close any open popovers
[self.popoverController dismissPopoverAnimated:NO];
// Initialize wbPopUpViewController
PopoverAircraftClass *wbPopOverController = [[PopoverAircraftClass alloc] initWithNibName:#"PopoverAircraftClass" bundle:[NSBundle mainBundle]];
wbPopOverController.contentSizeForViewInPopover = CGSizeMake(175, 216);
wbPopOverController.delegate = self; //for update
[wbPopOverController setAircraftList:aircraftList withCurrentValue:activeRegistration];
// Object converted to UIPopOverController (retained so we can reference it)
self.popoverController = [[[UIPopoverController alloc] initWithContentViewController:wbPopOverController] autorelease];
self.popoverController.passthroughViews = [NSArray arrayWithObject: self.view];
[self.popoverController presentPopoverFromRect:[sender frame] inView:self.layoutViewIB permittedArrowDirections:UIPopoverArrowDirectionUp animated:NO];
[wbPopOverController release];
}
I'm new at this as you can probably tell. Any ideas as to what's generating the NSException?