开发者

iPhone AVAudioPlayer app freeze on first play

I'm using AVAudioPlayer from iOS SDK for playing short sounds on each click in tableView rows. I have manually made @selector on button in each row that fires up method playSound:(id)receiver {}. From receiver I get sound url so I can play it up.

This method looks like that:

- (void)playSound:(id)sender {
    [audioPlayer prepareToPlay];
    UIButton *audioButton = (UIButton *)sender;
    [audioButton setImage:[UIImage imageNamed:@"sound_preview.png"] forState:UIControlStateNormal];
    NSString *soundUrl = [[listOfItems objectForKey:[NSString stringWithFormat:@"%i",currentPlayingIndex]] objectForKey:@"sound_url"];

    //here I get mp3 file from http url via NSRequest in NSData
    NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];
    NSError *error;
    audioPlayer = [[AVAudioPlayer alloc] initWithData:soundData error:&error];
    audioPlayer.numberOfLoops = 0;
    if (error) {
        NSLog(@"Error: %@",[error description]);
    }
    else {
        audioPlayer.delegate = self;
        [audioPlayer play];
    }
}

Everything works fine except for the first play of some sound. The application freezes for about 2 seconds and than sound is played. Second and every other sound play works just right after click on sound button.

I wonder why the开发者_如何学JAVAre is that about 2 seconds freeze on first play when application starts?


From your code snippet, audioPlayer must be an ivar, right?

At the top of the method, you invoke -prepareToPlay on the existing audioPlayer instance (which could be nil, at least on the first pass).

Later in the method, you replace this existing audio player with a brand new AVAudioPlayer instance. The previous -prepareToPlay is wasted. And you're leaking memory with each new AVAudioPlayer.

Instead of caching sound data or URLs, I would try creating a cache of AVAudioPlayer objects, one for each sound. In your -playSound: method, get a reference to the appropriate audio player for the table row, and -play.

You can use -tableView:cellForRowAtIndexPath: as the appropriate point to get the AVAudioPlayer instance for that row, maybe lazily creating the instance and caching it there as well.

You can try -tableView:willDisplayCell:forRowAtIndexPath: as the point where you would invoke -prepareToPlay on the row's AVAudioPlayer instance.

Or you could just do the prepare in -tableView:cellForRowAtIndexPath:. Experiment and see which works best.


Check whether you are getting data asynchronously in function..

NSData *soundData = [sharedAppSettingsController getSoundUrl:defaultDictionaryID uri:soundUrl];

If you are getting asynchronously the execution will be blocked until it will get data.


If your audio is less than 30 seconds long in length and is in linear PCM or IMA4 format, and is packaged as a .caf, .wav, or .aiff you can use system sounds:

Import the AudioToolbox Framework

In your .h file create this variable:

SystemSoundID mySound;

In your .m file implement it in your init method:

-(id)init{
if (self) {
//Get path of VICTORY.WAV <-- the sound file in your bundle
 NSString* soundPath = [[NSBundle mainBundle] pathForResource:@"VICTORY" ofType:@"WAV"];
//If the file is in the bundle
if (soundPath) {
    //Create a file URL with this path
    NSURL* soundURL = [NSURL fileURLWithPath:soundPath];

    //Register sound file located at that URL as a system sound
    OSStatus err = AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &mySound);

        if (err != kAudioServicesNoError) {
            NSLog(@"Could not load %@, error code: %ld", soundURL, err);
        }
    }
}
return self;
}

In your IBAction method you call the sound with this:

AudioServicesPlaySystemSound(mySound);

This works for me, plays the sound pretty damn close to when the button is pressed. Hope this helps you.


This sometimes happens in the simulator for me too. Everything seems to work fine on the device. Have you tested on actual hardware?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