첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestSolution.zip
0.02MB

[TestLibrary 프로젝트]

▶ PeakInfo.cs

namespace TestLibrary
{
    /// <summary>
    /// 피크 정보
    /// </summary>
    public class PeakInfo
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 최소값 - Minimum

        /// <summary>
        /// 최소값
        /// </summary>
        public float Minimum { get; private set; }

        #endregion
        #region 최대값 - Maximum

        /// <summary>
        /// 최대값
        /// </summary>
        public float Maximum { get; private set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - PeakInfo(minimum, maximum)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="minimum">최소값</param>
        /// <param name="maximum">최대값</param>
        public PeakInfo(float minimum, float maximum)
        {
            Maximum = maximum;
            Minimum = minimum;
        }

        #endregion
    }
}

 

728x90

 

▶ IPeakProvider.cs

using NAudio.Wave;

namespace TestLibrary
{
    /// <summary>
    /// 피크 공급자 인터페이스
    /// </summary>
    public interface IPeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        #region 초기화하기 - Initialize(provider, sampleCountPerPixel)

        /// <summary>
        /// 초기화하기
        /// </summary>
        /// <param name="provider">샘플 공급자 인터페이스</param>
        /// <param name="sampleCountPerPixel">픽셀당 샘플 수</param>
        void Initialize(ISampleProvider provider, int sampleCountPerPixel);

        #endregion
        #region 다음 피크 정보 구하기 - GetNextPeakInfo()

        /// <summary>
        /// 다음 피크 정보 구하기
        /// </summary>
        /// <returns>다음 피크 정보</returns>
        PeakInfo GetNextPeakInfo();

        #endregion
    }
}

 

반응형

 

▶ PeakProvider.cs

using NAudio.Wave;

namespace TestLibrary
{
    /// <summary>
    /// 피크 공급자
    /// </summary>
    public abstract class PeakProvider : IPeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 공급자 - Provider

        /// <summary>
        /// 공급자
        /// </summary>
        protected ISampleProvider Provider { get; private set; }

        #endregion
        #region 피크당 샘플 수 - SampleCountPerPeak

        /// <summary>
        /// 피크당 샘플 수
        /// </summary>
        protected int SampleCountPerPeak { get; private set; }

        #endregion
        #region 읽기 버퍼 - ReadBuffer

        /// <summary>
        /// 읽기 버퍼
        /// </summary>
        protected float[] ReadBuffer { get; private set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 초기화하기 - Initialize(provider, sampleCountPerPeak)

        /// <summary>
        /// 초기화하기
        /// </summary>
        /// <param name="provider">샘플 공급자</param>
        /// <param name="sampleCountPerPeak">피크당 샘플 수</param>
        public void Initialize(ISampleProvider provider, int sampleCountPerPeak)
        {
            Provider           = provider;
            SampleCountPerPeak = sampleCountPerPeak;
            ReadBuffer         = new float[sampleCountPerPeak];
        }

        #endregion
        #region 다음 피크 정보 구하기 - GetNextPeakInfo()

        /// <summary>
        /// 다음 피크 정보 구하기
        /// </summary>
        /// <returns>다음 피크 정보</returns>
        public abstract PeakInfo GetNextPeakInfo();

        #endregion
    }
}

 

300x250

 

▶ AveragePeakProvider.cs

using System;
using System.Linq;

namespace TestLibrary
{
    /// <summary>
    /// 평균 피크 공급자
    /// </summary>
    public class AveragePeakProvider : PeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 스케일
        /// </summary>
        private readonly float scale;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - AveragePeakProvider(scale)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="scale">스케일</param>
        public AveragePeakProvider(float scale)
        {
            this.scale = scale;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 다음 피크 구하기 - GetNextPeak()

        /// <summary>
        /// 다음 피크 구하기
        /// </summary>
        /// <returns>다음 피크</returns>
        public override PeakInfo GetNextPeakInfo()
        {
            int sampleCountRead = Provider.Read(ReadBuffer, 0, ReadBuffer.Length);

            float summary = (sampleCountRead == 0) ? 0 : ReadBuffer.Take(sampleCountRead).Select(sample => Math.Abs(sample)).Sum();

            float average = summary / sampleCountRead;
            
            return new PeakInfo(average * (0 - scale), average * scale);
        }

        #endregion
    }
}

 

▶ DecibelPeakProvider.cs

using System;

using NAudio.Wave;

namespace TestLibrary
{
    /// <summary>
    /// 데시벨 피크 공급자
    /// </summary>
    public class DecibelPeakProvider : IPeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 소스 공급자
        /// </summary>
        private readonly IPeakProvider sourceProvider;

