NAudio Mp3解码点击和弹出

时间:2018-06-15 15:54:41

标签: audio mp3 naudio decoding artifacts

我跟着this修改了NAudio演示以播放ShoutCast。

在我的完整代码中,我必须对传入的音频进行重新采样,然后通过网络再次将其传输到网络播放器。由于我获得了许多点击和弹出",我回到了演示代码,我发现这些工件是在解码块之后产生的。 如果我以mp3格式保存传入的流,很明显。 当我保存原始解码数据(没有除解码器之外的其他处理)时,我得到许多音频伪像。

我想知道我是否正在做一些错误,即使我的代码几乎等于NAudio演示。

这里的例子中的函数由我修改以保存原始数据。它被称为新线程。

 private void StreamMP3(object state)
    {

        //Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        //SettingsSection section = (SettingsSection)config.GetSection("system.net/settings");



        this.fullyDownloaded = false;
        string url = "http://icestreaming.rai.it/5.mp3";//(string)state;
        webRequest = (HttpWebRequest)WebRequest.Create(url);

        int metaInt = 0; // blocksize of mp3 data

        int framesize = 0;

        webRequest.Headers.Clear();
        webRequest.Headers.Add("GET", "/ HTTP/1.0");
        // needed to receive metadata informations
        webRequest.Headers.Add("Icy-MetaData", "1");
        webRequest.UserAgent = "WinampMPEG/5.09";

        HttpWebResponse resp = null;
        try
        {
            resp = (HttpWebResponse)webRequest.GetResponse();
        }
        catch (WebException e)
        {
            if (e.Status != WebExceptionStatus.RequestCanceled)
            {
                ShowError(e.Message);
            }
            return;
        }
        byte[] buffer = new byte[16384 * 4]; // needs to be big enough to hold a decompressed frame

        try
        {
            // read blocksize to find metadata block
            metaInt = Convert.ToInt32(resp.GetResponseHeader("icy-metaint"));

        }
        catch
        {
        }

        IMp3FrameDecompressor decompressor = null;
        byteOut = createNewFile(destPath, "salva", "raw");

        try
        {
            using (var responseStream = resp.GetResponseStream())
            {
                var readFullyStream = new ReadFullyStream(responseStream);
                readFullyStream.metaInt = metaInt;
                do
                {
                    if (mybufferedWaveProvider != null && mybufferedWaveProvider.BufferLength - mybufferedWaveProvider.BufferedBytes < mybufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4)
                    {
                        Debug.WriteLine("Buffer getting full, taking a break");
                        Thread.Sleep(500);
                    }
                    else
                    {
                        Mp3Frame frame = null;
                        try
                        {

                            frame = Mp3Frame.LoadFromStream(readFullyStream, true);

                            if (metaInt > 0)
                                UpdateSongName(readFullyStream.SongName);
                            else
                                UpdateSongName("No Song Info in Stream...");


                        }
                        catch (EndOfStreamException)
                        {
                            this.fullyDownloaded = true;
                            // reached the end of the MP3 file / stream
                            break;
                        }
                        catch (WebException)
                        {
                            // probably we have aborted download from the GUI thread
                            break;
                        }
                        if (decompressor == null)
                        {
                            // don't think these details matter too much - just help ACM select the right codec
                            // however, the buffered provider doesn't know what sample rate it is working at
                            // until we have a frame
                            WaveFormat waveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, frame.FrameLength, frame.BitRate);
                            decompressor = new AcmMp3FrameDecompressor(waveFormat);
                            this.mybufferedWaveProvider = new BufferedWaveProvider(decompressor.OutputFormat);
                            this.mybufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(200); // allow us to get well ahead of ourselves

                            framesize = (decompressor.OutputFormat.Channels * decompressor.OutputFormat.SampleRate * (decompressor.OutputFormat.BitsPerSample / 8) * 20) / 1000;
                            //this.bufferedWaveProvider.BufferedDuration = 250;
                        }
                        int decompressed = decompressor.DecompressFrame(frame, buffer, 0);
                        //Debug.WriteLine(String.Format("Decompressed a frame {0}", decompressed));
                        mybufferedWaveProvider.AddSamples(buffer, 0, decompressed);

                        while (mybufferedWaveProvider.BufferedDuration.Milliseconds >= 20)
                        {
                            byte[] read = new byte[framesize];
                            mybufferedWaveProvider.Read(read, 0, framesize);
                            byteOut.Write(read, 0, framesize);
                        }                            
                    }

                } while (playbackState != StreamingPlaybackState.Stopped);
                Debug.WriteLine("Exiting");
                // was doing this in a finally block, but for some reason
                // we are hanging on response stream .Dispose so never get there
                decompressor.Dispose();
            }
        }
        finally
        {
            if (decompressor != null)
            {
                decompressor.Dispose();
            }
        }
    }

1 个答案:

答案 0 :(得分:0)

