Воспроизведение аудио из потока с использованием С#

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


Решение с NAudio

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

  • Загрузите файл MP3 из URL-адреса в MemoryStream
  • Преобразование данных MP3 в волновые данные после полной загрузки
  • Воспроизведение волновых данных с помощью NAudio Класс WaveOut

Было бы неплохо иметь возможность играть в полузагруженный файл 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);
                    }
                }
            }
        }
    }

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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

В комплект входят образцы для преобразования файлов MP3 в PCM и читать ID3.

Ответ 7

Я изменил источник, опубликованный в вопросе, чтобы разрешить использование с API TTS 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), похоже, имеет проблему, определяющую, когда поток выполняется. Это может быть исправлено в более поздней версии или, возможно, есть обходной путь, который я не нашел времени, чтобы найти.

Ответ 8

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

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

Ответ 9

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

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

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