Juin 2016

Volume 31, numéro 6

Cet article a fait l'objet d'une traduction automatique.

Applications modernes - Lecture de fichiers audios dans un UWP

Par Frank La La

La plate-forme de Windows universelle (UWP) a des API riches pour l’enregistrement audio et vidéo. Toutefois, l’ensemble des fonctionnalités ne s’arrête pas à l’enregistrement. Avec seulement quelques lignes de code, les développeurs peuvent appliquer des effets spéciaux audio en temps réel. Effets tels que l’effet de reverb et echo sont intégrées à l’API et sont relativement faciles à implémenter. Dans cet article, je vais aborder certains des principes fondamentaux de l’enregistrement audio et en appliquant des effets spéciaux. Je vais créer une application UWP enregistrement audio, enregistrez-le et appliquer divers filtres et des effets spéciaux.

Configuration du projet pour l’enregistrement Audio

L’enregistrement audio nécessite l’application soit autorisé à accéder à l’aide du microphone et qui nécessite la modification de fichier de manifeste de l’application. Dans l’Explorateur de solutions, double-cliquez sur le fichier Package.appxmanifest. Il s’agit toujours de la racine dans le projet.

Une fois que la fenêtre d’éditeur de fichier manifeste application s’ouvre, cliquez sur l’onglet fonctionnalités. Dans la zone de liste de fonctionnalités, vérifiez la capacité du Microphone. Cela permettra à votre application l’accès au microphone de l’utilisateur final. Sans cela, votre application lève une exception lorsque vous tentez d’accéder à l’aide du microphone.

Enregistrement Audio

Avant de commencer à ajouter des effets spéciaux audio, vous souhaitez tout d’abord être en mesure d’enregistrer des données audio. Cela est assez simple. Tout d’abord, ajoutez une classe à votre projet pour encapsuler le code de l’enregistrement audio. Vous devez appeler cette classe AudioRecorder. Il a donc des méthodes publiques pour démarrer et arrêter l’enregistrement, ainsi que pour lire le clip audio que vous venez d’enregistrer. Pour ce faire, vous devez ajouter des membres à votre classe. Le premier sera MediaCapture, qui fournit des fonctionnalités de capture audio, vidéo et images à partir d’un périphérique de capture, tel qu’un microphone ou d’une webcam :

private MediaCapture _mediaCapture;

Vous allez également ajouter un InMemoryRandomAccessStream pour capturer l’entrée à l’aide du microphone en mémoire :

private InMemoryRandomAccessStream _memoryBuffer;

Afin de suivre l’état de votre enregistrement, vous allez ajouter une propriété accessible publiquement Boolean à votre classe :

public bool IsRecording { get; set; }

Enregistrement du fichier audio vous oblige à vérifier si vous êtes déjà l’enregistrement et si vous êtes, le code lève une exception. Dans le cas contraire, vous devez initialiser votre flux de mémoire, supprimez le fichier de l’enregistrement précédent et commencer l’enregistrement.

Étant donné que la classe MediaCapture fournit plusieurs fonctions, vous devrez spécifier que vous souhaitez capturer des données audio. Vous allez créer une instance de MediaCaptureInitializationSettings à cette fin. Le code crée une instance d’un objet MediaCapture, puis transmet le MediaCaptureInitializationSettings à la méthode InitializeAsync, comme indiqué dans Figure 1.

Figure 1 Création d’une Instance d’un objet MediaCapture

public async void Record()
  {
  if (IsRecording)
  {
    throw new InvalidOperationException("Recording already in progress!");
  }
  await Initialize();
  await DeleteExistingFile();
  MediaCaptureInitializationSettings settings =
    new MediaCaptureInitializationSettings
  {
    StreamingCaptureMode = StreamingCaptureMode.Audio
  };
  _mediaCapture = new MediaCapture();
  await _mediaCapture.InitializeAsync(settings);
  await _mediaCapture.StartRecordToStreamAsync(
    MediaEncodingProfile.CreateMp3(AudioEncodingQuality.Auto), _memoryBuffer);
  IsRecording = true;
}

Enfin, vous devez indiquer à l’objet MediaCapture pour démarrer l’enregistrement, le passage de paramètres qu’elle enregistre dans le format MP3 et l’emplacement où stocker les données.

Arrêt de l’enregistrement nécessite beaucoup moins de lignes de code :

public async void StopRecording()
{
  await _mediaCapture.StopRecordAsync();
  IsRecording = false;
  SaveAudioToFile();
}

La méthode StopRecording effectue trois opérations : il indique à l’objet de MediaCapture pour arrêter l’enregistrement, définit l’état d’enregistrement sur false et enregistre les données de flux audio dans un fichier MP3 sur le disque.

