0

Azure Web サイト (ACM または DMO コーデックがインストールされていない) で ASP.net を使用して波形を描画しようとしているため、NLayer を使用して mp3 ファイルを読み取る必要がありました。以下のコードは、通常の DmoMp3FrameDecompressor では完全に機能しますが、NLayer デコンプレッサを使用すると機能しません。

NLayer デコンプレッサのフォーマットは、16 ビット PCM ではなく 32 ビット Float である可能性があります。

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

最後にそれを理解しました。コメントと提案 (そして素晴らしい NAudio / NLayer ライブラリの構築) について @MarkHeath に感謝します!

重要なのは、 にWaveFloatTo16ProviderLengthアトリビュートがないため、ピクセルあたりのサンプル数を計算できないため、2 つのループが必要になることです。1 つは個々のサンプルをすべて順次読み取り、次に別のサンプルをピクセルごとにグループ化し、最大振幅を計算します。最後のループは、値をピクセル位置にマッピングし、画像に描画します。コードが必要ない場合はAutoFit、2 番目と 3 番目のループをマージできます。

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 に答える