36 votes

Démarrage lent pour AVAudioPlayer lors de la première lecture d'un son

J'essaie d'éliminer le décalage au démarrage lors de la lecture d'un fichier audio (très court - moins de 2 secondes) via AVAudioPlayer sur l'iPhone.

D'abord, le code :

NSString *audioFile = [NSString stringWithFormat:@"%@/%@.caf", [[NSBundle mainBundle] resourcePath], @"audiofile"];
NSData *audioData = [NSData dataWithContentsOfMappedFile:audioFile];

NSError *err;
AVAudioPlayer *audioPlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithData:audioData error:&err];

audioPlayer.delegate = self;
[audioPlayer play];

J'implémente également la méthode audioPlayerDidFinishPlaying pour libérer l'AVAudioPlayer une fois que j'ai terminé.

La première fois que je joue l'audio, le décalage est palpable -- au moins 2 secondes. Cependant, après cela, le son est lu immédiatement. Je soupçonne que le coupable est le [NSData dataWithContentsOfMappedFile] qui prend beaucoup de temps à lire le flash au départ, mais qui est rapide lors des lectures suivantes. Je ne suis pas sûr de savoir comment tester cela, cependant.

Est-ce le cas ? Si c'est le cas, dois-je simplement pré-cacher les objets NSData et les vider en cas de manque de mémoire ?

Mise à jour :

Le délai semble être lié à l'instanciation de l'AVAudioPlayer pour la première fois. Si je charge un fichier audio, que j'exécute [audioPlayer prepareToPlay] et que je le relâche immédiatement, le temps de chargement de tous mes autres fichiers audio est pratiquement imperceptible. Je fais donc maintenant cela dans l'applicationDidFinishLaunching et tout le reste fonctionne bien.

Je n'ai rien trouvé à ce sujet dans la documentation, mais cela semble être le cas.

37voto

John Biesnecker Points 2138

Le délai semble être lié à l'instanciation de l'AVAudioPlayer pour la première fois. Si je charge un fichier audio, que j'exécute [audioPlayer prepareToPlay] et que je le relâche immédiatement, le temps de chargement de tous mes autres fichiers audio est pratiquement imperceptible. Je fais donc maintenant cela dans l'applicationDidFinishLaunching et tout le reste fonctionne bien.

Je n'ai rien trouvé à ce sujet dans la documentation, mais cela semble être le cas.

11voto

Taylor Gautier Points 2044

Voici ce que j'ai fait (dans un fil séparé) :

[audioplayer start]
[audioplayer stop]
self.audioplayer = audioplayer

La méthode [audioplayer prepareToPlay] semble être une méthode asynchrone, de sorte que vous ne pouvez pas être sûr que le retour de la méthode indique que le son est prêt à être lu.

Dans mon cas, j'appelle start pour commencer à jouer - cela semble être synchrone. Ensuite, je l'arrête immédiatement. Dans le simulateur en tout cas, je n'entends aucun son sortir de cette activité. Maintenant que le son est "vraiment" prêt à être joué, j'assigne la variable locale à une variable membre pour que le code en dehors du thread y ait accès.

Je dois dire que je trouve quelque peu surprenant que, même sous iOS 4, il faille environ 2 secondes pour charger un fichier audio d'une longueur de 4 secondes seulement.....

8voto

M Jesse Points 931

Si votre audio dure moins de 30 secondes, qu'il est au format PCM linéaire ou IMA4 et qu'il est conditionné sous forme de .caf, .wav ou .aiff, vous pouvez utiliser les sons système :

Importer le cadre AudioToolbox

Dans votre fichier .h, créez cette variable :

SystemSoundID mySound;

Dans votre fichier .m, mettez-le en œuvre dans votre méthode init :

-(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;
}

Dans votre méthode IBAction, vous appelez le son avec ceci :

AudioServicesPlaySystemSound(mySound);

Cela fonctionne pour moi, le son est assez proche du moment où l'on appuie sur le bouton. J'espère que cela vous aidera.

3voto

Nick Lockwood Points 23277

J'ai écrit un simple wrapper pour AVAudioPlayer qui fournit une méthode prepareToPlay qui fonctionne réellement :

https://github.com/nicklockwood/SoundManager

En gros, il scanne votre application à la recherche de fichiers sonores, en choisit un et le joue à volume zéro. Cela initialise le matériel audio et permet au prochain son d'être joué instantanément.

3voto

hkatz Points 382

J'ai adopté une approche alternative qui fonctionne pour moi. J'ai vu cette technique mentionnée ailleurs (mais je ne me souviens plus où c'était).

En bref, il s'agit de pré-vérifier le système sonore en chargeant et en jouant un son court et "vide" avant de faire quoi que ce soit d'autre. Le code ressemble à ceci pour un court fichier mp3 que je précharge dans le contrôleur de la vue viewDidLoad d'une durée de 0,1 seconde :

   NSError* error = nil;
   NSString* soundfilePath = [[NSBundle mainBundle] pathForResource:@"point1sec" ofType:@"mp3"];
   NSURL* soundfileURL = [NSURL fileURLWithPath:soundfilePath];
   AVAudioPlayer* player = [[[AVAudioPlayer alloc] initWithContentsOfURL:soundfileURL error:&error] autorelease];
   [player play];  

Vous pouvez créer votre propre mp3 vierge si vous le souhaitez, ou faire une recherche google sur "mp3 vierges" pour trouver et télécharger un mp3 déjà construit par quelqu'un d'autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X