1

我正在尝试为我在学校做的一个项目实施离散傅立叶变换算法。但是创建一个类似乎很困难(不应该如此)。我正在使用 Visual Studio 2012。

基本上我需要一个名为 Complex 的类来存储我从 DFT 获得的两个值;实部和虚部。

到目前为止,这就是我所拥有的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SoundEditor_V3
{
    public class Complex
    {
        public double real;
        public double im;

        public Complex()
        {
            real = 0;
            im = 0;
        }
    }
}

问题是它不能将构造函数识别为构造函数,我只是在学习C#,但我在网上查了一下,这就是它应该的样子。但它将我的构造函数识别为一种方法。

这是为什么?我创建的课程错了吗?

它对我的傅立叶课也做同样的事情。所以每次我尝试创建一个傅立叶对象然后使用它的方法......没有这样的事情。

例如,我这样做:

Fourier fou = new Fourier();
fou.DFT(s, N,  amp, 0);

它告诉我fou 是一个“字段”,但被用作“类型”, 为什么这么说?

这也是我的傅立叶类的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SoundEditor_V3
{
   public class Fourier
   {

        //FOURIER
        //N = number of samples
        //s is the array of samples(data)
        //amp is the array where the complex result will be written to
        //start is the where in the array to start
        public void DFT(byte[] s, int N, ref Complex[] amp, int start)
        {
            Complex tem = new Complex();
            int f;
            int t;

            for (f = 0; f < N; f++)
            {

                tem.real = 0;
                tem.im = 0;

                for (t = 0; t < N; t++)
                {
                    tem.real += s[t + start] * Math.Cos(2 * Math.PI * t * f / N);
                    tem.im -= s[t + start] * Math.Sin(2 * Math.PI * t * f / N);

                }
                amp[f].real = tem.real;
                amp[f].im = tem.im;

            }
        }

        //INVERSE FOURIER
        public void IDFT(Complex[] A, ref int[] s)
        {
            int N = A.Length;
            int t, f;
            double result;
            for (t = 0; t < N; t++)
            {
                result = 0;
                for (f = 0; f < N; f++)
                {
                    result += A[f].real * Math.Cos(2 * Math.PI * t * f / N) - A[f].im * Math.Sin(2 * Math.PI * t * f / N);
                }
                s[t] = (int)Math.Round(result);
            }
        }
    }
}

我现在非常困惑,任何和所有的帮助都将不胜感激。谢谢你。

编辑:

这是我试图访问我的所有课程的地方:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SoundEditor_V3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        string filename;
        NAudio.Wave.WaveStream waveStream;

        private NAudio.Wave.DirectSoundOut sout = null;

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {

            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Wave File (*.wav)|*.wav";
            if (open.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            waveStream = new NAudio.Wave.WaveFileReader(open.FileName);
            filename = open.FileName;
            sout = new NAudio.Wave.DirectSoundOut();
            sout.Init(new NAudio.Wave.WaveChannel32(waveStream));

        }

        //Play
        private void Play_btn_Click(object sender, EventArgs e)
        {
            sout.Play();
        }

        //Stop
        private void Stop_btn_Click(object sender, EventArgs e)
        {
            sout.Stop();
            waveStream.Position = 0;
        }

        //Pause
        private void Pause_btn_Click(object sender, EventArgs e)
        {
            sout.Pause();
        }



        //display fourier
        //N = number of samples(length of array)
        //s is the array of samples(data)
        //amp is the array where the complex result will be written to
        //start is the where in the array to start
        static int N = 8;
        byte[] s = {1,2,3,4,5,6,7,8};


        Complex[] amp = new Complex[N];

        Fourier xfo = new Fourier();



        //xfo.DFT(s, N,  amp, 0); 

    }
}
4

4 回答 4

1

此调用应在方法内。截至目前,它看起来直接在一个类之下。

//xfo.DFT(s, N,  amp, 0);

