32

我尝试使用这些方法来检测响铃/静音开关是否处于活动状态:

如何以编程方式感知 iPhone 静音开关?

AVAudioSession 类别不按文档要求工作

但在我的 iPhone 4 上,“状态”值始终为“扬声器”(CFStringGetLength(state) 返回的长度值始终为 7)。有没有人成功使用过这个方法?如果是这样,在什么样的设备和 SDK 版本上?

我这样称呼它:


- (BOOL)deviceIsSilenced {
    CFStringRef state;
    UInt32 propertySize = sizeof(CFStringRef);
    OSStatus audioStatus = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
    if (audioStatus == kAudioSessionNoError) {
        NSLog(@"audio route: %@", state) // "Speaker" regardless of silent switch setting, but "Headphone" when my headphones are plugged in
        return (CFStringGetLength(state) <= 0);
    }
    return NO;
}

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
    audioSession.delegate = self;
    [audioSession setCategory:AVAudioSessionCategoryAmbient error:nil];
    [audioSession setActive:YES error:nil];
    NSLog(@"muted? %i", [self deviceIsSilenced]);
    ...
}

我在想,当手机上的物理开关被切换时,可能会触发其他一些(更准确的)kAudioSessionProperty 事件。有人有想法么?

顺便说一句,我将 AVAudioSessionCategoryAmbient 类别与我的 [AVAudioSession sharedInstance] 一起使用。

