Plusieurs techniques permettent de jouer des sons sur un device. La plus simple consiste à passer par les « System Sound Services » du framework
AudioToolbox. Une technique, plus complexe, consiste à utiliser un
AVAudioPlayer.
Je vais vous montrer comment utiliser ces deux techniques. À vous de choisir celle qui est le mieux adaptée aux applications que vous voulez réaliser.
La technique « System Sound Services »
Cette technique est particulièrement bien adaptée aux sons utilisés dans l'interface utilisateur d'une application : quand vous appuyez sur un bouton ou quand vous vous déplacez dans un contrôle par exemple.
Il y a cependant quelques restrictions.
- Les sons doivent avoir une durée maximale de 30 secondes.
- Seuls quelques codecs audio sont supportés :
- AMR (Adaptive Multi-Rate) ;
- iLBC (internet Low Bitrate Codec) ;
- IMA/ADPCM (IMA-4) ;
- Linear PCM ;
- µLaw and aLaw.
Codec ? Qu'est-ce encore que cela ?
Codec est l'abréviation de « codeur/décodeur ». Les fichiers audio sont généralement compressés pour réduire leur taille. Le format de compression (le codec) utilisé dépend de l'application dans laquelle le fichier audio est créé. Pour qu'il puisse être joué sur une machine donnée (un Mac, un PC, un iPhone, etc.), il faut que ce dernier dispose du décodeur correspondant. Les iPhone/iPod Touch/iPad disposent des codecs cités plus haut.
Si un fichier audio utilise un codec non reconnu, il ne sera pas joué sur le device. Pour éviter ce désagrément, je vous conseille de convertir vos sons au format CAF en utilisant le programme
afconvert sur votre Mac.
Cliquez sur l'icône
Applications dans le Dock, ouvrez le dossier
Utilitaires puis cliquez sur l'icône
Terminal. Déplacez-vous dans le dossier qui contient le fichier à convertir. Supposons que vous vouliez convertir le fichier
22-new.aif en
22-new.caf ; vous taperez quelque chose comme ceci :
Code : Console | afconvert -f caff -d LEI16@44100 22-new.aif 22-new.caf |
Ça me semble plutôt indigeste comme commande. Pourrais-je avoir quelques explications ?
Vous avez raison : la commande
afconvert n'est pas très « sexy ». Mais quelle efficacité ! Elle permet de convertir à peu près tous les formats de fichiers audio en une seule ligne. Pour avoir toutes les informations nécessaires à l'utilisation de cette commande, tapez
afconvert -h dans la fenêtre
Terminal.
À titre d'information, sachez que le paramètre
LEI16@44100 demande une conversion sur 16 bits avec un échantillonnage en 44100 Hz. Plus ces deux valeurs (16 et 44100) sont élevées, meilleur est le son obtenu. Mais aussi, plus grande est la taille du fichier. Faites quelques essais en utilisant une conversion sur 8, 16 et 24 bits en 11025, 22050 et 44100 Hz. Ce qui donne des paramètres compris entre LEI8@11025 et LEI24@44100. À vous de juger quel est le meilleur compromis entre la qualité sonore et la taille du fichier obtenu.
Voyons par la pratique comment utiliser les « System Sound Services » du framework
AudioToolbox. Nous allons travailler sur
un fichier audio nommé Applaudissements.caf.
Créez une nouvelle application basée sur le modèle
Single View Application et donnez-lui le nom « ecouteAudio ».
Toutes les méthodes relatives aux « System Sound Services » se trouvent dans le framework
AudioToolbox. La première étape va donc consister à ajouter ce framework à l'application. Pour cela, suivez les instructions visibles à la figure suivante.
- Cliquez sur la première icône affichée dans le volet de navigation.
- Sélectionnez l'onglet Build Phases dans la zone d'édition.
- Développez l'entrée Link Binary With Libraries.
- Cliquez sur l'icône +.
- Sélectionnez AudioToolbox.framework dans la boîte de dialogue.
- Cliquez sur Add pour ajouter le framework au projet.
Pour jouer un son, vous allez placer le fichier audio correspondant dans les ressources de l'application. Cliquez du bouton droit sur la première icône du volet de navigation et sélectionnez
New Group dans le menu contextuel. Donnez le nom « Resources » à ce nouveau dossier, puis glissez-déposez le fichier
Applaudissements.caf du Finder dans ce dossier.
Lorsque vous déposez le fichier audio dans le dossier Resources, une boîte de dialogue est affichée. Veillez à cocher la case Copy items into destination group's folder (if needed) pour que le fichier soit copié (et pas seulement référencé) dans les ressources.
En consultant la documentation Apple sur le terme « System Sound Services Reference », vous pouvez voir qu'il vous faudra utiliser les deux méthodes suivantes :
- AudioServicesCreateSystemSoundID
- AudioServicesPlaySystemSound
Ces méthodes proviennent du framework
AudioToolbox/AudioServices.h et sont déclarées dans le fichier d'en-têtes
AudioServices.h.
La prochaine étape va donc consister à faire référence au framework dans le fichier d'en-têtes de l'application. Cliquez sur
ViewController.h dans le volet de navigation et ajoutez l'instruction
#import suivante :
Code : Objective-C | #import <AudioToolbox/AudioServices.h>
|
Le fichier d'en-têtes doit maintenant ressembler à ceci :
Code : Objective-C | #import <UIKit/UIKit.h>
#import <AudioToolbox/AudioServices.h>
@interface ViewController : UIViewController
@end
|
Cliquez sur
ViewController.m dans le volet de navigation et complétez la méthode
viewDidLoad comme suit :
Code : Objective-C | - (void)viewDidLoad
{
[super viewDidLoad];
SystemSoundID bravo;
AudioServicesCreateSystemSoundID(CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("Applaudissements"), CFSTR("caf"), NULL), &bravo);
AudioServicesPlaySystemSound(bravo);
}
|
Examinons les instructions contenues dans cette méthode.
La ligne 4 définit l'objet
bravo de type
SystemSoundID. C'est dans cet objet que sera stocké le son à jouer.
La ligne 5 appelle la méthode
AudioServicesCreateSystemSoundID. Ne soyez pas effrayés par l'apparente complexité de cette méthode. Cette écriture un peu lourde vient du chaînage (entendez par là, de l'exécution consécutive) de plusieurs méthodes dans une seule instruction. Procédons par étapes, et vous verrez que cette instruction est tout à fait compréhensible.
La méthode
AudioServicesCreateSystemSoundID admet deux arguments : l'adresse URL du fichier audio à jouer et l'adresse d'un objet
SystemSoundID qui sera associée au son. Voici à quoi ressemble cette méthode :
Code : Objective-C | AudioServicesCreateSystemSoundID(URL-fichier-audio, adresse-SystemSoundID);
|
Bien que les applications iOS soient composées de plusieurs fichiers, elles apparaissent sous la forme d'un fichier unique appelé « bundle ». Ce fichier renferme toute l'arborescence de l'application. Ainsi par exemple, les ressources font partie du bundle de l'application.
Pour obtenir l'adresse URL d'un élément situé dans les ressources de l'application, nous utilisons la méthode
CFBundleCopyResourceURL. Cette méthode admet quatre arguments : le nom du bundle à examiner, le nom de la ressource, l'extension de la ressource et le dossier dans lequel elle est stockée. Voici à quoi ressemble cette méthode :
Code : Objective-C | CFBundleCopyResourceURL(nom-bundle,nom-ressource, extension-ressource, dossier-ressource);
|
Mais d'où viennent toutes ces informations ?
De la documentation Apple tout simplement ! Dans la page « System Sound Services Reference », examinez la section
AudioServicesCreateSystemSoundID et vous trouverez toutes les informations nécessaires.
Le bundle de l'application est obtenu avec la méthode
CFBundleGetMainBundle. Viennent ensuite le nom du fichier (
CFSTR("Applaudissements")), son extension (
CFSTR("caf")) et enfin le dossier du fichier (
NULL).
Pourquoi ne pas avoir utilisé les chaînes Applaudissements et caf dans le deuxième et le troisième argument ? Et pourquoi le dernier argument est égal à NULL ?
Une fois encore, je vous renvoie à la documentation Apple.
Vous y apprendrez que le deuxième et le troisième argument doivent être des chaînes constantes et non de simples chaînes. De plus, pour transformer une chaîne en une chaîne constante, il faut utiliser la fonction
CFSTR().
Quant au quatrième paramètre qui, rappelons-le, est censé définir le dossier dans lequel se trouve la ressource, la valeur
NULL facilite l'écriture. En effet, elle indique que c'est au device de trouver dans quel dossier la ressource a été stockée. Tant que vous n'avez pas plusieurs centaines de ressources, cette technique est tout à fait possible. Alors, pourquoi s'en passer ?
La méthode
CFBundleCopyResourceURL a donc retourné l'adresse URL de la ressource. Je vous rappelle que la méthode
AudioServicesCreateSystemSoundID demande deux paramètres : l'adresse URL du fichier à jouer et l'adresse d'un objet
SystemSoundID. Cette dernière est obtenue avec
&bravo.
Nous arrivons donc à l'instruction suivante :
Code : Objective-C | AudioServicesCreateSystemSoundID(CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("Applaudissements"), CFSTR("caf"), NULL), &bravo);
|
Je vous rassure tout de suite : le plus gros du travail a été fait. Maintenant, il suffit d'appeler la méthode
AudioServicesPlaySystemSound en lui transmettant l'objet
SystemSoundID pour déclencher la lecture du son :
Code : Objective-C | AudioServicesPlaySystemSound(bravo);
|
Vous pouvez (enfin !) lancer l'application. Des applaudissements vous acclament ! Vous avez réussi à jouer votre premier son dans le simulateur. Bien entendu, cette application fonctionne sans problème sur votre device.
Le code source se trouve dans le dossier
ecouteAudio.
ViewController.h
Secret (cliquez pour afficher)
Code : Objective-C | #import <UIKit/UIKit.h>
#import <AudioToolbox/AudioServices.h>
@interface ViewController : UIViewController
@end
|
ViewController.m
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55 | #import "ViewController.h"
@implementation ViewController
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
SystemSoundID bravo;
AudioServicesCreateSystemSoundID(CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR("Applaudissements"), CFSTR("caf"), NULL), &bravo);
AudioServicesPlaySystemSound(bravo);
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
|
La technique AVAudioPlayer
Les iPhone, iPod Touch et iPad disposent de l'application iPod, qui permet d'écouter de la musique et de visualiser des vidéos. Les techniques de programmation utilisées dans cette application sont accessibles aux développeurs Objective-C grâce (entre autres) à la classe
AVAudioPlayer. Cette approche a plusieurs avantages par rapport à la précédente.
Elle permet :
- de jouer des sons courts ou longs ;
- de jouer des sons compressés, au format MP3 par exemple ;
- d'utiliser plusieurs méthodes pour contrôler le son pendant qu'il est joué.
Elle repose sur :
- l'utilisation des frameworks AVFoundation et AudioToolbox ;
- la mise en place du delegate AVAudioPlayerDelegate ;
- la définition d'un objet AVAudioPlayer ;
- la mise en place de ressources dans l'application pour stocker le fichier audio à jouer ;
- l'utilisation de méthodes sur l'objet AVAudioPlayer pour contrôler le son pendant qu'il est joué.
Commençons sans plus attendre. Définissez un nouveau projet basé sur le modèle
Single View Application et donnez-lui le nom « audioPlayer ».
Insertion des frameworks dans le projet
Ajoutez les frameworks
AVFoundation.framework et
AudioToolbox.framework dans le projet. Comme indiqué à la figure suivante, cliquez sur la première icône affichée dans le volet de navigation (1), basculez sur l'onglet
Build Phases dans le volet droit de Xcode (2), développez l'entrée
Link Binary With Libraries (3), cliquez sur l'icône
+ (4) et ajoutez les deux frameworks dont nous avons parlé.
Définition de l'objet AVAudioPlayer et mise en place du delegate associé
Cliquez sur
ViewController.h dans le volet de navigation et insérez-y deux instructions
#import pour faire référence aux frameworks que vous avez ajoutés dans l'étape précédente :
Code : Objective-C | #import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
|
Définissez la variable d'instance
audioPlayer de classe
AVAudioPlayer :
Code : Objective-C | AVAudioPlayer * audioPlayer;
|
Cet objet va vous permettre de jouer des fichiers audio quelconques, à condition qu'ils soient compatibles avec les formats audio supportés par iOS : AAC, ALAC, HE-AAC, iLBC, IMA4, Linear PCM, MP3, µ-law ou a-law.
Pour vous tenir informés des événements relatifs à la lecture du fichier audio (position dans le son, fin du son atteint, etc.), mais également des événements externes (réception d'un appel téléphonique par exemple), l'application doit implémenter le protocole
AVAudioPlayerDelegate. Pour ce faire, il vous suffit d'ajouter ce protocole dans la définition de l'interface :
Code : Objective-C | @interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
...
}
|
Si vous avez suivi mes consignes, le fichier
ViewController.h doit maintenant ressembler à ceci :
Code : Objective-C | #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
AVAudioPlayer * audioPlayer;
}
@end
|
Ajout du fichier audio dans les ressources
Le fichier d'en-têtes étant entièrement écrit, nous allons passer à l'étape suivante en ajoutant un fichier audio dans les ressources de l'application.
Cliquez du bouton droit sur la première icône affichée dans le volet de navigation et sélectionnez
New Group dans le menu contextuel. Donnez le nom « Resources » au nouveau dossier. Ouvrez le Finder et glissez-déposez un fichier audio quelconque du Finder dans le dossier
Resources. Au relâchement du bouton gauche de la souris, une boîte de dialogue est affichée. Assurez-vous que la case
Copy items into destination group's folder soit cochée, puis cliquez sur
Finish.
Le code de l'application
Il ne reste plus qu'à écrire le code de l'application.
Cliquez sur
ViewController.m dans le volet de navigation. Cette première approche de la classe
AVAudioPlayer se voulant avant tout pratique, nous allons nous contenter de jouer un son, sans chercher à le contrôler. Complétez la méthode
viewDidLoad comme suit :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | - (void)viewDidLoad
{
[super viewDidLoad];
AudioSessionInitialize (NULL, NULL, NULL, (__bridge void *)self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
NSData *soundFileData;
soundFileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"morceau.mp3" ofType:NULL]]];
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileData error:NULL];
if(!([audioPlayer prepareToPlay]))
NSLog(@"La méthode prepareToPlay a renvoyé la valeur FALSE");
audioPlayer.delegate = self;
[audioPlayer setVolume:1.0f];
[audioPlayer play];
}
|
La ligne 5 initialise le contexte audio :
Code : Objective-C | AudioSessionInitialize (NULL, NULL, NULL, (__bridge void *)self);
|
Il n'est pas nécessaire de comprendre toutes les subtilités de cette instruction pour pouvoir l'utiliser. Sachez juste qu'elle doit toujours être appelée avant d'utiliser un objet
AVAudioPlayer.
La ligne 7 définit l'entier
sessionCategory et l'initialise avec la valeur :
Code : Objective-C | kAudioSessionCategory_MediaPlayback;
|
Si vous vous reportez à la documentation Apple, vous verrez que cette constante demande au device de jouer le son dans tous les cas, y compris si le bouton
muet est actif, ou encore si le device passe en mode veille.
La ligne 8 initialise la propriété
kAudioSessionProperty_AudioCategory de la session audio en lui transmettant la valeur définie dans l'instruction précédente. Le fichier audio sera donc joué dans tous les cas, y compris quand le bouton muet est actif, ou encore si le device passe en mode veille :
Code : Objective-C | AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
|
La ligne 9 définit l'objet
soundFileData de classe
NSData :
Code : Objective-C
Cet objet est utilisé pour faire référence au fichier audio à jouer :
Code : Objective-C | soundFileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"morceau.mp3" ofType:NULL]]];
|
Ne vous laissez pas surprendre par l'apparente complexité de cette instruction : elle se contente de chaîner trois messages. Si nous la décomposons en trois parties bien distinctes, tout sera bien plus clair.
L'objet
soundFileData est initialisé (
soundFileData =) avec un objet
NSData (
[NSData …) qui contient les données stockées à l'adresse spécifiée (
dataWithContentsOfURL:). Cette URL se trouve dans le « bundle principal », c'est-à-dire dans l'application elle-même ([
NSBundle mainBundle]).
Le nom du fichier est « morceau.mp3 » (
pathForResource:@"morceau.mp3"). L'extension du fichier n'est pas précisée (
ofType:NULL]) puisque le nom du fichier la contient déjà.
Alors, cette instruction ? Tout à fait compréhensible, n'est-ce pas ?
La ligne 13 réserve de la mémoire pour l'objet
audioPlayer (
[AVAudioPlayer alloc]) et l'initialise avec les données qui ont été placées dans l'objet
soundFileData dans l'instruction précédente (
initWithData:soundFileData) :
Code : Objective-C | audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileData error:NULL];
|
Le son est prêt à être joué. La méthode
prepareToPlay le précharge en mémoire. Si le préchargement ne s'est pas bien déroulé, la valeur
FALSE est retournée. Dans ce cas, un message d'erreur est affiché dans la console :
Code : Objective-C | if(!([audioPlayer prepareToPlay]))
NSLog(@"La méthode prepareToPlay a renvoyé la valeur FALSE");
|
L'instruction de la ligne 18 indique que les messages relatifs à l'objet
audioPlayer seront traités dans
ViewController.m :
Code : Objective-C | audioPlayer.delegate = self;
|
Cette instruction aurait tout aussi bien pu ne pas apparaître puisque dans cet exemple très simple, aucun des messages relatifs à l'objet audioPlayer n'est traité.
La ligne 20 définit le volume sonore :
Code : Objective-C | [audioPlayer setVolume:1.0f];
|
La valeur passée à la méthode setVolume doit être comprise entre 0.0f (aucun son) et 1.0f (volume maximum).
Et enfin, la ligne 22 lance la lecture du fichier audio :
Code : Objective-C
Vous pouvez lancer l'application en cliquant sur l'icône
Run. Je vous laisse savourer !
Le code de cette application se trouve dans le dossier
audioPlayer.
ViewController.h
Secret (cliquez pour afficher)
Code : Objective-C | #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
AVAudioPlayer * audioPlayer;
}
@end
|
ViewController.m
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67 | #import "ViewController.h"
@implementation ViewController
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
AudioSessionInitialize (NULL, NULL, NULL, (__bridge void *)self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
NSData *soundFileData;
soundFileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"morceau.mp3" ofType:NULL]]];
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileData error:NULL];
if(!([audioPlayer prepareToPlay]))
NSLog(@"La méthode prepareToPlay a renvoyé la valeur FALSE");
audioPlayer.delegate = self;
[audioPlayer setVolume:1.0f];
[audioPlayer play];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
|
Un peu plus loin avec la technique AVAudioPlayer
Je ne sais pas pour vous, mais moi, je trouve l'application précédente un peu « tristounette ». Certes, elle joue le fichier MP3 à la perfection, mais l'écran du device reste désespérément vide. Que diriez-vous d'ajouter un arrière-plan à l'application, un contrôle de progression pour savoir où en est la lecture du fichier et un contrôle de volume pour ajuster le volume sonore comme vous l'entendez ? Tentant non ?
Eh bien, allons-y.
Commencez par dupliquer le dossier de l'application
audioPlayer : cliquez sur le dossier
audioPlayer, appuyez sur
Command +
C, puis sur
Command +
V et renommez-la copie
audioPlayer-v2.
Ajout de nouveaux contrôles dans la vue
Cliquez sur
MainStoryboard.storyboard dans le volet de navigation, puis insérez trois contrôles
Label, un contrôle
Slider et un contrôle
Progress View dans la vue. Modifiez les dimensions par défaut de ces contrôles, la couleur d'arrière-plan et la couleur du texte des
Label et disposez les contrôles dans la vue pour obtenir quelque chose ressemblant à la figure suivante.
Je vous rappelle que vous pouvez modifier les caractéristiques d'un contrôle en utilisant le volet des utilitaires : si nécessaire, cliquez sur l'icône Hide or show the Utilities pour faire apparaître le volet des utilitaires, cliquez sur le contrôle dont vous voulez modifier les caractéristiques, puis sur l'icône Show the Attributes Inspector pour accéder aux caractéristiques du contrôle.
Définissez :
- l'outlet dureeTotale pour le premier contrôle Label (celui dans lequel est écrit « Label ») ;
- l'outlet laPosition pour le contrôle Progress View ;
- l'action leVolume pour le contrôle Slider.
Pour ceux qui auraient la mémoire courte, il suffit de contrôle-glisser-déposer un contrôle depuis la zone d'édition dans le fichier d'en-têtes, juste avant l'instruction @end pour créer un outlet ou une action. Choisissez Outlet ou Action dans la zone de texte Connection selon l'effet recherché, choisissez un nom dans la zone Name et cliquez sur Connect.
Pour en terminer avec le fichier d'en-têtes, définissez la variable d'instance
playbackTimer de type
NSTimer. Cette variable sera utilisée pour mettre à jour la position de lecture dans le contrôle
Progress View :
Code : Objective-C
Si vous avez suivi mes indications, le fichier d'en-têtes devrait ressembler à ceci :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
AVAudioPlayer * audioPlayer;
NSTimer* playbackTimer;
}
@property (weak, nonatomic) IBOutlet UILabel *dureeTotale;
@property (weak, nonatomic) IBOutlet UIProgressView *laPosition;
- (IBAction)leVolume:(id)sender;
@end
|
Vous allez maintenant ajouter quelques lignes de code pour donner vie à ces contrôles.
Mise en place d'un timer
Cliquez sur
ViewController.m dans le volet de navigation. Vous allez insérer du code juste au-dessus de l'instruction
[audioPlayer play].
Votre première action va consister à mettre en place un « timer », c'est-à-dire un mécanisme qui déclenchera l'exécution d'une méthode à intervalles réguliers. Cette méthode sera utilisée pour mettre à jour le contrôle
Progress View pendant la lecture du fichier audio :
Code : Objective-C | playbackTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(miseAJour:)
userInfo:nil
repeats:YES];
|
Cette instruction initialise l'objet
NSTimer playbackTimer en utilisant la méthode
scheduledTimerWithTimeInterval. Cette dernière demande cinq paramètres.
- La durée entre deux exécutions de la méthode, en secondes. Ici 0,5 seconde, soit deux fois par seconde.
- L'objet dans lequel se trouve la méthode à exécuter périodiquement. Ici, la valeur self indique que l'objet se trouve dans l'application.
- Le nom de la méthode à exécuter. Ici, miseAJour.
- Les informations à passer à la méthode. Ici, la valeur nil indique qu'aucune information n'est passée à la méthode miseAJour.
- La répétition ou la non-répétition de l'exécution de la méthode. Ici, la valeur YES provoque la répétition de la méthode miseAJour jusqu'à ce que le timer soit désactivé.
Affichage de la durée totale du fichier audio
Vous allez ajouter deux lignes de code dans la méthode
viewDidLoad, juste avant le message
[super viewDidLoad];.
Pour afficher la durée totale du fichier audio dans le
Label, commencez par définir la variable
longueur de type
float, et stockez-y la durée totale du fichier audio :
Code : Objective-C | float longueur=audioPlayer.duration;
|
Il ne reste plus qu'à afficher cette valeur dans le contrôle
Label dureeTotale :
Code : Objective-C | dureeTotale.text = [NSString stringWithFormat: @"Durée totale : %i secondes",(int)longueur];
|
Cette instruction peut paraître un peu complexe pour quelque chose d'aussi simple qu'afficher une valeur dans un
Label. Sa longueur s'explique par le fait qu'elle ne se contente pas de stocker une variable dans une autre. Rappelez-vous : la durée du fichier audio a été stockée dans un nombre à virgule (
float). Dans un premier temps, ce nombre est converti en un entier pour supprimer la virgule (
(int)longueur), puis en un
NSString (
[NSString stringWithFormat:) pour assurer la compatibilité avec la propriété
text du
Label.
La méthode viewDidLoad doit maintenant ressembler à ceci :
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 | - (void)viewDidLoad
{
AudioSessionInitialize (NULL, NULL, NULL, (__bridge void *)self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
NSData *soundFileData;
soundFileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"morceau.mp3" ofType:NULL]]];
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileData error:NULL];
if(!([audioPlayer prepareToPlay]))
NSLog(@"La méthode prepareToPlay a renvoyé la valeur FALSE");
audioPlayer.delegate = self;
[audioPlayer setVolume:1.0f];
playbackTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(miseAJour:)
userInfo:nil
repeats:YES];
[audioPlayer play];
float longueur=audioPlayer.duration;
dureeTotale.text = [NSString stringWithFormat: @"Durée totale : %i secondes",(int)longueur];
[super viewDidLoad];
}
|
Animation du Progress View
Vous devez certainement être pressés d'exécuter l'application pour voir le contrôle
Progress View se mettre à jour pendant que le morceau est joué. Mais réfléchissez un peu. Ne croyez-vous pas qu'il manque quelque chose pour cela ?
Mais c'est bien sûr : la méthode
miseAJour ! Voici le code à ajouter :
Code : Objective-C | -(void)miseAJour:(NSTimer*)timer
{
float total=audioPlayer.duration;
float f=audioPlayer.currentTime / total;
laPosition.progress=f;
}
|
La propriété
progress d'un contrôle
Progress View définit la position de la barre de progression. De type
float, elle est comprise entre 0.0 (complètement à gauche) et 1.0 (complètement à droite). Pour déplacer la barre en fonction de la position dans le morceau, il suffit de diviser la position actuelle par la durée du morceau et de l'affecter à la propriété
progress. C'est précisément ce que fait cette méthode.
Dans un premier temps, la durée totale du morceau est stockée dans la variable
float total :
Code : Objective-C | float total=audioPlayer.duration;
|
Dans un deuxième temps, la position actuelle (
audioPlayer.currentTime) est divisée par la durée du morceau (
total) et affectée à la variable
float f:
Code : Objective-C | float f=audioPlayer.currentTime / total;
|
Enfin, dans un troisième temps, la variable
f est affectée à la propriété
progress du contrôle
Progress View, ce qui provoque sa mise à jour :
Code : Objective-C
Cliquez sur l'icône
Run et observez le déplacement de la barre de progression. Impressionnant, non ?
Détection de la fin du son et arrêt du timer
Cette courte récréation terminée, retournons au code.
Quelques lignes plus tôt, nous parlions de la création d'un timer avec la méthode
scheduledTimerWithTimeInterval. Comme il a été vu, la méthode
miseAJour est exécutée indéfiniment deux fois par seconde. Vous serez d'accord avec moi : cette méthode n'a plus aucune utilité lorsque le morceau a été entièrement joué. C'est pourquoi nous allons y mettre fin à ce moment-là.
Une courte recherche dans la documentation Apple montre que la méthode exécutée lorsque le morceau est entièrement joué, a pour nom
audioPlayerDidFinishPlaying.
Une courte recherche dans la documentation Apple ? J'ai recherché et je n'ai rien trouvé ! Puis-je avoir quelques explications ?
Si un événement est généré lorsque le morceau a été entièrement joué, c'est dans le protocole
AVAudioPlayerDelegate qu'il faut le rechercher. Vous êtes d'accord avec moi ? Ce delegate est en effet en charge de tous les événements en rapport avec l'objet
AVAudioPlayer audioPlayer utilisé dans cette application. Pour trouver la méthode exécutée lorsque le morceau a été entièrement joué, j'ai donc tout naturellement consulté l'aide sur le protocole
AVAudioPlayerDelegate. Pour cela, j'ai affiché le fichier d'en-têtes (1), cliqué sur
AVAudioPlayerDelegate (2), puis sur
AVAudioPlayerDelegate Protocol Reference (3), comme à la figure suivante.
Le clic sur
AVAudioPlayerDelegate Protocol Reference provoque l'affichage de la fenêtre d'aide. Quelques secondes suffisent pour comprendre que la méthode recherchée est
audioPlayerDidFinishPlaying.
Après cet intermède, définissez la méthode suivante :
Code : Objective-C | -(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[playbackTimer invalidate];
}
|
La première ligne, d'apparence assez complexe, ne fait que reprendre le gabarit de la fonction. Ne vous en faites pas quant à sa syntaxe : la fonction autocomplete de Xcode l'écrit pratiquement toute seule au fur et à mesure que vous tapez quelques caractères au clavier.
Lorsque le fichier audio a été entièrement joué, la méthode
invalidate est appliquée à l'objet
playbackTime, ce qui provoque la suppression du timer :
Code : Objective-C | [playbackTimer invalidate];
|
Réglage du niveau sonore avec le Slider
Vous allez maintenant donner vie au contrôle
Slider pour que l'utilisateur puisse régler le niveau sonore.
Rappelez-vous : une action de type
Value Changed a été définie pour le contrôle
Slider. Chaque fois que la position du curseur sera modifiée par l'utilisateur, la méthode action correspondante (c'est-à-dire la méthode
leVolume) sera exécutée. Ajoutez les instructions suivantes dans cette méthode :
Code : Objective-C | - (IBAction)leVolume:(id)sender
{
UISlider *slider = (UISlider*) sender;
[audioPlayer setVolume:slider.value];
}
|
La ligne 3 définit l'objet slider de classe
UISlider à partir du contrôle
Slider (
sender) :
Code : Objective-C | UISlider *slider = (UISlider*) sender;
|
La ligne 4 applique la méthode
setVolume à l'objet
audioPlayer en lui transmettant la position du curseur (
slider.value) dans le contrôle
Slider :
Code : Objective-C | [audioPlayer setVolume:slider.value];
|
C'est aussi simple que cela. Vous pouvez tester, cela fonctionne parfaitement !
Ajout d'un arrière-plan
Pour terminer en beauté, vous allez ajouter un fond d'écran à l'application. Procurez-vous une image au format JPG ou PNG de 320x480 pixels. Au besoin, redimensionnez une image existante. Une fois en possession de l'image, faites-la glisser depuis le Finder vers le dossier
Resources de l'application et confirmez son insertion dans les ressources en cochant la case
Copy items into destination group's folder (if needed).
Pour utiliser cette image en arrière-plan de la vue, cliquez sur
ViewController.m dans le volet de navigation et ajoutez la ligne suivante au début de la méthode
viewDidLoad :
Code : Objective-C | self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"arb.jpg"]];
|
Dans mon application, l'image s'appelle « arb.jpg » ; ce sera certainement différent chez vous, n'oubliez pas de mettre à jour votre code en conséquence.
Comme vous pouvez le voir, la propriété
backgroundColor de l'arrière-plan de la vue (
self.view) est utilisée pour afficher l'arrière-plan. Cette propriété est initialisée avec une image (
initWithPatternImage) nommée « arb.jpg » (
imageNamed:@"arb.jpg").
Vous pouvez cliquer sur
Run et profiter de votre application. La figure suivante représente mon rendu final.
L'application se trouve dans le dossier
audioPlayer-v2.
ViewController.h
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | #import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>
@interface ViewController : UIViewController <AVAudioPlayerDelegate>
{
AVAudioPlayer * audioPlayer;
NSTimer* playbackTimer;
}
@property (weak, nonatomic) IBOutlet UILabel *dureeTotale;
@property (weak, nonatomic) IBOutlet UIProgressView *laPosition;
- (IBAction)leVolume:(id)sender;
@end
|
ViewController.m
Secret (cliquez pour afficher)
Code : Objective-C 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102 | #import "ViewController.h"
@implementation ViewController
@synthesize dureeTotale;
@synthesize laPosition;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
self.view.backgroundColor = [[UIColor alloc] initWithPatternImage:[UIImage imageNamed:@"arb.jpg"]];
AudioSessionInitialize (NULL, NULL, NULL, (__bridge void *)self);
UInt32 sessionCategory = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (sessionCategory), &sessionCategory);
NSData *soundFileData;
soundFileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"morceau.mp3" ofType:NULL]]];
audioPlayer = [[AVAudioPlayer alloc] initWithData:soundFileData error:NULL];
if(!([audioPlayer prepareToPlay]))
NSLog(@"La méthode prepareToPlay a renvoyé la valeur FALSE");
audioPlayer.delegate = self;
[audioPlayer setVolume:1.0];
playbackTimer = [NSTimer scheduledTimerWithTimeInterval:0.5
target:self
selector:@selector(miseAJour:)
userInfo:nil
repeats:YES];
[audioPlayer play];
float longueur=audioPlayer.duration;
dureeTotale.text = [NSString stringWithFormat: @"Durée totale : %i secondes",(int)longueur];
[super viewDidLoad];
}
-(void)miseAJour:(NSTimer*)timer
{
float total=audioPlayer.duration;
float f=audioPlayer.currentTime / total;
laPosition.progress=f;
}
-(void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag
{
[playbackTimer invalidate];
}
- (IBAction)leVolume:(id)sender
{
UISlider *slider = (UISlider*) sender;
[audioPlayer setVolume:slider.value];
}
- (void)viewDidUnload
{
[self setDureeTotale:nil];
[self setLaPosition:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
@end
|