2010-02-07 6 views
0

Je cherche cela depuis des semaines maintenant: Mon application a besoin d'une fonction de métronome et je l'ai fait avec un fil (copié et adapté à partir de l'exemple d'Apple Metronom) mais le timing est misérable. Je sais que les ressources du minuteur ne sont pas précises mais je ne trouve pas d'autre technique réalisable. Et j'ai juste besoin de brefs "bips" sonores afin d'attaquer la totalité de la lecture audio n'est pas nécessaire. Je divise le BPM (battements par minute) par 12 pour avoir la possibillité de jouer aussi les huitièmes, seizièmes et ainsi de suite. Donc le problème n'est pas le jeu audio, le problème est la distance temporelle précise entre deux "ticks".Exact Timer recherché!

Voici le code pour le calcul de la distance de tique:

- (double)Be_get12TickDistance:(NSInteger)bpm{ 
double result = 60.0/(bpm*12); 
return result; 

} Voici le fil:

- (void)startDriverTimer:(id)info { 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 

// Give sound thread high priority to keep the timing steady 
[NSThread setThreadPriority:1.0]; 
BOOL continuePlaying = YES; 

while (continuePlaying) { // loop until cancelled 
    // Use an autorelease pool to prevent the build-up of temporary date objects 
    NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init]; 

    SoundEffect *currentSoundEffect= [[self beepAndLEDKeeper] Be_GetBeepSound4CurrentTick:beatNumber theCurrentGroove:grooveNumber]; 
    [currentSoundEffect play]; 

    if ([[self beepAndLEDKeeper] Be_isLEDFlashPoint:beatNumber]){ 
     [self performSelectorOnMainThread:@ selector(setLEDtoHidden:) withObject:[NSNumber numberWithInt:LEDoldBeatNumber+oldGrooveNumber*9] waitUntilDone:NO]; 
     [self performSelectorOnMainThread:@ selector(setLEDtoUnHidden:) withObject:[NSNumber numberWithInt:LEDbeatNumber+grooveNumber*9] waitUntilDone:NO]; 
     LEDoldBeatNumber = LEDbeatNumber++; 
    } 

    oldBeatNumber = beatNumber++; 
    oldGrooveNumber = grooveNumber; 

    if (beatNumber >= grooveEnd[grooveNumber]) 
    { 
     beatNumber=0; 
     LEDbeatNumber=0; 
     if (++grooveNumber > 1) 
      grooveNumber=0; 
     if (grooveEnd[grooveNumber] == 0) 
      grooveNumber=0; 
    } 

    if ([soundPlayerThread isCancelled] == YES) 
     continuePlaying = NO; 
    else 
     [NSThread sleepForTimeInterval:self.duration]; 

    [loopPool release]; 
} 
[pool release]; 

}

Question: Est-ce que quelqu'un a un indice comment faire d'une manière facile? J'ai juste besoin d'une meilleure possibilité de timing, le jeu avec SoundEffect est assez bon.

Merci pour toute l'aide, Andreas

Répondre

0

Une stratégie qui fonctionne est d'avoir un fil d'arrière-plan qui peut accueillir pour un montant fixe de temps (par exemple, tous les 1/16e de seconde). Cela utilise l'horloge CPU plus précise, donc quand il revient de l'appel, vous serez beaucoup plus précis.

+0

Salut John, ok, vous voulez dire que j'ai une distance fixe entre une tique granulaire fine et ensuite construire mes tics BPM hors de lui? J'ai essayé cela une fois sur une conception 80C535 et j'ai dû réaliser que l'erreur de synchronisation pour certains taux de battement n'est pas négligeable parce que vous avez une erreur d'arrondi pour des battements comme 300bpm où une distance de 12tick est 16.66666667 ms. Cela ajoute ... mais je pourrais faire un tel thread avec le temps réel de la tique. Mais je n'utilise pas un thread d'arrière-plan en faisant [NSThread sleepForTimeInterval: self.duration]; ??? Andreas –

0
NSTimer timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(YOUR_METHOD:) userInfo:nil repeats:YES]; 

Remplacer scheduledTimerWithTimeInterval: 0,1 avec tout intervalle (en secondes) que vous voulez, et remplacer YOUR_METHOD avec quel que soit votre méthode.

+0

Ah, tu m'as battu - mais souviens-toi que si tu tiens le chronomètre comme ça, tu dois le garder! '[NSTimer scheduledTimer ...]' ramène par défaut un 'NSTimer' autoreleased. –

+0

Tout à fait raison! Merci d'avoir rattrapé l'erreur d'un programmeur Java endormi;) – James

+0

Ok, donc si j'utilise la version [NSTimer scheduleTimer ...] quand la minuterie est-elle disponible? Je demande parce que je commence et arrête cette minuterie constamment et je veux éviter les surprises de fuite de mémoire. Merci –

1

La solution la plus simple, et qui est assez précise, consiste simplement à utiliser un NSTimer. J'ai créé une application de démonstration simple pour le tester, et il était précis au millième de milliseconde (je juste NSLog ged chaque fois qu'une horloge a coché).

2010-02-07 17:11:07.548 TimerTest[12775:207] Ticked 
2010-02-07 17:11:07.648 TimerTest[12775:207] Ticked 
2010-02-07 17:11:07.748 TimerTest[12775:207] Ticked 
2010-02-07 17:11:07.848 TimerTest[12775:207] Ticked 
2010-02-07 17:11:07.948 TimerTest[12775:207] Ticked 
2010-02-07 17:11:08.048 TimerTest[12775:207] Ticked 
2010-02-07 17:11:08.148 TimerTest[12775:207] Ticked 

Cette sortie provient d'un périphérique iPod Touch 2nd Gen. (Notez que la précision diminuera si vous faites beaucoup de traitement intensif entre-temps, mais je ne pense pas qu'un métronome aura autant d'effet sur lui). La minuterie est venu du code très simple:

[NSTimer scheduledTimerWithTimeInterval:.1 target:self selector:@selector(tick) userInfo:nil repeats:YES]; 

mais je pense qu'il est en fait le code sonore qui est à l'origine du problème. Qu'est-ce que tu utilises pour jouer les sons? Si ce n'est pas OpenAL, vous ne pouvez pas vous attendre à ce qu'il soit très précis - il peut faire une pause avant la lecture, après la lecture, etc. Essayez d'obtenir une application de test et vérifiez si le timer fonctionne correctement. avec le son pour identifier la cause du problème.

+0

Salut Ian, merci pour la réponse, j'apprécie toute aide ici :-) J'utilise la classe "SoundEffect" comme utilisé dans la démo Metronome d'Apple. Je change également l'état caché de certaines images dans le fil principal mais c'est tout le traitement effectué dans la boucle de tick. Je vais essayer le NSTimer et voir si le changement de statut de LED Sound/Graphic est la racine du problème. Andreas –

+0

D'accord - dans mon projet, la minuterie se déclenche avec précision en une milliseconde, mais la lecture audio réelle s'écarte jusqu'à 25 millisecondes, en utilisant AVAudioPlayer ou les sons du système. – arlomedia