2013-04-27 13 views
7

Questa domanda potrebbe essere troppo vago per passare norme StackOverflow's, ma devo provare distacco, perché io sono a corto di opzioni ...:/AVAudioPlayer e Random lentezza

Per farla breve: Ho un app che sperimenta periodi casuali di lentezza. Non succede troppo spesso (probabilmente una volta al mese), ma quando fa il solo un riavvio completo dell'iDevice su cui è in esecuzione. I sintomi sono: 2-3 secondi tempi di risposta e animazioni lente e instabili; l'intera app diventa praticamente inutilizzabile.

Avevo eseguito l'app attraverso tutti i possibili strumenti di diagnostica, nessuno dei quali ha trovato qualcosa di sbagliato; nessuna perdita di memoria o utilizzo insolitamente elevato della CPU. Ma questo non è sorprendente, considerando che l'app è estremamente semplice, un'app tracker per un gioco di carte.

Tutto questo mi ha portato a credere che il AVAudioPlayer io uso per riprodurre i suoni quando l'utente tocca un button potrebbe essere la causa del problema (che è l'unico, relativamente alto elemento di complessità in tutta l'app). Sono, tuttavia, non sono sicuro, che è dove ho bisogno di aiuto. Includo un codice di esempio qui e forse qualcuno con esperienza nella riproduzione audio iOS potrebbe guardarlo e vedere se c'è qualche errore che ho trascurato.

Eccoci: Per prima cosa, inizializzo un "lettore silenzioso" che continua a riprodurre una traccia silenziosa ogni secondo per mantenere in vita AVAudioPlayer. È necessario a causa del tempo di risposta relativamente lungo delle esperienze AVAudioPlayer quando viene chiamato dopo un periodo di inattività più lungo.

NSString *silenceFilePath = [[NSBundle mainBundle] pathForResource: @"silence" ofType: @"wav"]; 
NSURL *silenceFileURL = [[NSURL alloc] initFileURLWithPath: silenceFilePath]; 
silencePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: silenceFileURL error: nil]; 
[silencePlayer setDelegate: self]; 
[silencePlayer prepareToPlay]; 

silenceTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f 
               target:self 
               selector:@selector(repeatSilence) 
               userInfo:nil 
               repeats:YES]; 

Il NSTimer chiama il seguente metodo:

- (void) repeatSilence 
{ 
    if (isAudioON == YES) 
    { 
     if (silencePlayer.playing) 
     { 
      [silencePlayer stop]; 
      silencePlayer.currentTime = 0; 
      [silencePlayer play]; 
     } 
     else 
     { 
      [silencePlayer play]; 
     } 
    } 
} 

Il resto è abbastanza semplice. Io inizio un'altra AVAudioPlayer per riprodurre un suono specifico tasto (ce ne sono due di questi):

NSString *buttonSoundFilePath = [[NSBundle mainBundle] pathForResource: @"button_pushed" ofType: @"wav"]; 
NSURL *buttonFileURL = [[NSURL alloc] initFileURLWithPath: buttonSoundFilePath]; 
buttonPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: buttonFileURL error: nil]; 
[buttonPlayer setDelegate: self]; 
[buttonPlayer prepareToPlay]; 

E quando il pulsante viene premuto suono il suono (allo stesso modo come ho fatto con il giocatore silenzioso):

if (isAudioON == YES) 
{ 
    if (buttonPlayer.playing) 
    { 
     [buttonPlayer stop]; 
     buttonPlayer.currentTime = 0; 
     [buttonPlayer play]; 
    } 
    else 
    { 
     [buttonPlayer play]; 
    } 
} 

E davvero tutto quello che c'è da fare. Temo, tuttavia, che, nonostante la semplice natura di questo metodo, in qualche modo questa riproduzione audio continua crei un raro esempio in cui iOS impazzisce. Ma tutto questo è solo una teoria, ed è per questo che ho bisogno di qualcuno con più esperienza per dare un'occhiata al mio codice e condividere la sua opinione.

Grazie!

Aggiornamento: Ho trovato altre righe di codice che potrebbero anche essere rilevanti per il problema. Con loro, ho impostato l'AVAudioPlayer di lavorare contemporaneamente con la musica di sottofondo (da un'altra applicazione, per esempio):

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; 
[[AVAudioSession sharedInstance] setActive:YES error:nil]; 
[[AVAudioSession sharedInstance] setDelegate:self]; 

risposta

1

quello che osservo dal codice di cui sopra, vorrei suggerire due cambiamenti.

1) Sono state utilizzate due istanze di AVAudioPlayer, una per riprodurre continuamente un suono silenzioso ogni secondo. Una seconda istanza di AVAudioPlayer è per riprodurre il suono quando si fa clic sul pulsante.Eseguendo due istanze di AVAudioPlayer contemporaneamente, come in un momento in cui la memoria è in esaurimento del dispositivo e il suono silenzioso e il suono del pulsante devono essere riprodotti contemporaneamente, il dispositivo si comporta in modo lento. È preferibile utilizzare un'istanza di AVAudioPlayer per riprodurre entrambi i suoni.

