0

我试图在 Azure 网站(没有安装 ACM 或 DMO 编解码器)上使用 ASP.net 绘制波形,所以我不得不使用 NLayer 来读取 mp3 文件。我下面的代码与常规的 DmoMp3FrameDecompressor 完美配合,但是当我使用 NLayer 解压缩器时,它就不行了。

也许 NLayer 解压器的格式是 32bit Float 而不是 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;
    }
}
4

1 回答 1

3

终于想通了。感谢@MarkHeath 的评论和建议(以及构建令人惊叹的 NAudio / NLayer 库)!

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

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;
于 2016-09-08T20:52:52.953 回答