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

TestProject.zip
0.15MB

▶ FFT.cs

using System;

namespace TestProject
{
    /// <summary>
    /// 고속 푸리엔 변환
    /// </summary>
    public class FFT
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Protected

        #region Field

        /// <summary>
        /// 사인 배정도 실수 배열
        /// </summary>
        /// <remarks>sin(pi / 2), sin(pi / 4), sin(pi / 8) ...</remarks>
        protected static double[] _sineDoubleArray = null;

        /// <summary>
        /// 사인 단정도 실수 배열
        /// </summary>
        protected static float[] _sineFloatArray = null;

        /// <summary>
        /// 코사인 배정도 실수 배열
        /// </summary>
        /// <remarks>cos(pi / 2), cos(pi / 4), cos(pi / 8) ...</remarks>
        protected static double[] _cosineDoubleArray = null;

        /// <summary>
        /// 코사인 단정도 실수 배열
        /// </summary>
        protected static float[] _cosineFloatArray = null;

        /// <summary>
        /// 테이블 잠금 객체
        /// </summary>
        protected static object _tableLock = new object();

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        // 배정도 실수
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        public static void ComputeForwardFFT(double[] sourceRealArray)
        {
            ComputeFFT(sourceRealArray, true);
        }

        #endregion
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray, targetRealArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        public static void ComputeForwardFFT(double[] sourceRealArray, double[] targetRealArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, true);
        }

        #endregion
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeForwardFFT(double[] sourceRealArray, double[] targetRealArray, double[] targetImaginaryArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, targetImaginaryArray, true);
        }

        #endregion
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray, sourceImaginaryArray, targetRealArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        public static void ComputeForwardFFT(float[] sourceRealArray, float[] sourceImaginaryArray, float[] targetRealArray)
        {
            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, true);
        }

        #endregion
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeForwardFFT
        (
            double[] sourceRealArray,
            double[] sourceImaginaryArray,
            double[] targetRealArray,
            double[] targetImaginaryArray
        )
        {
            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, true);
        }

        #endregion

        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        public static void ComputeInverseFFT(double[] sourceRealArray)
        {
            ComputeFFT(sourceRealArray, false);
        }

        #endregion
        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray, targetRealArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        public static void ComputeInverseFFT(double[] sourceRealArray, double[] targetRealArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, false);
        }

        #endregion
        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeInverseFFT(double[] sourceRealArray, double[] targetRealArray, double[] targetImaginaryArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, targetImaginaryArray, true);
        }

        #endregion
        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeInverseFFT
        (
            double[] sourceRealArray,
            double[] sourceImaginaryArray,
            double[] targetRealArray,
            double[] targetImaginaryArray
        )
        {
            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, false);
        }

        #endregion

        // 단정도 실수
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        public static void ComputeForwardFFT(float[] sourceRealArray)
        {
            ComputeFFT(sourceRealArray, true);
        }

        #endregion
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray, targetRealArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        public static void ComputeForwardFFT(float[] sourceRealArray, float[] targetRealArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, true);
        }

        #endregion
        #region 순방향 FFT 계산하기 - ComputeForwardFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 순방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeForwardFFT
        (
            float[] sourceRealArray,
            float[] sourceImaginaryArray,
            float[] targetRealArray,
            float[] targetImaginaryArray
        )
        {
            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, true);
        }

        #endregion

        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        public static void ComputeInverseFFT(float[] sourceRealArray)
        {
            ComputeFFT(sourceRealArray, false);
        }

        #endregion
        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray, targetRealArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        public static void ComputeInverseFFT(float[] sourceRealArray, float[] targetRealArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, false);
        }

        #endregion
        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeInverseFFT(float[] sourceRealArray, float[] targetRealArray, float[] targetImaginaryArray)
        {
            ComputeFFT(sourceRealArray, targetRealArray, targetImaginaryArray, true);
        }

        #endregion
        #region 역방향 FFT 계산하기 - ComputeInverseFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray)

        /// <summary>
        /// 역방향 FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        public static void ComputeInverseFFT
        (
            float[] sourceRealArray,
            float[] sourceImaginaryArray,
            float[] targetRealArray,
            float[] targetImaginaryArray
        )
        {
            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, false);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Protected

        #region 배열 값 설정하기 - SetArrayValue()

        /// <summary>
        /// 배열 값 설정하기
        /// </summary>
        protected static void SetArrayValue()
        {

            if(_sineDoubleArray != null)
            {
                return;
            }

            lock(_tableLock)
            {
                if(_sineDoubleArray != null)
                {
                    return;
                }

                int size = 20;

                double[] sineDoubleArray   = new double[size];
                float[]  sineFloatArray    = new float[size];
                double[] cosineDoubleArray = new double[size];
                float[]  cosineFloatArray  = new float[size];

                double pi = Math.PI;

                for(int i = 0; i < size; i++)
                {
                    sineDoubleArray[i]   = Math.Sin(pi);
                    sineFloatArray[i]    = (float)(sineDoubleArray[i]);
                    cosineDoubleArray[i] = Math.Cos(pi);
                    cosineFloatArray[i]  = (float)(cosineDoubleArray[i]);

                    pi /= 2;
                }

                _sineFloatArray    = sineFloatArray;
                _sineDoubleArray   = sineDoubleArray;
                _cosineFloatArray  = cosineFloatArray;
                _cosineDoubleArray = cosineDoubleArray;
            }
        }

        #endregion
        #region 비트 반전하기 - ReverseBit(value, bitCount)

        /// <summary>
        /// 비트 반전하기
        /// </summary>
        /// <param name="value">값</param>
        /// <param name="bitCount">비트 수</param>
        /// <returns>반전 값</returns>
        protected static int ReverseBit(int value, int bitCount)
        {
            int result = 0;

            for(int b = 0; b < bitCount; ++b)
            {
                result <<= 1;

                result |= value & 1;

                value >>= 1;
            }

            return result;
        }

        #endregion

        // 배정도 실수
        #region FFT 수행하기 - PerformFFT(sourceRealArray, sourceImaginaryArray, forward)

        /// <summary>
        /// FFT 수행하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void PerformFFT(double[] sourceRealArray, double[] sourceImaginaryArray, bool forward)
        {
            int    length = sourceRealArray.Length;
            double sign   = forward ? 1 : -1;
            int    index  = 0;

            for(int p = 1; p < length; p <<= 1)
            {
                int p2 = p << 1;
                
                double sine   = sign * _sineDoubleArray[index];
                double cosine = _cosineDoubleArray[index++];

                double wr = 1.0;
                double wi = 0.0;

                int j;

                for(j = 0; j < p; ++j)
                {
                    int k;

                    for(k = j; k < length; k += p2)
                    {
                        int k2 = k + p;

                        double tr = (wr * sourceRealArray[k2]) - (wi * sourceImaginaryArray[k2]);
                        double ti = (wr * sourceImaginaryArray[k2]) + (wi * sourceRealArray[k2]);

                        sourceRealArray[k2] = sourceRealArray[k] - tr;

                        sourceImaginaryArray[k2] = sourceImaginaryArray[k] - ti;

                        sourceRealArray[k] += tr;

                        sourceImaginaryArray[k] += ti;
                    }

                    double nwr = (wr * cosine) - (wi * sine);
                    double nwi = (wi * cosine) + (wr * sine);

                    wr = nwr;
                    wi = nwi;
                }
            }
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT
        (
            double[] sourceRealArray,
            double[] sourceImaginaryArray,
            double[] targetRealArray,
            double[] targetImaginaryArray,
            bool forward
        )
        {
            SetArrayValue();

            int length = sourceRealArray.Length;

            int i;

            int bitCount = 0;

            for(i = 1; i < length; i <<= 1)
            {
                bitCount++;
            }

            for(i = 0; i < length; i++)
            {
                int p = ReverseBit(i, bitCount);

                targetRealArray[p] = sourceRealArray[i];

                targetImaginaryArray[p] = sourceImaginaryArray[i];
            }

            PerformFFT(targetRealArray, targetImaginaryArray, forward);
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT(double[] sourceRealArray, bool forward)
        {
            double[] targetRealArray      = new double[sourceRealArray.Length];
            double[] sourceImaginaryArray = new double[sourceRealArray.Length];
            double[] targetImaginaryArray = new double[sourceRealArray.Length];

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                sourceImaginaryArray[i] = 0;
            }

            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward);

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                targetRealArray[i] = System.Math.Sqrt
                (
                    (targetRealArray[i] * targetRealArray[i]) + (targetImaginaryArray[i] * targetImaginaryArray[i])
                );
            }
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, targetRealArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT(double[] sourceRealArray, double[] targetRealArray, bool forward)
        {
            double[] sourceImaginaryArray = new double[sourceRealArray.Length];
            double[] targetImaginaryArray = new double[sourceRealArray.Length];

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                sourceImaginaryArray[i] = 0;            
            }

            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward);

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                targetRealArray[i] = Math.Sqrt
                (
                    (targetRealArray[i] * targetRealArray[i]) + (targetImaginaryArray[i] * targetImaginaryArray[i])
                );
            }
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, targetRealArray, targetImaginaryArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT(double[] sourceRealArray, double[] targetRealArray, double[] targetImaginaryArray, bool forward)
        {
            double[] sourceImaginaryArray = new double[sourceRealArray.Length];

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                sourceImaginaryArray[i] = 0;
            }

            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward);
        }

        #endregion

        // 단정도 실수
        #region FFT 수행하기 - PerformFFT(sourceRealArray, sourceImaginaryArray, forward)

        /// <summary>
        /// FFT 수행하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void PerformFFT(float[] sourceRealArray, float[] sourceImaginaryArray, bool forward)
        {
            int   length = sourceRealArray.Length;
            float sign   = forward ? 1 : -1;
            int   index  = 0;

            for(int p = 1; p < length; p <<= 1)
            {
                int p2 = p << 1;

                float sine   = sign * _sineFloatArray[index];
                float cosine = _cosineFloatArray[index++];

                float wr = 1.0f;
                float wi = 0.0f;

                int j;

                for(j = 0; j < p; j++)
                {
                    int k;

                    for(k = j; k < length; k += p2)
                    {
                        int k2 = k + p;

                        float tr = (wr * sourceRealArray[k2]) - (wi * sourceImaginaryArray[k2]);
                        float ti = (wr * sourceImaginaryArray[k2]) + (wi * sourceRealArray[k2]);

                        sourceRealArray[k2] = sourceRealArray[k] - tr;

                        sourceImaginaryArray[k2] = sourceImaginaryArray[k] - ti;

                        sourceRealArray[k] += tr;

                        sourceImaginaryArray[k] += ti;
                    }

                    float nwr = (wr * cosine) - (wi * sine);
                    float nwi = (wi * cosine) + (wr * sine);

                    wr = nwr;
                    wi = nwi;
                }
            }
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소수 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="targetImaginaryArray">타겟 허수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT
        (
            float[] sourceRealArray,
            float[] sourceImaginaryArray,
            float[] targetRealArray,
            float[] targetImaginaryArray,
            bool forward
        )
        {
            SetArrayValue();

            int length = sourceRealArray.Length;

            int i;

            int bitCount = 0;

            for(i = 1; i < length; i <<= 1)
            {
                bitCount++;
            }

            length = 1 << (bitCount-1);

            for(i = 0; i < length; i++)
            {
                int p = ReverseBit(i, bitCount);

                targetRealArray[p] = sourceRealArray[i];

                targetImaginaryArray[p] = sourceImaginaryArray[i];
            }

            PerformFFT(targetRealArray, targetImaginaryArray, forward);
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT(float[] sourceRealArray, bool forward)
        {
            float[] sourceImaginaryArray = new float[sourceRealArray.Length];
            float[] targetRealArray      = new float[sourceRealArray.Length];
            float[] targetImaginaryArray = new float[sourceRealArray.Length];

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                sourceImaginaryArray[i] = 0;
            }

            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward);

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                targetRealArray[i] = (float)Math.Sqrt
                (
                    (targetRealArray[i] * targetRealArray[i]) + (targetImaginaryArray[i] * targetImaginaryArray[i])
                );
            }
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, targetRealArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT(float[] sourceRealArray, float[] targetRealArray, bool forward)
        {
            float[] sourceImaginaryArray = new float[sourceRealArray.Length];
            float[] targetImaginaryArray = new float[sourceRealArray.Length];
            float[] temporaryRealArray   = new float[sourceRealArray.Length];

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                sourceImaginaryArray[i] = 0;
            }

            ComputeFFT(sourceRealArray, sourceImaginaryArray, temporaryRealArray, targetImaginaryArray, forward);

            for(int i = 0; i < targetRealArray.Length; i++)
            {
                targetRealArray[i] = (float)Math.Sqrt
                (
                    (temporaryRealArray[i] * temporaryRealArray[i]) + (targetImaginaryArray[i] * targetImaginaryArray[i])
                );
            }
        }

        #endregion
        #region FFT 계산하기 - ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, forward)

        /// <summary>
        /// FFT 계산하기
        /// </summary>
        /// <param name="sourceRealArray">소스 실수 배열</param>
        /// <param name="sourceImaginaryArray">소스 허수 배열</param>
        /// <param name="targetRealArray">타겟 실수 배열</param>
        /// <param name="forward">순방향 여부</param>
        protected static void ComputeFFT(float[] sourceRealArray, float[] sourceImaginaryArray, float[] targetRealArray, bool forward)
        {
            float[] targetImaginaryArray = new float[sourceRealArray.Length];

            ComputeFFT(sourceRealArray, sourceImaginaryArray, targetRealArray, targetImaginaryArray, forward);

            for(int i = 0; i < sourceRealArray.Length; i++)
            {
                targetRealArray[i] = (float)Math.Sqrt
                (
                    (targetRealArray[i] * targetRealArray[i]) + (targetImaginaryArray[i] * targetImaginaryArray[i])
                );
            }
        }

        #endregion
    }
}

 

