Putar audio dari aliran menggunakan C #

99

Apakah ada cara di C # untuk memutar audio (misalnya, MP3) secara langsung dari System.IO. Alirkan yang misalnya adalah returend dari WebRequest tanpa menyimpan data sementara ke disk?


Solusi dengan NAudio

Dengan bantuan NAudio 1.3, dimungkinkan untuk:

  1. Muat file MP3 dari URL ke dalam MemoryStream
  2. Ubah data MP3 menjadi data gelombang setelah selesai dimuat
  3. Putar ulang data gelombang menggunakan kelas WaveOut NAudio

Akan lebih baik untuk dapat memutar file MP3 yang setengah dimuat, tetapi ini tampaknya tidak mungkin karena desain perpustakaan NAudio .

Dan inilah fungsi yang akan melakukan pekerjaan itu:

    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);
                    }
                }
            }
        }
    }
Martin
sumber
4
senang melihat Anda berhasil. Tidak akan terlalu merepotkan untuk memutarnya dengan benar saat streaming. Masalah utamanya adalah Mp3FileReader saat ini mengharapkan untuk mengetahui panjangnya terlebih dahulu. Saya akan mempertimbangkan untuk menambahkan demo untuk versi berikutnya dari NAudio
Mark Heath
@Mark Heath apakah Anda sudah menyelesaikan masalah dan menambahkan demo dalam versi NAudio saat ini atau apakah masih dalam saluran Anda?
Martin
jangan takut, meskipun dengan perubahan yang dibuat pada NAudio 1.3 tidak memerlukan terlalu banyak penyesuaian untuk membuatnya berfungsi.
Mark Heath
Tandai: Apakah saya perlu memodifikasi di NAudio untuk membuatnya berfungsi, karena saya baru saja mengunduh NAudio1.3 tetapi menerima kode di atas tanpa perubahan, tetapi di sisi lain membuang pengecualian yang mengatakan sesuatu seperti "Konversi ACM tidak mungkin".
Mubashar
ngomong-ngomong saya mencoba bermain berikut translate.google.com/translate_tts?q=I+love+techcrunch
Mubashar

Jawaban:

56

Edit: Jawaban diperbarui untuk mencerminkan perubahan dalam versi terbaru NAudio

Itu mungkin menggunakan pustaka audio .NET open source NAudio yang saya tulis. Ini mencari codec ACM pada PC Anda untuk melakukan konversi. Mp3FileReader yang disertakan dengan NAudio saat ini mengharapkan untuk dapat memposisikan ulang dalam aliran sumber (itu membangun indeks frame MP3 di depan), sehingga tidak sesuai untuk streaming melalui jaringan. Namun, Anda masih dapat menggunakan kelas MP3Framedan AcmMp3FrameDecompressordi NAudio untuk mendekompresi MP3 yang dialirkan dengan cepat.

Saya telah memposting artikel di blog saya yang menjelaskan cara memutar aliran MP3 menggunakan NAudio . Pada dasarnya Anda memiliki satu utas mengunduh bingkai MP3, mendekompresi dan menyimpannya dalam file BufferedWaveProvider. Utas lain kemudian memutar ulang menggunakan BufferedWaveProvidersebagai input.

Mark Heath
sumber
3
The NAudio perpustakaan sekarang pengiriman dengan contoh aplikasi yang disebut Mp3StreamingDemo yang harus memberikan segala sesuatu harus live stream MP3 dari jaringan.
Martin
Apakah mungkin menggunakan perpustakaan Anda untuk mengalirkan langsung mic / line yang dimasukkan ke perangkat android?
realtebo
10

Kelas SoundPlayer dapat melakukan ini. Sepertinya yang perlu Anda lakukan hanyalah menyetel properti Stream ke aliran, lalu panggil Play.

edit
Saya rasa tidak bisa memutar file MP3; tampaknya terbatas pada .wav. Saya tidak yakin apakah ada sesuatu dalam kerangka yang dapat memutar file MP3 secara langsung. Semua yang saya temukan tentang itu melibatkan penggunaan kontrol WMP atau berinteraksi dengan DirectX.

OwenP
sumber
1
Saya telah mencoba menemukan pustaka .NET yang melakukan pengkodean dan penguraian kode MP3 selama bertahun-tahun sekarang, tetapi saya rasa itu tidak ada.
MusiGenesis
NAudio harus melakukan pekerjaan untuk Anda - setidaknya untuk decodeing (lihat posting pertama)
Martin
4
SoundPlayer memiliki masalah buruk dengan GC dan akan memutar sampah secara acak kecuali Anda menggunakan solusi resmi Microsoft (klik Workarounds): connect.microsoft.com/VisualStudio/feedback/…
Roman Starkov
SoundPlayer + memoryStream memiliki bug dengan desing. saat Anda memutar untuk kedua atau ketiga kali pertama, suara aneh akan bertambah di tengah audio Anda.
bh_earth0
Tautan solusi tidak berfungsi lagi, tetapi ada di Wayback Machine: web.archive.org/web/20120722231139/http://connect.microsoft.com/…
Forestrf
5

