C# 参考 cool edit 样式, 绘制音频波形图

由于项目需要音频波形显示,所以仿照 Cool Edit 实现一个简易的音频播放器。

部分代码参考:GitHub - naudio/NAudio.WaveFormRenderer: Simple utility to render waveforms of audio files in various styles using System.Drawing 

效果图

*(空格键播放选中区域)

 核心代码

using System;
using System.Drawing;
using System.Windows.Forms;

namespace JAudioWave
{
    public partial class JAudioWave : UserControl
    {
        private Pen gridPen;
        private Pen topPeakPen;
        private Pen bottomPeakPen;
        private MaxPeakProvider _maxPeakProvider;
        private WaveAudioRendererSettings _settings;

        private bool isMouseDown = false;
        private bool isStartMove = false;
        private int moveStartX = 0; // 每一次滑动蕲艾
        private int gStartX = 0; // 音频区域起点
        private int gEndX = 0; // 音频区域终点
        private int direction = 0; // 0:反方向 1:正方向

        public Action<int, int> OnSelectedAudioArea;

        public JAudioWave()
        {
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);// 不闪烁 支持双缓存
            InitializeComponent();
            init();
        }

        private void init()
        {
            initPen();
            TopHeight = 150;
            BottomHeight = 150;
            _maxPeakProvider = new MaxPeakProvider();

        }

        private void initPen()
        {
            gridPen = new Pen(Color.FromArgb(18, 59, 40));
            topPeakPen = new Pen(Color.FromArgb(75, 243, 167));
            bottomPeakPen = new Pen(Color.FromArgb(75, 243, 167));
        }

        private WaveAudioRendererSettings getSettings()
        {
            if (_settings != null)
                return _settings;

            _settings = new WaveAudioRendererSettings();
            _settings.TopHeight = TopHeight;
            _settings.BottomHeight = BottomHeight;
            _settings.Width = this.Width;
            _settings.TopPeakPen = topPeakPen;
            _settings.BottomPeakPen = bottomPeakPen;
            return _settings;
        }


        protected override void OnPaint(PaintEventArgs e)
        {
            Console.WriteLine("OnPaint");
            base.OnPaint(e);
            draw(e.Graphics);
            //_maxPeakProvider.ResetPeakInfoStatus();
        }


        private void draw(Graphics g)
        {
            // 背景
            WaveAudioRendererSettings settings = getSettings();
            g.FillRectangle(settings.BackgroundBrush, 0, 0, this.Width, this.Height);
            // 网格
            drawGraid(g, this.Width, this.Height);
            // 波形
            var midPoint = settings.TopHeight;
            int x = 0;
            WavePeakInfo currentPeak = _maxPeakProvider.GetPeakInfo(x);

            if (currentPeak == null)
                return;

            while (x < settings.Width)
            {
                WavePeakInfo nextPeak = _maxPeakProvider.GetPeakInfo(x);

                for (int n = 0; n < settings.PixelsPerPeak; n++)
                {
                    float lineHeight = settings.TopHeight * currentPeak.Max;
                    g.DrawLine(settings.TopPeakPen, x, midPoint, x, midPoint - lineHeight);
                    lineHeight = settings.BottomHeight * currentPeak.Min;
                    g.DrawLine(settings.BottomPeakPen, x, midPoint, x, midPoint - lineHeight);
                    x++;
                }

                for (int n = 0; n < settings.SpacerPixels; n++)
                {
                    float max = Math.Min(currentPeak.Max, nextPeak.Max);
                    float min = Math.Max(currentPeak.Min, nextPeak.Min);

                    float lineHeight = settings.TopHeight * max;
                    g.DrawLine(settings.TopSpacerPen, x, midPoint, x, midPoint - lineHeight);
                    lineHeight = settings.BottomHeight * min;
                    g.DrawLine(settings.BottomSpacerPen, x, midPoint, x, midPoint - lineHeight);
                    x++;
                }
                currentPeak = nextPeak;
            }
            drawRect(g, gEndX - gStartX, this.Height, gStartX, gEndX);
        }