반응형

 

▶ Line.cs

using System.Drawing;

namespace TestProject
{
    /// <summary>
    /// 선
    /// </summary>
    public class Line
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 명칭
        /// </summary>
        public string Name = string.Empty;
            
        /// <summary>
        /// 표시 여부
        /// </summary>
        public bool Visible = true;

        /// <summary>
        /// X 배열
        /// </summary>
        /// <remarks>NULL인 경우 Y 인덱스를 사용한다.</remarks>
        public double[] XArray = null;

        /// <summary>
        /// Y 배열
        /// </summary>
        public double[] YArray = null;

        /// <summary>
        /// 선 색상
        /// </summary>
        public Color LineColor = Color.Black;

        /// <summary>
        /// 선 너비
        /// </summary>
        public float LineWidth = 1f;

        #endregion

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

        #region 최소 X - MinimumX

        /// <summary>
        /// 최소 X
        /// </summary>
        public double MinimumX
        {
            get
            {
                double [] xArray = XArray;

                if(xArray == null)
                {
                    return 0;
                }

                if(xArray.Length < 1)
                {
                    return 0;
                }

                double minimumX = xArray[0];

                for(int i = 0; i < xArray.Length; i++)
                {
                    minimumX = minimumX < xArray[i] ? minimumX : xArray[i];
                }

                return minimumX;
            }
        }

