Pregunta

Estoy usando AVAudioPlayer para reproducir música en la aplicación de mi iPhone.

En una clase que escribí, tengo una matriz que contiene enteros ascendentes aleatorios. (2, 4, 9, 17, 18, 20, ...) Estos enteros representan momentos en la canción en los que debe ocurrir un determinado evento. Entonces, si toma la matriz anterior, después de 2 segundos de reproducción de la canción, se debe llamar a algún método. Después de 4 segundos, se debe llamar a otro método. Y así sucesivamente.

He intentado usar un NSTimer repetitivo:

NSTimer *myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerTick) userInfo:nil repeats:YES];

Cada vez que se dispara, comprueba si el valor del reproductor de audio y del índice de matriz actual son los mismos:

- (void) timerTick {
   if([[myArray objectAtIndex:currentIndex] intValue] == (int)(player.currentTime)) {
   //here the event-method is called
   currentIndex++;
   }
}

Este código realmente funciona, pero solo por un tiempo. Sin embargo, después de un tiempo, myTimer y el temporizador que controla el reproductor de música no están sincronizados. Por lo tanto, pierde un elemento de myArray y comienza un bucle infinito. No sé exactamente por qué se desincronizan, pero creo que podría deberse a que el temporizador y el reproductor no se inician exactamente al mismo tiempo o tal vez debido a los cortos retrasos en el rendimiento.

Creo que tengo que abordar esto de una manera totalmente diferente. ¿Observar el valor clave es una forma de hacer esto? Podría agregar mi clase como observador al objeto jugador, para que se le notifique cuando cambie el valor player.currentTime. Pero eso provocaría que se envíen MUCHAS notificaciones y creo que sería realmente malo para el rendimiento.

¡Cualquier ayuda muy apreciada!

¿Fue útil?

Solución

Ok, esta es mi solución: encontré una aplicación de código abierto que hace casi lo mismo que mi aplicación debería hacer, lo que me ayudó mucho. Voy a seguir con el código que ya tengo, con una pequeña modificación debería ser lo suficientemente preciso para mis propósitos. Aquí está:

currentIndex = 0;
myTimer = [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(timerTick) userInfo:nil repeats:YES];


- (void) timerTick {
 if(timerRunning){


     if([[myArray objectAtIndex:currentIndex] intValue] <= (int)(player.currentTime*100)) { 
         //some event code
         currentIndex++;
     }
 }
}

El cambio importante es de == a < = en la condición if. Cuando se vuelve asíncrono y pierde un elemento de myArray, el error se corrige la siguiente centésima de segundo. Eso es lo suficientemente bueno.

¡Gracias por tu ayuda!

Otros consejos

Podría ser que los temporizadores estén razonablemente sincronizados, pero su código tarda mucho en ejecutarse (es decir, más de 1 segundo).

¿No podrías usar el temporizador del reproductor de música y generar un hilo cada vez que ocurra un evento? De esta manera, el temporizador permanece ininterrumpido y su hilo hará lo que necesita hacer (digamos lo pesado).

Si realmente necesita dos temporizadores, creo que podría crear una secuencia de fondo que mantenga esos dos temporizadores sincronizados, pero creo que está buscando problemas allí ...

La sincronización del mundo real con la música es muy difícil porque los usuarios pueden notar errores de sincronización de solo una décima de segundo o menos. Puede encontrar que AVAudioPlayer es demasiado simple para lo que necesita. Es posible que deba controlar la velocidad de reproducción de la música con AudioQueueServices para que pueda sincronizar la música con el código en lugar de lo contrario. Podrías ver tiempo para activar tu código y luego comenzar los métodos antes de que la música suene realmente. Hecho hábilmente, esto sincronizaría la música la mayor parte del tiempo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top