更新:我也尝试过使用不同的音频类别和一些其他音频会话属性,在静音/取消静音开关时似乎没有触发。:(

2014 年 1 月 1 日更新:这有点骇人听闻,我在 iPhone 5S 上进行多任务处理时遇到了崩溃,但如果您想检测静音开关。它甚至适用于 iOS 7。

4

7 回答 7

31

好吧,感谢开发者论坛的某个人,我找到了答案,你们不会喜欢的!

我收到了 Apple 对此的回应。

他们说他们没有也从未提供过检测硬件静音开关的方法,也不打算这样做。

:(

IMO 检测静音开关并通知用户以防万一他们忘记它是打开的,这绝对是有价值的……我曾经有人抱怨他们没有任何声音,而静音开关就是原因!那好吧。

PS:如果您希望 Apple 添加此功能(当然您也这样做了!),请在http://bugreport.apple.com/为“iPhone SDK”提交新的“增强”错误报告

更新:虽然仍然没有官方方法来检查静音开关的状态,但有一个名为“SoundSwitch”的解决方法/库似乎可以解决问题。查看链接的新接受答案。

于 2011-11-04T12:17:54.977 回答
11

“但是,如果有人可以向我们展示如何使用这个 AudioSessionProperty_AudioRouteDescription,那么赏金就是他的。”

好吧,只是 NSLog() 结果,你得到

routes: {
    "RouteDetailedDescription_Inputs" =     (
    );
    "RouteDetailedDescription_Outputs" =     (
                {
            "RouteDetailedDescription_PortType" = Speaker;
        }
    );
}

不幸的是,我在静音和未静音的 iPad2/OS 5.0 上得到了相同的结果。所以它似乎在功能上等同于 kAudioSessionProperty_AudioRoute,没有乐趣。

看开发板发现这是一个经常遇到的问题,总结最好用

“我在 7 月份通过 rdar://9781189 报告了这个问题,但问题仍然存在于 GM 中。”

所以是的......看起来你在 5.0 中是 SOL。

附录:

“但是你正在记录的那个 CFDictionary 怎么样。我怎样才能访问“RouteDetailedDescription_PortType”键?”

免费桥接是您的朋友。

  CFDictionaryRef asCFType = nil;
  UInt32 dataSize = sizeof(asCFType);
  AudioSessionGetProperty(kAudioSessionProperty_AudioRouteDescription, &dataSize, &asCFType);
  NSDictionary *easyPeasy = (NSDictionary *)asCFType;
  NSDictionary *firstOutput = (NSDictionary *)[[easyPeasy valueForKey:@"RouteDetailedDescription_Outputs"] objectAtIndex:0];
  NSString *portType = (NSString *)[firstOutput valueForKey:@"RouteDetailedDescription_PortType"];
  NSLog(@"first output port type is: %@!", portType);

生产

第一个输出端口类型是:扬声器!

许多常见的 CFType 被桥接到更方便的类型。

http://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html

现在,需要一点练习才能正确猜出魔法咒语的施法会从上面的字典中得到有用的东西。沿着这些方向的类转储助手将帮助您加快速度:

  - (void)whatsInThis:(CFDictionaryRef)thingy
  {
     NSDictionary *dict = (NSDictionary *)thingy;
     for (NSString *key in dict.allKeys)
     {
        id value = [dict valueForKey:key];
        NSLog(@"key: %@ value type %@", key, [value class]);
        if ([value isKindOfClass:[NSArray class]])
        {
           NSArray *array = (NSArray *)value;
           for (id item in array)
           {
              NSLog(@" -- object type %@", [item class]);
              if ([item isKindOfClass:[NSDictionary class]])
                 [self whatsInThis:item];
           }
        }
     }
  }

这对于我们手头的字典产生(为清楚起见添加缩进)

key: RouteDetailedDescription_Inputs value type __NSCFArray
key: RouteDetailedDescription_Outputs value type __NSCFArray
  -- object type __NSCFDictionary
    key: RouteDetailedDescription_PortType value type __NSCFString

这可以让您确定可以从原始日志中推断出什么,知道 NSLog 在 ( ) 中显示数组,在 { } 中显示字典,因此正确的强制转换非常容易猜到。但是一些 CFType 结构比这更难解析。

于 2011-10-20T19:42:33.620 回答
3

我浏览了这个 VSSilentSwitch 库。
对我不起作用(当您开始实际使用音频时不起作用)。
我在想他是怎么做到的,然后意识到音频完成调用几乎是在我们沉默时声音开始播放时被调用的。
更具体地说:
正在播放的系统声音AudioServicesPlaySystemSound将在开始后立即完成播放。
当然,这仅适用于尊重静音开关的音频类别(默认AVAudioSessionCategoryAmbient尊重它)。
所以诀窍是创建一个系统声音,最好是无声的声音,然后一遍又一遍地播放,同时检查从播放到完成所用的时间(使用安装完成程序AudioServicesAddSystemSoundCompletion)。
如果很快调用完成过程(允许一些阈值) - 这意味着静音开关已打开。
这个技巧有很多警告,最大的一个是它不适用于所有音频类别。
如果您的应用在后台播放音频 - 请确保您在后台停止此测试,否则您的应用将永远在后台运行(并且也会被苹果拒绝)。

于 2013-06-02T15:32:14.260 回答
0

尝试将此行插入到 deviceIsSilenced 内对 AudioSessionGetProperty 的调用上方

AudioSessionInitialize(NULL, NULL, NULL, NULL);

然后应该在开关关闭时开始返回空字符串(尽管如果连接了 BT 耳机或附件,则会显示 Headphone 和其他一些状态)。

FWIW 我不相信公共 API 中有任何东西会在实际开关被移动时触发。

于 2011-08-02T18:32:10.850 回答
0

好的,在kAudioSessionProperty_AudioRoute使用 CMD + click 之后,我发现了这个:(

/*!
 @enum           AudioSession audio categories states
 @abstract       Deprecated AudioSession properties
 @constant       kAudioSessionProperty_AudioRoute 
 Deprecated in iOS 5.0; Use kAudioSessionProperty_AudioRouteDescription 
 */
enum {
    kAudioSessionProperty_AudioRoute                            = 'rout',   // CFStringRef      (get only)        
};

原来我们必须使用kAudioSessionProperty_AudioRouteDescription,但是这家伙返回 aCFDictionaryRef或其他东西,我完全不知道如何处理它....

如果没有人向我们展示如何使用kAudioSessionProperty_AudioRouteDescription,我将其作为答案,我将尝试接受我的答案...

但是,如果有人可以向我们展示如何使用它kAudioSessionProperty_AudioRouteDescription,那么赏金就是他的。

编辑:

显然,这是 iOS 5 的问题。我之前没有说明这一点,因为它看起来太明显了,但后来我认为搜索引擎可能不那么明显......如果你明白我的意思。

因此,由于引用的已弃用值,iOS 5 无法使用 iPhone 上的静音/静音开关。

于 2011-10-16T15:33:47.933 回答
0

我想你有错误的印象。路线是它要去的地方。您想知道音量。采用kAudioSessionProperty_CurrentHardwareOutputVolume

于 2011-10-20T13:52:08.410 回答
0

找到这个库 http://www.verietassoftware.com/index.php?option=com_content&view=article&id=27&Itemid=115

苹果会允许这些东西吗?这是一个 500Kb 的库,通过音频和电话设置做一些黑魔法

于 2012-03-18T16:09:40.190 回答