        #endregion
        #region 최대 X - MaximumX

        /// <summary>
        /// 최대 X
        /// </summary>
        public double MaximumX
        {
            get
            {
                double[] xArray = XArray;

                if(xArray == null)
                {
                    double[] yArray = YArray;

                    if(yArray == null)
                    {
                        return 0;
                    }

                    return yArray.Length;
                }

                if(xArray.Length < 1)
                {
                    return 0;
                }

                double maximumX = xArray[0];

                for(int i = 0; i < xArray.Length; i++)
                {
                    maximumX = maximumX > xArray[i] ? maximumX : xArray[i];
                }

                return maximumX;
            }
        }

        #endregion

        #region 최소 X - MinimumY

        /// <summary>
        /// 최소 X
        /// </summary>
        public double MinimumY
        {
            get
            {
                double[] yArray = YArray;

                if(yArray == null)
                {
                    return 0;
                }

                if(yArray.Length < 1)
                {
                    return 0;
                }

                double minimumY = yArray[0];

                for(int i = 0; i < yArray.Length; i++)
                {
                    minimumY = minimumY < yArray[i] ? minimumY : yArray[i];
                }

                return minimumY;
            }
        }

        #endregion
        #region 최대 Y - MaximumY

        /// <summary>
        /// 최대 Y
        /// </summary>
        public double MaximumY
        {
            get
            {
                double[] yArray = YArray;

                if(yArray == null)
                {
                    return 0;
                }

                if(yArray.Length < 1)
                {
                    return 0;
                }

                double maximumY = yArray[0];

                for(int i = 0; i < yArray.Length; i++)
                {
                    maximumY = maximumY > yArray[i] ? maximumY : yArray[i];
                }

                return maximumY;                
            }
        }

        #endregion

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

        #region 업데이트하기 - Update(xArray, yArray)

        /// <summary>
        /// 업데이트하기
        /// </summary>
        /// <param name="xArray">X 배열</param>
        /// <param name="yArray">Y 배열</param>
        public void Update(float[] xArray, float[] yArray)
        {
            if(xArray != null)
            {
                bool resize = XArray == null ? true : XArray.Length == xArray.Length ? false : true;

                double[] newXArray = resize ? new double[xArray.Length] : XArray;

                for(int i = 0; i < xArray.Length; i++)
                {
                    newXArray[i] = xArray[i];
                }

                XArray = newXArray;
            }
            else
            {
                XArray = null;
            }

            if(yArray != null)
            {
                bool resize = YArray == null ? true : YArray.Length == yArray.Length ? false : true;

                double[] newYArray = resize ? new double[yArray.Length] : YArray;

                for(int i = 0; i < yArray.Length; i++)
                {
                    newYArray[i] = yArray[i];
                }

                YArray = newYArray;
            }
            else
            {
                YArray = null;
            }                
        }

        #endregion
        #region 업데이트하기 - Update(yArray)

        /// <summary>
        /// 업데이트하기
        /// </summary>
        /// <param name="yArray">Y 배열</param>
        public void Update(float[] yArray)
        {
            Update(null, yArray);
        }

        #endregion

        #region 업데이트하기 - Update(xArray, yArray)

        /// <summary>
        /// 업데이트하기
        /// </summary>
        /// <param name="xArray">X 배열</param>
        /// <param name="yArray">Y 배열</param>
        public void Update(double[] xArray, double[] yArray)
        {
            if(xArray != null)
            {
                bool resize = XArray == null ? true : XArray.Length == xArray.Length ? false : true;

                double[] newXArray = resize ? new double[xArray.Length] : XArray;

                for(int i = 0; i < xArray.Length; i++)
                {
                    newXArray[i] = xArray[i];
                }

                XArray = newXArray;
            }
            else
            {
                XArray = null;
            }

            if(yArray != null)
            {
                bool resize = YArray == null ? true : YArray.Length == yArray.Length ? false : true;

                double[] newYArray = resize ? new double[yArray.Length] : YArray;

                for(int i = 0; i < yArray.Length; i++)
                {
                    newYArray[i] = yArray[i];
                }

                YArray = newYArray;
            }
            else
            {
                YArray = null;
            }
        }

        #endregion
        #region 업데이트하기 - Update(yArray)

        /// <summary>
        /// 업데이트하기
        /// </summary>
        /// <param name="yArray">Y 배열</param>
        public void Update(double[] yArray)
        {
            Update(null, yArray);
        }

        #endregion

        #region 그리기 - Draw(graphics, reactangle, lowerX, upperX, lowerY, upperY)

        /// <summary>
        /// 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="reactangle">사각형</param>
        /// <param name="lowerX">하한 X</param>
        /// <param name="upperX">상한 X</param>
        /// <param name="lowerY">하한 Y</param>
        /// <param name="upperY">상한 Y</param>
        public void Draw(Graphics graphics, Rectangle reactangle, double lowerX, double upperX, double lowerY, double upperY)
        {
            double[] xArray = XArray;
            double[] yArray = YArray;

            if(yArray == null)
            {
                return;
            }

            int length = xArray == null ? yArray.Length : yArray.Length > xArray.Length ? xArray.Length : yArray.Length;

            double endX = 0;
            double endY = 0;

            upperX = upperX == lowerX ? lowerX + 1 : upperX;
            upperY = upperY == lowerY ? lowerY + 1 : upperY;

            double xScale = reactangle.Width  / (upperX - lowerX);
            double yScale = reactangle.Height / (upperY - lowerY);

            using(Pen pen = new Pen(LineColor, LineWidth))
            {
                for(int i = 0; i < length; i++)
                {
                    double startX = xArray == null ? i : xArray[i];
                    double startY = yArray[i];

                    startX = reactangle.X + ((startX - lowerX) * xScale);
                    startY = reactangle.Y + reactangle.Height - ((startY - lowerY) * yScale);

                    if(i != 0)
                    {
                        graphics.DrawLine(pen, (float)startX, (float)startY, (float)endX, (float)endY);
                    }

                    endX = startX;
                    endY = startY;
                }
            }
        }

        #endregion
    }
}

 

728x90

 

