iphone caching audio nsdata avaudioplayer

iphone - Inicio lento para AVAudioPlayer la primera vez que se reproduce un sonido



caching nsdata (9)

Aquí hay una extensión Swift simple para AVAudioPlayer que usa la idea de reproducir y detener en el volumen 0 presentada en respuestas anteriores. PrepareToPlay () desafortunadamente al menos para mí no funcionó.

extension AVAudioPlayer { private func initDelaylessPlayback() { volume = 0 play() stop() volume = 1 } convenience init(contentsOfWithoutDelay : URL) throws { try self.init(contentsOf: contentsOfWithoutDelay, fileTypeHint: nil) initDelaylessPlayback() } }

Estoy intentando eliminar el retraso de inicio cuando reproduzco un archivo de audio (muy corto, menos de 2 segundos) a través de AVAudioPlayer en el iPhone.

Primero, el código:

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

También implementé el método audioPlayerDidFinishPlaying para liberar el AVAudioPlayer una vez que he terminado.

La primera vez que reproduzco el audio, el retraso es palpable: al menos 2 segundos. Sin embargo, después de eso, el sonido se reproduce inmediatamente. Sospecho que el culpable, entonces, es el [NSData dataWithContentsOfMappedFile] que lleva mucho tiempo leyendo desde el flash inicialmente, pero luego lo lee rápidamente. Aunque no estoy seguro de cómo probar eso.

Es ese el caso? Si es así, ¿debería precachear los objetos NSData y ser agresivo sobre borrarlos en condiciones de poca memoria?


Escribí un envoltorio simple para AVAudioPlayer que proporciona un método de preparación para reproducir que realmente funciona:

https://github.com/nicklockwood/SoundManager

Básicamente, escanea tu aplicación para buscar archivos de sonido, elige una y la reproduce a volumen cero. Eso inicializa el hardware de audio y permite que el siguiente sonido se reproduzca al instante.


Esto es lo que hice (en un hilo separado):

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

[audioplayer prepareToPlay] parece ser un método asincrónico, por lo que no puedes estar seguro de cuándo volverá si el audio está listo para reproducirse.

En mi caso, llamo a start para que realmente comience a reproducirse, esto parece ser sincrónico. Entonces lo detengo de inmediato. En el simulador, de todos modos, no escuché ningún sonido que salga de esta actividad. Ahora que el sonido está "realmente" listo para jugar, asigno la variable local a una variable miembro para que el código fuera del hilo tenga acceso a ella.

Debo decir que me resulta un tanto sorprendente que incluso en iOS 4 tome unos 2 segundos simplemente cargar un archivo de audio que tiene solo 4 segundos de duración ...


He tomado un enfoque alternativo que funciona para mí. He visto esta técnica mencionada en otra parte (aunque no recuerdo en el momento en que fue).

En resumen, compruebe previamente el sistema de sonido cargando y reproduciendo un sonido corto y "en blanco" antes de hacer cualquier otra cosa. El código se ve así para un archivo mp3 corto que viewDidLoad método viewDidLoad mi controlador de viewDidLoad que tiene una duración de .1 segundo:

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

Puede crear su propio mp3 en blanco si lo desea, o hacer una búsqueda en google en "mp3s en blanco" para buscar y descargar uno ya creado por otra persona.


La demora parece estar relacionada con crear instancias de AVAudioPlayer por primera vez. Si cargo cualquier audio, ejecuto [audioPlayer prepareToPlay] y luego lo lanzo de inmediato, los tiempos de carga de todos mis otros audios son casi imperceptibles. Así que ahora estoy haciendo eso en applicationDidFinishLaunching y todo lo demás funciona bien.

No puedo encontrar nada sobre esto en los documentos, pero ciertamente parece ser el caso.


La respuesta es

AVAudioPlayer *audioplayer; [audioplayer prepareToPlay];


No estoy seguro, pero sospecho que el objeto NSData es perezoso y carga el contenido del archivo a pedido. Puedes intentar "hacer trampa" llamando a [audioData getBytes:someBuffer length:1]; en algún punto temprano para que cargue ese archivo antes de que sea necesario.


Otra solución es crear un audio corto y silencioso y reproducirlo la primera vez que se inicia el reproductor.

  1. Ajuste el nivel del micrófono a 0 en Preferencias del sistema.
  2. Abre QuickTime.
  3. Crear nueva grabación de audio (Archivo> Nueva grabación de audio).
  4. Graba por 1 o 2 segundos.
  5. Guardar / Exportar el archivo de audio. (tendrá extensión .m4a y aproximadamente 2 kb en tamaño).
  6. Agrega el archivo a tu proyecto y juega.

Si no desea crear un nuevo archivo de sonido usted mismo, puede descargar un archivo de audio corto y silencioso aquí: https://www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0


Si su audio tiene menos de 30 segundos de duración y está en formato PCM o IMA4 lineal, y está empaquetado como .caf, .wav o .aiff, puede usar sonidos del sistema:

Importar el Marco de AudioToolbox

En su archivo .h, cree esta variable:

SystemSoundID mySound;

En su archivo .m, impleméntelo en su método 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; }

En su método IBAction, llama al sonido con esto:

AudioServicesPlaySystemSound(mySound);

Esto funciona para mí, reproduce el sonido bastante cerca cuando se presiona el botón. Espero que esto te ayude.