2012-03-12 16 views
10

Así que tengo una aplicación que reproduce un montón de canciones mientras el usuario puede hojear un cómic. Uso AVAudioPlayer y lo tengo configurado para reproducir las canciones en un orden establecido. Entonces, cuando una canción termina, la siguiente se reproducirá. Esto funciona sin problemas cuando la aplicación está abierta. El problema ocurre cuando la aplicación está en segundo plano. Configuré la aplicación para jugar en segundo plano, y eso funciona bien. Entonces, cuando el usuario presiona la pantalla de inicio, la música continúa sonando. El problema ocurre cuando la canción termina, se supone que debe reproducir la siguiente canción como lo hace cuando la aplicación está abierta. En cambio, nada sucede. De acuerdo con las declaraciones de mi NSLog, se están llamando a los métodos correctos, pero no ocurre nada. Aquí está mi código:iOS - AVAudioPlayer no continúa con la canción siguiente mientras está en segundo plano

- (void)audioPlayerDidFinishPlaying: (AVAudioPlayer *)player successfully: (BOOL) flag { 

NSLog(@"Song finished"); 

if ([songSelect isEqualToString: @"01icecapades"]) { 
    isPlay = @"yes"; 
    songSelect = @"02sugarcube"; 
    imageSelect = @"playbanner02"; 
    [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; 
    [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; 
} 
else if ([songSelect isEqualToString: @"02sugarcube"]) { 
    isPlay = @"yes"; 
    songSelect = @"03bullets"; 
    imageSelect = @"playbanner03"; 
    [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; 
    [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; 
} 
else if ([songSelect isEqualToString: @"03bullets"]) { 
    isPlay = @"yes"; 
    songSelect = @"04satanama"; 
    imageSelect = @"playbanner04"; 
    [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; 
    [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; 
} 
else if ([songSelect isEqualToString: @"04satanama"]) { 
    isPlay = @"yes"; 
    songSelect = @"05uglyjoke"; 
    imageSelect = @"playbanner05"; 
    [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; 
    [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; 
} 
else if ([songSelect isEqualToString: @"05uglyjoke"]) { 
    isPlay = @"yes"; 
    songSelect = @"01icecapades"; 
    imageSelect = @"playbanner01"; 
    [self performSelector:@selector(triggerSong) withObject:nil afterDelay:0]; 
    [self performSelector:@selector(triggerBanner) withObject:nil afterDelay:0]; 
}} 

anterior es el código que reconoce la canción que se está reproduciendo, y establece la canción correcta siguiente. Luego activa otro método que configura el jugador.

- (void)triggerSong { 
NSLog(@"triggerSong called"); 
NSString  *path; 
NSError  *error; 
// Path the audio file 
path = [[NSBundle mainBundle] pathForResource:songSelect ofType:@"mp3"]; 
// If we can access the file... 
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) 
{  
    // Setup the player 
    player = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error]; 
    //player = [initWithContentsOfURL:[NSURL fileURLWithPath:path] error:&error]; 
    [player setDelegate: self]; 
    // Set the volume (range is 0 to 1) 
    player.volume = 1.0f;  
    [player prepareToPlay]; 
    [player setNumberOfLoops:0]; 
    [player play]; 
    NSLog(@"player play"); 
    [error release]; 
    player.delegate = self; 
    // schedules an action every second for countdown 
    [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(updateTimeLeft) userInfo:nil repeats:YES]; 
}} 

Ahora Suponiendo que esta no es la mejor manera de hacer esto, pero funciona muy bien cuando la aplicación está en estado plano. He estado revisando la documentación y parece que no puedo encontrar la causa de este problema. Esperaba que alguien pudiera ver un error en mi enfoque. Como dije antes, se están llamando los dos NSLogs en el método triggerSong, por lo que no puedo ver por qué no se está llamando al jugador AVAudioPlayer.

También tengo el ajuste correcto en mi info.plist y tengo esto en mi viewDidLoad:

//Make sure the system follows our playback status 
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil]; 
[[AVAudioSession sharedInstance] setActive: YES error: nil]; 

Gracias por cualquier idea. Muy apreciado.

Respuesta

26

Relevant discussion

Respuesta corta:

Es necesario este código en el método init o viewDidLoad ya sea su primera de controlador de vista:

[[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 

respuesta larga W/MUESTRA:

Aquí está mi ejemplo. Al igual que usted, comencé con una aplicación que reproducía música de fondo pero que nunca podría seguir reproduciéndose después de que el primer clip terminara. Hice una copia del Music.mp3 original y lo llamé Music2.mp3. Mi intención era reproducir Music2.mp3 tan pronto como Music.mp3 finalizara (audioPlayerDidFinishPlaying :). Metí la pata alrededor con las tareas en segundo plano por un tiempo hasta que llegué a este trabajo sin la tarea de fondo:

-(id)init{ 
    self = [super initWithNibName:@"MediaPlayerViewController" bundle:nil]; 
    if(self){ 

     //Need this to play background playlist 
     [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 

     //MUSIC CLIP 
     //Sets up the first song... 
     NSString *musicPath = [[NSBundle mainBundle] pathForResource:@"Music" ofType:@"mp3"]; 
     if(musicPath){ 
      NSURL *musicURL = [NSURL fileURLWithPath:musicPath]; 
      audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL error:nil]; 
      [audioPlayer setDelegate:self]; 
     } 
    } 
    return self; 
} 


-(IBAction)playAudioFile:(id)sender{ 

    if([audioPlayer isPlaying]){ 
     //Stop playing audio and change test of button 
     [audioPlayer stop]; 
     [sender setTitle:@"Play Audio File" forState:UIControlStateNormal]; 
    } 
    else{ 
     //Start playing audio and change text of button so 
     //user can tap to stop playback 
     [audioPlayer play]; 
     [sender setTitle:@"Stop Audio File" forState:UIControlStateNormal]; 
    } 
} 


-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{ 
    [audioButton setTitle:@"Play Audio File" forState:UIControlStateNormal]; 
    [playRecordingButton setTitle:@"Play Rec File" forState:UIControlStateNormal]; 

    //PLAY THE SECOND SONG 
    NSString *musicPath2 = [[NSBundle mainBundle] pathForResource:@"Music2" ofType:@"mp3"]; 
    if(musicPath2){ 

     NSURL *musicURL2 = [NSURL fileURLWithPath:musicPath2]; 
     audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:musicURL2 error:nil]; 
     [audioPlayer setDelegate:self]; 
     NSLog(@"Play it again: \n%@", musicPath2); 
     [audioPlayer play]; 
    } 
} 

El resultado final es que mi aplicación está desempeñando Music2.mp3 en un bucle continuo, incluso si la aplicación está en el fondo.

+0

Su son los mejores! Muchas gracias. Funcionó perfectamente. –

+0

¡Ningún problema! La mejor de las suertes para ti. – Squatch

+1

FYI .. su enlace está equivocado ... debería ser https://devforums.apple.com/message/264397 –

4

sólo para confirmar lo que dijo Squatch, esta es también la solución en Swift:

UIApplication.sharedApplication().beginReceivingRemoteControlEvents() 
0

OS X presenta el mismo problema utilizando AVAudioPlayer, sin embargo UIApplication es un IOS de sólo constructo. OS X requiere el uso de NSApplication en su lugar, pero NSApplication no se devuelve hasta que la aplicación finalice, por lo que debemos usar los hilos. Como beneficio adicional, hay un assert() en algún lugar en las profundidades de NSApplication que exige el hilo principal.

Este híbrido función de C++ C/Objetivo es una solución para este problema OS X:

void do_the_dumb (void real_application(void)) { 
    std::thread thread ([real_application]() { 
     real_application(); 
     [[NSApplication sharedApplication] terminate: [NSApplication sharedApplication]]; 
    }); 
    [[NSApplication sharedApplication] run]; 
    thread.join(); 
}; 
Cuestiones relacionadas