通过上(扬声器)扬声器播放音频 [英] Play audio through upper (phone call) speaker

查看:248
本文介绍了通过上(扬声器)扬声器播放音频的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在我的应用中获取音频,以通过iPhone上的扬声器播放,这是您在通话中按下的扬声器.我知道这是有可能的,因为我曾经在App Store玩过一个游戏(该游戏是"Tap Tap Tap"中的"The Heist"),它可以模拟电话并准确地做到这一点.

I'm trying to get audio in my app to play through the upper speaker on the iPhone, the one you press to your ear during a phone call. I know it's possible, because I've played a game from the App Store ("The Heist" by "tap tap tap") that simulates phone calls and does exactly that.

我已经在网上进行了很多研究,但是我很难找到一个甚至讨论过这种可能性的人.绝大多数帖子似乎都是关于免提扬声器与插入式耳机的关系(例如Audio Session Category Route Overrides,但是那些再次似乎(如果我错了,请纠正我)只处理底部的免提扬声器,而不是手机顶部的扬声器.

I've done a lot of research online, but I'm having a surprisingly hard time finding ANYONE who has even discussed the possibility. The overwhelming majority of posts seem to be about the handsfree speaker vs plugged-in earphones, (like this and this and this), rather than the upper "phone call" speaker vs the handsfree speaker. (Part of that problem might be not having a good name for it: "phone speaker" often means the handsfree speaker at the bottom of the device, etc, so it's hard to do a really well-targeted search). I've looked into Apple's Audio Session Category Route Overrides, but those again seem to (correct me if I'm wrong) deal only with the handsfree speaker at the bottom, not the speaker at the top of the phone.

我发现一个帖子似乎与此有关:链接.它甚至提供了很多代码,所以我以为我可以自由出门了,但是现在我似乎无法使代码正常工作.为简单起见,我只是将DisableSpeakerPhone方法(如果我理解正确,应该是将音频重新路由到上扬声器的方法)复制到我的viewDidLoad中,以查看它是否有效,但是第一行断言"行失败,音频继续从底部播放. (我也按照注释中的建议导入了AudioToolbox Framework,所以这不是问题.)

I have found ONE post that seems to be about this: link. It even provides a bunch of code, so I thought I was home free, but now I can't seem to get the code to work. For simplicity I just copied the DisableSpeakerPhone method (which if I understand it correctly should be the one to re-route audio to the upper speaker) into my viewDidLoad to see if it would work, but the first "assert" line fails, and the audio continues to play out the bottom. (I also imported the AudioToolbox Framework, as suggested in the comment, so that isn't the problem.)

这是我正在使用的主要代码块(这是我复制到我的viewDidLoad中进行测试的代码),尽管我链接到的文章中还有其他一些方法:

Here is the main block of code I'm working with (this is what I copied into my viewDidLoad to test), although there are a few more methods in the article I linked to:

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

所以我的问题是:A)可以帮助任何人找出该代码为何不起作用的原因吗,或者B)可以提供一个更好的建议,使您可以按一下按钮并路由音频到上扬声器吗?

So my question is this: can anyone either A) help me figure out why that code doesn't work, or B) offer a better suggestion for being able to press a button and route the audio up to the upper speaker?

PS我对iOS编程越来越熟悉,但这是我第一次涉足AudioSessions等世界,因此非常感谢详细信息和代码示例!谢谢您的帮助!

PS I am getting more and more familiar with iOS programming, but this is my first foray into the world of AudioSessions and such, so details and code samples are much appreciated! Thank you for your help!

更新:

根据他是"的建议(如下),我删除了上面引用的代码,并将其替换为:

From the suggestion of "He Was" (below) I've removed the code quoted above and replaced it with:

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

开头的

.但是,它仍然不起作用(这意味着声音仍然从电话底部的扬声器而不是顶部的接收器出来).显然,AVAudioSessionCategoryPlayAndRecord的默认行为应该是自行将音频发送出接收器,所以还是有问题的.

at the beginning of viewDidLoad. It still isn't working, though, (by which I mean the audio is still coming out of the speaker at the bottom of the phone instead of the receiver at the top). Apparently the default behavior should be for AVAudioSessionCategoryPlayAndRecord to send audio out of the receiver on its own, so something is still wrong.

更具体地讲,我正在使用这段代码通过iPod Music Player播放音频(出于价值,它在上面的viewDidLoad中的AVAudioSession行之后立即初始化):

