Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/arduino/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Audio Arduino-如何在压电蜂鸣器上同时产生两个或多个音调?_Audio_Arduino_Frequency - Fatal编程技术网

Audio Arduino-如何在压电蜂鸣器上同时产生两个或多个音调?

Audio Arduino-如何在压电蜂鸣器上同时产生两个或多个音调?,audio,arduino,frequency,Audio,Arduino,Frequency,我的高中电子课决定买一些arduino uno套件,我必须说它们非常酷。够了,现在在课堂上我们正在试验压电蜂鸣器(看起来像)。我们学习了如何使用压电蜂鸣器创作歌曲。我们的老师告诉我们要“有创造力”。还有什么比使用凯蒂·佩里的“焰火”更具创造性的方法呢 利用一些创造性的自由,我找到了这首歌的一首很好的钢琴曲(链接)。现在我是一名钢琴演奏者(我学习了AP音乐理论),我遇到的问题是,我只能演奏一个音符,只有压电蜂鸣器。有没有可能在压电蜂鸣器上播放这首歌,让它听起来像是在钢琴上播放的(或者至少接近钢琴)

我的高中电子课决定买一些arduino uno套件,我必须说它们非常酷。够了,现在在课堂上我们正在试验压电蜂鸣器(看起来像)。我们学习了如何使用压电蜂鸣器创作歌曲。我们的老师告诉我们要“有创造力”。还有什么比使用凯蒂·佩里的“焰火”更具创造性的方法呢

利用一些创造性的自由,我找到了这首歌的一首很好的钢琴曲(链接)。现在我是一名钢琴演奏者(我学习了AP音乐理论),我遇到的问题是,我只能演奏一个音符,只有压电蜂鸣器。有没有可能在压电蜂鸣器上播放这首歌,让它听起来像是在钢琴上播放的(或者至少接近钢琴)。我的意思是,低音和高音谱号在蜂鸣器上同时播放


我知道这涉及到相移和增加音符的频率,但如何将其转化为压电蜂鸣器的代码?如果您能发布一些示例代码,将不胜感激。如果没有,请你尽可能清楚地解释一下。我不是编程高手,但也不是初学者。

这个第三方音调库可以在多个管脚上同时播放方波:


您可以在多个引脚和单个扬声器之间连接电阻,以获得一个扬声器的所有音调。

Arduinos仅提供数字输出:输出为on(+5V)或off(0V)。
tone()

说你想要一个100Hz的音调。100Hz表示输出每1/100秒或10ms重复一次。因此,
tone(PIN,100)
将设置每5毫秒调用一次定时器中断。第一次调用中断时,它将输出设置为低,并返回到程序正在执行的任何操作。下次调用时,它会将输出设置为高。因此,每5毫秒,输出就会发生变化,在50%的占空比下会出现一个方波,这意味着输出的开启时间正好是一半

这一切都很好,但大多数音频波形不是方波。如果您想要同时播放两个方波音调,甚至想要控制单个方波音调的音量,您需要能够输出比“开”和“关”更多的值

好消息是,有一个技巧你可以使用称为脉冲宽度调制(通常缩写为PWM)。这个想法是,您可能只能将输出设置为两个值中的一个,但您可以非常快地设置。人类可以听到高达20kHz的音频。如果你的输出速度比这快,比如200kHz(在Arduino的能力范围内,它的时钟频率为16MHz),你就听不到单独的输出转换,而是更长时间内的平均值

想象一下使用
音调()
生成200kHz音调。它太高了,听不见,但平均值介于开和关之间(50%占空比,记得吗?)。现在我们有三个可能的输出值:开、关和半开。这足以让我们同时播放两个方波:

高质量音频需要更多的值。CD存储16位音频,这意味着有65536个可能的值。虽然我们不会从Arduino中获得CD质量的音频,但我们可以通过选择50%以外的占空比来获得更多的输出值。事实上,Arduino已经为我们提供了硬件

满足
analogWrite()
。这使得使用Arduino内置PWM硬件的输出电平发生变化。坏消息是PWM频率通常为500Hz,这对于LED调光来说是不错的,但是对于音频来说太低了。所以我们必须自己编写硬件寄存器

有更多的信息,这里有一个关于如何在Arduino上实现PWM DAC的示例

我选择了7位分辨率,这意味着输出是一个16MHz/128=125kHz的方波,具有128个可能的占空比