        /// <summary>
        /// 동적 범위
        /// </summary>
        private readonly double dynamicRange;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - DecibelPeakProvider(sourceProvider, dynamicRange)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="sourceProvider">소스 공급자</param>
        /// <param name="dynamicRange">동적 범위</param>
        public DecibelPeakProvider(IPeakProvider sourceProvider, double dynamicRange)
        {
            this.sourceProvider = sourceProvider;
            this.dynamicRange   = dynamicRange;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 초기화하기 - Initialize(provider, sampleCountPerPixel)

        /// <summary>
        /// 초기화하기
        /// </summary>
        /// <param name="provider">샘플 공급자 인터페이스</param>
        /// <param name="sampleCountPerPixel">픽셀당 샘플 수</param>
        public void Initialize(ISampleProvider provider, int sampleCountPerPixel)
        {
            throw new NotImplementedException();
        }

        #endregion
        #region 다음 피크 정보 구하기 - GetNextPeakInfo()

        /// <summary>
        /// 다음 피크 정보 구하기
        /// </summary>
        /// <returns>다음 피크 정보</returns>
        public PeakInfo GetNextPeakInfo()
        {
            PeakInfo peakInfo = this.sourceProvider.GetNextPeakInfo();

            double maximumDecibel = 20 * Math.Log10(peakInfo.Maximum);

            if(maximumDecibel < 0 - dynamicRange)
            {
                maximumDecibel = 0 - dynamicRange;
            }

            float linear = (float)((dynamicRange + maximumDecibel) / dynamicRange);

            return new PeakInfo(0 - linear, linear);
        }

        #endregion
    }
}

 

▶ MaximumPeakProvider.cs

using System.Linq;

namespace TestLibrary
{
    /// <summary>
    /// 최대 피크 공급자
    /// </summary>
    public class MaximumPeakProvider : PeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 다음 피크 정보 구하기 - GetNextPeakInfo()

        /// <summary>
        /// 다음 피크 정보 구하기
        /// </summary>
        /// <returns>다음 피크 정보</returns>
        public override PeakInfo GetNextPeakInfo()
        {
            int sampleCountRead = Provider.Read(ReadBuffer,0,ReadBuffer.Length);

            float maximum = (sampleCountRead == 0) ? 0 : ReadBuffer.Take(sampleCountRead).Max();
            float minimum = (sampleCountRead == 0) ? 0 : ReadBuffer.Take(sampleCountRead).Min();

            return new PeakInfo(minimum, maximum);
        }

        #endregion
    }
}

 

▶ RMSPeakProvider.cs

using System;

namespace TestLibrary
{
    /// <summary>
    /// RMS 피크 공급자
    /// </summary>
    public class RMSPeakProvider : PeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 블럭 크기
        /// </summary>
        private readonly int blockSize;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - RMSPeakProvider(blockSize)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="blockSize">블럭 크기</param>
        public RMSPeakProvider(int blockSize)
        {
            this.blockSize = blockSize;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 다음 피크 정보 구하기 - GetNextPeakInfo()

        /// <summary>
        /// 다음 피크 정보 구하기
        /// </summary>
        /// <returns>다음 피크 정보</returns>
        public override PeakInfo GetNextPeakInfo()
        {
            int sampleCountRead = Provider.Read(ReadBuffer, 0, ReadBuffer.Length);

            float maximum = 0.0f;

            for(int x = 0; x < sampleCountRead; x += blockSize)
            {
                double total = 0.0;

                for(int y = 0; y < blockSize && x + y < sampleCountRead; y++)
                {
                    total += ReadBuffer[x + y] * ReadBuffer[x + y];
                }

                float rms = (float) Math.Sqrt(total / blockSize);

                maximum = Math.Max(maximum, rms);
            }

            return new PeakInfo(0 -maximum, maximum);
        }

        #endregion
    }
}

 

▶ SamplingPeakProvider.cs

using System;

namespace TestLibrary
{
    /// <summary>
    /// 샘플링 피크 공급자
    /// </summary>
    public class SamplingPeakProvider : PeakProvider
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 샘플 간격
        /// </summary>
        private readonly int sampleInterval;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - SamplingPeakProvider(sampleInterval)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="sampleInterval">샘플 간격</param>
        public SamplingPeakProvider(int sampleInterval)
        {
            this.sampleInterval = sampleInterval;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 다음 피크 정보 구하기 - GetNextPeakInfo()

        /// <summary>
        /// 다음 피크 정보 구하기
        /// </summary>
        /// <returns>다음 피크 정보</returns>
        public override PeakInfo GetNextPeakInfo()
        {
            int sampleCountRead = Provider.Read(ReadBuffer,0,ReadBuffer.Length);

            float minimum = 0.0f;
            float maximum = 0.0f;

            for(int x = 0; x < sampleCountRead; x += sampleInterval)
            {
                minimum = Math.Min(minimum, ReadBuffer[x]);
                maximum = Math.Max(maximum, ReadBuffer[x]);
            }

            return new PeakInfo(minimum,maximum);
        }

        #endregion
    }
}

 

