Воспроизведение звука из потока с помощью C#


есть ли способ в C# воспроизводить аудио (например, MP3) напрямую из System.IO.Stream который, например, был returend из WebRequest без временного сохранения данных на диск?


решение NAudio

С помощью NAudio 1.3 можно:

  1. загрузите MP3-файл из URL-адреса в MemoryStream
  2. конвертировать MP3 данные в волновые данные после того, как это было полностью загружен
  3. воспроизведение волновых данных с помощью NAudio ' S WaveOut class

было бы неплохо иметь возможность даже воспроизводить наполовину загруженный MP3-файл, но это кажется невозможным из-за NAudio дизайн библиотеки.

и это функция, которая будет делать работу:

    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);
                    }
                }
            }
        }
    }
9 87

9 ответов:

изменить: ответ обновлен, чтобы отразить изменения в последних версиях NAudio

это возможно с помощью NAudio открытым исходным кодом .Чистый аудио библиотека я написал. Он ищет кодек ACM на вашем ПК, чтобы сделать преобразование. Mp3FileReader, поставляемый с NAudio, в настоящее время ожидает, что сможет изменить положение в исходном потоке (он создает индекс MP3-кадров спереди), поэтому он не подходит для потоковой передачи по сети. Тем не менее, вы все еще можете используйте MP3Frame и AcmMp3FrameDecompressor классы в NAudio для распаковки потокового MP3 на лету.

я опубликовал статью в своем блоге, объясняющую как воспроизвести MP3-поток с помощью NAudio. По сути, у вас есть один поток, загружающий MP3-фреймы, распаковывающий их и хранящий их в BufferedWaveProvider. Затем другой поток воспроизводится с помощью BufferedWaveProvider в качестве входных данных.

на SoundPlayer класс может сделать это. Похоже, все, что вам нужно сделать, это установить его поток свойство к потоку, затем вызовите Play.

edit
Я не думаю, что он может воспроизводить MP3-файлы, хотя; это кажется ограниченным .в формате WAV. Я не уверен, есть ли что-нибудь в рамках, которые могут воспроизводить MP3-файл напрямую. Все, что я нахожу, включает в себя либо использование элемента управления WMP, либо взаимодействие с DirectX.

Басов могу сделать только это. Делегаты играть из Byte[] в памяти или через файл вернуть данные, так что вы можете играть, как только у вас есть достаточно данных, чтобы начать воспроизведение..

Я немного изменил тему starter source, поэтому теперь он может воспроизводить не полностью загруженный файл. Вот он (обратите внимание, что это всего лишь пример и точка для начала; вам нужно сделать некоторые исключения и обработку ошибок здесь):

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

NAudio обертывает WAVEOUTXXXX API. Я не смотрел на источник, но если NAudio предоставляет функцию waveOutWrite() таким образом, что автоматически не останавливает воспроизведение при каждом вызове, то вы должны быть в состоянии сделать то, что вы действительно хотите, а именно начать воспроизведение аудиопотока, прежде чем вы получите все данные.

использование функции waveOutWrite () позволяет "читать вперед" и сбрасывать меньшие куски аудио в очередь вывода-Windows автоматически воспроизводит куски плавно. Ваш код должен был бы взять сжатый аудиопоток и конвертировать его в небольшие куски WAV - аудио на лету; эта часть была бы действительно сложной-все библиотеки и компоненты, которые я когда-либо видел, преобразуют MP3-в-WAV весь файл за раз. Вероятно, ваш единственный реальный шанс-это сделать это с помощью WMA вместо MP3, потому что вы можете писать простые обертки C# вокруг мультимедийного SDK.

Я изменил источник, опубликованный в вопросе, чтобы разрешить использование с помощью TTS API Google, чтобы ответить на вопрос здесь:

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

вызвать с помощью:

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

завершить с помощью:

if (waiting)
    stop.Set();

обратите внимание, что я использую ParameterizedThreadDelegate в коде выше, и поток начинается с playThread.Start(10000);. 10000 представляет собой максимум 10 секунд аудио для воспроизведения, поэтому его нужно будет настроить, если ваш поток занимает больше времени, чем для воспроизведения. Это необходимо, потому что текущая версия NAudio (v1.5.4.0), похоже, имеет проблему с определением, когда поток будет воспроизводиться. Это может быть исправлено в более поздней версии или, возможно, есть обходной путь, который я не нашел времени, чтобы найти.

я завернул библиотеку декодера MP3 и сделал ее доступной для .NET разработчиков как mpg123.net.

включены образцы для преобразования MP3-файлов в PCM, и чтение ID3 теги.

Я всегда использовал FMOD для таких вещей, потому что это бесплатно для некоммерческого использования и хорошо работает.

тем не менее, я бы с удовольствием переключился на что-то меньшее (FMOD ~300k) и с открытым исходным кодом. Супер бонусные очки, если он полностью управляется, чтобы я мог скомпилировать / объединить его с моим .exe и не нужно проявлять дополнительную осторожность, чтобы получить переносимость на другие платформы...

(FMOD делает переносимость тоже, но вам, очевидно, нужны разные двоичные файлы для разных платформ)

Я не пробовал его из WebRequest, но оба Проигрыватель Windows MediaActiveX и MediaElement (с WPF) компоненты способны воспроизводить и буферизировать MP3-потоки.

Я использую его для воспроизведения данных, поступающих из SHOUTcast поток, и он работал отлично. Однако я не уверен, что это сработает в сценарии, который вы предлагаете.