▶ GraphControl.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 그래프 컨트롤
    /// </summary>
    public partial class GraphControl : UserControl
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 텍스트 색상
        /// </summary>
        public Color TextColor = Color.Black;

        /// <summary>
        /// 선 리스트
        /// </summary>
        public List<Line> LineList = new List<Line>();

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region Field

        /// <summary>
        /// 후면 버퍼 비트맵
        /// </summary>
        protected Bitmap backBufferBitmap = null;

        /// <summary>
        /// 전면 버퍼 비트맵
        /// </summary>
        protected Bitmap frontBufferBitmap = null;

        #endregion

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

        #region 레전드 표시 여부 - ShowLegend

        /// <summary>
        /// 레전드 표시 여부
        /// </summary>
        public bool ShowLegend { get; set; }

        #endregion
        #region 축 표시 여부 - ShowAxis

        /// <summary>
        /// 축 표시 여부
        /// </summary>
        public bool ShowAxis { get; set; }

        #endregion
        #region 그리드 표시 여부 - ShowGrid

        /// <summary>
        /// 그리드 표시 여부
        /// </summary>
        public bool ShowGrid { get; set; }

        #endregion

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

        #region 생성자 - GraphControl()

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

            ShowLegend = true;
            ShowAxis   = true;
            ShowGrid   = true;

            this.timer.Tick        += timer_Tick;
            this.pictureBox.Resize += pictureBox_Resize;
        }

        #endregion

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

        #region 타이머 시작하기 - StartTimer()

        /// <summary>
        /// 타이머 시작하기
        /// </summary>
        public void StartTimer()
        {
            this.timer.Enabled = true;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 버퍼 크기 변경하기 - ResizeBuffer()

        /// <summary>
        /// 버퍼 크기 변경하기
        /// </summary>
        protected void ResizeBuffer()
        {
            int width  = this.pictureBox.Width;
            int height = this.pictureBox.Height;

            width  = width  < 10 ? 10 : width;
            height = height < 10 ? 10 : height;

            Bitmap previousBitmap = this.backBufferBitmap;

            if(previousBitmap == null)
            {
                this.backBufferBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
            }
            else
            {
                if((this.backBufferBitmap.Width != width) || (this.backBufferBitmap.Height != height))
                {
                    this.backBufferBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
                }
            }

            if(this.backBufferBitmap != previousBitmap)
            {
                if(previousBitmap != null)
                {
                    previousBitmap.Dispose();
                }
            }
        }

        #endregion
        #region 레전드 그리기 - DrawLegend(graphics, rectangle, legendList, colorList)

        /// <summary>
        /// 레전드 그리기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="rectangle">사각형</param>
        /// <param name="legendList">레전드 리스트</param>
        /// <param name="colorList">색상 리스트</param>
        protected void DrawLegend(Graphics graphics, Rectangle rectangle, List<string> legendList, List<Color> colorList)
        {
            int legentCount = legendList.Count;

            int rectangleX = rectangle.X;

            for(int i = 0; i < legendList.Count; i++)
            {
                int rectangleWidth = rectangle.X  + (rectangle.Width * (i + 1) / legentCount);

                using(SolidBrush fillBrush = new SolidBrush(colorList[i]))
                {
                    graphics.FillRectangle(fillBrush, rectangleX, rectangle.Y, rectangleWidth, rectangle.Height);
                }

                using(SolidBrush stringBrush = new SolidBrush(colorList[i].GetBrightness() > 0.5 ? Color.Black : Color.White))
                {
                    SizeF legendSize = graphics.MeasureString(legendList[i], Font);

                    PointF legentPoint = new PointF
                    (
                        (float)(0.5 * (rectangleWidth + rectangleX - legendSize.Width)), 
                        (float)(rectangle.Y + 0.5 * (legendSize.Height - legendSize.Height))
                    );

                    graphics.DrawString(legendList[i], Font, stringBrush, legentPoint);
                }

                rectangleX = rectangleWidth;
            }
        }

        #endregion
        #region 그래프 칠하기 - PaintGraph(graphics, width, height)

        /// <summary>
        /// 그래프 칠하기
        /// </summary>
        /// <param name="graphics">그래픽스</param>
        /// <param name="width">너비</param>
        /// <param name="height">높이</param>
        protected void PaintGraph(Graphics graphics, int width, int height)
        {
            double upperX = 0d;
            double lowerX = 0d;
            double upperY = 0d;
            double lowerY = 0d;

            bool foundFirst = false;

            List<string> legendList = new List<string>();
            List<Color>  colorList  = new List<Color>();

            for(int i = 0; i < LineList.Count; i++)
            {
                if(LineList[i] == null)
                {
                    continue;
                }

                if(LineList[i].Visible == false)
                {
                    continue;
                }

                if(!foundFirst)
                {
                    upperX = LineList[i].MaximumX;
                    lowerX = LineList[i].MinimumX;
                    upperY = LineList[i].MaximumY;
                    lowerY = LineList[i].MinimumY;

                    foundFirst = true;
                }

                legendList.Add(LineList[i].Name     );
                colorList.Add (LineList[i].LineColor);

                upperX = upperX > LineList[i].MaximumX ? upperX : LineList[i].MaximumX;
                lowerX = lowerX < LineList[i].MinimumX ? lowerX : LineList[i].MinimumX;
                upperY = upperY > LineList[i].MaximumY ? upperY : LineList[i].MaximumY;
                lowerY = lowerY < LineList[i].MinimumY ? lowerY : LineList[i].MinimumY;
            }

            graphics.Clear(BackColor);

            Rectangle drawRectangle = new Rectangle(0, 0, width, height);

            if(ShowLegend)
            {
                int legendHeight = 5 + (int)graphics.MeasureString("AjtW", Font).Height;

                if(height - legendHeight > 10)
                {
                    drawRectangle = new Rectangle(0, 0, width, height - legendHeight);

                    Rectangle legendRectangle = new Rectangle(0, height - legendHeight + 2, width, legendHeight - 2);

                    DrawLegend(graphics, legendRectangle, legendList, colorList);    
                }
            }

            if(ShowAxis)
            {
                // 추후 추가
            }

            if(!foundFirst)
            {
                return;
            }

            for(int i = 0; i < LineList.Count; i++)
            {
                if(LineList[i] == null)
                {
                    continue;
                }

                if(LineList[i].Visible == false)
                {
                    continue;
                }

                LineList[i].Draw(graphics, drawRectangle, lowerX, upperX, lowerY, upperY);
            }
        }

        #endregion
        #region 버퍼 교체하기 - SwapBuffer()

        /// <summary>
        /// 버퍼 교체하기
        /// </summary>
        protected void SwapBuffer()
        {
            Bitmap temporaryBitmap = this.frontBufferBitmap;

            this.frontBufferBitmap = this.backBufferBitmap;

            this.backBufferBitmap = temporaryBitmap;
        }

        #endregion
        #region 그래프 그리기 - DrawGraph()

        /// <summary>
        /// 그래프 그리기
        /// </summary>
        protected void DrawGraph()
        {
            ResizeBuffer();

            using(Graphics graphics = Graphics.FromImage(this.backBufferBitmap))
            {
                PaintGraph(graphics, this.backBufferBitmap.Width, this.backBufferBitmap.Height);
            }

            SwapBuffer();

            this.pictureBox.Image = this.frontBufferBitmap;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 타이머 틱 처리하기 - timer_Tick(sender, e)

        /// <summary>
        /// 타이머 틱 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            DrawGraph();
        }

        #endregion
        #region 픽처 박스 크기 조정시 처리하기 - pictureBox_Resize(sender, e)

        /// <summary>
        /// 픽처 박스 크기 조정시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pictureBox_Resize(object sender, EventArgs e)
        {
            StartTimer();
        }

        #endregion
    }
}

 

300x250

 

▶ Stack.cs

using System;
using System.Drawing;

namespace TestProject
{
    /// <summary>
    /// 스택
    /// </summary>
    public class Stack
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Protected

        #region Field

        /// <summary>
        /// 색상 배열
        /// </summary>
        protected static Color[] _colorArray;

        /// <summary>
        /// 감마 수정 배열
        /// </summary>
        protected static int[] _gammaCorrectionArray = new int[1000];

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region Field

        /// <summary>
        /// 색상 배열
        /// </summary>
        public Color[] ColorArray = null;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Static

        #region 생성자 - Stack()

