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

99

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


Решение с NAudio

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

  1. Загрузите файл MP3 из URL-адреса в MemoryStream
  2. Преобразуйте данные MP3 в данные волны после того, как они были полностью загружены
  3. Воспроизведение данных с использованием волновых 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);
                    }
                }
            }
        }
    }
Мартин
источник
4
приятно видеть, что у тебя все работает. Не составит большого труда заставить его правильно воспроизводить во время потоковой передачи. Основная проблема заключается в том, что Mp3FileReader в настоящее время ожидает знать длину заранее. Я подумаю о добавлении демо для следующей версии NAudio
Марк Хит
@Mark Heath, вы уже решили проблему и добавили демо в текущую версию NAudio или она все еще у вас в пиплайне?
Мартин
пока не боюсь, хотя с изменениями, внесенными в NAudio 1.3, для его работы не потребуется слишком много настроек.
Марк Хит
Марк: Нужно ли мне вносить изменения в NAudio, чтобы заставить его работать, потому что я только что загрузил NAudio1.3, но он принимает вышеуказанный код без изменений, но, с другой стороны, выдает исключение, которое говорит что-то вроде «Преобразование ACM невозможно».
Mubashar
кстати, я пытаюсь сыграть после translate.google.com/translate_tts?q=I+love+techcrunch
Mubashar

Ответы:

56

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

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

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

Марк Хит
источник
3
Библиотека NAudio теперь поставляется с примером приложения под названием Mp3StreamingDemo, которое должно предоставить все необходимое для прямой трансляции MP3 из сети.
Мартин
Можно ли использовать вашу библиотеку для прямой трансляции микрофонного / линейного входа на устройство Android?
realtebo
10

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

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

OwenP
источник
1
Я много лет пытался найти библиотеку .NET, которая выполняет кодирование и декодирование MP3, но не думаю, что она существует.
MusiGenesis
NAudio должен сделать эту работу за вас - по крайней мере, для декодирования (см. Первый пост)
Мартин
4
SoundPlayer имеет неприятные проблемы с GC и будет случайным образом проигрывать мусор, если вы не используете официальный обходной путь Microsoft (щелкните Обходные пути): connect.microsoft.com/VisualStudio/feedback/…
Роман Старков
SoundPlayer + memoryStream имеет ошибку по дизайну. когда вы играете первый второй или третий раз, он добавляет странный шум в середине вашего звука.
bh_earth0
Ссылка на
обходной путь
5

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

слизняк
источник
3

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

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);
            }
        }
    }
}
ReVolly
источник
Вы тестировали свой код? Если да, то с какой версией NAudio он работал?
Мартин
Кроме того, ваш код выглядит так, как будто он весьма подвержен ошибкам и проблемам с потоками. Вы уверены, что знаете, что делаете?
Мартин
1) Да, был. 2) С последней версией. 3) Это просто доказательство концепции, о которой я говорил в предыдущем сообщении.
ReVolly
Как вы определяете «последнюю версию»? Это версия 1.3 или исходник на ревизии 68454?
Мартин
@ Мартин Извини, я здесь давно не был. Я использовал NAudio.dll версии 1.3.8.
ReVolly
2

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

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

М. Бэбкок
источник
1

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

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

MusiGenesis
источник
1

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

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

Даниэль Мошмондор
источник
Большой! Я с нетерпением жду возможности взглянуть на него в эти выходные.
Ларри Смитмиер
1

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

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

Рамиро Берреллеза
источник
0

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

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

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

Роман Старков
источник