2013-08-02 5 views
7

Я пытаюсь получить аудио в своем приложении, чтобы играть через верхний громкоговоритель на iPhone, который вы нажимаете на ухо во время телефонного звонка. Я знаю, что это возможно, потому что я играл в игру из App Store («The Heist» с помощью «tap tap tap»), который имитирует телефонные звонки и делает именно это.Воспроизведение звука через верхний (телефонный звонок) динамик

Я провел много исследований в Интернете, но мне неожиданно трудно найти ЛЮБОГО, кто даже обсудил эту возможность. Подавляющее большинство сообщений, по-видимому, касается громкоговорителей и подключенных наушников (например, this и this и this), а не верхнего динамика «телефонный звонок» и громкоговорителя. (Часть этой проблемы, возможно, не имеет хорошего имени для нее: «громкоговоритель телефона» часто означает громкоговоритель в нижней части устройства и т. Д., Поэтому трудно провести целенаправленный поиск). Я посмотрел на Apple Audio Session Category Route Overrides, но они снова кажутся (исправьте меня, если я ошибаюсь) касаются только громкоговорителя внизу, а не громкоговорителя в верхней части телефона.

Я нашел ОДИН пост, который, кажется, об этом: link. Он даже предоставляет кучу кода, поэтому я думал, что дома свободен, но теперь я не могу заставить код работать. Для простоты я просто скопировал метод DisableSpeakerPhone (который, если я правильно его понял, должен был переадресовать звук в верхний громкоговоритель) в мой viewDidLoad, чтобы увидеть, будет ли это работать, но первая строка «assert» не работает, а звук продолжает воспроизводить дно. (Я также импортировал AudioToolbox Framework, как это было предложено в комментарии, так что это не проблема.)

Вот основной блок кода, с которым я работаю (это то, что я скопировал в свой viewDidLoad для тестирования), хотя есть несколько методов в этой статье я связан с:

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); 
} 

Так что мой вопрос заключается в следующем: может кто-либо А) помочь мне выяснить, почему этот код не работает, или B) предлагают лучшее предложение о возможности нажатия кнопки и маршрутизации аудио до верхнего громкоговорителя?

PS Я все больше и больше знаком с программированием на iOS, но это мой первый набег на мир AudioSessions и тому подобное, поэтому детали и образцы кода очень ценятся! Спасибо за помощь!

UPDATE:

С предложением "Он был" (ниже) я удалил код цитируемый выше, и заменил его:

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

в начале viewDidLoad. Тем не менее, он все еще не работает (я имею в виду, что звук все еще выходит из динамика внизу телефона вместо приемника наверху). По-видимому, поведение по умолчанию должно быть для AVAudioSessionCategoryPlayAndRecord, чтобы отправить аудио из ресивера самостоятельно, поэтому что-то по-прежнему не так.

Более конкретно, что я делаю с этим кодом играет звук через IPod Music Player (инициализируется сразу после строки AVAudioSession выше в viewDidLoad, для чего это стоит):

_musicPlayer = [MPMusicPlayerController iPodMusicPlayer]; 

и средства массовой информации для что IPod Music Player выбран через MPMediaPickerController:

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

    [self dismissViewControllerAnimated:YES completion:nil]; 
} 

все это кажется довольно простым для меня, у меня нет никаких ошибок или предупреждений, и я знаю, что СМИ Picker и музыкальный плеер правильно работает becaus e правильные песни начинают играть, это просто из неправильного динамика. Может ли быть «игровой носитель, использующий этот метод AudioSession» или что-то еще? Или есть способ проверить, какая категория аудиосеансов активна в настоящий момент, чтобы подтвердить, что ничто не могло ее перевернуть или что-то еще? Есть ли способ решительно сказать, что код ИСПОЛЬЗУЕТ приемник, а не полагаться на значение по умолчанию? Я чувствую, что я на линии одного двора, мне просто нужно пересечь этот последний бит ...

