C#NAudio在没有DMO或ACM的情况下渲染波形ASP.net [英] C# NAudio rendering a waveform ASP.net without DMO or ACM

查看:121
本文介绍了C#NAudio在没有DMO或ACM的情况下渲染波形ASP.net的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在Azure网站(未安装ACM或DMO编解码器)上使用ASP.net绘制波形,因此我不得不使用NLayer读取mp3文件.我下面的代码与常规DmoMp3FrameDecompressor完美搭配,但是当我使用NLayer解压缩器时却不能.

I'm trying to draw out a waveform using ASP.net on an Azure Website (which doesn't have the ACM or DMO codecs installed), so I had to use NLayer to read the mp3 file. The code I have below works perfectly with the regular DmoMp3FrameDecompressor, but when I use the NLayer decompressor it doesn't.

也许NLayer解压缩器的格式是32位Float,而不是16位PCM.

Maybe the format of the NLayer decompressor is 32bit Float and not 16bit PCM.

byte[] data = new WebClient().DownloadData(URL);

int maxAmplitude = 0;

short[,] dataArray = new short[Width, 2];

//using (Mp3FileReader wavestream = new Mp3FileReader(new MemoryStream(data), wf => new DmoMp3FrameDecompressor(wf)))

using (Mp3FileReader wavestream = new Mp3FileReader(new MemoryStream(data), new Mp3FileReader.FrameDecompressorBuilder(waveFormat => new NLayer.NAudioSupport.Mp3FrameDecompressor(waveFormat))))
{
    WaveChannel32 channelStream = new WaveChannel32(wavestream);

    int bytesPerSample = (wavestream.WaveFormat.BitsPerSample / 8) * channelStream.WaveFormat.Channels;

    wavestream.Position = 0;

    long lenSamples = wavestream.Length / bytesPerSample;

    int samplesPerPixel = (int)(lenSamples / Width);

    int bytesRead1;
    byte[] waveData1 = new byte[samplesPerPixel * bytesPerSample];

    // First get all the data

    for (int x = 0; x < Width; x++)
    {
        short low = 0;
        short high = 0;
        bytesRead1 = wavestream.Read(waveData1, 0, samplesPerPixel * bytesPerSample);
        if (bytesRead1 == 0)
            break;
        for (int n = 0; n < bytesRead1; n += 2)
        {
            short sample = BitConverter.ToInt16(waveData1, n);
            if (sample < low) low = sample;
            if (sample > high) high = sample;
        }

        if (-low > maxAmplitude) maxAmplitude = -low;
        if (high > maxAmplitude) maxAmplitude = high;

        dataArray[x, 0] = low;
        dataArray[x, 1] = high;
    }
}

推荐答案

最后弄清楚了.感谢@MarkHeath提出您的意见和建议(以及构建了令人惊叹的NAudio/NLayer库)!

Finally figured it out. Thanks @MarkHeath for your comments and suggestions (and for building the amazing NAudio / NLayer libraries)!

关键是WaveFloatTo16Provider没有Length属性,因此您无法计算每个像素的样本数,因此需要两个循环.一个依次读取所有单个样本,然后另一个按每个像素对样本进行分组,并计算最大振幅.然后,最后的循环将这些值映射到像素位置,并将它们绘制到图像上.如果不需要AutoFit代码,则可以合并第二个和第三个循环.

The key is that the WaveFloatTo16Provider doesn't have a Length attribute, so you can't compute the number of samples per pixel, so you need to have two loops. One which sequentially reads all the individual samples and then another which then groups the samples per pixel, and calculates the max amplitude. The final loop then maps the values to pixel positions and draws them to an image. If you don't need the AutoFit code, then you can merge the second and third loops.

Bitmap bmp = new Bitmap(Width, Height);

using (Graphics g = Graphics.FromImage(bmp))
{
    g.Clear(Color.White);
    Pen pen1 = new Pen(Color.Gray);

    string hexValue = "#" + sColor;
    Color colour1 = System.Drawing.ColorTranslator.FromHtml(hexValue);
    pen1.Color = colour1;

    int maxAmplitude = 0;

    short[,] dataArray = new short[Width, 2];

    using (Mp3FileReader wavestreamFloat = new Mp3FileReader(
           new MemoryStream(new WebClient().DownloadData(URL)), 
           new Mp3FileReader.FrameDecompressorBuilder(
                 waveFormat => new NLayer.NAudioSupport.Mp3FrameDecompressorwaveFormat))))
    {
        IWaveProvider stream16 = new WaveFloatTo16Provider(wavestreamFloat);

        int bytesPerSample = (stream16.WaveFormat.BitsPerSample / 8) * stream16.WaveFormat.Channels;

        int bytesRead = 0;
        byte[] buffer = new byte[8192];

        List<short> rawDataArray = new List<short>();

        do
        {
            bytesRead = stream16.Read(buffer, 0, buffer.Length);

            for (int n = 0; n < bytesRead; n += bytesPerSample)
            {
                short sample = BitConverter.ToInt16(buffer, n);
                rawDataArray.Add(sample);
            }

        } while (bytesRead != 0);

        // Now that we have all the samples

        long lenSamples = rawDataArray.Count;

        int samplesPerPixel = (int)(lenSamples / Width);

        int nCounter = 0;

        for (int x = 0; x < Width; x++)
        {
            short low = 0;
            short high = 0;

            for (int n = 0; n < samplesPerPixel; n++)
            {
                short sample = rawDataArray[nCounter++];
                if (sample < low) low = sample;
                if (sample > high) high = sample;
            }

            if (-low > maxAmplitude) maxAmplitude = -low;
            if (high > maxAmplitude) maxAmplitude = high;

            dataArray[x, 0] = low;
            dataArray[x, 1] = high;
        }

        // Now lay it out on the image. This is where we resize it to AutoFit.

        for (int x = 0; x < Width; x++)
        {
            short low = dataArray[x, 0];
            short high = dataArray[x, 1];

            if (AutoFit)
            {
                low = (short)((int)low * (int)short.MaxValue / (int)maxAmplitude);
                high = (short)((int)high * (int)short.MaxValue / (int)maxAmplitude);
            }

            float lowPercent = ((((float)low) - short.MinValue) / ushort.MaxValue);
            float highPercent = ((((float)high) - short.MinValue) / ushort.MaxValue);
            float lowValue = Height * lowPercent;
            float highValue = Height * highPercent;

            g.DrawLine(pen1, x, lowValue, x, highValue);
        }
        g.Flush();
    }
}

return bmp;

这篇关于C#NAudio在没有DMO或ACM的情况下渲染波形ASP.net的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