Enregistrer les données Audio sur le disque

Une fois les données audio capturées dans la InMemoryRandomAccessStream, vous souhaitez enregistrer le contenu sur le disque, comme indiqué dans Figure 2. Enregistrer les données audio à partir d’un flux de données en mémoire nécessite que vous copiez le contenu sur un autre flux, puis envoyez ces données sur disque. Utilisation des utilitaires dans l’espace de noms Windows.ApplicationModel.Package, vous êtes en mesure d’obtenir le chemin d’accès au répertoire d’installation de votre application. (Au cours du développement, il s’agira dans le répertoire \bin\x86\Debug du projet.) Voici où vous souhaitez le fichier devant être enregistrées. Vous pouvez facilement modifier le code pour enregistrer un autre emplacement ou demandez à l’utilisateur de choisir l’emplacement où enregistrer le fichier.

Figure 2 l’enregistrement de données Audio sur le disque

private async void SaveAudioToFile()
{
  IRandomAccessStream audioStream = _memoryBuffer.CloneStream();
  StorageFolder storageFolder = Package.Current.InstalledLocation;
  StorageFile storageFile = await storageFolder.CreateFileAsync(
    DEFAULT_AUDIO_FILENAME, CreationCollisionOption.GenerateUniqueName);
  this._fileName = storageFile.Name;
  using (IRandomAccessStream fileStream =
    await storageFile.OpenAsync(FileAccessMode.ReadWrite))
  {
    await RandomAccessStream.CopyAndCloseAsync(
      audioStream.GetInputStreamAt(0), fileStream.GetOutputStreamAt(0));
    await audioStream.FlushAsync();
    audioStream.Dispose();
  }
}

Lecture Audio

Maintenant que vous avez vos données audio à l’intérieur d’un tampon en mémoire et sur le disque, vous avez deux possibilités pour lire à partir de : mémoire et disque.

Le code pour lire le contenu audio de la mémoire est très simple. Créer une nouvelle instance du contrôle MediaElement, définir sa source dans la mémoire tampon en mémoire, transmettez un type MIME et puis appelez la méthode Play.

public void Play()
{
  MediaElement playbackMediaElement = new MediaElement();
  playbackMediaElement.SetSource(_memoryBuffer, "MP3");
  playbackMediaElement.Play();
}

Lecture à partir du disque de nécessite un peu de code supplémentaire, comme l’ouverture de fichiers est une tâche asynchrone. Pour que le thread d’interface utilisateur de communiquer avec une tâche en cours d’exécution sur un autre thread, vous devez utiliser le CoreDispatcher. Le CoreDispatcher envoie des messages entre le thread de qu'un bloc de code spécifié est en cours d’exécution et le thread d’interface utilisateur. Avec elle, le code peut obtenir le contexte de l’interface utilisateur à partir d’un autre thread. Pour une excellente description de CoreDispatcher, lisez le billet de blog de David Crook sur le sujet, consultez bit.ly/1SbJ6up.

Outre les étapes supplémentaires pour gérer le code asynchrone, la méthode ressemble au précédent qui utilise la mémoire tampon en mémoire :

public async Task PlayFromDisk(CoreDispatcher dispatcher)
{
  await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
  {
    MediaElement playbackMediaElement = new MediaElement();
    StorageFolder storageFolder = Package.Current.InstalledLocation;
    StorageFile storageFile = await storageFolder.GetFileAsync(this._fileName);
    IRandomAccessStream stream = await storageFile.OpenAsync(FileAccessMode.Read);
    playbackMediaElement.SetSource(stream, storageFile.FileType);
    playbackMediaElement.Play();
  });
}

Création de l’interface utilisateur

Avec la classe AudioRecorder terminée, la seule chose qui reste à faire est de build via l’interface de l’application. L’interface de ce projet est relativement simple, comme il vous suffit est un bouton pour enregistrer et un bouton pour lire l’enregistrement audio, comme indiqué dans Figure 3. En conséquence, le code XAML est simple : un TextBlock et un panneau d’empilement avec deux boutons :

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid.RowDefinitions>
    <RowDefinition Height="43"/>
    <RowDefinition Height="*"/>
  </Grid.RowDefinitions>
<TextBlock FontSize="24">Audio in UWP</TextBlock>
<StackPanel HorizontalAlignment="Center" Grid.Row="1" >
  <Button Name="btnRecord" Click="btnRecord_Click">Record</Button>
  <Button Name="btnPlay" Click="btnPlay_Click">Play</Button>
</StackPanel>
</Grid>

Interface utilisateur AudioRecorder
Interface utilisateur de la figure 3 AudioRecorder