当然,一旦你有了PWM输出工作的乐趣才刚刚开始。 对于多个声音,你不能依靠中断来设置波形的频率,你必须自己拉伸它们。基本的数字信号处理(DSP)知识将非常有用。您将需要紧凑的代码从中断处理程序中生成音频数据,然后需要播放例程在正确的时间触发正确的音符。天空是极限

无论如何,这里有一些代码:

#define PIN 9

/* these magic constants were generated by the following perl script:
   #!/usr/bin/perl -lw
   my $freq = 16000000/256;
   my $A4 = 440;
   print int(128*$freq/$A4*exp(-log(2)*$_/12)) for (-9..2);
*/
const uint16_t frtab[] = {
  30578, 28861, 27241, 25712,
  24269, 22907, 21621, 20408,
  19262, 18181, 17161, 16198
};

#define VOICES 4

struct voice { 
  uint16_t freq;
  int16_t frac;
  uint8_t octave;
  uint8_t off;
  int8_t vol;
  const uint8_t *waveform;
} voice[VOICES];

#define PITCH 50 /* global pitch adjustment */

/* some waveforms. 16 samples each */
const uint8_t square_50[] = {
  0, 0, 0, 0, 0, 0, 0, 0,15,15,15,15,15,15,15,15
};
const uint8_t square_25[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,15,15,15
};
const uint8_t square_12[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,15
};
const uint8_t square_6[] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15
};
const uint8_t sawtooth[] = {
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15
};
const uint8_t triangle[] = {
  0, 2, 4, 6, 8,10,12,14,15,13,11, 9, 7, 5, 3, 1
};
const uint8_t nicebass[] = {
  0, 8,14,18,22,23,24,25,26,25,24,23,22,18,14, 8
};

void setup() {
  /* TIMER0 is used by the Arduino environment for millis() etc.
   So we use TIMER1.
 */
  pinMode(PIN, OUTPUT);
  /* fast PWM, no prescaler */
  TCCR1A = 0x80;
  TCCR1B = 0x11;
  /* 7-bit precision => 125kHz PWM frequency */
  ICR1H = 0;
  ICR1L = 0x7f;
  /* enable interrupts on TIMER1 overflow */
  TIMSK1 = 1;
  OCR1AH = 0; /* hi-byte is unused */
  for (uint8_t i=0; i<VOICES; i++)
    clear_voice(i);
}

void set_voice(uint8_t v, uint8_t note, uint8_t volume, const uint8_t *waveform) {
  note += PITCH;
  voice[v].octave = note/12;
  voice[v].freq = frtab[note%12];
  voice[v].frac = 0;
  voice[v].off = 0;
  voice[v].waveform = waveform;
  voice[v].vol = volume;
}

void clear_voice (uint8_t v) {
  voice[v].freq = 0;
}

uint8_t s = 0;

ISR(TIMER1_OVF_vect) {
  /* Calculate new data every 4 pulses, i.e. at 125/4 = 31.25kHz.
     Being interrupted unnecessarily is kinda wasteful, but using another timer is messy.
  */
  if (s++ & 3)
    return;

  int8_t i;
  int8_t out = 0;
  for (i=0; i<VOICES; i++) {
    if (voice[i].freq) {
      voice[i].frac -= 128<<voice[i].octave;
      if (voice[i].frac < 0) { /* overflow */
        voice[i].frac += voice[i].freq;
        voice[i].off++;
      }
      /* warning: vol isn't a real volume control, only for square waves */
       out += (voice[i].waveform[voice[i].off & 15]) & voice[i].vol;
    }
  }

  /* out is in the range 0..127. With 4-bit samples this gives us headroom for 8 voices.
     Or we could use more than 4-bit samples (see nicebass).
   */
  OCR1AL = out;
}

/* tune data */
const uint8_t bass[8][4] = {
  { 12, 19, 23, 24 },
  {  5, 12, 19, 21 },
  { 12, 19, 23, 24 },
  {  5, 12, 19, 21 },
  { 14, 16, 17, 21 },
  {  7, 19, 14, 19 },
  { 14, 16, 17, 21 },
  {  7, 19, 14, 19 }
};

