我正在尝试为我在学校做的项目实施离散傅立叶变换算法。但创建一个类似乎很难(不应该这样)。 我正在使用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);
}
}
答案 0 :(得分:1)
此调用应位于方法内。截至目前,它直接在一个类下面。
//xfo.DFT(s, N, amp, 0);
同时为ref
添加amp
。 (因为void DFT(...., ref Complex[] amp,....)
需要ref amp
参数。
xfo.DFT(s, N, ref amp, 0);
答案 1 :(得分: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(复制字段的名称已重命名):
//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是输出。好吧,如果它是输出,为什么你不做它的方法返回它?
的Bam!
//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
关键字并返回IEnumerable<Complex>
是一个很好的机会。但是,我会以一种阵列的形式接受你想要的东西。
现在,您可能不希望返回数组。可能您只想修改预先存在的数组的部分。在这种情况下,你应该开始检查你的界限。即使这是真的,你也根本不需要参考!因为数组是引用类型。这是一个通过值传递的引用,如果你无法理解这个想法,只要相信我,你可以修改一个数组的内容,看看外面反映没有ref ...通过引用传递引用允许你改变引用另一个,你不是那样做的。
要说明:
void Main()
{
var x = new int[1];
Do(x);
Console.WriteLine(x);
}
void Do (int[] array)
{
array[0] = 1;
}
上一个程序的输出(使用LinqPad编译)是&#34; 1&#34;。
但是,让我们回到您的代码,是吗?
我不知道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
关键字。有了它,编译器会将变量的类型分配给我用来初始化它的类型。所以在这种情况下结果是Complex[]
,但我不必在代码中写两次。
最后,你复制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>
,它是一个接口,表示可以迭代以获取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,我可以在没有傅里叶类型的对象的情况下使用它,因为它是静态的(实际上我不能创建傅里叶类型的对象,因为它是静态的)。
此方法接收类型为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]
输出是否正确?我没有吓人的想法!我必须更多地了解傅立叶(但看起来合法)。
验证后,输出正确。
最后一点注意事项:使用debuger逐步执行代码,您可能会发现一个惊喜(提示:收益)。
答案 2 :(得分: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;
}
}
}
关于你的其他课程,构造函数在哪里? ;)
答案 3 :(得分:0)
感谢您的帮助;我实际上最终搞清楚了一切。 我无法访问我试图访问它们的区域中的方法。我必须将它们放在方法块中,因为这些都是在表单内编码的。 无论如何,这是我的理解。
但是,再次感谢您的所有建议,他们都很有帮助。