Dans la classe code-behind, vous créez une variable membre de AudioRecorder. Il s’agit de l’objet de votre application utilise pour enregistrer et lire des données audio :

AudioRecorder _audioRecorder;

Vous devez instancier la classe AudioRecorder dans le constructeur de MainPage de votre application :

public MainPage()
{
  this.InitializeComponent();
  this._audioRecorder = new AudioRecorder();
}

Le bouton btnRecord réellement Active ou désactive le démarrage et l’arrêt de l’enregistrement audio. Pour tenir l’utilisateur informé de l’état actuel de la AudioRecorder, la méthode btnRecord_Click modifie le contenu du bouton btnRecord, ainsi que démarre et arrête l’enregistrement.

Vous avez deux options pour le Gestionnaire d’événements pour le bouton btnPlay: à lire à partir de la mémoire tampon ou lire à partir d’un fichier stocké sur le disque.

Pour lire à partir de la mémoire tampon en mémoire, le code est simple :

private void btnPlay_Click(object sender, RoutedEventArgs e)
{
  this._audioRecorder.Play();
}

Comme mentionné précédemment, la lecture du fichier à partir du disque se produit en mode asynchrone. Cela signifie que la tâche s’exécutera sur un thread différent du thread d’interface utilisateur. Le planificateur du système d’exploitation détermine quel thread de la tâche s’exécute sur l’exécution. En passant l’objet Dispatcher à la méthode PlayFromDisk permet le thread obtenir l’accès au contexte de l’interface utilisateur du thread d’interface utilisateur :

private async void btnPlay_Click(object sender, RoutedEventArgs e)
{
  await this._audioRecorder.PlayFromDisk(Dispatcher);
}

Application d’effets spéciaux

Maintenant que vous avez l’enregistrement de votre application et la lecture audio, le moment est venu pour explorer certaines des fonctionnalités connues de la série UWP : effets audio en temps réel. Inclus dans les API dans l’espace de noms Windows.Media.Audio sont un nombre d’effets spéciaux qui peut ajouter un contact supplémentaire aux applications.

Pour ce projet, vous allez placer tout le code des effets spéciaux dans sa propre classe. Toutefois, avant de créer la nouvelle classe, vous apporterez une dernière modification à la classe AudioRecorder. Je vais ajouter la méthode suivante :

public async Task<StorageFile>
   GetStorageFile(CoreDispatcher dispatcher)
{
  StorageFolder storageFolder =
    Package.Current.InstalledLocation;
  StorageFile storageFile =
    await storageFolder.GetFileAsync(this._fileName);
  return storageFile;
}

La méthode GetStorageFile retourne un objet StorageFile pour le fichier audio enregistré. Voici comment ma classe effets spéciaux accédera les données audio.

Présentation de la AudioGraph

La classe AudioGraph est centrale pour des scénarios avancés audio dans la série UWP. Un AudioGraph peut acheminer des données audio à partir des nœuds de la source d’entrée aux nœuds de source de sortie via différents nœuds de mixage. L’ampleur et la puissance AudioGraph se trouve au-delà de la portée de cet article, mais c’est quelque chose que je prévois d’étudier plus en détail dans les futures d’articles. Pour le moment, le point important est que chaque nœud dans un graphique d’audio peut avoir plusieurs effets audio appliquées. Pour plus d’informations sur AudioGraph, veillez à lire l’article sur le centre de développement Windows à bit.ly/1VCIBfD.

Tout d’abord, que vous souhaitez ajouter une classe appelée AudioEffects à votre projet et ajoutez les membres suivants :

private AudioGraph _audioGraph;
private AudioFileInputNode _fileInputNode;
private AudioDeviceOutputNode _deviceOutputNode;

Pour créer une instance de la classe AudioGraph, vous devez créer un objet AudioGraphSettings, qui contient les paramètres de configuration pour le AudioGraph. Vous appelez ensuite la méthode AudioGraph.CreateAsync ces paramètres de configuration. La méthode CreateAsync retourne un objet CreateAudioGraphResult. Cette classe fournit l’accès au graphique audio créé et une valeur d’état si la création de graphes audio a échoué ou a réussi.

Vous devez également créer un nœud de sortie pour lire le son. Pour ce faire, appelez la méthode CreateDeviceOutputNodeAsync sur la classe AudioGraph et définissez la variable de membre à la propriété DeviceOutputNode de la CreateAudioDeviceOutputNodeResult. Le code pour initialiser le AudioGraph et le AudioDeviceOutputNode tout réside dans la méthode InitializeAudioGraph ici :

