102 votes

Jouer de l'audio à partir d'un flux en utilisant C#

Existe-t-il un moyen en C# de lire un fichier audio (par exemple, MP3) directement à partir d'un fichier System.IO.Stream qui, par exemple, a été obtenue à partir d'une demande Web sans sauvegarder temporairement les données sur le disque ?


Solution avec NAudio

Avec l'aide de NAudio 1.3 il est possible de :

  1. Chargement d'un fichier MP3 à partir d'une URL dans un MemoryStream
  2. Convertir les données MP3 en données wave après leur chargement complet
  3. Lecture des données d'ondes à l'aide de NAudio La classe WaveOut

Il aurait été agréable de pouvoir lire un fichier MP3 à moitié chargé, mais cela semble impossible en raison de l'absence d'un système de gestion de l'information. NAudio la conception de la bibliothèque.

C'est cette fonction qui fera le travail :

    public static void PlayMp3FromUrl(string url)
    {
        using (Stream ms = new MemoryStream())
        {
            using (Stream stream = WebRequest.Create(url)
                .GetResponse().GetResponseStream())
            {
                byte[] buffer = new byte[32768];
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
            }

            ms.Position = 0;
            using (WaveStream blockAlignedStream =
                new BlockAlignReductionStream(
                    WaveFormatConversionStream.CreatePcmStream(
                        new Mp3FileReader(ms))))
            {
                using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                {
                    waveOut.Init(blockAlignedStream);
                    waveOut.Play();                        
                    while (waveOut.PlaybackState == PlaybackState.Playing )                        
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }
    }

4 votes

Il est bon de voir que vous avez réussi à le faire fonctionner. Il n'y a pas beaucoup de travail à faire pour qu'il soit lu correctement pendant la diffusion en continu. Le principal problème est que le Mp3FileReader s'attend actuellement à connaître la longueur à l'avance. Je vais essayer d'ajouter une démo pour la prochaine version de NAudio.

0 votes

@Mark Heath as-tu déjà résolu le problème et ajouté la démo dans la version actuelle de NAudio ou est-elle toujours dans ta pipline ?

0 votes

J'ai peur que ce ne soit pas encore le cas, mais grâce aux changements apportés dans NAudio 1.3, il ne sera pas nécessaire de faire beaucoup d'ajustements pour que cela fonctionne.

58voto

Mark Heath Points 22240

Edit : Réponse mise à jour pour refléter les changements dans les versions récentes de NAudio

Il est possible d'utiliser le NAudio bibliothèque audio .NET open source que j'ai écrite. Elle recherche un codec ACM sur votre PC pour effectuer la conversion. Le Mp3FileReader fourni avec NAudio s'attend actuellement à pouvoir se repositionner dans le flux source (il construit un index des trames MP3 en amont), il n'est donc pas approprié pour la diffusion en continu sur le réseau. Cependant, vous pouvez toujours utiliser l'outil MP3Frame et AcmMp3FrameDecompressor dans NAudio pour décompresser à la volée les MP3 diffusés en continu.

J'ai publié sur mon blog un article expliquant comment lire un flux MP3 à l'aide de NAudio . Essentiellement, vous avez un thread qui télécharge des trames MP3, les décompresse et les stocke dans un fichier BufferedWaveProvider . Un autre fil est ensuite lu à l'aide de la fonction BufferedWaveProvider comme entrée.

4 votes

En NAudio est désormais livrée avec un exemple d'application appelé Mp3StreamingDemo qui devrait fournir tout ce dont on a besoin pour diffuser en direct un MP3 à partir du réseau.

0 votes

Est-il possible d'utiliser votre bibliothèque pour diffuser en direct une entrée micro/ligne vers un appareil Android ?

10voto

OwenP Points 11164

En SoundPlayer peut le faire. Il semble que tout ce que vous avez à faire est de définir son Flux au flux, puis appeler Play .

éditer
Je ne pense pas qu'il puisse lire les fichiers MP3 ; il semble limité aux fichiers .wav. Je ne suis pas certain qu'il y ait quoi que ce soit dans le framework qui puisse lire un fichier MP3 directement. Tout ce que j'ai trouvé à ce sujet implique soit l'utilisation d'un contrôle WMP, soit une interaction avec DirectX.

1 votes

Cela fait des années que j'essaie de trouver une bibliothèque .NET qui fasse de l'encodage et du décodage de MP3, mais je ne pense pas qu'elle existe.

0 votes

NAudio devrait faire le travail pour vous - au moins pour le décodage (voir le premier message).

4 votes

SoundPlayer a de vilains problèmes avec GC et jouera aléatoirement de la merde à moins que vous n'utilisiez la solution de contournement officielle de Microsoft (cliquez sur Solutions de contournement) : connect.microsoft.com/VisualStudio/feedback/

5voto

Basse peut le faire. Jouer à partir d'un octet[] en mémoire ou d'un délégué de fichier où vous renvoyez les données, ce qui vous permet de jouer dès que vous avez assez de données pour commencer la lecture

2voto

M.Babcock Points 10653

J'ai modifié la source affichée dans la question pour permettre l'utilisation de l'API TTS de Google afin de répondre à la question. aquí :

bool waiting = false;
AutoResetEvent stop = new AutoResetEvent(false);
public void PlayMp3FromUrl(string url, int timeout)
{
    using (Stream ms = new MemoryStream())
    {
        using (Stream stream = WebRequest.Create(url)
            .GetResponse().GetResponseStream())
        {
            byte[] buffer = new byte[32768];
            int read;
            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                ms.Write(buffer, 0, read);
            }
        }
        ms.Position = 0;
        using (WaveStream blockAlignedStream =
            new BlockAlignReductionStream(
                WaveFormatConversionStream.CreatePcmStream(
                    new Mp3FileReader(ms))))
        {
            using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
            {
                waveOut.Init(blockAlignedStream);
                waveOut.PlaybackStopped += (sender, e) =>
                {
                    waveOut.Stop();
                };
                waveOut.Play();
                waiting = true;
                stop.WaitOne(timeout);
                waiting = false;
            }
        }
    }
}

Invoquer avec :

var playThread = new Thread(timeout => PlayMp3FromUrl("http://translate.google.com/translate_tts?q=" + HttpUtility.UrlEncode(relatedLabel.Text), (int)timeout));
playThread.IsBackground = true;
playThread.Start(10000);

Terminer par :

if (waiting)
    stop.Set();

Remarquez que j'utilise le ParameterizedThreadDelegate dans le code ci-dessus, et le fil de discussion est démarré avec playThread.Start(10000); . La valeur 10000 représente un maximum de 10 secondes d'audio à jouer, il faudra donc l'ajuster si votre flux prend plus de temps à jouer. Ceci est nécessaire car la version actuelle de NAudio (v1.5.4.0) semble avoir un problème pour déterminer quand la lecture du flux est terminée. Il est possible que ce problème soit corrigé dans une version ultérieure ou qu'il existe une solution de contournement que je n'ai pas pris le temps de trouver.

2voto

Virtuality Points 96

J'ai légèrement modifié la source de démarrage du sujet, afin qu'elle puisse maintenant lire un fichier qui n'est pas complètement chargé. Le voici (notez que ce n'est qu'un exemple et que c'est un point de départ ; vous devez gérer les exceptions et les erreurs ici) :

