34 votes

La détection de la sonnerie, du silence et de la sourdine de l'iPhone à l'aide d'AVAudioPlayer ne fonctionne pas ?

J'ai essayé d'utiliser ces méthodes pour tenter de détecter si le commutateur Ring/Silent est actif ou non :

Comment détecter par programmation le commutateur de sourdine de l'iPhone ?

La catégorie AVAudioSession ne fonctionne pas comme le veut la documentation

Mais sur mon iPhone 4, la valeur "state" est toujours "Speaker" (et la valeur de longueur renvoyée par CFStringGetLength(state) est toujours 7). Quelqu'un a-t-il utilisé cette méthode avec succès ? Si oui, sur quel type d'appareil et quelle version du SDK ?

Je l'appelle comme ça :

- (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]);
    ...
}

Je pensais que peut-être un autre événement (plus précis) kAudioSessionProperty est déclenché lorsque l'interrupteur physique du téléphone est ... activé. Quelqu'un a une idée ?

Au fait, j'utilise la catégorie AVAudioSessionCategoryAmbient avec mon [AVAudioSession sharedInstance].

Update: J'ai également essayé d'utiliser différentes catégories d'audio, et une poignée d'autres propriétés de session audio, mais aucune ne semble fonctionner lors de la désactivation ou de la désactivation du commutateur.

1er janvier 2014 Mise à jour : C'est un peu un hack, et j'ai rencontré un crash alors que je faisais du multitâche avec sur mon iPhone 5S, mais les SoundSwitch La bibliothèque liée dans la nouvelle réponse acceptée est la voie à suivre si vous voulez détecter le commutateur silencieux. Elle fonctionne même sous iOS 7.

32voto

taber Points 1463

J'ai trouvé la réponse grâce à quelqu'un des Forums des développeurs, et vous n'allez pas aimer ça !

J'ai eu une réponse d'Apple à ce sujet.

Ils ont dit qu'ils n'ont pas et n'ont jamais fourni une méthode pour détecter un interrupteur muet matériel et n'ont pas l'intention de le faire.

:(

IMO il y a définitivement un intérêt à détecter l'interrupteur silencieux et à avertir l'utilisateur au cas où il aurait oublié qu'il était activé... J'ai eu des gens qui se sont plaints qu'ils n'avaient pas de son et que l'interrupteur silencieux en était la raison ! Mais bon.

PS : Si vous souhaitez qu'Apple ajoute cette fonctionnalité (et bien sûr, vous le souhaitez !), veuillez remplir un nouveau rapport de bogue "Enhancement" pour "iPhone SDK" à l'adresse suivante http://bugreport.apple.com/

Update: Bien qu'il n'y ait toujours pas de moyen officiel de vérifier l'état du commutateur de sourdine, il existe une solution de contournement/une bibliothèque appelée "SoundSwitch" qui semble faire l'affaire. Consultez la nouvelle réponse acceptée pour le lien.

11voto

alexcurylo Points 1110

"Cependant, si quelqu'un pouvait nous montrer comment utiliser cette AudioSessionProperty_AudioRouteDescription, la prime lui revient de droit."

Eh bien, il suffit de NSLog() le résultat et vous obtenez

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

Malheureusement, j'obtiens le même résultat sur un iPad2/OS 5.0, que le son soit coupé ou non. Il semble donc qu'il s'agisse d'un équivalent fonctionnel de kAudioSessionProperty_AudioRoute.

Une recherche sur les forums de développeurs montre que c'est un problème fréquemment rencontré, résumé par la phrase suivante

"J'ai signalé ce problème avec rdar://9781189 en juillet dernier et le problème est toujours présent dans la GM".

Alors oui ... il semble bien que vous êtes SOL avec cela dans 5.0.

ADDENDUM :

"Mais qu'en est-il de ce CFDictionary que vous enregistrez. Comment puis-je accéder à la clé "RouteDetailedDescription_PortType" ?"

Le pontage sans frais est votre ami.

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

produit

le premier type de port de sortie est : Haut-parleur !

De nombreux types CFT courants sont reliés à des types plus pratiques.

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

Maintenant, il faut un peu d'entraînement pour deviner correctement quels lancers d'incantations magiques permettront d'obtenir quelque chose d'utile dans un dictionnaire comme celui ci-dessus. Une aide de classe de ce type vous aidera à vous mettre à niveau :

  - (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];
           }
        }
     }
  }

ce qui, pour notre dictionnaire, donne (en ajoutant des indentations pour plus de clarté)

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

qui vous permet de savoir avec certitude ce que vous auriez pu déduire du log original, sachant que NSLog affiche les tableaux dans ( ) et les dictionnaires dans { }, donc les casts corrects étaient éminemment devinables. Mais certaines structures CFType sont plus difficiles à analyser que cela.

4voto

iMoses Points 937

J'ai parcouru cette bibliothèque VSSilentSwitch.
Cela n'a pas fonctionné pour moi (cela ne fonctionne pas lorsque vous commencez à utiliser réellement l'audio).
J'ai réfléchi à la façon dont il a procédé, puis j'ai réalisé que l'appel d'achèvement audio est appelé presque dès que le son commence à être joué lorsque nous sommes silencieux.
Pour être un peu plus précis :
Les sons du système joués à l'aide de AudioServicesPlaySystemSound terminera la lecture aussi vite qu'elle a commencé.
Bien sûr, cela ne fonctionnera que sur les catégories audio qui respectent le commutateur silencieux (la valeur par défaut est la suivante AVAudioSessionCategoryAmbient le respecte).
L'astuce consiste donc à créer un son système, de préférence un son silencieux, et à le faire jouer encore et encore, tout en vérifiant le temps qu'il a fallu entre la lecture et l'achèvement (installez une procédure d'achèvement à l'aide de la fonction AudioServicesAddSystemSoundCompletion ).
Si le proc d'achèvement est appelé très tôt (permettre à un peu de seuil) - cela signifie que l'interrupteur silencieux est activé.
Cette astuce comporte de nombreux inconvénients, le plus important étant le fait qu'elle ne fonctionne pas sur toutes les catégories audio.
Si votre application joue de l'audio en arrière-plan - assurez-vous d'arrêter ce test pendant qu'elle est en arrière-plan ou votre application fonctionnera éternellement en arrière-plan (et sera rejetée par Apple, aussi).

1voto

Speakus Points 739

Je pense que vous pouvez essayer le comportement suivant pour détecter l'état du commutateur silencieux si vous en avez vraiment besoin : Le callback AudioServicesAddSystemSoundCompletion n'est pas appelé en mode silencieux.

0voto

flake Points 1

Essayez d'insérer cette ligne au-dessus de votre appel à AudioSessionGetProperty dans deviceIsSilenced.

AudioSessionInitialize(NULL, NULL, NULL, NULL) ;

Il devrait alors commencer à renvoyer une chaîne vide lorsque l'interrupteur est désactivé (mais il affichera Headphone et d'autres états si un casque BT ou un accessoire est connecté, par exemple).

Pour information, je ne crois pas qu'il y ait quoi que ce soit dans l'API publique qui se déclenche lorsque l'interrupteur est déplacé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X