public async Task InitializeAudioGraph()
{
  AudioGraphSettings settings = new AudioGraphSettings(AudioRenderCategory.Media);
  CreateAudioGraphResult result = await AudioGraph.CreateAsync(settings);
  this._audioGraph = result.Graph;
  CreateAudioDeviceOutputNodeResult outputDeviceNodeResult =
    await this._audioGraph.CreateDeviceOutputNodeAsync();
  _deviceOutputNode = outputDeviceNodeResult.DeviceOutputNode;
}

Lecture de données audio à partir d’un AudioGraph objet est simple ; appelez simplement la méthode Play. Le AudioGraph étant un membre privé de la classe AudioEffects, vous devez encapsuler une méthode publique autour de lui pour le rendre accessible :

public void Play()
{
this._audioGraph.Start();
}

Maintenant que vous avez le nœud de périphérique de sortie créé sur le AudioGraph, vous devez créer un nœud d’entrée à partir du fichier audio stocké sur le disque. Vous devrez également ajouter une connexion sortante à le FileInputNode. Dans ce cas, vous souhaitez que le nœud sortant à votre périphérique de sortie audio. C’est exactement ce que vous faire dans la méthode LoadFileIntoGraph :

public async Task LoadFileIntoGraph(StorageFile audioFile)
{
  CreateAudioFileInputNodeResult audioFileInputResult =
    await this._audioGraph.CreateFileInputNodeAsync(audioFile);
  _fileInputNode = audioFileInputResult.FileInputNode;
  _fileInputNode.AddOutgoingConnection(_deviceOutputNode);
  CreateAndAddEchoEffect();
}

Vous remarquerez également une référence à la méthode CreateAndAddEchoEffect, que j’aborderai ensuite.

Ajout de l’effet Audio

Il existe quatre effets audio intégrées dans l’API graph audio : echo, effet de reverb, égaliseur et limiteur. Dans ce cas, vous souhaitez ajouter de l’écho pour le son enregistré. Ajout de cet effet est aussi simple que la création d’un objet EchoEffectDefition et en définissant les propriétés de l’effet. Une fois créé, vous devez ajouter la définition de l’effet à un nœud. Dans ce cas, vous souhaitez ajouter l’effet de la _fileInputNode, qui contient les données audio enregistrées et enregistrées sur disque :

private void CreateAndAddEchoEffect()
{
  EchoEffectDefinition echoEffectDefinition = new EchoEffectDefinition(this._audioGraph);
  echoEffectDefinition.Delay = 100.0f;
  echoEffectDefinition.WetDryMix = 0.7f;
  echoEffectDefinition.Feedback = 0.5f;
  _fileInputNode.EffectDefinitions.Add(echoEffectDefinition);
}

En résumé

Maintenant que vous disposez de la classe AudioEffect terminée, vous pouvez l’utiliser à partir de l’interface utilisateur. Tout d’abord, vous allez ajouter un bouton à la page principale de votre application :

<Button Content="Play with Special Effect" Click="btnSpecialEffectPlay_Click" />

Et dans le Gestionnaire d’événements click, vous obtenez le fichier où sont stockées les données audio, créez une instance de la classe AudioEffects et la transmettre au fichier de données audio. Une fois que tout est terminé, il vous suffit pour lire le son d’appeler la méthode Play :

private async void btnSpecialEffectPlay_Click(object sender, RoutedEventArgs e)
{
  var storageFile = await this._audioRecorder.GetStorageFile(Dispatcher);
  AudioEffects effects = new AudioEffects();
  await effects.InitializeAudioGraph();
  await effects.LoadFileIntoGraph(storageFile);
  effects.Play();
}

Vous exécutez l’application et cliquez sur Enregistrer pour enregistrer un élément petit. Pour écouter tel qu’il a été enregistré, cliquez sur le bouton lecture. Pour écouter l’audio même avec un écho ajouté, cliquez sur lire avec effet spécial.

Synthèse

La série UWP a non seulement riche prise en charge pour la capture audio, mais propose également des fonctionnalités exceptionnelles pour appliquer des effets spéciaux sur un support en temps réel. Inclus avec la plate-forme sont plusieurs effets peuvent être appliqués à l’audio. parmi lesquels on peut trouver la résonance, la réverbération, l’égaliseur et le limiteur. Ces effets peuvent être appliquées individuellement ou dans n’importe quel nombre de combinaisons. La seule limite est votre imagination.


Frank La Vigneest un spécialiste de la technologie de l’équipe Microsoft Technology et Engagement postale, où il aide les utilisateurs à exploiter la technologie afin de créer une Communauté plus sûre. Régulièrement à l’adresse FranksWorld.com et a un YouTube World TV de Frank appelé de canal (youtube.com/FranksWorldTV).

Remercie les experts techniques suivants d'avoir relu cet article : Drew Batchelor et Jose Luis manières