▶ WaveFormRendererSetting.cs

using System.Drawing;
using System.Drawing.Drawing2D;

namespace TestLibrary
{
    /// <summary>
    /// 웨이브 폼 렌더링 설정
    /// </summary>
    public class WaveFormRendererSetting
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 명칭 - Name

        /// <summary>
        /// 명칭
        /// </summary>
        public string Name { get; set; }

        #endregion
        #region 너비 - Width

        /// <summary>
        /// 너비
        /// </summary>
        public int Width { get; set; }

        #endregion
        #region 상단 높이 - TopHeight

        /// <summary>
        /// 상단 높이
        /// </summary>
        public int TopHeight { get; set; }

        #endregion
        #region 하단 높이 - BottomHeight

        /// <summary>
        /// 하단 높이
        /// </summary>
        public int BottomHeight { get; set; }

        #endregion
        #region 피크당 픽셀 수 - PixelCountPerPeak

        /// <summary>
        /// 피크당 픽셀 수
        /// </summary>
        public int PixelCountPerPeak { get; set; }

        #endregion
        #region 공간 픽셀 수 - SpacerPixelCount

        /// <summary>
        /// 공간 픽셀 수
        /// </summary>
        public int SpacerPixelCount { get; set; }

        #endregion
        #region 상단 피크 펜 - TopPeakPen

        /// <summary>
        /// 상단 피크 펜
        /// </summary>
        public virtual Pen TopPeakPen { get; set; }

        #endregion
        #region 상단 공간 펜 - TopSpacerPen

        /// <summary>
        /// 상단 공간 펜
        /// </summary>
        public virtual Pen TopSpacerPen { get; set; }

        #endregion
        #region 하단 피크 펜 - BottomPeakPen

        /// <summary>
        /// 하단 피크 펜
        /// </summary>
        public virtual Pen BottomPeakPen { get; set; }

        #endregion
        #region 하단 공간 펜 - BottomSpacerPen

        /// <summary>
        /// 하단 공간 펜
        /// </summary>
        public virtual Pen BottomSpacerPen { get; set; }

        #endregion
        #region 데시벨 스케일 - DecibelScale

        /// <summary>
        /// 데시벨 스케일
        /// </summary>
        public bool DecibelScale { get; set; }

        #endregion
        #region 배경 색상 - BackgroundColor

        /// <summary>
        /// 배경 색상
        /// </summary>
        public Color BackgroundColor { get; set; }

        #endregion
        #region 배경 이미지 - BackgroundImage

        /// <summary>
        /// 배경 이미지
        /// </summary>
        public Image BackgroundImage { get; set; }

        #endregion
        #region 배경 브러시 - BackgroundBrush