        /// <summary>
        /// 생성자
        /// </summary>
        static Stack()
        {
            Color[] colorArray =
            {
                Color.Black,
                Color.Blue,
                Color.Green,
                Color.GreenYellow,
                Color.Yellow,
                Color.Orange,
                Color.OrangeRed,
                Color.Red
            };

            _colorArray = new Color[colorArray.Length * 100];

            int k = 0;

            for(int i = 0; i < colorArray.Length - 1; i++)
            {
                for(int j = 0; j < 100; j++)
                {
                    Color lowColor  = colorArray[i];
                    Color highColor = colorArray[i + 1];

                    int r = ((lowColor.R * (100 - j)) + (highColor.R * j)) / 100;
                    int g = ((lowColor.G * (100 - j)) + (highColor.G * j)) / 100;
                    int b = ((lowColor.B * (100 - j)) + (highColor.B * j)) / 100;

                    _colorArray[k++] = Color.FromArgb(r, g, b);
                }
            }

            float gammaCorrection = 0.3f;

            float gain = (float)((_colorArray.Length - 1) / Math.Pow(_gammaCorrectionArray.Length, gammaCorrection));

            for(int i = 0; i < _gammaCorrectionArray.Length; i++)
            {
                int value = (int)(gain * Math.Pow((double)i, gammaCorrection));

                value = value < 0 ? 0 : value > _colorArray.Length - 1 ? _colorArray.Length - 1 : value;

                _gammaCorrectionArray[i] = value;
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - Stack(valueArray, minimumValue, maximumValue)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="valueArray">값 배열</param>
        /// <param name="maximumValue">최소 값</param>
        /// <param name="minimumValue">최대 값</param>
        public Stack(float[] valueArray, double minimumValue, double maximumValue)
        {
            float scale = (float)(_gammaCorrectionArray.Length / (maximumValue - minimumValue));

            ColorArray = new Color[valueArray.Length / 2];

            for(int i = 0; i < ColorArray.Length; i++)
            {
                int value = (int)((valueArray[i] - minimumValue) * scale);

                value = value < 0 ? 0 : value > _colorArray.Length - 1 ? _colorArray.Length - 1 : value;
                value = _gammaCorrectionArray[value];

                ColorArray[i] = _colorArray[value];
            }
        }

        #endregion
    }
}

 

▶ SoundVisualizer.cs

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 사운드 시각화기
    /// </summary>
    public partial class SoundVisualizer : UserControl
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region Field

        /// <summary>
        /// 종료 여부
        /// </summary>
        protected bool closed = false;

        /// <summary>
        /// 후면 버퍼 비트맵
        /// </summary>
        protected Bitmap backBufferBitmap = null;

        /// <summary>
        /// 전면 버퍼 비트맵
        /// </summary>
        protected Bitmap frontBufferBitmap = null;

        /// <summary>
        /// 스택 리스트
        /// </summary>
        protected List<Stack> stackList = new List<Stack>();

        /// <summary>
        /// 스택 리스트 잠금 객체
        /// </summary>
        protected object stackListLockObject = new object();

        /// <summary>
        /// 최소 값
        /// </summary>
        protected float minimumValue = 0;

        /// <summary>
        /// 최대 값
        /// </summary>
        protected float maximumValue = 0;

        /// <summary>
        /// 값 배열 리스트
        /// </summary>
        protected List<float[]> valueArrayList = new List<float[]>();

        /// <summary>
        /// 스레드 잠금 객체
        /// </summary>
        protected object threadLockObject = new object();

        /// <summary>
        /// 작업 스레드
        /// </summary>
        protected Thread workThread = null;

        #endregion

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

        #region 스펙트럼 너비 - SpectralWidth

        /// <summary>
        /// 스펙트럼 너비
        /// </summary>
        public int SpectralWidth { get; set; }

        #endregion
        #region FFT 데이터 표시 여부 - ShowFFT

        /// <summary>
        /// FFT 데이터 표시 여부
        /// </summary>
        public bool ShowFFT { get; set; }

        #endregion

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

        #region 생성자 - SoundVisualizer()
 
        /// <summary>
        /// 생성자
        /// </summary>
        public SoundVisualizer()
        {
            InitializeComponent();

            ShowFFT       = true;
            SpectralWidth = 256;

            Disposed               += UserControl_Disposed;
            this.pictureBox.Resize += pictureBox_Resize;
            this.timer.Tick        += timer_Tick;
        }

        #endregion

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

        #region 타이머 시작하기 - StartTimer()

        /// <summary>
        /// 타이머 시작하기
        /// </summary>
        public void StartTimer()
        {
            this.timer.Enabled = true;
        }

        #endregion
        #region 데이터 추가하기 - AppendData(valueArray)