好的,我发现了问题。我将shoutcast元数据包含在MP3Frame中。 请参阅评论&#34;这里我收集MP3框架的字样&#34;找到正确的点来获取没有流式元数据的MP3帧。

以下代码在没有音频工件的情况下运行:

private void SHOUTcastReceiverThread()
    {
        //-*- String server = "http://216.235.80.18:8285/stream"; 
        //String serverPath = "/";
        //String destPath = "C:\\temp\\";           // destination path for saved songs

        HttpWebRequest request = null; // web request
        HttpWebResponse response = null; // web response


        int metaInt = 0; // blocksize of mp3 data
        int count = 0; // byte counter
        int metadataLength = 0; // length of metadata header

        string metadataHeader = ""; // metadata header that contains the actual songtitle
        string oldMetadataHeader = null; // previous metadata header, to compare with new header and find next song

        //CircularQueueStream framestream = new CircularQueueStream(2048);
        QueueStream framestream = new QueueStream();
        framestream.Position = 0;

        bool bNewSong = false;

        byte[]  buffer = new byte[512]; // receive buffer

        byte[] dec_buffer = new byte[decSIZE];

        Mp3Frame frame;
        IMp3FrameDecompressor decompressor = null;

        Stream socketStream = null; // input stream on the web request

        // create web request
        request = (HttpWebRequest)WebRequest.Create(server);

        // clear old request header and build own header to receive ICY-metadata
        request.Headers.Clear();
        request.Headers.Add("GET", serverPath + " HTTP/1.0");
        request.Headers.Add("Icy-MetaData", "1"); // needed to receive metadata informations
        request.UserAgent = "WinampMPEG/5.09";

        // execute request
        try
        {
            response = (HttpWebResponse)request.GetResponse();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
            return;
        }

        // read blocksize to find metadata header
        metaInt = Convert.ToInt32(response.GetResponseHeader("icy-metaint"));

        try
        {
            // open stream on response
            socketStream = response.GetResponseStream();
            var readFullyStream = new ReadFullyStream(socketStream);
            frame = null;
            // rip stream in an endless loop
            do
            {
                if (IsBufferNearlyFull)
                {
                    Debug.WriteLine("Buffer getting full, taking a break");
                    Thread.Sleep(500);
                    frame = null;
                }
                else
                {
                    int bufLen = readFullyStream.Read(buffer, 0, buffer.Length);

                    try
                    {
                        if (framestream.CanRead && framestream.Length > 512)
                            frame = Mp3Frame.LoadFromStream(framestream);
                        else
                            frame = null;
                    }
                    catch (Exception ex)
                    {
                        frame = null;
                    }

                    if (bufLen < 0)
                    {
                        Debug.WriteLine("Buffer error 1: exit.");
                        return;
                    }

                    // processing RAW data
                    for (int i = 0; i < bufLen; i++)
                    {
                        // if there is a header, the 'headerLength' would be set to a value != 0. Then we save the header to a string
                        if (metadataLength != 0)
                        {
                            metadataHeader += Convert.ToChar(buffer[i]);
                            metadataLength--;
                            if (metadataLength == 0) // all metadata informations were written to the 'metadataHeader' string
                            {
                                string fileName = "";
                                string fileNameRaw = "";

                                // if songtitle changes, create a new file
                                if (!metadataHeader.Equals(oldMetadataHeader))
                                {
                                    // flush and close old byteOut stream
                                    if (byteOut != null)
                                    {
                                        byteOut.Flush();
                                        byteOut.Close();
                                        byteOut = null;
                                    }

                                    if (byteOutRaw != null)
                                    {
                                        byteOutRaw.Flush();
                                        byteOutRaw.Close();
                                        byteOutRaw = null;
                                    }
                                    timeStart = timeEnd;

                                    // extract songtitle from metadata header. Trim was needed, because some stations don't trim the songtitle
                                    //fileName = Regex.Match(metadataHeader, "(StreamTitle=')(.*)(';StreamUrl)").Groups[2].Value.Trim();
                                    fileName = Regex.Match(metadataHeader, "(StreamTitle=')(.*)(';)").Groups[2].Value.Trim();

                                    // write new songtitle to console for information                                        
                                    if (fileName.Length == 0)                                        
                                        fileName = "shoutcast_test";

                                    fileNameRaw = fileName + "_raw";

                                    framestream.reSetPosition();

                                    SongChanged(this, metadataHeader);
                                    bNewSong = true;

                                    // create new file with the songtitle from header and set a stream on this file
                                    timeEnd = DateTime.Now;
                                    if (bWrite_to_file)
                                    {
                                        byteOut = createNewFile(destPath, fileName, "mp3");
                                        byteOutRaw = createNewFile(destPath, fileNameRaw, "raw");
                                    }

                                    timediff = timeEnd - timeStart;
                                    // save new header to 'oldMetadataHeader' string, to compare if there's a new song starting
                                    oldMetadataHeader = metadataHeader;
                                }
                                metadataHeader = "";
                            }
                        }
                        else // write mp3 data to file or extract metadata headerlength
                        {
                            if (count++ < metaInt) // write bytes to filestream
                            {
                               //HERE I COLLECT THE BYTES OF THE MP3 FRAME
                                framestream.Write(buffer, i, 1);
                            }
                            else // get headerlength from lengthbyte and multiply by 16 to get correct headerlength
                            {
                                metadataLength = Convert.ToInt32(buffer[i]) * 16;
                                count = 0;
                            }
                        }
                    }//for   

                    if (bNewSong)
                    {
                        decompressor = createDecompressor(frame);
                        bNewSong = false;
                    }

                    if (frame != null && decompressor != null)
                    {
                        framedec(decompressor, frame);
                    }

                    // fine Processing dati RAW
                }//Buffer is not full

                SHOUTcastStatusProcess();

            } while (playbackState != StreamingPlaybackState.Stopped);

        } //try
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            if (byteOut != null)
                byteOut.Close();

            if (socketStream != null)
                socketStream.Close();

            if (decompressor != null)
            {
                decompressor.Dispose();
                decompressor = null;
            }
            if (null != request)
                request.Abort();

            if (null != framestream)
                framestream.Dispose();

            if (null != bufferedWaveProvider)
                bufferedWaveProvider.ClearBuffer();

            //if (null != bufferedWaveProviderOut)
            //    bufferedWaveProviderOut.ClearBuffer();

            if (null != mono16bitFsinStream)
            {
                mono16bitFsinStream.Close();
                mono16bitFsinStream.Dispose();
            }
            if (null != middleStream2)
            {
                middleStream2.Close();
                middleStream2.Dispose();
            }
            if (null != resampler)
                resampler.Dispose();
        }
    }