EDIT: Я просто подумал о теории, в которой это что-то об iPod Music Player, t хотите воспроизвести из приемника. Мое рассуждение: можно настроить песню, чтобы начать играть через официальное приложение iPod, а затем легко настроить его (пауза, пропустить и т. Д.) Через приложение, которое я разрабатываю. Непрерывное воспроизведение из одного приложения в другое заставило меня подумать, что, возможно, iPod Music Player имеет собственные настройки маршрута аудио, или, может быть, он не останавливается, чтобы проверить настройки в новом приложении? Кто-нибудь, кто знает, что они говорят, думает, что это может быть что-то вроде этого?

+0

Пробовал просто утверждает, удаление содержимого диска и посмотреть, что происходит? – Undo

+0

Прокомментируйте все это, и это похоже на отсутствие кода там: звук проходит через нижний динамик. Я предполагаю, что из-за той же проблемы, из-за которой утверждения терпят неудачу, я просто недостаточно опытен, чтобы знать, что это может быть. – Nerrolken

+1

Я тоже не ... Удачи! – Undo

ответ

4

Сначала вы должны инициализировать свою аудио сессию.

Используя C API

AudioSessionInitialize (NULL, NULL, NULL, NULL); 

В iOS6 вы можете использовать методы AVAudioSession вместо (вам нужно будет импортировать базу AVFoundation использовать AVAudioSession):

Инициализация с использованием AVAudioSession

self.audioSession = [AVAudioSession sharedInstance]; 

Настройка категории audioSession с использованием AVAudioSession

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

Для дальнейших исследований, если вы хотите, лучшие условия поиска, здесь полные имена констант для колонок: документы

const CFStringRef kAudioSessionOutputRoute_BuiltInReceiver; 
const CFStringRef kAudioSessionOutputRoute_BuiltInSpeaker; 

см яблока here

Но настоящая загадка, почему ты возникают проблемы с маршрутизацией на приемник. Это поведение по умолчанию для категории playAndRecord. Документация Яблока kAudioSessionOverrideAudioRoute_None:

«Указывает, для категории kAudioSessionCategory_PlayAndRecord, что выход аудио должен идти к приемнику Это аудио маршрут выход по умолчанию для этой категории..»

обновление

В обновленном вопросе вы обнаружите, что используете класс MPMusicPlayerController.Этот класс вызывает глобальный музыкальный проигрыватель (тот же плеер, что и в приложении «Музыка»). Этот музыкальный плеер отделен от вашего приложения, и поэтому он не поддерживает тот же сеанс аудио, что и аудиозапись вашего приложения. Все свойства, которые вы установили в аудиосистеме вашего приложения, будут игнорироваться MPMusicPlayerController.

Если вы хотите контролировать поведение звука вашего приложения, вам необходимо использовать внутреннюю среду для вашего приложения. Это будет AVAudioRecorder/AVAudioPlayer или Core Audio (звуковые очереди, аудиоустройства или OpenAL). Какой бы метод вы ни использовали, аудио-сеанс можно контролировать либо через свойства AVAudioSession, либо через Core Audio API. Core Audio дает вам более тонкий контроль, но с каждым новым выпуском iOS больше его переносят на AVFoundation, поэтому начните с этого.

Также помните, что аудиозапись предоставляет вам возможность описать предполагаемое поведение аудио вашего приложения в отношении общей среды iOS, но оно не даст вам полного контроля. Apple позаботится о том, чтобы ожидания пользователя в отношении поведения их устройств оставались согласованными между приложениями, и когда одно приложение должно прерывать звуковой поток другого пользователя.

обновление 2

В вашем редактировании вы намекаете на возможность аудио сессий проверки настройки аудио сессии других приложений. Этого не происходит . Идея состоит в том, что каждое приложение устанавливает предпочтения для собственного поведения звука, используя его автономную аудио сессию. Операционная система арбитраж между противоречивыми требованиями к звуку, когда более чем одно приложение конкурирует за недоступный ресурс, такой как внутренний микрофон или один из динамиков, и обычно решает в пользу такого поведения, которое, скорее всего, будет соответствовать ожиданиям пользователя устройства в целом.