        /// <summary>
        /// 배경 브러시
        /// </summary>
        public Brush BackgroundBrush
        {
            get
            {
                if(BackgroundImage == null)
                {
                    return new SolidBrush(BackgroundColor);
                }

                return new TextureBrush(BackgroundImage, WrapMode.Clamp);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 생성자 - WaveFormRendererSetting()

        /// <summary>
        /// 생성자
        /// </summary>
        protected WaveFormRendererSetting()
        {
            Width             = 800;
            TopHeight         = 50;
            BottomHeight      = 50;
            PixelCountPerPeak = 1;
            SpacerPixelCount  = 0;
            BackgroundColor   = Color.Beige;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 그라디언트 펜 생성하기 - CreateGradientPen(height, startColor, endColor)

        /// <summary>
        /// 그라디언트 펜 생성하기
        /// </summary>
        /// <param name="height">높이</param>
        /// <param name="startColor">시작 색상</param>
        /// <param name="endColor">종료 색상</param>
        /// <returns>그라디언트 펜</returns>
        protected static Pen CreateGradientPen(int height, Color startColor, Color endColor)
        {
            LinearGradientBrush brush = new LinearGradientBrush(new Point(0, 0), new Point(0, height), startColor, endColor);

            return new Pen(brush);
        }


        #endregion
    }
}

 

▶ SoundCloudBlockWaveFormSetting.cs

using System.Drawing;

namespace TestLibrary
{
    /// <summary>
    /// 사운드 클라우드 블럭 웨이브 폼 설정
    /// </summary>
    public class SoundCloudBlockWaveFormSetting : WaveFormRendererSetting
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 상단 공간 시작 색상
        /// </summary>
        private readonly Color topSpacerStartColor;

        /// <summary>
        /// 상단 펜
        /// </summary>
        private Pen topPen;

        /// <summary>
        /// 상단 공간 펜
        /// </summary>
        private Pen topSpacerPen;

        /// <summary>
        /// 하단 펜
        /// </summary>
        private Pen bottomPen;

        /// <summary>
        /// 하단 공간 펜
        /// </summary>
        private Pen bottomSpacerPen;

        /// <summary>
        /// 최근 상단 높이
        /// </summary>
        private int lastTopHeight;

        /// <summary>
        /// 최근 하단 높이
        /// </summary>
        private int lastBottomHeight;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 상단 피크 펜 - TopPeakPen

        /// <summary>
        /// 상단 피크 펜
        /// </summary>
        public override Pen TopPeakPen
        {
            get
            {
                return this.topPen;
            }
            set
            {
                this.topPen = value;
            }
        }

        #endregion
        #region 상단 공간 그라디언트 시작 색상 - TopSpacerGradientStartColor

        /// <summary>
        /// 상단 공간 그라디언트 시작 색상
        /// </summary>
        public Color TopSpacerGradientStartColor { get; set; }

        #endregion
        #region 상단 공간 펜 - TopSpacerPen

        /// <summary>
        /// 상단 공간 펜
        /// </summary>
        public override Pen TopSpacerPen
        {
            get
            {
                if(this.topSpacerPen == null || this.lastBottomHeight != BottomHeight || this.lastTopHeight != TopHeight)
                {
                    this.topSpacerPen = CreateGradientPen(TopHeight, TopSpacerGradientStartColor, this.topSpacerStartColor);

                    this.lastTopHeight    = TopHeight;
                    this.lastBottomHeight = BottomHeight;
                }

                return topSpacerPen;
            }
            set
            {
                this.topSpacerPen = value;
            }
        }

        #endregion
        #region 하단 피크 펜 - BottomPeakPen

        /// <summary>
        /// 하단 피크 펜
        /// </summary>
        public override Pen BottomPeakPen
        {
            get
            {
                return bottomPen;
            }
            set
            {
                bottomPen = value;
            }
        }

        #endregion
        #region 하단 공간 펜 - BottomSpacerPen

        /// <summary>
        /// 하단 공간 펜
        /// </summary>
        public override Pen BottomSpacerPen
        {
            get
            {
                return this.bottomSpacerPen;
            }
            set
            {
                this.bottomSpacerPen = value;
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - SoundCloudBlockWaveFormSetting(topPeakColor, topSpacerStartColor, bottomPeakColor, bottomSpacerColor)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="topPeakColor">상단 피크 색상</param>
        /// <param name="topSpacerStartColor">상단 공간 시작 색상</param>
        /// <param name="bottomPeakColor">하단 피크 색상</param>
        /// <param name="bottomSpacerColor">하단 공간 색상</param>
        public SoundCloudBlockWaveFormSetting(Color topPeakColor, Color topSpacerStartColor, Color bottomPeakColor, Color bottomSpacerColor)
        {
            this.topPen              = new Pen(topPeakColor);
            this.topSpacerStartColor = topSpacerStartColor;
            this.bottomPen           = new Pen(bottomPeakColor);
            this.bottomSpacerPen     = new Pen(bottomSpacerColor);

            PixelCountPerPeak           = 4;
            SpacerPixelCount            = 2;
            BackgroundColor             = Color.White;
            TopSpacerGradientStartColor = Color.White;
        }

        #endregion
    }
}

 

▶ SoundCloudOriginalSetting.cs

using System;
using System.Drawing;
using System.Drawing.Drawing2D;

namespace TestLibrary
{
    /// <summary>
    /// 사운드 클라우드 원본 설정
    /// </summary>
    public class SoundCloudOriginalSetting : WaveFormRendererSetting
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 최근 상단 높이
        /// </summary>
        private int lastTopHeight;

        /// <summary>
        /// 최근 하단 높이
        /// </summary>
        private int lastBottomHeight;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 상단 피크 펜 - TopPeakPen

        /// <summary>
        /// 상단 피크 펜
        /// </summary>
        public override Pen TopPeakPen
        {
            get
            {
                if(base.TopPeakPen == null || this.lastTopHeight != TopHeight)
                {
                    base.TopPeakPen = CreateGradientPen(TopHeight, Color.FromArgb(120, 120, 120), Color.FromArgb(50, 50, 50));

                    this.lastTopHeight = TopHeight;
                }

                return base.TopPeakPen;
            }
            set
            {
                base.TopPeakPen = value;
            }
        }

        #endregion
        #region 하단 피크 펜 - BottomPeakPen

        /// <summary>
        /// 하단 피크 펜
        /// </summary>
        public override Pen BottomPeakPen 
        {
            get
            {
                if(base.BottomPeakPen == null || this.lastBottomHeight != BottomHeight || this.lastTopHeight != TopHeight)
                {
                    base.BottomPeakPen = CreateSoundCloudBottomPen(TopHeight, BottomHeight);

                    this.lastTopHeight    = TopHeight;
                    this.lastBottomHeight = BottomHeight;
                }

                return base.BottomPeakPen;
            }

            set
            {
                base.BottomPeakPen = value;
            }
        }

        #endregion
        #region 하단 공간 펜 - BottomSpacerPen

        /// <summary>
        /// 하단 공간 펜
        /// </summary>
        public override Pen BottomSpacerPen
        {
            get
            {
                throw new InvalidOperationException("No spacer pen required");
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - SoundCloudOriginalSetting()

        /// <summary>
        /// 생성자
        /// </summary>
        public SoundCloudOriginalSetting()
        {
            PixelCountPerPeak = 1;
            SpacerPixelCount  = 0;
            BackgroundColor   = Color.White;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region 사운드 클라우드 하단 펜 생성하기 - CreateSoundCloudBottomPen(topHeight, bottomHeight)

        /// <summary>
        /// 사운드 클라우드 하단 펜 생성하기
        /// </summary>
        /// <param name="topHeight"></param>
        /// <param name="bottomHeight"></param>
        /// <returns>사운드 클라우드 하단 펜</returns>
        private Pen CreateSoundCloudBottomPen(int topHeight, int bottomHeight)
        {
            LinearGradientBrush brush = new LinearGradientBrush
            (
                new Point(0, topHeight),
                new Point(0, topHeight + bottomHeight), 
                Color.FromArgb(16, 16, 16),
                Color.FromArgb(150, 150, 150)
            );

            ColorBlend colorBlend = new ColorBlend(3);

            colorBlend.Colors[0]    = Color.FromArgb(16, 16, 16);
            colorBlend.Colors[1]    = Color.FromArgb(142, 142, 142);
            colorBlend.Colors[2]    = Color.FromArgb(150, 150, 150);
            colorBlend.Positions[0] = 0;
            colorBlend.Positions[1] = 0.1f;
            colorBlend.Positions[2] = 1.0f;

            brush.InterpolationColors = colorBlend;

            return new Pen(brush);
        }

        #endregion
    }
}

 

▶ StandardWaveFormRendererSetting.cs

using System.Drawing;

namespace TestLibrary
{
    /// <summary>
    /// 표준 웨이브 폼 렌더러 설정
    /// </summary>
    public class StandardWaveFormRendererSetting : WaveFormRendererSetting
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 상단 피크 펜 - TopPeakPen

        /// <summary>
        /// 상단 피크 펜
        /// </summary>
        public override Pen TopPeakPen { get; set; }

        #endregion
        #region 상단 공간 펜 - TopSpacerPen

        /// <summary>
        /// 상단 공간 펜
        /// </summary>
        public override Pen TopSpacerPen { get; set; }
        
        #endregion
        #region 하단 피크 펜 - BottomPeakPen

        /// <summary>
        /// 하단 피크 펜
        /// </summary>
        public override Pen BottomPeakPen { get; set; }

        #endregion
        #region 하단 공간 펜 - BottomSpacerPen

        /// <summary>
        /// 하단 공간 펜
        /// </summary>
        public override Pen BottomSpacerPen { get; set; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - StandardWaveFormRendererSetting()

        /// <summary>
        /// 생성자
        /// </summary>
        public StandardWaveFormRendererSetting()
        {
            PixelCountPerPeak = 1;
            SpacerPixelCount  = 0;
            TopPeakPen        = Pens.Maroon;
            BottomPeakPen     = Pens.Peru;
        }

        #endregion
    }
}

 

▶ WaveFormRenderer.cs

using System;
using System.Drawing;

using NAudio.Wave;

namespace TestLibrary
{
    /// <summary>
    /// 웨이브 폼 렌더러
    /// </summary>
    public class WaveFormRenderer
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 렌더링하기 - Render(peakProvider, setting)

        /// <summary>
        /// 렌더링하기
        /// </summary>
        /// <param name="peakProvider">피크 공급자</param>
        /// <param name="setting">설정</param>
        /// <returns>이미지</returns>
        private static Image Render(IPeakProvider peakProvider, WaveFormRendererSetting setting)
        {
            if(setting.DecibelScale)
            {
                peakProvider = new DecibelPeakProvider(peakProvider, 48);
            }

            Bitmap bitmap = new Bitmap(setting.Width, setting.TopHeight + setting.BottomHeight);

            if(setting.BackgroundColor == Color.Transparent)
            {
                bitmap.MakeTransparent();
            }

            using(Graphics graphics = Graphics.FromImage(bitmap))
            {
                graphics.FillRectangle(setting.BackgroundBrush, 0,0,bitmap.Width,bitmap.Height);

                int      middlePoint     = setting.TopHeight;
                int      x               = 0;
                PeakInfo currentPeakInfo = peakProvider.GetNextPeakInfo();

                while(x < setting.Width)
                {
                    PeakInfo nextPeakInfo = peakProvider.GetNextPeakInfo();
                    
                    for(int i = 0; i < setting.PixelCountPerPeak; i++)
                    {
                        float lineHeight = setting.TopHeight * currentPeakInfo.Maximum;

                        graphics.DrawLine(setting.TopPeakPen, x, middlePoint, x, middlePoint - lineHeight);

                        lineHeight = setting.BottomHeight * currentPeakInfo.Minimum;

                        graphics.DrawLine(setting.BottomPeakPen, x, middlePoint, x, middlePoint - lineHeight);

                        x++;
                    }

                    for(int i = 0; i < setting.SpacerPixelCount; i++)
                    {
                        float minimum = Math.Max(currentPeakInfo.Minimum, nextPeakInfo.Minimum);
                        float maximum = Math.Min(currentPeakInfo.Maximum, nextPeakInfo.Maximum);

                        float lineHeight = setting.TopHeight * maximum;

                        graphics.DrawLine(setting.TopSpacerPen, x, middlePoint, x, middlePoint - lineHeight);

                        lineHeight = setting.BottomHeight * minimum;

                        graphics.DrawLine(setting.BottomSpacerPen, x, middlePoint, x, middlePoint - lineHeight); 

                        x++;
                    }

                    currentPeakInfo = nextPeakInfo;
                }
            }

            return bitmap;
        }

        #endregion
        #region 렌더링하기 - Render(waveStream, peakProvider, setting)

        /// <summary>
        /// 렌더링하기
        /// </summary>
        /// <param name="waveStream">웨이브 스트림</param>
        /// <param name="peakProvider">피크 공급자</param>
        /// <param name="setting">설정</param>
        /// <returns>이미지</returns>
        public Image Render(WaveStream waveStream, IPeakProvider peakProvider, WaveFormRendererSetting setting)
        {
            int  byteCountPerSample  = (waveStream.WaveFormat.BitsPerSample / 8);
            long sampleCount         = waveStream.Length / (byteCountPerSample);
            int  sampleCountPerPixel = (int)(sampleCount / setting.Width);
            int  stepSize            = setting.PixelCountPerPeak + setting.SpacerPixelCount;

            peakProvider.Initialize(waveStream.ToSampleProvider(), sampleCountPerPixel * stepSize);

            return Render(peakProvider, setting);
        }

        #endregion
        #region 렌더링하기 - Render(waveStream, setting)

        /// <summary>
        /// 렌더링하기
        /// </summary>
        /// <param name="waveStream">웨이브 스트림</param>
        /// <param name="setting">설정</param>
        /// <returns>이미지</returns>
        public Image Render(WaveStream waveStream, WaveFormRendererSetting setting)
        {
            return Render(waveStream, new MaximumPeakProvider(), setting);
        }

        #endregion
    }
}

 

[TestProject 프로젝트]

▶ MainForm.cs

using System;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;

using NAudio.Wave;

using TestLibrary;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 오디오 파일 경로
        /// </summary>
        private string audioFilePath;

        /// <summary>
        /// 이미지 파일 경로
        /// </summary>
        private string imageFilePath;

        /// <summary>
        /// 웨이브 폼 렌더러
        /// </summary>
        private readonly WaveFormRenderer render;

        /// <summary>
        /// 웨이브 폼 렌더러 설정
        /// </summary>
        private readonly WaveFormRendererSetting setting;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            this.render = new WaveFormRenderer();

            Color topSpacerColor1 = Color.FromArgb(64, 83 , 22 , 3  );
            Color topSpacerColor2 = Color.FromArgb(64, 224, 224, 224);

            #region 설정 항목을 설정한다.

            StandardWaveFormRendererSetting setting1 = new StandardWaveFormRendererSetting()
            {
                Name = "Standard"
            };

            SoundCloudOriginalSetting setting2 = new SoundCloudOriginalSetting()
            {
                Name = "SoundCloud Original"
            };

            SoundCloudBlockWaveFormSetting setting3 = new SoundCloudBlockWaveFormSetting
            (
                Color.FromArgb(102, 102, 102),
                Color.FromArgb(103, 103, 103),
                Color.FromArgb(179, 179, 179),
                Color.FromArgb(218, 218, 218)
            )
            {
                Name = "SoundCloud Light Blocks"
            };

            SoundCloudBlockWaveFormSetting setting4 = new SoundCloudBlockWaveFormSetting
            (
                Color.FromArgb(52 , 52 , 52 ),
                Color.FromArgb(55 , 55 , 55 ),
                Color.FromArgb(154, 154, 154),
                Color.FromArgb(204, 204, 204)
            )
            {
                Name = "SoundCloud Darker Blocks"
            };

            SoundCloudBlockWaveFormSetting setting5 = new SoundCloudBlockWaveFormSetting
            (
                Color.FromArgb(255, 76 , 0  ),
                Color.FromArgb(255, 52 , 2  ),
                Color.FromArgb(255, 171, 141),
                Color.FromArgb(255, 213, 199)
            )
            {
                Name = "SoundCloud Orange Blocks"
            };

            SoundCloudBlockWaveFormSetting setting6 = new SoundCloudBlockWaveFormSetting
            (
                Color.FromArgb(196, 197, 53, 0 ),
                topSpacerColor1,
                Color.FromArgb(196, 79 , 26, 0 ),
                Color.FromArgb(64 , 79 , 79, 79)
            ) 
            { 
                Name                        = "SoundCloud Orange Transparent Blocks", 
                PixelCountPerPeak           = 2, 
                SpacerPixelCount            = 1,
                TopSpacerGradientStartColor = topSpacerColor1,
                BackgroundColor             = Color.Transparent
            };

            SoundCloudBlockWaveFormSetting setting7 = new SoundCloudBlockWaveFormSetting
            (
                Color.FromArgb(196, 224, 225, 224),
                topSpacerColor2,
                Color.FromArgb(196, 128, 128, 128),
                Color.FromArgb(64 , 128, 128, 128)
            )
            {
                Name                        = "SoundCloud Gray Transparent Blocks",
                PixelCountPerPeak           = 2,
                SpacerPixelCount            = 1,
                TopSpacerGradientStartColor = topSpacerColor2,
                BackgroundColor             = Color.Transparent
            };

            #endregion

            this.setting = setting1;

            #region 피크 계산 전략 콤보 박스를 설정한다.

            this.peakCalculationStrategyComboBox.Items.Add("Max Absolute Value");
            this.peakCalculationStrategyComboBox.Items.Add("Max Rms Value"     );
            this.peakCalculationStrategyComboBox.Items.Add("Sampled Peaks"     );
            this.peakCalculationStrategyComboBox.Items.Add("Scaled Average"    );

            this.peakCalculationStrategyComboBox.SelectedIndex = 0;

            #endregion
            #region 렌더링 스타일 콤보 박스를 설정한다.

            this.renderingStyleComboBox.DisplayMember = "Name";

            this.renderingStyleComboBox.DataSource = new WaveFormRendererSetting[]
            {
                setting1, 
                setting2, 
                setting3, 
                setting4, 
                setting5, 
                setting6,
                setting7
            };

            this.renderingStyleComboBox.SelectedIndex = 5;

            #endregion
            #region 상단 색상 버튼을 설정한다.

            this.topColorButton.BackColor = this.setting.TopPeakPen.Color;

            #endregion
            #region 하단 색상 버튼을 설정한다.

            this.bottomColorButton.BackColor = this.setting.BottomPeakPen.Color;

            #endregion
            #region Rendering 레이블을 설정한다.

            this.renderingLabel.Visible = false;

            #endregion
            #region 이벤트를 설정한다.

            this.loadButton.Click                                     += loadButton_Click;
            this.peakCalculationStrategyComboBox.SelectedIndexChanged += peakCalculationStrategyComboBox_SelectedIndexChanged; ;
            this.decibelCheckBox.CheckedChanged                       += decibelCheckBox_CheckedChanged;
            this.renderingStyleComboBox.SelectedIndexChanged          += renderingStyleComboBox_SelectedIndexChanged;
            this.topColorButton.Click                                 += colorButton_Click;
            this.bottomColorButton.Click                              += colorButton_Click;
            this.loadBackgroundImageButton.Click                      += loadBackgroundImageButton_Click;
            this.refreshImageButton.Click                             += refreshImageButton_Click;
            this.saveButton.Click                                     += saveButton_Click;

            #endregion
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 오디오 파일 로드 버튼 클릭시 처리하기 - loadButton_Click(sender, e)

        /// <summary>
        /// 오디오 파일 로드 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void loadButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.Filter = "MP3 파일|*.mp3|WAV 파일|*.wav";

            if(openFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                this.audioFilePath = openFileDialog.FileName;

                RenderWaveform();
            }
        }

        #endregion
        #region 피크 계산 전략 콤보 박스 선택 인덱스 변경시 처리하기 - peakCalculationStrategyComboBox_SelectedIndexChanged(sender, e)

        /// <summary>
        /// 피크 계산 전략 콤보 박스 선택 인덱스 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void peakCalculationStrategyComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            RenderWaveform();
        }

        #endregion
        #region 데시빌 체크 박스 체크 변경시 처리하기 - decibelCheckBox_CheckedChanged(sender, e)

        /// <summary>
        /// 데시빌 체크 박스 체크 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void decibelCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            RenderWaveform();
        }

