2013-08-02 19 views
7

Próbuję uzyskać dźwięk w mojej aplikacji, aby odtwarzać dźwięk z górnego głośnika w iPhonie, tego, który przyciskasz do ucha podczas rozmowy telefonicznej. Wiem, że to możliwe, ponieważ grałem w grę z App Store ("The Heist" przez "tap tap tap"), która symuluje połączenia telefoniczne i robi dokładnie to.Odtwarzanie dźwięku przez górny głośnik (telefon)

Zrobiłem wiele badań w Internecie, ale mam zaskakująco ciężko znaleźć KAŻDEGO, który nawet omawiał tę możliwość. Zdecydowana większość postów dotyczy raczej zestawu głośnomówiącego niż podłączonych słuchawek (takich jak this i this i this), a nie górnego głośnika "połączenie telefoniczne" z głośnikiem zestawu głośnomówiącego. (Część tego problemu może nie mieć dobrego imienia: "głośnik telefonu" często oznacza głośnik zestawu głośnomówiącego na dole urządzenia itp., Więc trudno jest zrobić naprawdę dobrze ukierunkowane wyszukiwanie). Zajrzałem do Apple'a: Audio Session Category Route Overrides, ale wydaje mi się, że znowu (popraw mnie, jeśli się mylę) zajmują się tylko głośnikiem zestawu głośnomówiącego na dole, a nie głośnikiem na górze telefonu.

Znalazłem ONE post, który wydaje się być o tym: link. Zapewnia nawet mnóstwo kodu, więc pomyślałem, że jestem w domu za darmo, ale teraz nie mogę sprawić, że kod zadziała. Dla uproszczenia właśnie skopiowałem metodę DisableSpeakerPhone (która, jeśli dobrze rozumiem, powinna to być ta, która zmieni trasę dźwięku do wyższego głośnika) na mój viewDidLoad, aby sprawdzić, czy zadziała, ale pierwsza linia "assert" zawiedzie, a dźwięk nadal odtwarza dno. (Zaimportowałem także framework AudioToolbox, zgodnie z sugestią w komentarzu, więc to nie jest problem).

Oto główny blok kodu, nad którym pracuję (to właśnie skopiowałem do mojego viewDidLoad, aby przetestować), choć istnieje kilka metod więcej w artykule związane z:

void DisableSpeakerPhone() { 
    UInt32 dataSize = sizeof(CFStringRef); 
    CFStringRef currentRoute = NULL; 
    OSStatus result = noErr; 

    AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &dataSize, &currentRoute); 

    // Set the category to use the speakers and microphone. 
    UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord; 
    result = AudioSessionSetProperty (
             kAudioSessionProperty_AudioCategory, 
             sizeof (sessionCategory), 
             &sessionCategory 
            ); 
    assert(result == kAudioSessionNoError); 

    Float64 sampleRate = 44100.0; 
    dataSize = sizeof(sampleRate); 
    result = AudioSessionSetProperty (
             kAudioSessionProperty_PreferredHardwareSampleRate, 
             dataSize, 
             &sampleRate 
            ); 
    assert(result == kAudioSessionNoError); 

    // Default to speakerphone if a headset isn't plugged in. 
    // Overriding the output audio route 

    UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_None; 
    dataSize = sizeof(audioRouteOverride); 
    AudioSessionSetProperty(
          kAudioSessionProperty_OverrideAudioRoute, 
          dataSize, 
          &audioRouteOverride); 

    assert(result == kAudioSessionNoError); 

    AudioSessionSetActive(YES); 
} 

Więc moje pytanie brzmi: może ktoś albo A) mi pomóc dowiedzieć się, dlaczego ten kod nie działa, lub B) oferują lepszą sugestię, aby móc nacisnąć przycisk i skierować dźwięk do górnego głośnika?

PS Z coraz większą łatwością poznaję programowanie na iOS, ale jest to moja pierwsza wizyta w świecie AudioSessions i tym podobne, więc szczegóły i przykłady kodu są bardzo doceniane! Dziękuję za pomoc!

