C# NAudio rendering a waveform ASP.net without DMO or ACM

一笑奈何 提交于 2019-12-25 07:28:48

问题


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.

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

回答1:


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

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;


来源:https://stackoverflow.com/questions/39398668/c-sharp-naudio-rendering-a-waveform-asp-net-without-dmo-or-acm

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!