        #endregion
        #region 렌더링 스타일 콤보 박스 선택 인덱스 변경시 처리하기 - renderingStyleComboBox_SelectedIndexChanged(sender, e)

        /// <summary>
        /// 렌더링 스타일 콤보 박스 선택 인덱스 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void renderingStyleComboBox_SelectedIndexChanged(object sender, EventArgs e)
        {
            RenderWaveform();
        }

        #endregion
        #region 색상 버튼 클릭시 처리하기 - colorButton_Click(sender, e)

        /// <summary>
        /// 색상 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void colorButton_Click(object sender, EventArgs e)
        {
            Button button = sender as Button;

            ColorDialog colorDialog = new ColorDialog();

            colorDialog.Color = button.BackColor;

            if(colorDialog.ShowDialog(this) == DialogResult.OK)
            {
                button.BackColor = colorDialog.Color;

                this.setting.TopPeakPen    = new Pen(this.topColorButton.BackColor   );
                this.setting.BottomPeakPen = new Pen(this.bottomColorButton.BackColor);

                RenderWaveform();
            }
        }

        #endregion
        #region 배경 이미지 로드 버튼 클릭시 처리하기 - loadBackgroundImageButton_Click(sender, e)

        /// <summary>
        /// 배경 이미지 로드 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void loadBackgroundImageButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.Filter = "이미지 파일|*.bmp;*.png;*.jpg";