        /// <summary>
        /// 데이터 추가하기
        /// </summary>
        /// <param name="valueArray">값</param>
        public void AppendData(float[] valueArray)
        {
            lock(this.threadLockObject)
            {
                this.valueArrayList.Add(valueArray);

                if(this.workThread == null)
                {
                    this.workThread = new Thread(new ThreadStart(ProcessWorkThread));

                    this.workThread.Name = "Sound visualization work thread.";

                    this.workThread.Start();
                }
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Protected
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 사용자 컨트롤 리소스 해제시 처리하기 - UserControl_Disposed(sender, e)

        /// <summary>
        /// 사용자 컨트롤 리소스 해제시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        protected void UserControl_Disposed(object sender, EventArgs e)
        {
            this.closed = true;
        }

        #endregion
        #region 픽처 박스 크기 조정시 처리하기 - pictureBox_Resize(sender, e)

        /// <summary>
        /// 픽처 박스 크기 조정시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        protected void pictureBox_Resize(object sender, EventArgs e)
        {
            this.timer.Enabled = true;
        }

        #endregion
        #region 타이머 틱 처리하기 - timer_Tick(object sender, EventArgs e)

        /// <summary>
        /// 타이머 틱 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            Redraw();
        }

        #endregion

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

        #region  버퍼 크기 변경하기 - ResizeBuffer()

        /// <summary>
        /// 버퍼 크기 변경하기
        /// </summary>
        protected void ResizeBuffer()
        {
            int width  = this.pictureBox.Width;
            int height = SpectralWidth / 2;

            width  = width  < 10 ? 10 : width;
            height = height < 10 ? 10 : height;
            
            Bitmap previousButmap = this.backBufferBitmap;

            if(previousButmap == null)
            {
                this.backBufferBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
            }
            else
            {
                if((this.backBufferBitmap.Width != width) || (this.backBufferBitmap.Height != height))
                {
                    this.backBufferBitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
                }
            }

            if(this.backBufferBitmap != previousButmap)
            {
                if(previousButmap != null)
                {
                    previousButmap.Dispose();
                }
            }
        }

        #endregion
        #region 그래프 칠하기 - PaintGraph(graphics, bitmap, width, height)

        /// <summary>
        /// 그래프 칠하기
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="bitmap">비트맵</param>
        /// <param name="width">너비</param>
        /// <param name="height">높이</param>
        protected void PaintGraph(Graphics graphics, Bitmap bitmap, int width, int height)
        {
            List<Stack> stackList = new List<Stack>();

            lock(this.stackListLockObject)
            {
                if(this.stackList.Count > 1)
                {
                    stackList.AddRange(this.stackList);

                    this.stackList.Clear();
                }
            }
            
            if(this.frontBufferBitmap == null)
            {
                graphics.Clear(Color.Black);
            }
            else
            {
                graphics.DrawImage(this.frontBufferBitmap, new Point(stackList.Count, 0));

                if(stackList.Count < 1)
                {
                    return;
                }
            }

            for(int i = 0; i < stackList.Count; i++)
            {
                int colorArrayLength = stackList[0].ColorArray.Length; 

                for(int j = 0; j < height; j++)
                {
                    bitmap.SetPixel(i, colorArrayLength - j - 1, stackList[stackList.Count - 1 - i].ColorArray[j]);
                }
            }
        }

        #endregion
        #region 버퍼 교체하기 - SwapBuffer()

        /// <summary>
        /// 버퍼 교체하기
        /// </summary>
        protected void SwapBuffer()
        {
            Bitmap temporaryBitmap = this.frontBufferBitmap;

            this.frontBufferBitmap = this.backBufferBitmap;

            this.backBufferBitmap  = temporaryBitmap;
        }

        #endregion
        #region 다시 그리기 - Redraw()

        /// <summary>
        /// 다시 그리기
        /// </summary>
        protected void Redraw()
        {
            ResizeBuffer();

            using(Graphics graphics = Graphics.FromImage(this.backBufferBitmap))
            {
                PaintGraph(graphics, this.backBufferBitmap, this.backBufferBitmap.Width, this.backBufferBitmap.Height);
            }

            SwapBuffer();

            this.pictureBox.Image = this.frontBufferBitmap;
        }

        #endregion
        #region 스택 리스트 추가하기 - AddStackList(sourceValueArray)

        /// <summary>
        /// 스택 리스트 추가하기
        /// </summary>
        /// <param name="sourceValueArray">소스 값 배열</param>
        protected void AddStackList(float[] sourceValueArray)
        {
            List<Stack> stackList = new List<Stack>();

            int     k                  = 0;
            float[] temporarValueArray = new float[SpectralWidth];
            float[] fftArray           = new float[temporarValueArray.Length];

            int nextK = 0;

            while(k <= sourceValueArray.Length - temporarValueArray.Length)
            {
                nextK = k + (temporarValueArray.Length >> 3);

                for(int i = 0; i < temporarValueArray.Length; i++)
                {
                    temporarValueArray[i] = sourceValueArray[k++];
                }

                FFT.ComputeForwardFFT(temporarValueArray, fftArray);

                float[] targetValueArray = ShowFFT ? fftArray : temporarValueArray;

                float minmiumValue = targetValueArray[0];
                float maximumValue = targetValueArray[0];
              
                for(int i = 0; i < targetValueArray.Length; i++)
                {
                    minmiumValue = minmiumValue < targetValueArray[i] ? minmiumValue : targetValueArray[i];
                    maximumValue = maximumValue > targetValueArray[i] ? maximumValue : targetValueArray[i];
                }

                minmiumValue = float.IsNaN(minmiumValue) ? this.minimumValue : minmiumValue;
                maximumValue = float.IsNaN(maximumValue) ? this.maximumValue : maximumValue;

                minmiumValue = float.IsInfinity(minmiumValue) ? this.minimumValue : minmiumValue;
                maximumValue = float.IsInfinity(maximumValue) ? this.maximumValue : maximumValue;

                this.minimumValue = (255 * this.minimumValue + minmiumValue) / 256;
                this.maximumValue = (255 * this.maximumValue + maximumValue) / 256;

                stackList.Add(new Stack(targetValueArray, this.minimumValue, this.maximumValue));

                k = nextK;
            }

            lock(this.stackListLockObject)
            {
                this.stackList.AddRange(stackList);
            }
        }

        #endregion
        #region 작업 스레드 처리하기 - ProcessWorkThread()

        /// <summary>
        /// 작업 스레드 처리하기
        /// </summary>
        protected void ProcessWorkThread()
        {
            float[] lastValueArray = null;

            while(this.closed == false)
            {
                Thread.Sleep(100);

                float[] valueArray = null;

                lock(this.threadLockObject)
                {
                    if(this.valueArrayList.Count > 0)
                    {
                        valueArray = this.valueArrayList[0];

                        this.valueArrayList.RemoveAt(0);
                    }
                }

                if(valueArray != null)
                {
                    if(lastValueArray != null)
                    {
                        int spectralWidth = SpectralWidth;

                        float[] temporaryValueArray = new float[(int)(spectralWidth * 1.75f)];

                        int k                   = 0; 
                        int swo                 = spectralWidth - (spectralWidth >> 3);
                        int lastValueArrayStart = lastValueArray.Length - swo;

                        lastValueArrayStart = lastValueArrayStart < 0 ? 0 : lastValueArrayStart;

                        for(int i = lastValueArrayStart; i < lastValueArray.Length; i++)
                        {
                            temporaryValueArray[k++] = lastValueArray[i];
                        }
                      
                        for(int i = 0; i < swo && k < temporaryValueArray.Length && i < valueArray.Length; i++)
                        {
                            temporaryValueArray[k++] = valueArray[i];
                        }

                        AddStackList(temporaryValueArray);
                    }

                    AddStackList(valueArray);
                    
                    lastValueArray = valueArray;
                }
            }
        }

        #endregion
    }
}

 

▶ SoundSource.cs

using Microsoft.DirectX.DirectSound;
using Microsoft.Win32.SafeHandles;
using System;
using System.Collections.Generic;
using System.Threading;

namespace TestProject
{
    /// <summary>
    /// 사운드 소스
    /// </summary>
    public class SoundSource : IDisposable
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Delegate
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 데이터 처리하기 대리자 - ProcessDataDelegate(valueArray)

        /// <summary>
        /// 데이터 처리하기 대리자
        /// </summary>
        /// <param name="valueArray">값 배열</param>
        public delegate void ProcessDataDelegate(float[] valueArray);

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Event
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 데이터 처리시 이벤트 - ProcessData

        /// <summary>
        /// 데이터 처리시 이벤트
        /// </summary>
        public event ProcessDataDelegate ProcessData;

        #endregion
        #region FFT 데이터 처리시 이벤트 - ProcessFFTData

        /// <summary>
        /// FFT 데이터 처리시 이벤트
        /// </summary>
        public event ProcessDataDelegate ProcessFFTData;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region Field

        /// <summary>
        /// 리소스 해제 잠금 객체
        /// </summary>
        protected object disposeLock = new object();

        /// <summary>
        /// 리소스 해제 여부
        /// </summary>
        protected bool disposed = false;

        /// <summary>
        /// 캡처
        /// </summary>
        protected Capture capture = null;

        /// <summary>
        /// 캡처 버퍼
        /// </summary>
        protected CaptureBuffer captureBuffer = null;

        /// <summary>
        /// 캡처 스레드
        /// </summary>
        protected Thread captureThread = null;

        /// <summary>
        /// 캡처 버퍼 길이
        /// </summary>
        protected int captureBufferLength = 0;

        /// <summary>
        /// 통지
        /// </summary>
        protected Notify notify;

        /// <summary>
        /// 버퍼 위치 이벤트
        /// </summary>
        protected AutoResetEvent bufferPositionEvent = null;

        /// <summary>
        /// 버퍼 위치 이벤트 핸들
        /// </summary>
        protected SafeWaitHandle bufferPositionEventHandle = null;

        /// <summary>
        /// 스레드 종료 이벤트
        /// </summary>
        protected ManualResetEvent threadTerminateEvent = null;

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 초 단위 버퍼 시간
        /// </summary>
        private const int BUFFER_TIME_IN_SECOND = 5;

        /// <summary>
        /// 초 단위 통지 포인트
        /// </summary>
        private const int NOTIFICATION_POINT_IN_SECOND = 2;

        #endregion

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

        #region GUID - GUID

        /// <summary>
        /// GUID
        /// </summary>
        public Guid GUID { get; set; }

        #endregion
        #region 설명 - Description

        /// <summary>
        /// 설명
        /// </summary>
        public string Description { get; set; }

        #endregion
        #region 디폴트 여부 - IsDefault

        /// <summary>
        /// 디폴트 여부
        /// </summary>
        public bool IsDefault
        {
            get
            {
                return GUID == Guid.Empty;
            }
        }

        #endregion
        #region 캡처 여부 - IsCapturing

        /// <summary>
        /// 캡처 여부
        /// </summary>
        public bool IsCapturing { get; protected set; }

        #endregion
        #region 샘플 비율 (단위 : HZ) - SampleRate

        /// <summary>
        /// 샘플 비율 (단위 : HZ)
        /// </summary>
        public int SampleRate { get; set; }

        #endregion

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

        #region 생성자 - SoundSource()