More specifically what I'm doing with this code is playing audio through the iPod Music Player (initialized right after the AVAudioSession lines above in viewDidLoad, for what it's worth):

_musicPlayer = [MPMusicPlayerController iPodMusicPlayer];

,并通过MPMediaPickerController选择该iPod音乐播放器的媒体:

and the media for that iPod Music Player is chosen through an MPMediaPickerController:

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

    [self dismissViewControllerAnimated:YES completion:nil];
}

在我看来,这一切都非常简单,我没有任何错误或警告,而且我知道Media Picker和Music Player可以正常工作,因为开始播放正确的歌曲,只是出了错的扬声器.是否可以使用使用此AudioSession播放媒体"方法或其他方法?还是有办法检查当前处于活动状态的音频会话类别,以确认没有任何东西可以将其切换回去?有没有一种方法可以强调告诉代码使用接收器,而不是依靠默认值来使用接收器?我觉得我在一条码的线上,我只需要越过最后一点...

This all seems fairly straightforward to me, I've got no errors or warnings, and I know that the Media Picker and Music Player are working correctly because the correct songs start playing, it's just out of the wrong speaker. Could there be a "play media using this AudioSession" method or something? Or is there a way to check what audio session category is currently active, to confirm that nothing could have switched it back or something? Is there a way to emphatically tell the code to USE the receiver, rather than relying on the default to do so? I feel like I'm on the one-yard line, I just need to cross that final bit...

编辑:我只是想到了一个理论,其中涉及iPod音乐播放器的某些内容不希望在接收器中播放.我的推理:可以设置一首歌曲,以通过官方iPod应用程序开始播放,然后通过正在开发的应用程序无缝调整(暂停,跳过等)它.从一个应用程序到另一个应用程序的连续播放使我觉得iPod音乐播放器可能具有自己的音频路由设置,或者它可能不会停止检查新应用程序中的设置?知道他们在说什么的人是否会认为是这样?

I just thought of a theory, wherein it's something about the iPod Music Player that doesn't want to play out of the receiver. My reasoning: it is possible to set a song to start playing through the official iPod app and then seamlessly adjust it (pause, skip, etc) through the app I'm developing. The continuous playback from one app to the next made me think that maybe the iPod Music Player has its own audio route settings, or maybe it doesn't stop to check the settings in the new app? Does anyone who knows what they're talking about think it could it be something like that?

推荐答案

您必须首先初始化音频会话.

You have to initialise your audio session first.

使用C API

  AudioSessionInitialize (NULL, NULL, NULL, NULL);

在iOS6中,您可以改用AVAudioSession方法(您需要导入AVFoundation框架才能使用AVAudioSession):

In iOS6 you can use AVAudioSession methods instead (you will need to import the AVFoundation framework to use AVAudioSession):

使用AVAudioSession初始化

Initialization using AVAudioSession

 self.audioSession = [AVAudioSession sharedInstance];

使用AVAudioSession设置audioSession类别

Setting the audioSession category using AVAudioSession

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

为了进行进一步的研究,如果您想要更好的搜索词,以下是说话人常量的全名:

For further research, if you want better search terms, here are the full names of the constants for the speakers:

const CFStringRef kAudioSessionOutputRoute_BuiltInReceiver;
const CFStringRef kAudioSessionOutputRoute_BuiltInSpeaker;

请参见苹果的文档此处

但是真正的奥秘在于为什么您在路由到接收器时遇到任何麻烦.这是playAndRecord类别的默认行为. Apple的kAudioSessionOverrideAudioRoute_None文档:

But the real mystery is why you are having any trouble routing to the receiver. It's the default behaviour for the playAndRecord category. Apple's documentation of kAudioSessionOverrideAudioRoute_None:

对于kAudioSessionCategory_PlayAndRecord类别,指定输出音频应到达接收器.这是该类别的默认输出音频路由."

"Specifies, for the kAudioSessionCategory_PlayAndRecord category, that output audio should go to the receiver. This is the default output audio route for this category."

更新

