2009-05-22 14 views
36

Je suis en train d'éliminer le décalage de démarrage lors de la lecture d'un (très court - moins de 2 secondes) fichier audio via AVAudioPlayer sur l'iPhone.Démarrage lent pour AVAudioPlayer la première fois un son est joué

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]; 

je mets en œuvre aussi la méthode de audioPlayerDidFinishPlaying pour libérer le AVAudioPlayer une fois que je suis fait.

La première fois que je joue l'audio le retard est palpable - au moins 2 secondes. Cependant, après que le son joue immédiatement. Je soupçonne que le coupable, alors, est le [NSData dataWithContentsOfMappedFile] prenant beaucoup de temps à lire à partir du flash, mais ensuite être rapide sur les lectures ultérieures. Je ne suis pas sûr de savoir comment tester cela, cependant.

Est-ce le cas? Si oui, dois-je simplement pré-mettre en cache les objets NSData et être agressif pour les effacer dans des conditions de mémoire insuffisante?

+0

Merci pour l'info. Vous devriez répondre à la question et accepter votre réponse. L'initialisation de l'AVAudioPlayer au démarrage a permis de lire l'audio sans délai dans le reste de l'application. – brianegge

+1

@johnbiesnecker joue n'importe quel son avec 0 volume, alors tous les appels sonores doivent être dans un bloc de file d'attente global asynchrone, avec une priorité par défaut ou haute, le son ne doit pas être joué dans la file d'attente principale. –

+1

En plus de ce que dit @JuanPabloBoero, j'ai aussi joué le son de volume 0 lors de la réception de 'UIApplicationWillEnterForegroundNotification', sinon il y a un délai après la reprise de l'application de l'arrière-plan. – Pang

Répondre

36

Le délai semble être lié à l'instanciation de AVAudioPlayer pour la première fois. Si je charge un fichier audio, lancez [audioPlayer prepareToPlay] puis relâchez-le immédiatement, les temps de chargement de tous mes autres fichiers audio sont très proches de ceux qui sont imperceptibles. Donc maintenant je le fais dans applicationDidFinishLaunching et tout le reste fonctionne bien.

Je ne trouve rien à ce sujet dans la documentation, mais il semble certainement être le cas.

+1

Hmm, je ne sais pas à ce sujet. prepareToPlay n'a pas fonctionné complètement pour moi.prepareToPlay sorte de travaux, mais comme il semble charger l'audio de manière asynchrone, il n'y a aucun moyen de savoir exactement quand l'audio est vraiment prêt à jouer. Voir ma réponse récente pour la solution que j'ai trouvée. –

+0

Je reçois également un délai avec AVQueuePlayer chaque fois que je charge un actif pour la première fois. Le joueur n'est initialisé qu'une seule fois. On dirait un problème commun: http://stackoverflow.com/questions/11171374/how-to-reduce-ios-avplayer-start-delay – Oren

-1

Je ne suis pas sûr, mais je soupçonne que l'objet NSData est d'être paresseux et charger le contenu du fichier sur la demande. Vous pouvez essayer de "tricher" en appelant le [audioData getBytes:someBuffer length:1]; à un certain point pour l'amener à charger ce fichier avant qu'il ne soit nécessaire.

-1

La réponse est

AVAudioPlayer *audioplayer; 
[audioplayer prepareToPlay]; 
+2

En fait, [audioplayer prepareToPlay] n'est pas la réponse ... le délai I parlait de se produit la première fois que vous allouez et initez un objet AVAudioPlayer, avant d'appeler l'une de ses méthodes. Cela n'arrive que la toute première fois que vous en chargez un. prepareToPlay ne fait que précharger l'audio, mais n'élimine pas le décalage que je demandais réellement. Incidemment, UIWebView présente le même comportement - si vous allouez puis relâchez immédiatement un, le reste se charge très rapidement. –

10

Voici ce que je l'ai fait (dans un thread séparé):

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

[audioplayer prepareToPlay] semble être une méthode asynchrone, vous ne pouvez pas soyez sûr quand il revient si l'audio est en fait prêt à jouer.

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

Je dois dire que je trouve cela un peu surprenant de constater que, même sur iOS 4, il prend quelques 2 secondes juste pour charger un fichier audio qui est seulement 4 secondes de longueur ....

+1

le faire pendant le chargement de la vue, c'est un hack rapide et cela fonctionne – yasirmturk

2

J'ai pris une autre approche ça marche pour moi. J'ai vu cette technique mentionnée ailleurs (je ne me souviens pas pour le moment où c'était).

En bref, le système de contrôle en amont de son en chargeant et en jouant un court, son « blanc » avant de faire quoi que ce soit d'autre. Le code ressemble à ceci pour un petit fichier mp3 je précharger selon moi la méthode du contrôleur viewDidLoad qui est de 0,1 secondes Durée:

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 vide si vous voulez, ou faire une recherche google sur " mp3 vides "pour trouver et télécharger un déjà construit par quelqu'un d'autre.

+0

en utilisant cela avec NSZombies activé révèle un message "impl" étant envoyé au joueur après dealloc. Je l'ai obtenu pour éviter cela en ne le libérant pas automatiquement, et en lui envoyant manuellement un message [player release] dans l'élément audioPlayerDidFinishPlaying: successful: (dans callback) – unsynchronized

+0

suite au dernier commentaire, voir ma réponse mise à jour à cette question pour une solution de code source: http://stackoverflow.com/a/7426406/830899 – unsynchronized

8

Si votre audio est inférieure à 30 secondes de long en longueur et est au format PCM ou IMA4 linéaire, et est emballé comme un .caf, .wav ou .aiff vous pouvez utiliser les sons système:

Importer la AudioToolbox cadre

dans votre fichier .h créer cette variable:

SystemSoundID mySound; 

dans votre fichier .m mettre 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, joue le son sacrément proche lorsque le bouton est pressé. J'espère que cela vous aide.

+0

Ceci est une excellente réponse. Merci de l'avoir posté! –

3

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

https://github.com/nicklockwood/SoundManager

Il fondamentalement juste analyse votre application pour les fichiers sonores, prend un et il joue à zéro volume. Cela initialise le matériel audio et permet au son suivant de jouer instantanément.

+0

Je viens de l'essayer et il semble vraiment bien. Merci ;) – Beny

0

Autre solution de contournement consiste à créer un court & audio silencieux et le lire au premier démarrage du lecteur.

  1. Réglez le niveau du microphone sur 0 dans les Préférences Système.
  2. Ouvrez QuickTime.
  3. Créer un nouvel enregistrement audio (Fichier> Nouvel enregistrement audio).
  4. Enregistrer pendant 1 ou 2 secondes.
  5. Sauvegarder/Exporter le fichier audio. (il aura l'extension .m4a et environ 2 Ko sur la taille).
  6. Ajoutez le fichier à votre projet et jouez-le.

Si vous ne voulez pas créer un nouveau fichier vous-même son, vous pouvez télécharger un court & fichier audio silencieux ici: https://www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0

0

est ici une simple extension Swift à AVAudioPlayer qui utilise le jeu et arrêt à 0 idée de volume présentée dans les réponses précédentes. PrepareToPlay() malheureusement au moins pour moi n'a pas fait l'affaire.

extension AVAudioPlayer { 
    private func initDelaylessPlayback() { 
     volume = 0 
     play() 
     stop() 
     volume = 1 
    } 

    convenience init(contentsOfWithoutDelay : URL) throws { 
     try self.init(contentsOf: contentsOfWithoutDelay, fileTypeHint: nil) 
     initDelaylessPlayback() 
    } 
}