        public void drawGraid(Graphics g, int width, int height)
        {
            //垂直
            for (int i = 0; i < width;)
            {
                g.DrawLine(gridPen, new Point(i, 0), new Point(i, width));
                i += 45;
            }
            //水平
            for (int j = 0; j < height;)
            {
                g.DrawLine(gridPen, new Point(0, j), new Point(width, j));
                j += 45;
            }

        }

        private void drawSelectRect(int startX, int endX)
        {

            Graphics g = Graphics.FromHwnd(this.Handle);
            drawRect(g, endX - startX, this.Height, startX, endX);
        }

        private void drawRect(Graphics g, int width, int height, int startX, int endX)
        {
            if (direction == 0) // 反方向
            {
                if (startX < endX)
                {
                    #region 反向
                    int diff = endX - startX;
                    WaveAudioRendererSettings settings = getSettings();
                    g.FillRectangle(settings.BackgroundBrush, startX, 0, diff, this.Height);

                    int num = startX / 45;
                    int a = startX % 45;
                    int start = a == 0 ? num * 45 : num * 45 + 45;

                    //Pen myPen = new Pen(Color.FromArgb(10, 215, 221, 224));
                    Pen myPen = new Pen(Color.DarkBlue);


                    //垂直
                    for (int i = start; i < endX;)
                    {
                        g.DrawLine(gridPen, new Point(i, 0), new Point(i, height));
                        i += 45;
                    }

                    if (diff > 0)
                    {
                        //水平
                        for (int j = 0; j < height;)
                        {
                            g.DrawLine(gridPen, new Point(startX, j), new Point(endX - 1, j));
                            j += 45;
                        }
                    }

                    //水平
                    //for (int j = 0; j < height;)
                    //{
                    //    g.DrawLine(gridPen, new Point(startX, j), new Point(endX, j));
                    //    j += 45;
                    //}


                    WavePeakInfo currentPeak = _maxPeakProvider.GetPeakInfo(startX);

                    if (currentPeak == null)
                        return;

                    var midPoint = settings.TopHeight;
                    for (int x = startX; x < endX; x++)
                    {
                        WavePeakInfo nextPeak = _maxPeakProvider.GetPeakInfo(x);
                        Pen _p = new Pen(Color.FromArgb(18, 59, 40));
                        if (nextPeak.IsSelected)
                        {
                            _p = topPeakPen;
                        }
                        else
                        {
                            _p = new Pen(Color.FromArgb(18, 59, 40));
                        }
                        var lineHeight = settings.TopHeight * currentPeak.Max;
                        g.DrawLine(topPeakPen, x, midPoint, x, midPoint - lineHeight);
                        lineHeight = settings.BottomHeight * currentPeak.Min;
                        g.DrawLine(topPeakPen, x, midPoint, x, midPoint - lineHeight);

                        //_maxPeakProvider.UpdatePeakInfoStatus(x);
                        currentPeak = nextPeak;
                    }
                    #endregion
                }
                else
                {
                    int diff = startX - endX;
                    Rectangle rect = new Rectangle();
                    rect.Width = diff;
                    rect.Height = height;
                    rect.Location = new Point(endX, 0);
                    SolidBrush mysbrush1 = new SolidBrush(Color.FromArgb(180, 238, 238, 247));
                    g.FillRectangle(mysbrush1, rect);
                }

            }
            else // 正方向
            {
                if (startX > endX)
                {
                    #region 反向
                    int diff = startX - endX;
                    WaveAudioRendererSettings settings = getSettings();
                    g.FillRectangle(settings.BackgroundBrush, endX, 0, diff, this.Height);

                    int num = endX / 45;
                    int a = endX % 45;
                    int start = a == 0 ? num * 45 : num * 45 + 45;

                    Pen myPen = new Pen(Color.DarkBlue);

                    //垂直
                    for (int i = start; i < startX;)
                    {
                        g.DrawLine(gridPen, new Point(i, 0), new Point(i, height));
                        i += 45;
                    }

                    if (diff > 0)
                    {
                        //水平
                        for (int j = 0; j < height;)
                        {
                            g.DrawLine(gridPen, new Point(endX, j), new Point(startX - 1, j));
                            j += 45;
                        }
                    }

                    WavePeakInfo currentPeak = _maxPeakProvider.GetPeakInfo(endX);

                    if (currentPeak == null)
                        return;

                    var midPoint = settings.TopHeight;
                    for (int x = endX; x < startX; x++)
                    {
                        WavePeakInfo nextPeak = _maxPeakProvider.GetPeakInfo(x);
                        Pen _p = new Pen(Color.FromArgb(18, 59, 40));
                        if (nextPeak.IsSelected)
                        {
                            _p = topPeakPen;
                        }
                        else
                        {
                            _p = new Pen(Color.FromArgb(18, 59, 40));
                        }
                        var lineHeight = settings.TopHeight * currentPeak.Max;
                        g.DrawLine(topPeakPen, x, midPoint, x, midPoint - lineHeight);
                        lineHeight = settings.BottomHeight * currentPeak.Min;
                        g.DrawLine(topPeakPen, x, midPoint, x, midPoint - lineHeight);

                        //_maxPeakProvider.UpdatePeakInfoStatus(x);
                        currentPeak = nextPeak;
                    }
                    #endregion
                }
                else
                {
                    int diff = endX - startX;
                    Rectangle rect = new Rectangle();
                    rect.Width = diff;
                    rect.Height = height;
                    rect.Location = new Point(startX, 0);
                    SolidBrush mysbrush1 = new SolidBrush(Color.FromArgb(180, 238, 238, 247));
                    g.FillRectangle(mysbrush1, rect);
                }
            }
        }