private Stream ms = new MemoryStream();
public void PlayMp3FromUrl(string url)
{
    new Thread(delegate(object o)
    {
        var response = WebRequest.Create(url).GetResponse();
        using (var stream = response.GetResponseStream())
        {
            byte[] buffer = new byte[65536]; // 64KB chunks
            int read;
            while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
            {
                var pos = ms.Position;
                ms.Position = ms.Length;
                ms.Write(buffer, 0, read);
                ms.Position = pos;
            }
        }
    }).Start();

    // Pre-buffering some data to allow NAudio to start playing
    while (ms.Length < 65536*10)
        Thread.Sleep(1000);

    ms.Position = 0;
    using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
    {
        using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
        {
            waveOut.Init(blockAlignedStream);
            waveOut.Play();
            while (waveOut.PlaybackState == PlaybackState.Playing)
            {
                System.Threading.Thread.Sleep(100);
            }
        }
    }
}

0 votes

Avez-vous testé votre code ? Si oui, avec quelle version de NAudio a-t-il fonctionné ?

1 votes

De plus, votre code semble être sujet à des erreurs dues à des problèmes de threading. Êtes-vous sûr de savoir ce que vous faites ?

0 votes

1) Oui, je l'ai fait. 2) Avec la dernière version. 3) C'est juste une preuve de concept comme je l'ai indiqué dans mon message précédent.

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