来自微控制器的和弦声音?


14

我可以通过切换连接到压电蜂鸣器的单个引脚(以可变速率)来发出单声道声音。

如何在软件中生成两个混合音频信号以创建复音?

这是我用来播放简单音乐的代码。

#define F_CPU 8000000UL // 8MHz
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/delay.h>

// number of timer0 overflows/sec
#define INT_PER_SEC 31250

// Frequencies (in Hz) of notes
#define F_FSH_4 370
#define F_A_4 440
#define F_B_4 494
#define F_E_4 330
#define F_CSH_5 554
#define F_D_5 587
#define F_FSH_5 740
#define F_CSH_4 277
#define F_GSH_4 415

// number of timer0 overflows for notes
#define REST -1 // special case
#define FSH_4 INT_PER_SEC/F_FSH_4
#define A_4 INT_PER_SEC/F_A_4
#define B_4 INT_PER_SEC/F_B_4
#define E_4 INT_PER_SEC/F_E_4
#define CSH_5 INT_PER_SEC/F_CSH_5
#define D_5 INT_PER_SEC/F_D_5
#define FSH_5 INT_PER_SEC/F_FSH_5
#define CSH_4 INT_PER_SEC/F_CSH_4
#define GSH_4 INT_PER_SEC/F_GSH_4

#define SEMIQUAVER_TIME 60  // ms
#define BREATH_TIME 20      // ms

volatile uint32_t intrs = 0;
volatile int32_t curNote = REST;

// TIMER0 overflow
ISR(TIMER0_OVF_vect)
{
    if (curNote == REST)
        intrs = 0;
    else
    {
        intrs++;
        if (intrs >= curNote)
        {
            PORTD ^= _BV(PD4);
            intrs = 0;
        }
    }
}


void play(int32_t note, uint32_t len)
{
    int i;
    curNote = note;
    for (i = 0; i< len; i++)
        _delay_ms(SEMIQUAVER_TIME);
    curNote = REST;
    _delay_ms(BREATH_TIME);
}

int main(void)
{
    /* setup clock divider. Timer0 overflows on counting to 256.
     * 8Mhz / 1 (CS0=1) = 8000000 increments/sec. Overflows every 256, so 31250
     * overflow interrupts/sec */
    TCCR0B |= _BV(CS00);

    // enable overflow interrupts
    TIMSK0 |= _BV(TOIE0);

    // PD4 as output
    DDRD = _BV(PD4);

    TCNT0 = 0;
    intrs = 0;

    curNote = REST;

    // enable interrupts
    sei();

    while (1)
    {
        // Axel F
        play(FSH_4, 2);
        play(REST, 2);
        play(A_4, 3);
        play(FSH_4, 2);
        play(FSH_4, 1);
        play(B_4, 2);
        play(FSH_4, 2);
        play(E_4, 2);
        play(FSH_4, 2);
        play(REST, 2);
        play(CSH_5, 3);
        play(FSH_4, 2);
        play(FSH_4, 1);
        play(D_5, 2);
        play(CSH_5, 2);
        play(A_4, 2);
        play(FSH_4, 2);
        play(CSH_5, 2);
        play(FSH_5, 2);
        play(FSH_4, 1);
        play(E_4, 2);
        play(E_4, 1);
        play(CSH_4, 2);
        play(GSH_4, 2);
        play(FSH_4, 6);
        play(REST, 12);
    }
}

嘿,这东西能散发出人类语言吗?我是说喜欢单词吗?
Rick_2047 2010年


@Joby,您提供的资源很棒,但是我看到了演示,实际上并没有说出任何可听见的信息。您知道其他人吗?
Rick_2047 2010年

不是没有DAC,没有。
Toby Jaffey 2010年

@Joby您对DAC有什么看法?
Rick_2047 2010年

Answers:


8

一个简单的技巧就是使用两个带PWM的引脚,并将它们连接到扬声器的相对侧。然后以不同的速度调制每个引脚,您可以一次演奏两个音符...基本上,扬声器正在为您混合它们。超过两个注释,您将必须如上所述使用软件进行处理。


