Phát âm thanh từ luồng bằng C #


99

Có cách nào trong C # để phát âm thanh (ví dụ: MP3) trực tiếp từ System.IO.Stream mà chẳng hạn như đã được returend từ WebRequest mà không lưu dữ liệu tạm thời vào đĩa không?


Giải pháp với NAudio

Với sự trợ giúp của NAudio 1.3, bạn có thể:

  1. Tải tệp MP3 từ một URL vào MemoryStream
  2. Chuyển đổi dữ liệu MP3 thành dữ liệu sóng sau khi tải xong
  3. Phát lại dữ liệu sóng bằng lớp WaveOut của NAudio

Thật tuyệt khi có thể phát một nửa tệp MP3 đã tải, nhưng điều này dường như là không thể do thiết kế thư viện NAudio .

Và đây là chức năng sẽ thực hiện công việc:

    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
thật vui khi thấy bạn đã làm việc Sẽ không quá nhiều việc để phát lại đúng cách trong khi phát trực tuyến. Vấn đề chính là Mp3FileReader hiện đang mong đợi biết trước độ dài. Tôi sẽ xem xét thêm bản demo cho phiên bản tiếp theo của NAudio
Mark Heath

@Mark Heath bạn đã giải quyết được vấn đề và thêm bản demo trong phiên bản NAudio hiện tại chưa hay nó vẫn nằm trong đường ống của bạn?
Martin

e là chưa, mặc dù với những thay đổi được thực hiện trong NAudio 1.3, nó sẽ không yêu cầu chỉnh sửa quá nhiều để làm cho nó hoạt động.
Mark Heath

Mark: Tôi có cần sửa đổi trong NAudio để nó hoạt động không, vì tôi vừa tải xuống NAudio1.3 nhưng nó đang chấp nhận mã ở trên mà không thay đổi, nhưng mặt khác lại ném ra ngoại lệ có nội dung như "Không thể chuyển đổi ACM".
Mubashar

bằng cách này tôi đang cố gắng để chơi sau translate.google.com/translate_tts?q=I+love+techcrunch
Mubashar

Câu trả lời:


56

Chỉnh sửa: Câu trả lời được cập nhật để phản ánh những thay đổi trong các phiên bản gần đây của NAudio

Có thể sử dụng thư viện âm thanh .NET mã nguồn mở NAudio mà tôi đã viết. Nó tìm kiếm codec ACM trên PC của bạn để thực hiện chuyển đổi. Mp3FileReader được cung cấp với NAudio hiện đang mong đợi có thể định vị lại trong luồng nguồn (nó xây dựng một chỉ mục của khung MP3 ở phía trước), vì vậy nó không thích hợp để phát trực tuyến qua mạng. Tuy nhiên, bạn vẫn có thể sử dụng các lớp MP3FrameAcmMp3FrameDecompressortrong NAudio để giải nén MP3 đã phát trực tiếp.

Tôi đã đăng một bài viết trên blog của mình giải thích cách phát lại luồng MP3 bằng NAudio . Về cơ bản, bạn có một chuỗi tải các khung MP3, giải nén chúng và lưu trữ chúng trong một BufferedWaveProvider. Một chuỗi khác sau đó sẽ phát lại bằng cách sử dụng BufferedWaveProviderlàm đầu vào.


3
Các NAudio thư viện hiện đang vận chuyển với một ứng dụng ví dụ gọi Mp3StreamingDemo mà nên cung cấp một tất cả mọi thứ sẽ cần phải live stream MP3 từ mạng.
Martin

Có thể sử dụng thư viện của bạn để phát trực tiếp micrô / đường truyền vào thiết bị Android không?
realtebo

10

Lớp SoundPlayer có thể làm điều này. Có vẻ như tất cả những gì bạn phải làm là đặt thuộc tính Luồng của nó thành luồng, sau đó gọi Play.

chỉnh sửa
Tôi không nghĩ rằng nó có thể chơi các tệp MP3; nó dường như bị giới hạn ở .wav. Tôi không chắc liệu có thứ gì trong khung có thể phát trực tiếp tệp MP3 hay không. Mọi thứ tôi tìm thấy liên quan đến việc sử dụng điều khiển WMP hoặc tương tác với DirectX.


1
Tôi đã cố gắng tìm một thư viện .NET có chức năng mã hóa và giải mã MP3 trong nhiều năm nay, nhưng tôi không nghĩ nó tồn tại.
MusiGenesis 20/10/08

NAudio nên làm công việc cho bạn - ít nhất là cho decodeing (xem bài viết đầu tiên)
Martin

4
SoundPlayer có vấn đề khó chịu với GC và sẽ chơi một cách ngẫu nhiên rác trừ khi bạn sử dụng Microsoft workaround chính thức (nhấp Cách giải quyết): connect.microsoft.com/VisualStudio/feedback/...
La Mã Starkov

SoundPlayer + memoryStream có lỗi do desing. khi bạn phát lần thứ hai hoặc thứ ba đầu tiên, nó sẽ thêm tiếng ồn lạ vào giữa âm thanh của bạn.
bh_earth0