UPDATE:

Z sugestią "Był" (poniżej) Mam usunięty kod cytowany powyżej i zastąpił go:

[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error:nil]; 
[[AVAudioSession sharedInstance] setActive: YES error:nil]; 

na początku viewDidLoad. Nadal nie działa (przez co mam na myśli, że dźwięk nadal wychodzi z głośnika na dole telefonu, zamiast odbiornika na górze). Wygląda na to, że domyślnym zachowaniem powinno być AVAudioSessionCategoryPlayAndRecord wysyłanie dźwięku z odbiornika samodzielnie, więc coś jest nadal nie tak.

Dokładniej, co robię z tego kodu jest odtwarzanie dźwięku za pośrednictwem iPod Music Player (zainicjowany tuż po linie AVAudioSession powyżej w viewDidLoad, na co warto):

_musicPlayer = [MPMusicPlayerController iPodMusicPlayer]; 

i media dla że odtwarzacz muzyczny iPod jest wybierany przez MPMediaPickerController:

- (void) mediaPicker: (MPMediaPickerController *) mediaPicker didPickMediaItems: (MPMediaItemCollection *) mediaItemCollection { 
    if (mediaItemCollection) { 
     [_musicPlayer setQueueWithItemCollection: mediaItemCollection]; 
     [_musicPlayer play]; 
    } 

    [self dismissViewControllerAnimated:YES completion:nil]; 
} 

To wszystko wydaje się dość proste do mnie, mam żadnych błędów lub ostrzeżeń, i wiem, że media Picker i odtwarzacz muzyczny pracują prawidłowo becaus e poprawne utwory zaczynają się odtwarzać, po prostu z niewłaściwego głośnika. Czy może istnieć metoda "odtwarzania multimediów za pomocą tej metody AudioSession"? A może istnieje sposób sprawdzenia, która kategoria sesji audio jest obecnie aktywna, aby potwierdzić, że nic nie mogło jej wyłączyć? Czy istnieje sposób, aby zdecydowanie powiedzieć kodowi, aby KORZYSTAĆ z odbiornika, zamiast polegać na domyślnym, aby to zrobić? Czuję, że jestem na jednej linii jardów, po prostu trzeba przejść tę ostateczną trochę ...

EDIT: Pomyślałem teorii, przy czym jest to coś, o iPod Music Player, który nie robi” t chcesz grać z odbiornika. Moje rozumowanie: możliwe jest ustawienie utworu, aby rozpocząć grę za pośrednictwem oficjalnej aplikacji dla iPoda, a następnie płynnie ją dostosować (pauza, pominięcie itp.) Za pomocą aplikacji, którą rozwijam. Ciągłe odtwarzanie z jednej aplikacji do następnej sprawiło, że pomyślałem, że może iPod Music Player ma własne ustawienia trasy audio, a może nie zatrzymuje się, by sprawdzić ustawienia w nowej aplikacji? Czy ktoś, kto wie, o czym mówią, myśli, że to może być coś takiego?

+0

Próbowałem tylko nuking twierdzi, i widząc, co się dzieje? – Undo

+0

Skomentuj je wszystkie i wygląda na to, że w ogóle nie ma tam kodu: dźwięk przechodzi przez dolny głośnik. Wyobrażam sobie, że to z powodu tego samego problemu, który powoduje, że twierdzenia zawodzą, po prostu nie jestem wystarczająco doświadczony, aby wiedzieć, co to może być. – Nerrolken

+1

Ja też nie ... Powodzenia! – Undo

Odpowiedz

4

Musisz najpierw zainicjalizować swoją sesję audio.

Korzystanie z C API

AudioSessionInitialize (NULL, NULL, NULL, NULL); 

W iOS6 można zamiast tego użyć metody AVAudioSession (trzeba będzie importować ramy AVFoundation używać AVAudioSession):

Inicjowanie użyciu AVAudioSession

self.audioSession = [AVAudioSession sharedInstance]; 

Ustawianie kategorii audioSession przy użyciu AVAudioSession