        /// <summary>
        /// 생성자
        /// </summary>
        public SoundSource()
        {
            CaptureDevicesCollection collection = new CaptureDevicesCollection();

            for(int i = 0; i < collection.Count; i++)
            {
                if(collection[i].DriverGuid == Guid.Empty)
                {
                    GUID        = collection[i].DriverGuid;
                    Description = collection[i].Description;

                    break;
                }
            }

            this.bufferPositionEvent       = new AutoResetEvent(false);
            this.bufferPositionEventHandle = this.bufferPositionEvent.SafeWaitHandle;
            this.threadTerminateEvent      = new ManualResetEvent(true);
        }

        #endregion
        #region 생성자 - SoundSource(guid, description)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="guid">GUID</param>
        /// <param name="description">설명</param>
        public SoundSource(Guid guid, string description)
        {
            GUID        = guid;
            Description = description;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Destructor

        #region 소멸자 - ~SoundSource()

        /// <summary>
        /// 소멸자
        /// </summary>
        ~SoundSource()
        {
            Dispose(false);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 사운드 소스 리스트 구하기 - GetSoundSourceList()

        /// <summary>
        /// 사운드 소스 리스트 구하기
        /// </summary>
        /// <returns>사운드 소스 리스트</returns>
        public static List<SoundSource> GetSoundSourceList()
        {
            CaptureDevicesCollection collection = new CaptureDevicesCollection();

            List<SoundSource> sourceSourceList = new List<SoundSource>();

            foreach(DeviceInformation deviceInformation in collection)
            {
                sourceSourceList.Add(new SoundSource(deviceInformation.DriverGuid, deviceInformation.Description));
            }

            return sourceSourceList;
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Instance
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 시작하기 - Start()

        /// <summary>
        /// 시작하기
        /// </summary>
        public void Start()
        {
            if(IsCapturing)
            {
                return;
            }

            IsCapturing = true;

            this.capture = new Capture(GUID);

            WaveFormat waveFormat = new WaveFormat();

            waveFormat.Channels              = (short)this.capture.Caps.Channels;
            waveFormat.BitsPerSample         = 16;
            waveFormat.SamplesPerSecond      = 22000;
            waveFormat.FormatTag             = WaveFormatTag.Pcm;
            waveFormat.BlockAlign            = (short)((waveFormat.Channels * waveFormat.BitsPerSample + 7) / 8);
            waveFormat.AverageBytesPerSecond = waveFormat.BlockAlign * waveFormat.SamplesPerSecond;

            this.captureBufferLength = waveFormat.AverageBytesPerSecond * BUFFER_TIME_IN_SECOND;

            CaptureBufferDescription captureBufferDescription = new CaptureBufferDescription();

            captureBufferDescription.Format      = waveFormat;
            captureBufferDescription.BufferBytes = this.captureBufferLength;

            this.captureBuffer = new CaptureBuffer(captureBufferDescription, this.capture);

            int waitHandleCount = BUFFER_TIME_IN_SECOND * NOTIFICATION_POINT_IN_SECOND;

            BufferPositionNotify[] bufferPositionNotifyArray = new BufferPositionNotify[waitHandleCount];

            for(int i = 0; i < waitHandleCount; i++)
            {
                BufferPositionNotify bufferPositionNotify = new BufferPositionNotify();

                bufferPositionNotify.Offset            = (i + 1) * this.captureBufferLength / bufferPositionNotifyArray.Length - 1;
                bufferPositionNotify.EventNotifyHandle = this.bufferPositionEventHandle.DangerousGetHandle();

                bufferPositionNotifyArray[i] = bufferPositionNotify;
            }

            this.notify = new Notify(this.captureBuffer);

            this.notify.SetNotificationPositions(bufferPositionNotifyArray);

            this.threadTerminateEvent.Reset();

            this.captureThread = new Thread(new ThreadStart(ProcessCaptureThread));

            this.captureThread.Name = "Sound capture";

            this.captureThread.Start();
        }

        #endregion
        #region 중단하기 - Stop()

        /// <summary>
        /// 중단하기
        /// </summary>
        public void Stop()
        {
            if(IsCapturing)
            {
                IsCapturing = false;

                this.threadTerminateEvent.Set();

                this.captureThread.Join();

                this.notify.Dispose();

                this.captureBuffer.Dispose();

                this.capture.Dispose();
            }
        }

        #endregion

        #region 리소스 해제하기 - Dispose(disposing)

        /// <summary>
        /// 리소스 해제하기
        /// </summary>
        /// <param name="disposing">리소스 해제 여부</param>
        public void Dispose(bool disposing)
        {
            lock(this.disposeLock)
            {
                if(this.disposed)
                {
                    return;
                }

                this.disposed = true;
            }

            GC.SuppressFinalize(this);

            if(IsCapturing)
            {
                Stop();
            }

            if(this.bufferPositionEventHandle != null)
            {
                this.bufferPositionEventHandle.Dispose();
            }

            if(this.bufferPositionEvent != null)
            {
                this.bufferPositionEvent.Close();
            }

            if(this.threadTerminateEvent != null)
            {
                this.threadTerminateEvent.Close();
            }
        }

        #endregion
        #region 리소스 해제하기 - IDisposable.Dispose()

        /// <summary>
        /// 리소스 해제하기
        /// </summary>
        void IDisposable.Dispose()
        {
            Dispose(true);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Private

        #region 값 처리하기 - ProcessValue(valueArray)

        /// <summary>
        /// 값 처리하기
        /// </summary>
        /// <param name="valueArray">값 배열</param>
        protected void ProcessValue(short[] valueArray)
        {
            ProcessDataDelegate processData    = ProcessData;
            ProcessDataDelegate processFFTData = ProcessFFTData;

            if((processData == null) && (processFFTData == null))
            {
                return;
            }

            int size = 2;

            while(size < valueArray.Length)
            {
                size *= 2;
            }

            float[] xfArray = new float[size];
            float[] xtArray = new float[valueArray.Length];

            for(int i = 0; i < valueArray.Length; i++)
            {
                float value = (float)valueArray[i];

                xfArray[i] = value;
                xtArray[i] = value;
            }

            for(int i = valueArray.Length; i < size; i++)
            {
                xfArray[i] = 0;
            }
       
            if(processData != null)
            {
                processData(xtArray);
            }

            if(processFFTData != null)
            {
                float[] fftArray = new float[xfArray.Length];

                FFT.ComputeForwardFFT(xfArray, fftArray);

                processFFTData(fftArray);
            }
        }

        #endregion
        #region 캡처 스레드 처리하기 - ProcessCaptureThread()

        /// <summary>
        /// 캡처 스레드 처리하기
        /// </summary>
        private void ProcessCaptureThread()
        {
            this.captureBuffer.Start(true);

            try
            {
                int nextCapturePosition = 0;

                WaitHandle[] handleArray = new WaitHandle[] {this.threadTerminateEvent, this.bufferPositionEvent };

                while(WaitHandle.WaitAny(handleArray) > 0)
                {
                    int capturePosition;
                    int readPosition;

                    this.captureBuffer.GetCurrentPosition(out capturePosition, out readPosition);

                    int lockSize = readPosition - nextCapturePosition;

                    if(lockSize < 0)
                    {
                        lockSize += this.captureBufferLength;
                    }

                    if((lockSize & 1) != 0)
                    {
                        lockSize--;
                    }

                    int itemCount = lockSize >> 1;

                    short[] valueArray = (short[])this.captureBuffer.Read
                    (
                        nextCapturePosition,
                        typeof(short),
                        LockFlag.None,
                        itemCount
                    );

                    ProcessValue(valueArray);

                    nextCapturePosition = (nextCapturePosition + lockSize) % this.captureBufferLength;
                }
            }
            catch(Exception exception)
            {
                Console.WriteLine(exception.Message);
            }
            finally
            {
                this.captureBuffer.Stop();
            }
        }

        #endregion
    }
}

 

▶ SoundPlayback.cs

using Microsoft.DirectX.DirectSound;
using System;
using System.IO;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 사운드 재생
    /// </summary>
    public class SoundPlayback : IDisposable
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region Field

        /// <summary>
        /// 장치
        /// </summary>
        protected Device device = null;

        /// <summary>
        /// 2차 버퍼
        /// </summary>
        protected SecondaryBuffer secondaryBuffer = null;

        /// <summary>
        /// 위치
        /// </summary>
        protected int location = 0;

        /// <summary>
        /// 리소스 해제 여부
        /// </summary>
        protected bool disposed = false;

        #endregion


        //////////////////////////////////////////////////////////////////////////////////////////////////// Destructor

        #region 소멸자 - ~SoundPlayback()

        /// <summary>
        /// 소멸자
        /// </summary>
        ~SoundPlayback()
        {
            Dispose(false);
        }

        #endregion

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

        #region 셋업하기 - Setup(ownerControl, filePath)

        /// <summary>
        /// 셋업하기
        /// </summary>
        /// <param name="ownerControl">소유자 컨트롤</param>
        /// <param name="filePath">파일 경로</param>
        public void Setup(Control ownerControl, string filePath)
        {
            this.device = new Device();

            this.device.SetCooperativeLevel(ownerControl, CooperativeLevel.Priority);

            this.secondaryBuffer = new SecondaryBuffer(filePath, this.device);

            this.secondaryBuffer.SetCurrentPosition(0);
        }

        #endregion
        #region 데이터 구하기 - GetData(valueArray)

        /// <summary>
        /// 데이터 구하기
        /// </summary>
        /// <param name="valueArray">값 배열</param>
        public bool GetData(float[] valueArray)
        {
            try
            {
                int bitCountPerSample = this.secondaryBuffer.Format.BitsPerSample;

                byte[] byteArray = new byte[valueArray.Length * bitCountPerSample / 8];

                MemoryStream stream = new MemoryStream(byteArray);

                stream.Position = 0;

                this.secondaryBuffer.Read(this.location, stream, valueArray.Length, LockFlag.None);

                this.location += valueArray.Length;

                int k = 0;

                if(bitCountPerSample == 16)
                {
                    for(int i = 0; i < valueArray.Length; i++)
                    {
                        int lower = byteArray[k++];
                        int upper = byteArray[k++];

                        valueArray[i] = (float)(lower + (upper * 256));
                    }
                }
                else
                {
                    for(int i = 0; i < valueArray.Length; i++)
                    {
                        valueArray[i] = (float)(byteArray[k++]);
                    }
                }
            }
            catch(Exception)
            {
                return false;
            }

            return true;
        }

        #endregion

        #region 리소스 해제하기 - Dispose(disposing)

        /// <summary>
        /// 리소스 해제하기
        /// </summary>
        /// <param name="disposing">리소스 해제 여부</param>
        public void Dispose(bool disposing)
        {
            if(this.disposed)
            {
                return;
            }

            this.disposed = true;
            
            GC.SuppressFinalize(this);

            this.secondaryBuffer.Dispose();

            this.device.Dispose();
        }

        #endregion
        #region 리소스 해제하기 - IDisposable.Dispose()

        /// <summary>
        /// 리소스 해제하기
        /// </summary>
        void IDisposable.Dispose()
        {
            Dispose(true);
        }

        #endregion
    }
}

 

▶ MainForm.cs

using System;
using System.ComponentModel;
using System.Windows.Forms;

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