const uint8_t melody[2][8][16] = {
  {/* first voice */
    {31, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 0,28,26,24 },
    { 0, 0, 0, 0, 0, 1, 2, 3,53,54,53,54, 0, 1, 2, 3 },
    {31, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 5,28, 5,26 },
    { 5,28,24, 0, 0, 1, 2, 3,53,54,56,54, 0, 1, 2, 3 },

    {29, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 5, 0,28, 5 },
    {28, 5, 0,26, 0, 1, 2, 3,54,56,58,56, 0, 1, 2, 3 },
    {29, 0, 0, 0, 0, 1, 2, 3,31,29,28,29, 5, 0,28, 5 },
    {28, 5, 0,26, 0, 1, 2, 3, 0,19,21,23,24,26,28,29 },
  },

  {/* second voice */
    {24, 0, 0, 0, 0, 1, 2, 3,24,24,24,24, 0,24,24,21 },
    { 0, 0, 0, 0, 0, 1, 2, 3,49,51,49,51, 0, 1, 2, 3 },
    {24, 0, 0, 0, 0, 1, 2, 3,24,24,24,24, 5,24, 5,24 },
    { 5,23,21, 0, 0, 1, 2, 3,49,51,53,51, 0, 1, 2, 3 },

    {26, 0, 0, 0, 0, 1, 2, 3,24,26,24,24, 5, 0,24, 5 },
    {24, 5, 0,24, 0, 0, 0, 0,51,51,54,54, 0, 1, 2, 3 },
    {26, 0, 0, 0, 0, 1, 2, 3,24,26,24,24, 5, 0,24, 5 },
    {24, 5, 0,23, 0, 1, 2, 3, 0, 5, 0,19,21,23,24,26 },  
  }
};

void loop() {
  uint8_t pos, i, j;

  for (pos=0; pos<8; pos++) {
    for (i=0; i<16; i++) {
      /* melody: voices 0 and 1 */
      for (j=0; j<=1; j++) {
        uint8_t m = melody[j][pos][i];
        if (m>10) {
           /* new note */
           if (m > 40) /* hack: new note, keep volume */
             set_voice(j, m-30, voice[j].vol, square_50);
           else /* new note, full volume */
             set_voice(j, m, 15, square_50);
        } else {
          voice[j].vol--; /* fade existing note */
          switch(m) { /* apply effect */
            case 1: voice[j].waveform = square_25; break;
            case 2: voice[j].waveform = square_12; break;
            case 3: voice[j].waveform = square_6; break;
            case 4: clear_voice(j); break; /* unused */
            case 5: voice[j].vol -= 8; break;
          }
          if (voice[j].vol < 0)
            voice[j].vol = 0; /* just in case */
        }
      }

      /* bass: voices 2 and 3 */
      set_voice(2, bass[pos][i%4], 31, nicebass);
      set_voice(3, bass[pos][0]-12, 15-i, sawtooth);

      delay(120); /* time per event */
    }
  }
}
#定义引脚9
/*这些神奇常量由以下perl脚本生成:
#!/usr/bin/perl-lw
my$freq=16000000/256;
我的$A4=440;
打印(-9..2)的整数(128*$freq/$A4*exp(-log(2)*$\u12));
*/
const uint16\u t frtab[]={
30578, 28861, 27241, 25712,
24269, 22907, 21621, 20408,
19262, 18181, 17161, 16198
};
#定义声音4
结构语音{
uint16_t频率;
国际压裂;
八度音程;
uint8关闭;
国际卷;
常数8_t*波形;
}声音;
#定义节距50/*全局节距调整*/
/*一些波形。各16个样品*/
const uint8_t square_50[]{
0, 0, 0, 0, 0, 0, 0, 0,15,15,15,15,15,15,15,15
};
const uint8_t square_25[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,15,15,15
};
const uint8_t square_12[]{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15,15
};
const uint8_t square_6[]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,15
};
常数8_t锯齿[]={
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15
};
常数8_t三角形[]={
0, 2, 4, 6, 8,10,12,14,15,13,11, 9, 7, 5, 3, 1
};
const uint8_t nicebass[]={
0, 8,14,18,22,23,24,25,26,25,24,23,22,18,14, 8
};
无效设置(){
/*计时器0由Arduino环境用于millis()等。
所以我们使用定时器1。
*/
引脚模式(引脚,输出);
/*快速PWM,无预分频器*/
TCCR1A=0x80;
TCCR1B=0x11;
/*7位精度=>125kHz PWM频率*/
ICR1H=0;
ICR1L=0x7f;
/*启用计时器1溢出时的中断*/