Liên kết workaround không hoạt động nữa, nhưng đó là trên Wayback Machine: web.archive.org/web/20120722231139/http://connect.microsoft.com/...
Forestrf

5

Bass có thể làm được điều này. Phát từ Byte [] trong bộ nhớ hoặc đại biểu thông qua tệp nơi bạn trả lại dữ liệu, do đó bạn có thể phát ngay khi có đủ dữ liệu để bắt đầu phát lại ..


3

Tôi đã sửa đổi một chút nguồn bắt đầu chủ đề, vì vậy bây giờ nó có thể phát tệp chưa tải đầy đủ. Đây là nó (lưu ý rằng nó chỉ là một mẫu và là một điểm để bắt đầu; bạn cần thực hiện một số ngoại lệ và xử lý lỗi ở đây):

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

Bạn đã kiểm tra mã của bạn? Nếu vậy, phiên bản NAudio nào đã hoạt động?
Martin

Ngoài ra - mã của bạn có vẻ như nó khá dễ xảy ra lỗi với các vấn đề phân luồng. Bạn có chắc mình biết mình đang làm gì không?
Martin

1) Có, tôi đã làm. 2) Với phiên bản mới nhất. 3) Nó chỉ là một bằng chứng về khái niệm như tôi đã nêu trong tin nhắn trước của mình.
ReVolly

Bạn định nghĩa "phiên bản mới nhất" như thế nào? Đó là Phiên bản 1.3 hay nguồn tại bản sửa đổi 68454?
Martin

@Martin Xin lỗi, đã lâu rồi không đến đây. NAudio.dll tôi đã sử dụng có phiên bản 1.3.8.
ReVolly

2

Tôi đã chỉnh sửa nguồn được đăng trong câu hỏi để cho phép sử dụng với API TTS của Google nhằm trả lời câu hỏi ở đây :

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

Gọi bằng:

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

Chấm dứt bằng:

if (waiting)
    stop.Set();

Lưu ý rằng tôi đang sử dụng ParameterizedThreadDelegateđoạn mã ở trên và chuỗi được bắt đầu bằng playThread.Start(10000);. 10000 đại diện cho tối đa 10 giây âm thanh sẽ được phát, vì vậy nó sẽ cần được điều chỉnh nếu luồng của bạn mất nhiều thời gian hơn để phát. Điều này là cần thiết vì phiên bản hiện tại của NAudio (v1.5.4.0) dường như có vấn đề trong việc xác định thời điểm phát xong luồng. Nó có thể được sửa trong phiên bản mới hơn hoặc có lẽ có một cách giải quyết khác mà tôi không dành thời gian để tìm.


1

NAudio bao bọc API WaveOutXXXX. Tôi chưa xem xét nguồn, nhưng nếu NAudio hiển thị hàm waveOutWrite () theo cách không tự động dừng phát lại trên mỗi cuộc gọi, thì bạn sẽ có thể làm những gì bạn thực sự muốn, đó là bắt đầu phát luồng âm thanh trước khi bạn nhận được tất cả dữ liệu.

Sử dụng hàm waveOutWrite () cho phép bạn "đọc trước" và đưa các phần âm thanh nhỏ hơn vào hàng đợi đầu ra - Windows sẽ tự động phát các phần đó một cách liền mạch. Mã của bạn sẽ phải lấy luồng âm thanh nén và chuyển đổi nó thành các phần nhỏ âm thanh WAV ngay lập tức; phần này sẽ thực sự khó - tất cả các thư viện và thành phần mà tôi từng thấy đều thực hiện chuyển đổi MP3-sang-WAV toàn bộ tệp cùng một lúc. Có lẽ cơ hội thực tế duy nhất của bạn là sử dụng WMA thay vì MP3, vì bạn có thể viết các trình bao bọc C # đơn giản xung quanh SDK đa phương tiện.



1

Tôi chưa thử nó từ WebRequest, nhưng cả hai thành phần Windows Media Player ActiveX và MediaElement (từ WPF ) đều có khả năng phát và đệm các luồng MP3.

Tôi sử dụng nó để phát dữ liệu đến từ luồng SHOUTcast và nó hoạt động tốt. Tuy nhiên, tôi không chắc liệu nó có hoạt động theo kịch bản bạn đề xuất hay không.


0

Tôi đã luôn sử dụng FMOD cho những thứ như thế này vì nó miễn phí cho mục đích phi thương mại và hoạt động tốt.

Điều đó nói rằng, tôi rất vui khi chuyển sang thứ gì đó nhỏ hơn (FMOD là ~ 300k) và mã nguồn mở. Điểm siêu thưởng nếu nó được quản lý đầy đủ để tôi có thể biên dịch / hợp nhất nó với .exe của mình và không phải chăm sóc thêm để có được khả năng di động sang các nền tảng khác ...

(FMOD cũng có tính di động nhưng rõ ràng bạn cần các mã nhị phân khác nhau cho các nền tảng khác nhau)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.