public class QueueStream : MemoryStream
{
    long ReadPosition = 0;
    long WritePosition = 0;

    public QueueStream() : base() { }

    public override int Read(byte[] buffer, int offset, int count)
    {
        Position = ReadPosition;

        var temp = base.Read(buffer, offset, count);            

        ReadPosition = Position;

        return temp;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        Position = WritePosition;

        base.Write(buffer, offset, count);

        WritePosition = Position;
    }

    public void reSetPosition()
    {
        WritePosition = 0;
        ReadPosition = 0;
        Position = 0;
    }
}


    private void framedec(IMp3FrameDecompressor decompressor, Mp3Frame frame)
    {
        int Ndecoded_samples = 0;
        byte[] dec_buffer = new byte[decSIZE];

        Ndecoded_samples = decompressor.DecompressFrame(frame, dec_buffer, 0);
        bufferedWaveProvider.AddSamples(dec_buffer, 0, Ndecoded_samples);

        NBufferedSamples += Ndecoded_samples;

        brcnt_in.incSamples(Ndecoded_samples);

        if (Ndecoded_samples > decSIZE)
        {
            Debug.WriteLine(String.Format("Too many samples {0}", Ndecoded_samples));
        }

        if (byteOut != null)
            byteOut.Write(frame.RawData, 0, frame.RawData.Length);
        if (byteOutRaw != null) // as long as we don't have a songtitle, we don't open a new file and don't write any bytes            
            byteOutRaw.Write(dec_buffer, 0, Ndecoded_samples);


        frame = null;
    }

    private IMp3FrameDecompressor createDecompressor(Mp3Frame frame)
    {
        IMp3FrameDecompressor dec = null;
        if (frame != null)
        {
            // don't think these details matter too much - just help ACM select the right codec
            // however, the buffered provider doesn't know what sample rate it is working at
            // until we have a frame

            WaveFormat srcwaveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, frame.FrameLength, frame.BitRate);
            dec = new AcmMp3FrameDecompressor(srcwaveFormat);

            bufferedWaveProvider = new BufferedWaveProvider(dec.OutputFormat);// decompressor.OutputFormat
            bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(400); // allow us to get well ahead of ourselves


            // ------------------------------------------------
            //Create an intermediate format with same sampling rate, 16 bit, mono  
            middlewavformat = new WaveFormat(dec.OutputFormat.SampleRate, 16, 1);
            outwavFormat = new WaveFormat(Fs_out, 16, 1);


            // wave16ToFloat = new Wave16ToFloatProvider(provider); // I have tried with and without this converter.
            wpws = new WaveProviderToWaveStream(bufferedWaveProvider);
            //Check middlewavformat.Encoding == WaveFormatEncoding.Pcm;
            mono16bitFsinStream = new WaveFormatConversionStream(middlewavformat, wpws);                        
            middleStream2 = new BlockAlignReductionStream(mono16bitFsinStream);
            resampler = new MediaFoundationResampler(middleStream2, outwavFormat);                
        }
        return dec;
    }