refamp. (因为void DFT(...., ref Complex[] amp,....)需要一个ref amp参数。

xfo.DFT(s, N, ref amp, 0);
于 2012-10-05T04:20:35.873 回答
1

哦,男孩,还有很大的改进空间。

首先,您将类 Complex 用作结构,实际上不需要将其作为类,因此将其设为结构:

public struct Complex
{
    public double Imaginary;
    public double Real;
}

不需要构造函数,默认构造函数(编译器添加)将根据字段的类型将字段设置为其默认值,并且double默认值是0.0(无论如何,这是您分配给它们的值*)。

我还将 im 重命名为 Imaginary,不要告诉我你必须输入更多,因为你有智能感知。如果你没有去下载 Mono Develop 或 Visual Studio Express。哦,我能感觉到你的想法:我们不应该依赖工具。嗯,没错,这是编写易于阅读的代码的另一个原因,即使对于那些不熟悉这些概念的人也是如此(它也使搜索更容易)。

*:我想指出它0是一个整数文字并且0.0是双精度的,但是编译器会重新调整它并优化转换,所以它在实际用途中是相同的。

让我们转到您的傅立叶类,首先是方法 DFT,我在下面复制它(重命名了 Complex 的字段名称):

    //FOURIER
    //N = number of samples
    //s is the array of samples(data)
    //amp is the array where the complex result will be written to
    //start is the where in the array to start
    public void DFT(byte[] s, int N, ref Complex[] amp, int start)
    {
        Complex tem = new Complex();
        int f;
        int t;

        for (f = 0; f < N; f++)
        {

            tem.real = 0;
            tem.im = 0;

            for (t = 0; t < N; t++)
            {
                tem.real += s[t + start] * Math.Cos(2 * Math.PI * t * f / N);
                tem.im -= s[t + start] * Math.Sin(2 * Math.PI * t * f / N);
            }
            amp[f].real = tem.real;
            amp[f].im = tem.im;

        }
    }

首先要注意的是,您说 N 是样本数,s 是样本数组。好吧,如果您有一个数组,您可以查询数组的大小,即使您只想允许处理数组的一部分(我认为您想要),这也是一个好主意。但是,真的 N 和 s?

看,这就像魔术:

   //FOURIER
   //amp is the array where the complex result will be written to
   //start is the where in the array to start
   public void DFT(byte[] samples, int samplesCount, ref Complex[] amp, int start)
   {
       Complex tem = new Complex();
       int f;
       int t;

       for (f = 0; f < samplesCount; f++)
       {

           tem.Real = 0;
           tem.Imaginary = 0;

           for (t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);
           }
           amp[f].Real = tem.Real;
           amp[f].Imaginary = tem.Imaginary;
       }
   }

好的,接下来你说 amp 是输出。那么如果它是输出,你为什么不让它te方法返回呢?

砰!

   //FOURIER
   //start is the where in the array to start
   Complex[] DFT(byte[] samples, int samplesCount, int start)
   {
       var = new Complex[samplesCount];
       Complex tem = new Complex();
       int f;
       int t;
       for (f = 0; f < samplesCount; f++)
       {

           tem.Real = 0;
           tem.Imaginary = 0;

           for (t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);
           }
           result[f].Real = tem.Real;
           result[f].Imaginary = tem.Imaginary;
       }
       return result;
   }

它真的需要是一个数组吗?yield我认为这是使用关键字和 return的好机会IEnumerable<Complex>。但我会认为你确实想要一个数组。

现在,您可能不想返回一个数组。可能您只想修改预先存在的数组的一部分。在这种情况下,您应该开始检查您的界限。即使那是真的,你根本不需要 ref !因为数组是引用类型。它是按值传递的引用,如果您不能完全理解这个想法,请相信我,您可以修改数组的内容并在没有引用的情况下看到它反映在外部...按引用传递引用允许您更改由另一个人引用,而您没有这样做。

演示:

void Main()
{
    var x = new int[1];
    Do(x);
    Console.WriteLine(x);
}

void Do (int[] array)
{
    array[0] = 1;
}

上一个程序(用 LinqPad 编译)的输出是“1”。

但是让我们回到你的代码,好吗?

我不知道f和是什么t。谢天谢地,我知道我是虚构的(它是,对吧?)。所以我不会重命名它们。但我会将它们的定义移到循环中:

   Complex[] DFT(byte[] samples, int samplesCount, int start)
   {
       var result = new Complex[samplesCount];
       Complex tem = new Complex();
       for (int f = 0; f < samplesCount; f++)
       {
           tem.Real = 0;
           tem.Imaginary = 0;
           for (int t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);

           }
           result[f].Real = tem.Real;
           result[f].Imaginary = tem.Imaginary;
       }
       return result;
   }

请注意我对var关键字的使用。有了它,编译器将变量的类型分配给我用来初始化它的类型。所以在这种情况下,结果是 aComplex[]但我不必在代码中写两次。

最后,复制Complex对象内容的那部分,好吧,我也会改变它。为什么?因为 Complex 现在是一个结构体,而结构体是值类型。所以它的内容被复制而不是参考。

   //FOURIER
   //start is the where in the array to start
   Complex[] DFT(byte[] samples, int samplesCount, int start)
   {
       var result = new Complex[samplesCount];
       Complex tem = new Complex();
       for (int f = 0; f < samplesCount; f++)
       {
           tem.Real = 0;
           tem.Imaginary = 0;
           for (int t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);

           }
           result[f] = tem;
       }
       return result;
   }

我知道您真的只想处理数组的一部分。但是请耐心等待……您将学到一些东西,并且无论如何该代码都会很有用。

我想要的下一件事是返回一个IEnumerable<Complex>接口,它表示可以迭代以获取复杂类型对象的任何内容。我也会使用yield关键字。

此外,我已经摆脱了 sampleCount 并改用 samples.Length 。