            if(openFileDialog.ShowDialog() == DialogResult.OK)
            {
                this.imageFilePath = openFileDialog.FileName;

                RenderWaveform();
            }
        }

        #endregion
        #region 이미지 갱신 버튼 클릭시 처리하기 - refreshImageButton_Click(sender, e)

        /// <summary>
        /// 이미지 갱신 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void refreshImageButton_Click(object sender, EventArgs e)
        {
            RenderWaveform();
        }

        #endregion
        #region 저장 버튼 클릭시 처리하기 - saveButton_Click(sender, e)

        /// <summary>
        /// 저장 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void saveButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog saveFileDialog = new SaveFileDialog();

            saveFileDialog.Filter = "PNG 파일|*.png";

            if(saveFileDialog.ShowDialog(this) == DialogResult.OK)
            {
                this.pictureBox.Image.Save(saveFileDialog.FileName);
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Function

        #region 웨이브 폼 렌더러 설정 구하기 - GetWaveFormRendererSetting()

        /// <summary>
        /// 웨이브 폼 렌더러 설정 구하기
        /// </summary>
        /// <returns>웨이브 폼 렌더러 설정</returns>
        private WaveFormRendererSetting GetWaveFormRendererSetting()
        {
            WaveFormRendererSetting setting = (WaveFormRendererSetting)this.renderingStyleComboBox.SelectedItem;

            setting.TopHeight    = (int)this.topHeightNumericUpDown.Value;
            setting.BottomHeight = (int)this.bottomHeightNumericUpDown.Value;
            setting.Width        = (int)this.imageWidthNumericUpDown.Value;
            setting.DecibelScale = this.decibelCheckBox.Checked;

            return setting;
        }

        #endregion
        #region 피크 공급자 구하기 - GetPeakProvider()

        /// <summary>
        /// 피크 공급자 구하기
        /// </summary>
        /// <returns>피크 공급자</returns>
        private IPeakProvider GetPeakProvider()
        {
            switch(this.peakCalculationStrategyComboBox.SelectedIndex)
            {
                case 0  : return new MaximumPeakProvider();
                case 1  : return new RMSPeakProvider((int)this.blockSizeNumericUpDown.Value);
                case 2  : return new SamplingPeakProvider((int)this.blockSizeNumericUpDown.Value);
                case 3  : return new AveragePeakProvider(4);
                default : throw  new InvalidOperationException("Unknown calculation strategy");
            }
        }

        #endregion
        #region 렌더링 스레딩 처리하기 - ProcessRenderingThread(peakProvider, setting)

        /// <summary>
        /// 렌더링 스레딩 처리하기
        /// </summary>
        /// <param name="peakProvider">피크 공급자</param>
        /// <param name="setting">설정</param>
        private void ProcessRenderingThread(IPeakProvider peakProvider, WaveFormRendererSetting setting)
        {
            Image image = null;

            try
            {
                using(AudioFileReader reader = new AudioFileReader(this.audioFilePath))
                {
                    image = this.render.Render(reader, peakProvider, setting);
                }
            }
            catch(Exception exception)
            {
                MessageBox.Show(exception.Message);
            }

            BeginInvoke
            (
                (Action)
                (
                    () =>
                    {
                        this.renderingLabel.Visible = false;

                        this.pictureBox.Image = image;

                        Enabled = true;
                    }
                )
            );
        }

        #endregion
        #region 웨이브 폼 렌더링하기 - RenderWaveform()

        /// <summary>
        /// 웨이브 폼 렌더링하기
        /// </summary>
        private void RenderWaveform()
        {
            if(this.audioFilePath == null)
            {
                return;
            }

            WaveFormRendererSetting setting = GetWaveFormRendererSetting();

            if(this.imageFilePath != null)
            {
                setting.BackgroundImage = new Bitmap(this.imageFilePath);
            }

            this.pictureBox.Image = null;

            this.renderingLabel.Visible = true;

            Enabled = false;

            IPeakProvider peakProvider = GetPeakProvider();

            Task.Factory.StartNew(() => ProcessRenderingThread(peakProvider, setting));
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요