Jouer un son en background (WinRT XAML)

by Nicolas Calvi 22. septembre 2012 19:22

Récemment je me suis confronté à un problème étonnant sur une application WinRT XAML. Je voulais jouer un son mais pas à partir d'une définition dans mon XAML, mais plutôt en code behind. 

J'ai donc cherché sur internet comment faire et force est de constater que la seule solution sans aller à créer un interop DirectSound, c'est de passer par le MediaElement. Cependant, passer par ce contrôle n'a pas été aussi simple qu'il n'y parait.

Déjà la première chose à savoir et qu'il m'a fait perdre un temps infini, c'est que le contrôle MediaElement NE GERE PAS LES WAV. Aussi étonnant que cela puisse paraître, il n'arrive pas gérer des WAVs, ou en tout cas ceux que je lui donnais, je les ai pourtant vérifié en les lisant avec différents lecteurs et le fichier n'avait aucun soucis. Dans notre cas il faudra donc passer par des MP3, donc ne vous faite pas avoir !

Ensuite, pour pouvoir jouer les sons de façon simple et de pouvoir en lancer le même fichier plusieurs fois, j'ai donc créé une classe qui permet de gérer ça de façon transparent. Cette classe n'a besoin que du chemin du fichier dans les ressources de l'application et de l'instance d'un Panel (GridCanvasPanel, peu importe) afin de pouvoir stocker son instance dans un arbre visuel. En effet, le MediaElement a besoin de se trouver dans l'arbre visuel pour fonctionner. Cela n'est pas étonnant car à la base c'est un contrôle XAML visuel.

Le principe de cette classe et quand on lui invoque la fonction Play(), c'est de créer un MediaElement, de lui affecter le fichier, l'ajouter dans l'arbre visuel, jouer le son et quand celui-ci est fini, retirer l'instance de ce MediaElement de l'arbre visuel. Simple mais efficace.

Vous pouvez télécharger cette classe via ce lien : SoundItem.cs (3,73 kb)

Voici le code source de cette classe :

using System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace MonApplication
{
    /// <summary>
    /// Représente un son a jouer avec sa gestion
    /// </summary>
    public class SoundItem
    {
        #region Propriétés

        /// <summary>
        /// Obtient la racine de l'arbre visuel que l'on va utiliser pour stocker nos MediaElements
        /// </summary>
        public Panel Root
        {
            get;
            private set;
        }

        /// <summary>
        /// Obtient l'URI associé au son
        /// </summary>
        public Uri Uri
        {
            get;
            private set;
        }

        #endregion


        #region Constructeur

        /// <summary>
        /// Constructeur de la classe
        /// </summary>
        /// <param name="root">Racine de l'arbre visuel que l'on va utiliser pour stocker nos MediaElements</param>
        /// <param name="filename">Fichier son de référence</param>
        public SoundItem(string filename, Panel root)
        {
            this.Root = root;
            this.Uri = new Uri("ms-appx:/" + filename);
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Joue le son
        /// </summary>
        public void Play()
        {
            // Création du MediaElement

            MediaElement sound = new MediaElement();
            sound.AutoPlay = false;
            sound.AudioCategory = AudioCategory.ForegroundOnlyMedia;
            sound.MediaOpened += this.OnMediaOpened;
            sound.MediaFailed += this.OnMediaFailed;
            sound.MediaEnded += this.OnMediaEnded;

            //  Ajour à l'arbre visuel

            this.Root.Children.Add(sound);

            // Chargement du son

            sound.Source = this.Uri;
        }

        #endregion

        #region Fonctions privées

        /// <summary>
        /// Se produit sur la fin de lecture d'un son
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void OnMediaEnded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            MediaElement sound = (MediaElement)sender;

            // On retire les handlers

            sound.MediaOpened -= this.OnMediaOpened;
            sound.MediaFailed -= this.OnMediaFailed;
            sound.MediaEnded -= this.OnMediaEnded;

            // Suppression de l'arbre visuel

            this.Root.Children.Remove(sound);
        }

        /// <summary>
        /// Se produit sur l'échec d'ouverture du son
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void OnMediaFailed(object sender, Windows.UI.Xaml.ExceptionRoutedEventArgs e)
        {
            MediaElement sound = (MediaElement)sender;

            // On retire les handlers

            sound.MediaOpened -= this.OnMediaOpened;
            sound.MediaFailed -= this.OnMediaFailed;
            sound.MediaEnded -= this.OnMediaEnded;

            // Suppression de l'arbre visuel

            this.Root.Children.Remove(sound);
        }

        /// <summary>
        /// Se produit sur la réussite de lecture du son
        /// </summary>
        /// <param name="sender">Source de l'appel</param>
        /// <param name="e">Argument de l'appel</param>
        private void OnMediaOpened(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            MediaElement sound = (MediaElement)sender;

            // On joue le son

            sound.Play();
        }

        #endregion
    }
}

Si vous avez des questions sur le fonctionnement cette classe ou sur cet article, n'hésitez pas à me demander.

Ajouter un commentaire

biuquote
  • Commentaire
  • Prévisualiser
Loading