编程一个低
我用Java编写了世嘉主系统仿真器(尽管这个问题不是Java特定的),并且完成了除SN76489声音芯片之外的所有事情。 这个芯片如何让声音很容易 - 我遇到的问题是将其转换为可通过PC /笔记本电脑/任何JVM运行的形式。
我已经确定了以下步骤;
由于SN76489以大约221khz的采样速率运行 - 这意味着它输出的波形频率可达110khz(尽管实际上我怀疑这种情况有多高)。 因此,我需要在下采样之前实施低通滤波器。
然后我想把它缩小到44.1khz,所以我可以通过音频线输出它(在我的情况下是Java Source Data Line)。
为此,我需要设置22.05khz的低通滤波器,但问题是我不知道(数学上)低通滤波器的工作原理。 我需要这个才能写出一个。
目前,我的声音芯片创建了一个0.2秒的缓冲区,并在上面存储了221khz的样本。 据我的理解,我可以缩减采样率,但是如果我没有先应用低通滤波器进行降采样,就会明白我可能会在结果声音流中出现混叠毛刺。
任何人都可以推荐最简单的数学思想算法来做到这一点 - 我意识到由于变量涉及低通从来没有'确切',但我只需要一个足够让我的大脑沉闷(这并没有真正的处理波前处理)理解。
如果有帮助,具体如下:SN76489同时输出三个方波和一个噪声通道。 将这些信号汇总在一起,然后输出到混频器/放大器 - 链路中的这个阶段是我在下采样然后放大波形之前要运行低通滤波器的地方。 任何帮助的人可以给我非常感激。 我意识到背景阅读是必需的,但我想知道'我需要阅读什么'。 非常感谢。
更新:最后我提出了一个更简单的方法 - 尽管如此,还是不太完善。 SN76489通过从一个寄存器值产生每个音频通道来工作 - 输出1的极性,该值递减,依此类推 - 直到该值为0,然后该值被复位并且极性切换为-1,以此类推。 然后将该值乘以音量以获得该样本的最终幅度,并与其他通道相加。
我现在只需要防止一个寄存器的值产生一个高于我要求的nyquist极限的方波。 这给我一个更好的信号,但它仍然有一些嗡嗡/爆裂 - 不知道为什么最大可能的频率应该是18,473Hz。 这种爆发/嗡嗡声是因为当芯片从一个频率切换到下一个频道时,它不允许当前波形完全结束? 作为一个例子,芯片输出1111,然后是00 - 而不是全四个零,并切换到一个新的频率 - 这可能会导致别名?
编辑:我已经在下面包含了一个过滤器实现来回答你的问题。 但是,以如此高的采样率实现具有信号的高阶滤波器将每秒消耗数百万次操作。 如果您首先对芯片的输出进行频谱分析,可能是最好的。 如果没有超过几kHz的声音能量,那么抗混叠滤波器会浪费处理资源。 即使有能量达到中等高频,也可能首先抽取信号,然后在应用第二级抽取之前进行滤波。 作为一个方面说明,您可能还希望将速度降至比44.1 kHz低得多的速率。 对于主系统仿真器,您可能会采用8或10 kHz的采样率(我们在这里不是说高保真)。 但无论如何,要回答你关于如何用你指定的采样率和截止值来实现低通滤波器的问题。 。 。
好的,首先要设计一个低通滤波器。 matlab decimate函数听起来不错,所以我们将在这个例子中复制该方法。 该文件说明如下
抽取矢量y的长度比输入矢量x短r倍。 默认情况下,十进制采用截止频率为0.8 *(Fs / 2)/ r的八阶低通切比雪夫I型滤波器。 它在正向和反向都会过滤输入序列,以消除所有相位失真,从而有效地将滤波器顺序加倍。
Cheby滤波器是一个不错的选择,因为它们比Butterworth设计的陡峭的抑制性能更好,而且牺牲了一点通带波动。 我们不能在实时系统的两个方向上进行IIR过滤,但这应该适合您的目的。 我们可以使用下面的Matlab代码来制作滤波器系数。 。 。 。
sr = 221e3;
srDesired = 44.1e3;
order = 8;
passBandRipple = 1; %//dB
Wp = 0.8 * (srDesired/2) / (sr/2);
[b,a] = cheby1 (order, passBandRipple, Wp, 'low');
freqz(b,a,[],sr, 'half');
sos = tf2sos(b,a)
这给了我们一个8阶IIR滤波器,其响应如下。 这看起来像我们想要的。 阶段响应对于此应用程序并不重要。 截止频率为0.8 * 22.050 kHz,因为您希望接近奈奎斯特极限的信号在抽取前衰减很多。
最后的tf2sos命令将我们刚刚创建的过滤器转换为二阶段,您可以使用级联的双二阶滤波器部分来实现。 该命令的输出如下。 。 。
A部分
b = 1.98795003258633e-07,3.97711540624783e-07,1.98854354149782e-07,
a = 1 -1.81843900641769,0.835282840946310
B部分
b = 1,22.0501937393162,1.02534004997240,
a = 1,-1.77945624664044,0.860871442492022
C部分
b = 1,1.99938921206706,0.999702296645625,
a = 1,-1.73415546937221,0.907015729252152
D节
b = 1,1.97498006006623,0.975285456788754,
a = 1,-1.72600279649390,0.966884508765457
现在,您可以在滤波器级联中为每个双二阶滤波器使用这些滤波器系数。 您可以使用类似以下示例中的代码来实现此过滤器。 这是C代码,但你应该能够很容易地将其转换为Java。 请注意,下面的代码中没有a0系数。 上面的第二个顺序部分正确标准化,以便a0总是等于1.请将其忽略。
//Make the following vars private members of your filter class
// b0, b1, b2, a1, a2 calculated above
// m1, m2 are the memory locations
// dn is the de-denormal coeff (=1.0e-20f)
void processBiquad(const float* in, float* out, unsigned length)
{
for(unsigned i = 0; i < length; ++i)
{
register float w = in[i] - a1*m1 - a2*m2 + dn;
out[i] = b1*m1 + b2*m2 + b0*w;
m2 = m1; m1 = w;
}
dn = -dn;
}
您应该为此过滤器创建一个类,然后实例化4个单独的类(每个过滤器1个),将a和b值设置为上面指定的类。 接下来,将一个阶段的输入连接到下一个的输出,以便为您提供级联。
http://en.wikipedia.org/wiki/Butterworth_filter
C代码生成器:http://www-users.cs.york.ac.uk/~fisher/mkfilter/(应该可以轻松转换为Java)
链接地址: http://www.djcxy.com/p/10649.html上一篇: Programming a low
下一篇: architecture/sample for synchronizing CoreData against Web