在更新的问题中,您表明您正在使用MPMusicPlayerController类.此类调用全局音乐播放器(音乐"应用程序中使用的同一播放器).此音乐播放器与您的应用程序是分开的,因此不会与您的应用程序的audioSession共享相同的音频会话.您在应用的audioSession上设置的任何属性都将被MPMusicPlayerController忽略.

In your updated question you reveal that you are using the MPMusicPlayerController class. This class invokes the global music player (the same player used in the Music app). This music player is separate from your app, and so doesn't share the same audio session as your app's audioSession. Any properties you set on your app's audioSession will be ignored by the MPMusicPlayerController.

如果要控制应用程序的音频行为,则需要使用应用程序内部的音频框架.这将是AVAudioRecorder/AVAudioPlayer或核心音频(音频队列,音频单元或OpenAL).无论使用哪种方法,都可以通过AVAudioSession属性或Core Audio API来控制音频会话. Core Audio为您提供了更细粒度的控制,但是在每个新的iOS版本中,更多的内容都将移植到AVFoundation,因此从此开始.

If you want control over your app's audio behaviour, you need to use an audio framework internal to your app. This would be AVAudioRecorder / AVAudioPlayer or Core Audio (Audio Queues, Audio Units or OpenAL). Whichever method you use, the audio session can be controlled either via AVAudioSession properties or via the Core Audio API. Core Audio gives you more fine-grained control, but with each new release of iOS more of it is ported over to AVFoundation, so start with that.

还请记住,音频会话为您提供了一种相对于整个iOS环境描述应用程序音频的预期行为的方式,但这不会让您完全控制. Apple会确保应用程序之间以及当一个应用程序需要中断另一个人的音频流时,用户对设备的音频行为的期望保持一致.

Also remember that the audio session provides a way for you to describe the intended behaviour of your app's audio in relation to the total iOS environment, but it will not hand you total control. Apple takes care to ensure that the user's expectations of their device's audio behaviour remain consistent between apps, and when one app needs to interrupt another's audio stream.

更新2

在编辑中,您暗示音频会话可能会检查其他应用程序的音频会话设置.不会发生 1 .想法是,每个应用程序都使用其独立的音频会话为其自身的音频​​行为设置首选项.当多个应用竞争不共享资源(例如内部麦克风或扬声器之一)竞争时,操作系统会在冲突的音频要求之间进行仲裁,并且通常会决定最有可能采取这种行为以满足用户对整个设备的期望.

In your edit you allude to the possibility of audio sessions checking other app's audio session settings. That does not happen1. The idea is that each app sets it's preferences for it's own audio behaviour using it's self-contained audio session. The operating system arbitrates between conflicting audio requirements when more than one app competes for an unshareable resource, such as the internal microphone or one of the speakers, and will usually decide in favour of that behaviour which is most likely to meet the user's expectations of the device as a whole.

MPMusicPlayerController类有点不寻常,因为它使您能够使一个应用程序对另一个应用程序具有一定程度的控制权.在这种情况下,您的应用没有播放音频,而是向音乐播放器发送了一个请求,以您的名义播放音频.您的控件受MPMusicPlayerController API范围的限制.为了获得更多控制权,您的应用必须提供自己的音频播放实现.

The MPMusicPlayerController class is slightly unusual in that it gives you the ability for one app to have some degree of control over another. In this case, your app is not playing the audio, it is sending a request to the Music Player to play audio on your behalf. Your control is limited by the extent of the MPMusicPlayerController API. For more control, your app will have to provide it's own implementation of audio playback.

在您的评论中,您想知道:

In your comment you wonder:

是否可以通过某种方式从MPMusicPlayerController中提取MPMediaItem,然后通过特定于应用程序的音频会话进行播放,或者类似的方法?

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?

这是一个新问题的(大)主题.这是一个很好的开始阅读(来自Chris Adamson的博客)从iphone媒体库到pcm示例,其中涉及数十个令人费解的步骤和可能造成损失的步骤-这应该使您对将要面对的复杂性有所了解.自iOS6以来,这种可能变得更容易了,但我不太确定!

That's a (big) subject for a new question. Here is a good starting read (from Chris Adamson's blog) From iPod Library to PCM Samples in Far Fewer Steps Than Were Previously Necessary - it's the sequel to From iphone media library to pcm samples in dozens of confounding and potentially lossy steps - that should give you a sense to the complexity you will face. This may have got easier since iOS6 but I wouldn't be so sure!

1 在ios6中有一个otherAudioPlaying只读BOOL属性,仅此而已

1 there is an otherAudioPlaying read-only BOOL property in ios6, but that's about it

这篇关于通过上(扬声器)扬声器播放音频的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