2013-04-27 18 views
7

To pytanie może być zbyt ogólnikowe, aby przejść standardy StackOverflow's, ale muszę spróbować opublikowania go, bo jestem na wyczerpaniu opcji ...:/AVAudioPlayer i Losowa Powolność

Krótko mówiąc: Mam aplikację, która doświadcza losowych okresów spowolnienia. Nie zdarza się to zbyt często (prawdopodobnie raz na miesiąc), ale gdy tylko zrobi się , tylko kompletny restart iDevice, który uruchamia, pomaga. Objawy to: 2-3-sekundowe czasy odpowiedzi i powolne, niepewne animacje; cała aplikacja w zasadzie staje się bezużyteczna.

Przeprowadziłem aplikację przez wszystkie możliwe narzędzia diagnostyczne, z których żaden nie znalazł niczego złego; brak wycieków pamięci lub wyjątkowo wysokie użycie procesora. Ale nie jest to zaskakujące, biorąc pod uwagę, że aplikacja jest bardzo prosta, aplikacja do śledzenia gier karcianych.

Wszystko to doprowadziło mnie do przekonania, że ​​AVAudioPlayer używam do odtwarzania dźwięków, gdy użytkownik dotknie button może być przyczyną problemu (jest to jedyny, stosunkowo wysoki element złożoności w całej aplikacji). Nie jestem jednak pewien, w czym potrzebuję pomocy. Załączam tu przykładowy kod, a być może ktoś z doświadczeniem w odtwarzaniu dźwięku mógłby sprawdzić i zobaczyć, czy jest jakiś błąd, którego przeoczyłem.

Oto: Najpierw zainicjuję "cichy odtwarzacz", który co sekundę odtwarza cichy utwór, aby utrzymać przy życiu AVAudioPlayer. Jest to konieczne ze względu na stosunkowo długi czas odpowiedzi, gdy jest wywoływany po dłuższym okresie bezczynności.

NSString *silenceFilePath = [[NSBundle mainBundle] pathForResource: @"silence" ofType: @"wav"]; 
NSURL *silenceFileURL = [[NSURL alloc] initFileURLWithPath: silenceFilePath]; 
silencePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: silenceFileURL error: nil]; 
[silencePlayer setDelegate: self]; 
[silencePlayer prepareToPlay]; 

silenceTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f 
               target:self 
               selector:@selector(repeatSilence) 
               userInfo:nil 
               repeats:YES]; 

NSTimer wywołuje następujące metody:

- (void) repeatSilence 
{ 
    if (isAudioON == YES) 
    { 
     if (silencePlayer.playing) 
     { 
      [silencePlayer stop]; 
      silencePlayer.currentTime = 0; 
      [silencePlayer play]; 
     } 
     else 
     { 
      [silencePlayer play]; 
     } 
    } 
} 

reszta jest dość prosta. I zainicjować kolejną AVAudioPlayer grać specyficzny dźwięk przycisku (są dwa z nich):

NSString *buttonSoundFilePath = [[NSBundle mainBundle] pathForResource: @"button_pushed" ofType: @"wav"]; 
NSURL *buttonFileURL = [[NSURL alloc] initFileURLWithPath: buttonSoundFilePath]; 
buttonPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL: buttonFileURL error: nil]; 
[buttonPlayer setDelegate: self]; 
[buttonPlayer prepareToPlay]; 

A gdy przycisk jest wciśnięty Gram dźwięk (tak samo jak ja z cichego gracza):

if (isAudioON == YES) 
{ 
    if (buttonPlayer.playing) 
    { 
     [buttonPlayer stop]; 
     buttonPlayer.currentTime = 0; 
     [buttonPlayer play]; 
    } 
    else 
    { 
     [buttonPlayer play]; 
    } 
} 

I naprawdę wszystko na to. Obawiam się jednak, że pomimo prostej natury tej metody, takie ciągłe odtwarzanie dźwięku tworzy rzadką instancję, w której iOS po prostu oszalał. Ale to wszystko jest tylko teorią i dlatego potrzebuję kogoś z większym doświadczeniem, aby rzucić okiem na mój kod i podzielić się z nim swoją opinią.

Dzięki!

Aktualizacja: Znalazłem kilka linii kodu, które mogą być również istotne dla problemu. Wraz z nimi, ja ustawić AVAudioPlayer pracować jednocześnie z odtwarzania muzyki w tle (z innej aplikacji, na przykład):

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; 
[[AVAudioSession sharedInstance] setActive:YES error:nil]; 
[[AVAudioSession sharedInstance] setDelegate:self]; 

Odpowiedz

1

Co mogę obserwować z kodem powyżej, chciałbym zaproponować dwie zmiany.

1) Użyłeś dwóch instancji AVAudioPlayer, z których jeden odtwarza cichy dźwięk w sposób ciągły na sekundę. Druga instancja AVAudioPlayer służy do odtwarzania dźwięku po kliknięciu przycisku.Uruchamianie dwóch instancji AVAudioPlayer w tym samym czasie, jak w pewnym momencie, gdy pamięć urządzenia jest niska, a cichy dźwięk i dźwięk przycisku muszą być odtwarzane w tym samym czasie, gdy urządzenie będzie działać wolno. Lepiej jest użyć jednej instancji AVAudioPlayer, aby odtwarzać oba dźwięki.