[self.audioSession setCategory:AVAudioSessionCategoryPlayAndRecord 
             error:nil]; 

do dalszych badań, jeśli chcesz lepsze warunki wyszukiwania, tutaj są pełne nazwy stałych dla głośników: docs

const CFStringRef kAudioSessionOutputRoute_BuiltInReceiver; 
const CFStringRef kAudioSessionOutputRoute_BuiltInSpeaker; 

zobaczyć apple here

Ale prawdziwą tajemnicą jest, dlaczego mają jakiekolwiek problemy z routingiem do odbiornika. Jest to domyślne zachowanie dla kategorii playAndRecord. Dokumentacja Apple z kAudioSessionOverrideAudioRoute_None:

"określa, dla kategorii kAudioSessionCategory_PlayAndRecord, że wyjście audio powinien iść do odbiornika To jest droga dźwięku domyślne wyjście dla tej kategorii.".

aktualizacja

W zaktualizowanym pytaniu ujawniasz, że używasz klasy MPMusicPlayerController.Ta klasa wywołuje globalny odtwarzacz muzyki (ten sam odtwarzacz, który jest używany w aplikacji Muzyka). Ten odtwarzacz muzyki jest oddzielny od aplikacji i nie udostępnia tej samej sesji audio, co sesja audio aplikacji. Wszystkie właściwości ustawione w aplikacji audioSession zostaną zignorowane przez MPMusicPlayerController.

Jeśli chcesz kontrolować zachowanie audio w aplikacji, musisz użyć wewnętrznej ścieżki dźwiękowej w swojej aplikacji. Będzie to AVAudioRecorder/AVAudioPlayer lub Core Audio (kolejki audio, jednostki audio lub OpenAL). Bez względu na to, z której metody korzystasz, sesję audio można kontrolować za pomocą właściwości AVAudioSession lub za pomocą interfejsu Core Audio API. Core Audio zapewnia dokładniejszą kontrolę, ale z każdą nową wersją iOS jest przenoszona do AVFoundation, więc zacznij od tego.

Należy również pamiętać, że sesja audio umożliwia opisanie zamierzonego zachowania dźwięku w aplikacji w odniesieniu do całego środowiska iOS, ale nie zapewni pełnej kontroli. Apple dba o to, aby oczekiwania użytkownika dotyczące zachowania dźwięku w urządzeniu pozostały spójne między aplikacjami, a gdy jedna aplikacja musiała przerwać inny strumień audio.

Aktualizacja 2

W swojej edytuj swoje nawiązują do możliwości sesji audio, sprawdzanie ustawień sesji dźwiękowych innych aplikacji. To się nie zdarza . Chodzi o to, że każda aplikacja ustawia swoje preferencje dotyczące własnego zachowania dźwiękowego za pomocą własnej, niezależnej sesji audio. System operacyjny rozstrzyga pomiędzy sprzecznymi wymaganiami dotyczącymi dźwięku, gdy więcej niż jedna aplikacja konkuruje o zasoby nie dające się zliczyć, takie jak mikrofon wewnętrzny lub jeden z głośników, i zwykle decyduje się na to zachowanie, które najprawdopodobniej spełni oczekiwania użytkownika urządzenia jako całości.

Klasa MPMusicPlayerController jest nieco nietypowa, ponieważ daje jednej aplikacji pewną kontrolę nad inną. W takim przypadku Twoja aplikacja nie odtwarza dźwięku, wysyła żądanie do odtwarzacza muzyki, aby odtwarzać dźwięk w Twoim imieniu. Twoja kontrola jest ograniczona przez zasięg API MPMusicPlayerController. Aby uzyskać większą kontrolę, Twoja aplikacja będzie musiała zapewnić własną implementację odtwarzania dźwięku.

W swoim komentarzu, że zastanawiam się:

Could there be a way to pull an MPMediaItem from the MPMusicPlayerController and then play them through the app-specific audio session, or anything like that?