1
如果您正在使用PWM(以比所需信号高得多的频率进行开关),那么您已经可以仅使用一个输出引脚将多个信号混合在一起。
endolith'4

5

获取复音的标准方法是以某个固定的中断速率(通常为8000 Hz或44100 Hz)进行中断,从每个声源获取“高”(+1)或“低”(-1)(或中间值) ,将所有数字加起来得到总数,然后将该总数发送到DAC。

就像其他人在这里所说的那样,只要有一点聪明,高速PWM就可以代替DAC。

“微复调”页面提供了一些细节和技巧。


3

我认为这个不错的老式PC DOS小游戏宝石通过PC扬声器Digger使用了真实的和弦声音。

我不知道他们是怎么做到的,但是您可以从该站点下载C源代码。


我仍然可以听到脑海中的音调
Toby Jaffey 2010年



2

如果您使用软件来安排演讲者的时间,最简单的方法可能是生成两个独立的数据流并在它们之间交替。无论扬声器输出是由I / O引脚还是DAC控制,这种方法都可以很好地工作。例如:

int选择器;
uint16_t phase [8],freq [8];

无效中断(无效) { 选择器++; 选择器&= 7; 相位[选择器] +频率[选择器]; DAC_OUT =正弦波[相位[选择器] >> 8]; }

上面是我在1996年基于PIC的音乐盒中使用的基本方法(使用汇编代码而不是C)。请注意,中断率必须是有效采样率的8倍,但每个中断只需要对单个语音进行处理。请注意,如果输出滤波效果良好,则此方法将产生比有效地将数字进行采样然后输出的采样率高3位的DAC分辨率,但在采样率及其倍数处会产生大量噪声。因此,过滤比其他情况更为重要。


1

他们曾经在旧的游戏系统上以及“ PC扬声器 ”时代这样做,但是我不知道该怎么做。

首先猜测:考虑一下您理想情况下产生的波浪,然后想象将其变形为严重削波的正方形,然后通过在适当的时间切换输出来创建该正方形。但是会有很多互调

第二个想法:您能大大增加振荡频率并输出PWM式的模拟信号吗?


2
我记得很久以前看过NES仿真器,我相信他们使用了三个具有可编程频率的波形。两个方波和一个三角波。
mjh2007'4

……显然有一个噪声源 en.wikipedia.org/wiki/NES_Sound_Format
endolith

1

如前所述,您可以使用与PC扬声器(仅支持打开/关闭,可选地连接到PWM控制器)相同的方式来执行此操作。基本上,我对方法的理解是打开和关闭扬声器足够快以至于永远不会完全打开或关闭(有点像开关模式电源的工作方式。)这使扬声器不断在打开和关闭之间移动,从而产生模拟信号。

唯一的麻烦是您需要一个真正的扬声器(我认为压电装置移动得太快,以至于无法完全打开和完全关闭),并且您需要能够足够快地切换位。我做了一些实验,得出最大速度约为5MHz,对于11025 Hz的音频信号来说,这应该足够了(可能是你希望获得的最佳质量)。

当然,8位11025Hz @ 11 KB /秒,这比串行端口的速度快得多。它将只允许在闪存中存储大约一两秒钟的音频,因此,您几乎只能播放动态生成的音频,只要留出足够的CPU时间来使扬声器旋转!

还有其他两种方法也可以实现此目的,并且看来上述方法的Arduino已经实现


2
无论扬声器本身移动多快,都可以在扬声器之前使用滤波器来平滑PWM。
endolith'Mar


1

播放声音A片刻,例如大约50毫秒,然后播放声音B并来回切换。想法是切换速度快于耳朵的分辨力,听起来就像是两个人同时演奏。



0

是我的代码,用于同时播放2首乐曲。抱歉,您必须注册AVR怪胎才能访问。


4
我愿意给你一个给予好评,如果你在这里发布的代码,或者在某个地方我并不需要一个帐户...
托比Jaffey
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.