2) Zachowaj cichy dźwięk, dopóki użytkownik nie kliknie przycisku. Gdy użytkownik kliknie przycisk, wstrzymaj timer i zatrzymaj bieżący dźwięk i odtwórz dźwięk przycisku za pomocą tej samej instancji AVAudioPlayer. W ten sposób czasomierz, który nie jest polecany, zostanie zatrzymany na chwilę, gdy trzeba będzie odtworzyć dźwięk przycisku, gdy dźwięk przycisku zostanie zakończony, aby wznowić odliczanie czasu, aby odtworzyć cichy dźwięk w tej samej instancji AVAudioPlayer. To zabierze dużo obciążenia z pamięci i przetwarzania.

+0

Dobre spostrzeżenia. Dam im szansę w ten weekend i podziel się wynikami! –

4

Z tego, co wyciągam z twojego pytania, wydaje się, że jedynymi dźwiękami, które chcesz odtwarzać, są stosunkowo proste i krótkie efekty dźwiękowe. Jeśli tak jest, nie używałbym AVAudioPlayer, ponieważ jest to zasobna biblioteka zasobów dla czegoś tak prostego, jak odtwarzanie efektów dźwiękowych przy ustalonym wolumenie. Korzystałbym bezpośrednio z usług audio. Oto przykład klasy efektów dźwiękowych, którą wyciągnąłem z innego miejsca (nie pamiętam już gdzie).

SoundEffect.h

#import <Foundation/Foundation.h> 
#import <AudioToolbox/AudioToolbox.h> 

@interface SoundEffect : NSObject 
{ 
    SystemSoundID soundID; 
} 

- (id)initWithSoundNamed:(NSString *)filename; 
- (void)play; 

@end 

SoundEffect.m

#import "SoundEffect.h" 

@implementation SoundEffect 

- (id)initWithSoundNamed:(NSString *)filename 
{ 
    if ((self = [super init])) 
    { 
     NSURL *fileURL = [[NSBundle mainBundle] URLForResource:filename withExtension:nil]; 
     if (fileURL != nil) 
     { 
      SystemSoundID theSoundID; 
      OSStatus error = AudioServicesCreateSystemSoundID((__bridge CFURLRef)fileURL, &theSoundID); 
      if (error == kAudioServicesNoError) 
       soundID = theSoundID; 
     } 
    } 
    return self; 
} 

- (void)dealloc 
{ 
    AudioServicesDisposeSystemSoundID(soundID); 
} 

- (void)play 
{ 
    AudioServicesPlaySystemSound(soundID); 
} 

@end 

Po tej klasy, można łatwo odtwarzać żadnego efektu dźwiękowego za pomocą następującego kodu:

SoundEffect *mySoundEffect = [[SoundEffect alloc] initWithSoundNamed:@"mySound.wav"]; 
[shareSoundEffect play]; 

Dla optymalizacji ładowałem wszystkie twoje efekty dźwiękowe gdzieś i zapisałem je statycznie, tak aby rzeczywisty dźwięk pliki są wstępnie ładowane, a następnie po prostu wywołuj je, gdy są potrzebne. Wykonanie tego jest DUŻO mniej zasobochłonnym sposobem odtwarzania efektów dźwiękowych niż przy użyciu AVAudioPlayer. Kto wie, może to rozwiąże twój problem.

Wstępne ładowanie pliku dźwiękowego:

Chodziło mi o to jest prostsze niż się wydaje. Chodzi tylko o to, że utworzysz statyczne odwołanie do każdego obiektu Sound Effect po jego inicjalizacji, kiedy program po raz pierwszy się załaduje. Jest na to kilka sposobów. Zwykle używam metody + obciążenia. Byłoby to wyglądać mniej więcej tak:

dodać zmienną statyczną w wykonaniu klasy SoundEffect

static SoundEffect *mySoundEffect; 

Następny zainicjować tej zmiennej w metodzie obciążenia + dla klasy efekt dźwiękowy.

+(void)load 
{ 
    mySoundEffect = [[SoundEffect alloc] initWithSoundNamed:@"mySound.wav"]; 
} 

Teraz wystarczy utworzyć statyczną metodę dźwięku klasy efekt jak poniżej:

+(void)playMySound 
{ 
    [mySoundEffect play]; 
} 

Po tym, można po prostu grać ten dźwięk w dowolnym momencie poprzez wywołanie:

[SoundEffect playMySound]; 

Uwaga dotycząca regulacji głośności:

Używam tego kodu w kilku moich aplikacjach na IOS i mogę potwierdzić ng:

  1. Standardowe regulatory głośności z boku urządzenia będą działać normalnie, ale tylko wtedy, gdy ustawienia użytkownika określają dzwonki i alerty za pomocą przycisków.
  2. Nie ma sposobu, aby kontrolować głośność dźwięku w kodzie.
+0

Zdecydowanie spróbuję tego. Jednak zauważyłem zmienną SystemSoundID w twoim kodzie. Czy to oznacza, że ​​twoja metoda wykorzystuje dźwięk systemowy do odtwarzania dźwięków? Jeśli tak jest, moim problemem jest brak kontroli wartości podczas korzystania z dźwięków systemowych. Próbowałem ich. Możesz również podać przykład "wstępnego ładowania" plików dźwiękowych? Nie znam tej praktyki. –

+0

To prawda, w tym przypadku nie będzie możliwe programowe sterowanie głośnością efektu dźwiękowego. Jeśli potrzebujesz efektu dźwiękowego przy niższej lub większej głośności, musisz mieć różne wersje pliku dźwiękowego przy coraz niższych woluminach. Jeśli potrzebujesz dynamicznej kontroli nad głośnością efektu dźwiękowego, to niestety to rozwiązanie może nie być odpowiednie dla Ciebie. :( – csundman

+0

Edytowane z objaśnieniem wstępnego ładowania efektów dźwiękowych – csundman