I'm working on an app that, when shaken, will play a sound over and over until the shaking stops. I'm using multiple AVAudioPlayers to accomplish this and it mostly works, but at times the sound comes out distorted, like a sound the Decepticons would make in Transformers. Does any one have an idea as to why this would be happening? The file format I'm using is mp3 and the code is as follows:
Property Declarations:
@property (strong, nonatomic) AVAudioPlayer *musicPlayerForRoar;
@property (strong, nonatomic) AVAudioPlayer *musicPlayerForRoar2;
@property (strong, nonatomic) AVAudioPlayer *musicPlayerForRoar3;
@property (strong, nonatomic) AVAudioPlayer *musicPlayerForRoar4;
@property (strong, nonatomic) AVAudioPlayer *musicPlayerForRoar5;
PlayRoar Method. PlayRoar, PlayRoar1, PlayRoar2, etc are the same in every way except for which audio player I initialize:
- (void)playRoar {
NSError *error;
self.musicPlayerForRoar = [[AVAudioPlayer alloc]initWithContentsOfURL:[self urlForRoarFile] error:&error];
self.musicPlayerForRoar.delegate = self;
[self.musicPlayerForRoar play];
}
/*Idea for the following two methods came from code on http://www.iosing.com/2011/12/making-a-jingle-bells-app-part-2-coding/*/
static BOOL L0AccelerationIsShaking(UIAcceleration* last, UIAcceleration* current, double threshold) {
double deltaX = fabs(last.x - current.x), deltaY = fabs(last.y - current.y), deltaZ = fabs(last.z - current.z);
return (deltaX > threshold && deltaY > threshold) || (deltaX > threshold && deltaZ > threshold) || (deltaY > threshold && deltaZ > threshold);
}
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration{
if (self.lastAcceleration) {
if (!histeresisExcited && L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.7)) {
histeresisExcited = YES;
//Use to prevent all sound files from playing at once initially. Each time the device is shaken, this is incremented. Depending on its value, the audio players are fired
shakesCounter++;
BOOL nonePlaying = (self.musicPlayerForRoar.isPlaying && self.musicPlayerForRoar2.isPlaying && self.musicPlayerForRoar3.isPlaying && self.musicPlayerForRoar4.isPlaying && self.musicPlayerForRoar5.isPlaying);
if (!nonePlaying) {
[self playRoar];
NSLog(@"first playing");
}
if (!self.musicPlayerForRoar2.isPlaying && shakesCounter > 1) {
[self playRoar2];
NSLog(@"second playing");
}
//
// if (!self.musicPlayerForRoar3.isPlaying && shakesCounter > 2) {
// [self playRoar3];
// NSLog(@"third playing");
// }
//
//
// if (!self.musicPlayerForRoar4.isPlaying && shakesCounter > 3) {
// [self playRoar4];
// NSLog(@"fourth playing");
// }
//
// if (!self.musicPlayerForRoar5.isPlaying && shakesCounter > 4) {
// [self playRoar5];
// NSLog(@"fifth playing");
// }
if (shakesCounter > 5) {
shakesCounter = 0;
}
}
else if (histeresisExcited && !L0AccelerationIsShaking(self.lastAcceleration, acceleration, 0.2)) {
histeresisExcited = NO;
}
}
self.lastAcceleration = acceleration;
}
To achieve the sound I was going for, I ended up using the C level audio services. The following code solved my problem, and produces a great sound when the device is shaken. Since it uses an asynchronous method (AudioServicesPlaySystemSound()), the distortion is gone and the sound plays as expected.
- (void)playRoar {
CFBundleRef mainBundle = CFBundleGetMainBundle(); /* Define mainBundle as the current app's bundle */
CFURLRef fileURL = CFBundleCopyResourceURL(mainBundle, (CFStringRef)@"BearcatGrowl", CFSTR("mp3"), NULL); /* Set Bundle as Main Bundle, Define Sound Filename, Define Sound Filetype */
UInt32 soundID; /* define soundID as a 32Bit Unsigned Integer */
AudioServicesCreateSystemSoundID (fileURL, &soundID); /* Assign Sound to SoundID */
AudioServicesPlaySystemSound(soundID); /* Now play the sound associated with this sound ID */
}
I also found this solution at http://www.iosing.com/2011/12/making-a-jingle-bells-app-part-3-coding-the-sound/, so thank you very much to them for the code they provided. I hope this helps someone else trying to produce sound when the device is shaken.