Forma correcta de reproducir sonido varias veces en iOS

I needed to implement playing some .aiff sound 3 times in a row. Sound length is 0.1 ms and intervals between playing should be also 0.1 ms. So I decided to do it with NSTimer. This is my solution:

-(void)playSound{
    timerCounter = 0;
    timerForSound = [NSTimer scheduledTimerWithTimeInterval:0.2
                                                     target:self 
                                                   selector:@selector(playSoundThreeTimes)
                                                   userInfo:nil
                                                    repeats:YES];
}

-(void)playSoundThreeTimes{
    // play alert sound
    AudioServicesPlaySystemSound(beepSound);
    timerCounter++;
    if(timerCounter == 3){ // sound played 3 times
        [timerForSound invalidate];
        timerCounter = 0;
    }
}

The problem is that the sound lags sometimes, even if I set interval 0.25 (0.2 is because sound length + interval = 0.2).

Can anyone suggest a better way to do it or tell what the problem could be caused by.

Artem

Edit: Adding this to asynchronous thread also doesn't work:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [self playSound];
    });

preguntado el 02 de febrero de 12 a las 11:02

3 Respuestas

I could be wrong, but audio should be on it's own thread, however I'm not sure how to do about it in ObjC (I program audio in c++).

NSTimer is not always going to be accurate as (I think) it's a message thread. At high intervals (1 second+) it's not going to be noticeable, but at such short speeds, it's probably going to lag.

Respondido 02 Feb 12, 15:02

Two reasons: 1. NSTimer isn't 100% accurate. 2. You are firing events on the main thread which is where the rest of the UI is.

Like Adam says, you can put it on its own thread. Or maybe use something like CADisplayLink instead of NSTimer.

Edit: If you are right and the culprit is AudioSystemServices... I don't know if it will work but can you try creating 3 different beepSound references and then call beepSound1, beepSound2, beepSound3? Because I think it might be waiting for previous sound to stop. If it doesn't work, I think maybe you need to go with Audio Queue or OpenAL perhaps...

Respondido 03 Feb 12, 13:02

The idea of putting sound playing on its own thread is right, but I cannot use NSTimer or performSelector: afterDelay: on the asynchronous thread, because both use messaging or smth. - Artem

I know. You can do something like @selector(playSound) for the timer and in playSound you have only one line [self performSelectorInBackground:@selector(playSoundBackground)], and that one plays it, but I don't know if it helps. - vakio

Here is a question you might find useful about how to have an NSTimer on another thread with a runloop: stackoverflow.com/questions/1421817/… - vakio

btw CADisplayLink is also not a possibility cause it does not let to control the frequency of calling methods - Artem

Maybe. In that line of thought... I don't know if it will work but can you try creating 3 different beepSound references and then call beepSound1, beepSound2, beepSound3? Because I think it might be waiting for previous sound to stop. If it doesn't work, I think maybe you need to go with Audio Queue or OpenAL perhaps... Sorry I couldn't help more. - vakio

Prueba esto:

-(void)playMyAudio {

    //play audio method
    if (loop<3) {   
        [self performSelector:@selector(playAudio) withObject:nil afterDelay:0.1];
        loop++;
    }
    else {
       loop = 0
    }
}

if still lags, the only way i found is to create 3,4 avplayer and play in FIFO mode with AVAudioSession

Respondido 02 Feb 12, 21:02

It is pretty much the same that with timer, lags as well - Artem

and this is also wrong because playAudio will be called 3 times at the same time, because there is no delay in for loop. - Artem

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.