Класс MPMusicPlayerController немного необычен тем, что дает возможность одному приложению иметь некоторый контроль над другим. В этом случае ваше приложение не воспроизводит аудио, оно отправляет запрос на воспроизведение музыкального проигрывателя от вашего имени. Ваш контроль ограничен степенью API MPMusicPlayerController. Для большего контроля ваше приложение должно будет обеспечить собственную реализацию воспроизведения звука.

В Ваш комментарий Вы задаетесь вопросом:

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?

Это (большой) предмет для нового вопроса. Вот хороший стартовый текст (из блога Криса Адамсона) From iPod Library to PCM Samples in Far Fewer Steps Than Were Previously Necessary - это продолжение к From iphone media library to pcm samples in dozens of confounding and potentially lossy steps - это должно дать вам ощущение сложности, с которой вы столкнетесь. У этого может стало легче с iOS6, но я бы не был так уверен!


есть otherAudioPlaying только для чтения BOOL свойство в iOS6, но это об этом

+0

Спасибо! Я только что посмотрел в «AVAudioSessionCategoryPlayAndRecord», и это может быть именно то, что я ищу. Тем не менее, я добавил строки '[[AVAudioSession sharedInstance] setCategory: AVAudioSessionCategoryPlayAndRecord error: nil]; [[AVAudioSession sharedInstance] setActive: YES error: nil]; 'вместо кода, который я отправил в' viewDidLoad', и я не получаю никаких ошибок, но он все еще воспроизводится через динамик. Любая идея, что я делаю неправильно? Должны ли эти строки не войти в 'viewDidLoad', или мне нужно что-либо делать с моей игрой' [self.musicPlayer], 'строками или чем-нибудь? – Nerrolken

+0

@AlexanderWinn - да, они должны идти в viewDidLoad. Вам необходимо инициализировать audioSession перед установкой каких-либо свойств на нем. '[AVAudioSession sharedInstance]' достигает этого. Что касается '[self.musicPlayer play]', вы не разместили этот код в своем исходном вопросе. Возможно, вам следует обновить вопрос с помощью большего контекста кода, поскольку код, который вы уже опубликовали, должен работать. – foundry

+0

Обновлено. Кажется, что-то похожее на это может быть проблемой? И еще раз спасибо за вашу помощь! – Nerrolken

24

борется с этим на некоторое время тоже. Возможно, это поможет кому-то позже. Вы также можете использовать более новые методы переопределения портов. Многие из методов в вашем примере кода фактически устарели.

Так что если у вас есть AudioSession sharedInstance, получая,

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

Сеанс категория должна быть AVAudioSessionCategoryPlayAndRecord Вы можете получить выходной ток, проверив это значение.

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

И теперь в зависимости от порта, который вы хотите, чтобы отправить его, просто переключать вывод с помощью

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

Это должно быть быстрый способ переключения выходов на динамик телефона и приемника.

+1

Работы Великий! Мне просто пришлось изменить категорию сеанса и переопределить выходной аудиопорт, просто! thanks – YoGiN

+0

Пробовал это решение iOS 8. Это работает. Просто выполните [session overrideOutputAudioPort: AVAudioSessionPortOverrideNone error: nil]; , если вы не хотите, чтобы он переключался и просто воспроизводил звук в ушной части. – coolcool1994

+0

можете ли вы посмотреть это. https://stackoverflow.com/questions/45424446/avaudiosession-playing-audio-through-earpiece-speaker – Shohrab

1

Swift 3,0 Код

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

вы можете посмотреть это. https://stackoverflow.com/questions/45424446/avaudiosession-playing-audio-through-earpiece-speaker – Shohrab

Смежные вопросы