Bass bisa melakukan ini. Putar dari Byte [] dalam memori atau file melalui delegasi tempat Anda mengembalikan data, sehingga Anda dapat memutar segera setelah Anda memiliki cukup data untuk memulai pemutaran ..

siput
sumber
3

Saya sedikit mengubah sumber permulaan topik, sehingga sekarang dapat memutar file yang tidak terisi penuh. Ini dia (perhatikan, ini hanya contoh dan merupakan titik awal; Anda perlu melakukan beberapa pengecualian dan penanganan kesalahan di sini):

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
sumber
Apakah Anda menguji kode Anda? Jika ya, dengan versi NAudio mana yang berfungsi?
Martin
Selain itu - kode Anda sepertinya cukup rentan terhadap masalah threading. Apakah Anda yakin tahu apa yang Anda lakukan?
Martin
1) Ya, saya lakukan. 2) Dengan versi terbaru. 3) Itu hanya bukti konsep seperti yang saya nyatakan dalam pesan saya sebelumnya.
ReVolly
Bagaimana Anda mendefinisikan "versi terbaru"? Apakah ini Versi 1.3 atau sumber pada revisi 68454?
Martin
@Martin Maaf, sudah lama tidak ke sini. NAudio.dll yang saya gunakan memiliki versi 1.3.8.
ReVolly
2

Saya telah mengubah sumber yang diposting dalam pertanyaan untuk memungkinkan penggunaan dengan API TTS Google untuk menjawab pertanyaan tersebut sini :

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

Panggil dengan:

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

Akhiri dengan:

if (waiting)
    stop.Set();

Perhatikan bahwa saya menggunakan ParameterizedThreadDelegatekode di atas, dan utas dimulai dengan playThread.Start(10000);. 10000 mewakili maksimal 10 detik audio untuk diputar sehingga perlu di-tweak jika streaming Anda membutuhkan waktu lebih lama dari itu untuk diputar. Hal ini diperlukan karena versi NAudio (v1.5.4.0) saat ini tampaknya memiliki masalah dalam menentukan kapan streaming selesai diputar. Ini mungkin diperbaiki di versi yang lebih baru atau mungkin ada solusi yang saya tidak meluangkan waktu untuk menemukannya.

M. Babcock
sumber
1

NAudio membungkus API WaveOutXXXX. Saya belum melihat sumbernya, tetapi jika NAudio mengekspos fungsi waveOutWrite () dengan cara yang tidak secara otomatis menghentikan pemutaran pada setiap panggilan, maka Anda harus dapat melakukan apa yang Anda inginkan, yaitu mulai memainkan aliran audio sebelum Anda menerima semua data.

Menggunakan fungsi waveOutWrite () memungkinkan Anda untuk "membaca ke depan" dan membuang potongan audio yang lebih kecil ke dalam antrian keluaran - Windows secara otomatis akan memutar potongan tersebut dengan mulus. Kode Anda harus mengambil aliran audio terkompresi dan mengubahnya menjadi potongan kecil audio WAV dengan cepat; bagian ini akan sangat sulit - semua pustaka dan komponen yang pernah saya lihat melakukan konversi MP3-ke-WAV seluruh file dalam satu waktu. Mungkin satu-satunya kesempatan realistis Anda adalah melakukan ini menggunakan WMA daripada MP3, karena Anda dapat menulis pembungkus C # sederhana di sekitar SDK multimedia.

MusiGenesis
sumber
1

Saya membungkus pustaka decoder MP3 dan membuatnya tersedia untuk pengembang .NET sebagai mpg123.net .

Termasuk contoh untuk mengkonversi file MP3 ke PCM , dan membaca tag ID3 .

Daniel Mošmondor
sumber
Bagus! Saya berharap untuk melihatnya akhir pekan ini.
Larry Smithmier
1

Saya belum mencobanya dari WebRequest, tetapi baik komponen Windows Media Player ActiveX dan MediaElement (dari WPF ) mampu memutar dan menyangga aliran MP3.

Saya menggunakannya untuk memutar data yang berasal dari aliran SHOUTcast dan itu berfungsi dengan baik. Namun, saya tidak yakin apakah itu akan berhasil dalam skenario yang Anda usulkan.

Ramiro Berrelleza
sumber
0

Saya selalu menggunakan FMOD untuk hal-hal seperti ini karena gratis untuk penggunaan non-komersial dan berfungsi dengan baik.

Karena itu, saya dengan senang hati akan beralih ke sesuatu yang lebih kecil (FMOD ~ 300k) dan open-source. Poin bonus super jika dikelola sepenuhnya sehingga saya dapat mengkompilasi / menggabungkannya dengan .exe saya dan tidak perlu ekstra hati-hati untuk mendapatkan portabilitas ke platform lain ...

(FMOD juga dapat dibawa, tetapi Anda jelas membutuhkan binari yang berbeda untuk platform yang berbeda)

Roman Starkov
sumber