że to (duża) z zastrzeżeniem dla nowego pytania. Oto dobry początek czytania (z bloga Chrisa Adamsona) From iPod Library to PCM Samples in Far Fewer Steps Than Were Previously Necessary - jest to kontynuacja From iphone media library to pcm samples in dozens of confounding and potentially lossy steps - która powinna dać ci poczucie złożoności, z jaką będziesz się zmierzyć. To może stało się łatwiejsze od iOS6, ale nie byłbym tego taki pewien!


istnieje otherAudioPlaying tylko do odczytu właściwość BOOL w iOS6, ale to o to

+0

Dziękujemy! Właśnie zapoznałem się z "AVAudioSessionCategoryPlayAndRecord" i może to być dokładnie to, czego szukam. Jednak dodałem linie: [[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil]; [[AVAudioSession shared_stance] setActive: TAK błąd: nil]; 'w miejsce kodu, który opublikowałem w' viewDidLoad', i nie dostaję żadnych błędów, ale nadal jest odtwarzany przez głośnik. Masz pojęcie, co robię źle? Czy te linie nie powinny iść w 'viewDidLoad', czy też muszę coś zrobić z moimi liniami' [self.musicPlayer play]; 'lub czymkolwiek? – Nerrolken

+0

@AlexanderWinn - tak, powinny przejść w viewDidLoad. Przed ustawieniem jakichkolwiek właściwości należy zainicjować audioSession. "[AVAudioSession sharedInstance]' osiąga to. W odniesieniu do '[self.musicPlayer play]' nie wpisałeś tego kodu w oryginalnym pytaniu. Być może powinieneś zaktualizować pytanie z większym kontekstem kodu, ponieważ kod, który już napisałeś, powinien po prostu działać. – foundry

+0

Zaktualizowano. Czy coś tam wygląda, może to być problem? I jeszcze raz dziękuję za pomoc! – Nerrolken

24

zmagała się z tym przez jakiś czas też. Może to pomoże komuś później. Możesz także użyć nowszych metod nadpisywania portów. Wiele metod w twoim przykładowym kodzie jest rzeczywiście przestarzałych.

Więc jeśli masz AudioSession sharedInstance dostając,

NSError *error = nil; 
AVAudioSession *session = [AVAudioSession sharedInstance]; 
[session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]; 
[session setActive: YES error:nil]; 

kategoria sesja musi być AVAudioSessionCategoryPlayAndRecord można uzyskać prąd wyjściowy o sprawdzenie tej wartości.

AVAudioSessionPortDescription *routePort = session.currentRoute.outputs.firstObject; 
NSString *portType = routePort.portType; 

I teraz w zależności od portu, który chcesz wysłać go, po prostu przełączyć wyjście używając

if ([portType isEqualToString:@"Receiver"]) { 
     [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error]; 
} else { 
     [session overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error]; 
} 

To powinien być szybki sposób przełączać wyjścia do telefonu i odbiornika głośników.

+1

Działa wspaniały! Musiałem tylko zmienić kategorię sesji i przesłonić wyjściowy port audio, łatwo! dzięki – YoGiN

+0

Wypróbowałem to rozwiązanie iOS 8. To działa. Po prostu [session overrideOutputAudioPort: AVAudioSessionPortOverrideNone error: nil]; jeśli nie chcesz przełączać i po prostu odtwarzać dźwięk w uchu. – coolcool1994

+0

czy możesz to sprawdzić. https://stackoverflow.com/questions/45424446/avaudiosession-playing-audio-through-earpiece-speaker – Shohrab

1

Swift 3.0 Kod

func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {  
let routePort: AVAudioSessionPortDescription? = obsession. current Route. outputs. first 
let portType: String? = routePort?.portType 
if (portType == "Receiver") { 
    try? audioSession.overrideOutputAudioPort(.speaker) 
    } 
    else { 
     try? audioSession.overrideOutputAudioPort(.none) 
    } 
+0

czy możesz to sprawdzić. https://stackoverflow.com/questions/45424446/avaudiosession-playing-audio-through-earpiece-speaker – Shohrab

Powiązane problemy