2) Continuare a riprodurre il suono silenzioso finché l'utente non fa clic sul pulsante. Quando l'utente fa clic sul pulsante, mette in pausa il timer e interrompe il suono corrente e riproduce il suono del pulsante utilizzando la stessa istanza di AVAudioPlayer. In questo modo il timer che non è consigliato per lo più verrà fermato per un po 'quando sarà necessario riprodurre il suono del pulsante, quando il suono del pulsante è terminato, riprendere il timer per riprodurre il suono silenzioso con la stessa istanza di AVAudioPlayer. Questo porterà via molto carico dalla memoria e dall'elaborazione.

+0

Buoni approfondimenti. Darò loro una prova questo fine settimana e condividerò i risultati! –

4

Da quello che ho ricavato dalla tua domanda, sembra che gli unici suoni che vuoi suonare siano effetti sonori relativamente semplici e brevi. Se questo è effettivamente il caso, non userei AVAudioPlayer, dato che è una libreria pesante per qualcosa semplice come suonare effetti sonori ad un volume fisso. Vorrei utilizzare direttamente i servizi audio. Ecco un esempio di una classe di effetti sonori che ho tirato da qualche altra parte (non ricordo più dove).

SoundEffect.h

#import <Foundation/Foundation.h> 
#import <AudioToolbox/AudioToolbox.h> 

@interface SoundEffect : NSObject 
{ 
    SystemSoundID soundID; 
} 

- (id)initWithSoundNamed:(NSString *)filename; 
- (void)play; 

@end 

SoundEffect.m

#import "SoundEffect.h" 

@implementation SoundEffect 

- (id)initWithSoundNamed:(NSString *)filename 
{ 
    if ((self = [super init])) 
    { 
     NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil]; 
     if (fileURL != nil) 
     { 
      SystemSoundID theSoundID; 
      OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)fileURL, &theSoundID); 
      if (error == kAudioServicesNoError) 
       soundID = theSoundID; 
     } 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    AudioServicesDisposeSystemSoundID(soundID); 
} 

- (void)play 
{ 
    AudioServicesPlaySystemSound(soundID); 
} 

@end 

Una volta che avete questa classe, si può facilmente riprodurre qualsiasi effetto sonoro utilizzando il seguente codice:

SoundEffect *mySoundEffect = [[SoundEffect alloc] initWithSoundNamed:@"mySound.wav"]; 
[shareSoundEffect play]; 

Per ottimizzare, vorrei caricare tutti i tuoi effetti sonori da qualche parte e memorizzarli staticamente, in modo che il suono reale i file sono precaricati, quindi basta chiamarli su "riproduci" ogni volta che ne hai bisogno. Fare questo è un modo molto meno pesante di risorse per riprodurre effetti sonori rispetto all'utilizzo di AVAudioPlayer. Chissà, forse risolverà il tuo problema.

precarico del file audio:

Quello che volevo dire con questo è più semplice di quanto sembri. Voglio solo dire che creerai un riferimento statico a ciascun oggetto Sound Effect dopo averli inizializzati al momento del caricamento del programma. Ci sono diversi modi per farlo. In genere utilizzo il metodo di caricamento +. Sarebbe simile a questa:

aggiungere una variabile statica nella implementazione della classe SoundEffect

static SoundEffect *mySoundEffect; 

Avanti, inizializzare quella variabile nel metodo di carico + per la classe Sound Effect.

+(void)load 
{ 
    mySoundEffect = [[SoundEffect alloc] initWithSoundNamed:@"mySound.wav"]; 
} 

Ora basta creare un metodo statico per la classe effetto sonoro simile a questo:

+(void)playMySound 
{ 
    [mySoundEffect play]; 
} 

Dopo di che, si può solo giocare che il suono in qualsiasi momento chiamando il numero:

[SoundEffect playMySound]; 

Una nota sui controlli del volume:

Io uso questo codice in molte delle mie app IOS e posso confermare il seguente ng:

  1. I controlli del volume standard sul lato del dispositivo funzioneranno normalmente, ma solo se le impostazioni dell'utente specificano che suonerie e avvisi possono essere modificati con i pulsanti.
  2. Non c'è modo di controllare il volume del suono nel codice.
+0

Lo proverò sicuramente. Tuttavia, ho notato la variabile SystemSoundID nel codice. Questo significa che il tuo metodo utilizza l'oggetto sonoro di sistema per riprodurre suoni? Se questo è il caso, il mio problema è la mancanza di controllo del valore quando si usano i suoni di sistema. Li ho provati Inoltre, puoi fornire un esempio di come "pre-caricare" i file audio? Non ho familiarità con questa pratica. –

+0

Ciò è corretto, in questo caso non sarà possibile controllare in modo programmatico il volume dell'effetto sonoro. Se hai bisogno che l'effetto sonoro sia a un volume più basso o più alto, dovresti avere versioni diverse del file audio a volumi sempre più alti. Se è necessario avere il controllo dinamico sul volume dell'effetto sonoro, sfortunatamente questa soluzione potrebbe non essere adatta a te. :( – csundman

+0

Modificato con la spiegazione del pre-caricamento degli effetti sonori – csundman