看看它有多漂亮:

    //FOURIER
    public IEnumerable<Complex> DFT(byte[] samples, int startIndex)
    {
        int samplesLength = samples.Length;
        for (int f = 0; f < samplesLength; f++)
        {
            Complex resultItem = new Complex();
            for (int t = 0; t < samplesLength; t++)
            {
                resultItem.Real += samples[t + startIndex] * Math.Cos(2 * Math.PI * t * f / samplesLength);
                resultItem.Imaginary -= samples[t + startIndex] * Math.Sin(2 * Math.PI * t * f / samplesLength);
            }
            yield return resultItem;
        }
    }

事实上,我也会去掉 startIndex (无论如何我们都不会检查边界*)。

*:也就是说,我们不检查索引是否在数组大小内。我知道,我知道,您稍后会添加它们……可能。

不管怎样,你在这里学习了一些 C#。

    //FOURIER
    public IEnumerable<Complex> DFT(byte[] samples)
    {
        int samplesLength = samples.Length;
        for (int f = 0; f < samplesLength; f++)
        {
            Complex resultItem = new Complex();
            for (int t = 0; t < samplesLength; t++)
            {
                resultItem.Real += samples[t] * Math.Cos(2 * Math.PI * t * f / samplesLength);
                resultItem.Imaginary -= samples[t] * Math.Sin(2 * Math.PI * t * f / samplesLength);
            }
            yield return resultItem;
        }
    }

好吧,接下来困扰我的事情是傅立叶类没有状态(它没有字段,或者任何值被持久化的变量......不知何故)。所以,让它成为一个带有静态方法的静态类:

public static class Fourier
{
    //FOURIER
    public static IEnumerable<Complex> DFT(byte[] samples)
    {
        int samplesLength = samples.Length;
        for (int f = 0; f < samplesLength; f++)
        {
            Complex resultItem = new Complex();
            for (int t = 0; t < samplesLength; t++)
            {
                resultItem.Real += samples[t] * Math.Cos(2 * Math.PI * t * f / samplesLength);
                resultItem.Imaginary -= samples[t] * Math.Sin(2 * Math.PI * t * f / samplesLength);
            }
            yield return resultItem;
        }
    }
}

当然你注意到我没有添加 IDFT。那是家庭作业。

现在,让我们看看你如何使用它。就我而言,我创建了一个 ConsoleApplication,只是为了让它快速启动并运行(不浪费时间设计 GUI)。

我想要的是调用 Fourier.DFT,因为它是静态的,所以我可以在没有 Fourier 类型的对象的情况下调用它(实际上我无法创建 Fourier 类型的对象,因为它是静态的)。

此方法接收类型为 的参数byte[]。那一个将是new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }。该方法将返回一些我可以用来迭代以获取 Complex 类型的对象的东西。所以我想把它放在一个循环中。

这就是我的代码的样子:

class Program
{
    static void Main(string[] args)
    {
        //display fourier
        foreach (var item in Fourier.DFT(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }))
        {
            Console.WriteLine(item);
        }
    }
}

现在......输出是......鼓声......

好吧,我看不到它,我忘记了,Console.ReadLine();但是在添加了输出之后...

Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex

等等,什么?碰巧我还没有告诉它如何将 Complex 类型的对象转换为字符串。所以让我们补充一下:

public struct Complex
{
    public double Imaginary;
    public double Real;

    public override string ToString()
    {
        return string.Format("Complex [Real: {0}, Imaginary: {1}]", Real, Imaginary);
    }
}

现在我的输出是:

Complex [Real: 36, Imaginary: 0]
Complex [Real: -4, Imaginary: 9,65685424949238]
Complex [Real: -4, Imaginary: 4]
Complex [Real: -4, Imaginary: 1,65685424949239]
Complex [Real: -4, Imaginary: -3,91874033223161E-15]
Complex [Real: -4,00000000000001, Imaginary: -1,65685424949239]
Complex [Real: -4,00000000000002, Imaginary: -4,00000000000001]
Complex [Real: -3,99999999999997, Imaginary: -9,65685424949237]

输出是否正确?我没有什么鬼主意!我必须更多地了解傅立叶(但它看起来是合法的)。

经过验证,输出是正确的。


最后一点:使用调试器逐步检查代码,您可能会发现一个惊喜(提示:yield)。

于 2012-10-05T05:41:45.850 回答
0

你需要一个方法,试试这个:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SoundEditor_V3
{
    public class Complex
    {
        public double real;
        public double im;

        public Complex()
        {
            real = 0;
            im = 0;
        }

        public Setval (double newReal, double newIm)
        {
            real = newReal;
            im = newIm;
        }
    }
}

关于你的其他类,构造函数在哪里?;)

于 2012-10-05T03:52:59.753 回答
0

谢谢你的帮助; 我实际上最终弄清楚了一切。我无法访问我试图访问它们的区域中的方法。我不得不将它们放在一个方法块中,因为这一切都被编码在一个表单中。反正这是我的理解。

但是,再次感谢您的所有建议,它们都很有帮助。

于 2012-10-06T22:28:16.517 回答