        public void refresh()
        {
            gStartX = 0;
            gEndX = 0;
            this.Refresh();
        }

        public int TopHeight { get; set; }

        public int BottomHeight { get; set; }

        public int Bit { get; set; }

        private void JAudioWave_MouseDown(object sender, MouseEventArgs e)
        {
            refresh();
            isMouseDown = true;
            string x = e.X.ToString(); //x坐标
            string y = e.Y.ToString(); //Y坐标
            moveStartX = e.X;
            gStartX = e.X;
            isStartMove = true;
        }

        private void JAudioWave_MouseMove(object sender, MouseEventArgs e)
        {
            if (!isMouseDown)
                return;
            string x = e.X.ToString(); //x坐标
            string y = e.Y.ToString(); //Y坐标

            if (isStartMove)
            {
                direction = moveStartX > e.X ? 0 : 1;
                isStartMove = false;
            }
            drawSelectRect(moveStartX, e.X);
            moveStartX = e.X;
        }

        private void JAudioWave_MouseUp(object sender, MouseEventArgs e)
        {
            if (!isMouseDown)
                return;
            isMouseDown = false;
            gEndX = e.X;
            if (OnSelectedAudioArea != null && gStartX != gEndX)
            {
                int sx = gStartX;
                int ex = gEndX;
                if (direction == 0)
                {
                    sx = gEndX;
                    ex = gStartX;
                }
                else
                {
                    sx = gStartX;
                    ex = gEndX;
                }

                OnSelectedAudioArea.BeginInvoke(_maxPeakProvider.GetSize(sx), _maxPeakProvider.GetSize(ex), null, null);
            }
        }

        private void JAudioWave_Load(object sender, EventArgs e)
        {
        }

        // ============================ 对外接口 ===========================================

        public void LoadAudio(string file, int bitsPerSample)
        {
            gStartX = 0;
            gEndX = 0;
            WaveAudioRendererSettings settings = getSettings();
            _maxPeakProvider.Init(bitsPerSample, file);
            _maxPeakProvider.Load(settings);
        }

    }

}

功能

  1. 加载音频,显示出音频波形
  2. 左右滑动鼠标选择区域,空格键播放选中区域音频。

TODO

  1. 来回滑动, 波形存在细微变动,暂未找到原因。
  2. 滑动过快, 波形容易造成短暂异常。
  3. 音频播放通过系统api接口简单实现(将选中区域保存本地,来进行播放),有待改善。

*** 以上1.2 问题, 如有大侠解决,望指导一二,不胜感激。 

 原版 cool edit 效果图

**  哪位大侠能实现这个效果,也希望能共享下。不胜感激。(可左右来回滑动来选择区域,而且不卡顿.)

完整demo  仿照CoolEdit样式,绘制音频波形图-互联网文档类资源-CSDN下载


  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值