        #region Field

        /// <summary>
        /// 사운드 소스
        /// </summary>
        protected SoundSource soundSource = null;

        /// <summary>
        /// 사운드 재생
        /// </summary>
        protected SoundPlayback soundPlayback = null;

        #endregion

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

        #region 생성자 - MainForm()

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

            this.listenButton.Click += listenButton_Click;
            this.fileButton.Click   += fileButton_Click;
            this.timer.Tick         += timer_Tick;
        }

        #endregion

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

        #region 종료시 처리하기 - OnClosing(e)

        /// <summary>
        /// 종료시 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            if(this.soundSource != null)
            {
                this.soundSource.Dispose(true);
            }

            if(this.soundPlayback != null)
            {
                this.soundPlayback.Dispose(true);
            }
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region 사운드 소스 데이터 처리하기 - soundSource_ProcessData(valueArray)

        /// <summary>
        /// 사운드 소스 데이터 처리하기
        /// </summary>
        /// <param name="valueArray">값 배열</param>
        private void soundSource_ProcessData(float[] valueArray)
        {
            this.soundVisualizer.AppendData(valueArray);

            this.soundVisualizer.StartTimer();
        }

        #endregion
        #region 사운드 소스 FFT 데이터 처리하기 - soundSource_ProcessFFTData(valueArray)

        /// <summary>
        /// 사운드 소스 FFT 데이터 처리하기
        /// </summary>
        /// <param name="valueArray">값 배열</param>
        private void soundSource_ProcessFFTData(float[] valueArray)
        {
            float[] temporaryValueArray = new float[valueArray.Length / 2];

            for(int i = 0; i < temporaryValueArray.Length; i++)
            {
                temporaryValueArray[i] = valueArray[i];
            }

            if(this.graphControl.LineList.Count < 1)
            {
                this.graphControl.LineList.Add(new Line());
            }

            this.graphControl.LineList[0].Update(temporaryValueArray);

            this.graphControl.StartTimer();
        }

        #endregion
        #region 실시간 청취 버튼 클릭시 처리하기 - listenButton_Click(sender, e)

        /// <summary>
        /// 실시간 청취 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void listenButton_Click(object sender, EventArgs e)
        {
            if(this.soundSource != null)
            {
                if(this.soundSource.IsCapturing)
                {
                    this.soundSource.Stop();
                }
                else
                {
                    this.soundSource.Start();
                }
            }
            else
            {
                this.soundSource = new SoundSource();

                this.soundSource.ProcessData    += soundSource_ProcessData;
                this.soundSource.ProcessFFTData += soundSource_ProcessFFTData;

                this.soundSource.Start();
            }
        }

        #endregion
        #region 파일 분석 버튼 클릭시 처리하기 - fileButton_Click(sender, e)

        /// <summary>
        /// 파일 분석 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void fileButton_Click(object sender, EventArgs e)
        {
            if(this.soundPlayback == null)
            {
                this.soundPlayback = new SoundPlayback();
            }

            DialogResult result = this.openFileDialog.ShowDialog();

            if(result != DialogResult.OK)
            {
                return;
            }

            this.soundPlayback.Setup(this, this.openFileDialog.FileName);

            this.timer.Enabled = true;
        }

        #endregion
        #region 타이머 틱 처리하기 - timer_Tick(sender, e)

        /// <summary>
        /// 타이머 틱 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void timer_Tick(object sender, EventArgs e)
        {
            float[] valueArray = new float[1024];
 
            if(this.soundPlayback.GetData(valueArray) == false)
            {
                this.timer.Enabled = false;

                return;
            }
     
            soundSource_ProcessData(valueArray);

            float[] fftArray = new float[valueArray.Length];

            FFT.ComputeForwardFFT(valueArray, fftArray);

            soundSource_ProcessFFTData(fftArray);
            soundSource_ProcessData(fftArray);
        }

        #endregion
    }
}

※ 32비트 버전으로 실행해야 한다.

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

댓글을 달아 주세요