2015-03-11 18 views
6

Lớp học điện tử trung học của tôi quyết định mua một số bộ dụng cụ arduino uno, mà tôi phải nói là vô cùng tuyệt vời. Đủ về điều đó, ngay bây giờ trong lớp chúng tôi đang thử nghiệm với buzzer piezo (nó trông giống như this). Chúng tôi đã học về cách tạo các bài hát bằng cách sử dụng bộ rung áp. Giáo viên của chúng tôi nói với chúng tôi là "sáng tạo". Cách nào tốt hơn để sáng tạo hơn là sử dụng "Pháo hoa" của Katy Perry.Arduino - Làm thế nào để tạo ra hai hoặc nhiều tông cùng một lúc trên bộ rung chuông?

Sử dụng một số quyền tự do sáng tạo, tôi đã tìm thấy một đoạn đàn piano đẹp của bài hát này (liên kết here). Bây giờ tôi là một người chơi piano (tôi đã lấy lý thuyết AP Music), và vấn đề tôi gặp phải là tôi chỉ có thể chơi một nốt nhạc duy nhất là bộ rung Piezo. Có thể chơi bài hát trên một chiếc chuông piezo để nó có vẻ như nó đang được chơi trên một cây đàn piano (hoặc ít nhất là gần). Tôi có nghĩa là các ghi chú của âm trầm và âm trầm treble được phát đồng thời trên bộ rung.

Tôi hiểu rằng nó liên quan đến thay đổi pha và thêm tần số ghi chú, nhưng làm thế nào để bạn dịch mã này thành mã cho bộ rung chuông? Nếu bạn có thể đăng một số mã ví dụ sẽ được đánh giá cao. Nếu không, bạn có thể giải thích nó theo cách rõ ràng nhất có thể được không. Tôi không phải là một bậc thầy về lập trình, nhưng tôi cũng không phải là người mới bắt đầu. thư viện Tone

Trả lời

1

của bên thứ ba này có thể chơi sóng vuông đồng thời trên nhiều chân: Link

Bạn có thể kết nối điện trở giữa nhiều chân và một loa duy nhất để có được tất cả tông ra khỏi một loa.

+1

Tôi không hiểu - bạn có thể đăng một số mã ví dụ giải thích cách sử dụng thư viện Tone của bên thứ ba mà bạn tham chiếu để phát đồng thời hay không âm trên một loa? –

14

Arduinos chỉ cung cấp đầu ra kỹ thuật số: đầu ra là bật (+ 5V) hoặc tắt (0V). Hàm tone(), mà tôi cho rằng bạn đã chạy vào thời điểm này, tạo ra một làn sóng vuông ở tần số được chỉ định.

Giả sử bạn muốn có một giai điệu 100Hz. 100Hz nghĩa là đầu ra lặp lại mỗi 1/100 giây, hoặc 10ms. Vì vậy, tone(PIN,100) sẽ đặt gián đoạn hẹn giờ được gọi là mỗi 5ms. Lần đầu tiên ngắt được gọi, nó đặt đầu ra thấp và trả về bất kỳ chương trình nào của bạn đang làm. Lần sau nó được gọi là nó đặt đầu ra cao. Vì vậy, mỗi 5ms đầu ra thay đổi, và bạn nhận được một làn sóng vuông ở chu kỳ nhiệm vụ 50%, có nghĩa là đầu ra là cho một nửa thời gian chính xác.

Đây là tất cả đều rất tốt, nhưng hầu hết các dạng sóng âm thanh không phải là sóng vuông. Nếu bạn muốn chơi hai tông màu vuông đồng thời, hoặc thậm chí để điều khiển âm lượng của một âm thanh sóng vuông đơn, bạn cần có khả năng xuất nhiều giá trị hơn chỉ là "bật" và "tắt".

Tin tốt là có một mẹo bạn có thể sử dụng được gọi là điều chế độ rộng xung (thường được viết tắt là PWM). Ý tưởng là bạn chỉ có thể đặt đầu ra của mình thành một trong hai giá trị, nhưng bạn có thể thực hiện nhanh như vậy. Con người có thể nghe thấy tần số âm thanh lên đến khoảng 20kHz. Nếu bạn lừa phỉnh đầu ra của bạn nhanh hơn mà, nói tại 200kHz (cũng trong khả năng của Arduino, mà là ở tốc độ 16MHz), bạn không nghe thấy các hiệu ứng chuyển tiếp sản lượng cá nhân, nhưng trung bình giá trị trong khoảng thời gian lâu hơn.

Hãy tưởng tượng tạo âm 200 kHz với tone(). Đó là cách quá cao để nghe, nhưng giá trị trung bình là nửa chừng giữa và tắt (chu kỳ nhiệm vụ 50%, nhớ không?). Vì vậy, chúng tôi hiện có ba giá trị đầu ra có thể có: bật, tắt và nửa chừng. Đây là đủ để cho phép chúng tôi chơi hai sóng vuông đồng thời: Diagram of summing square waves

âm thanh chất lượng cao đòi hỏi nhiều giá trị hơn này. CD lưu trữ âm thanh 16 bit, có nghĩa là có 65536 giá trị có thể.Và trong khi chúng tôi sẽ không nhận được âm thanh chất lượng CD ra khỏi Arduino, chúng tôi có thể nhận được nhiều giá trị đầu ra hơn bằng cách chọn một chu kỳ nhiệm vụ khác hơn 50%. Trong thực tế, Arduino có phần cứng để điều này cho chúng tôi.

Gặp gỡ analogWrite(). Điều này tạo ra các mức đầu ra khác nhau bằng cách sử dụng phần cứng PWM tích hợp của Arduino. Tin xấu là tần số PWM thường là 500Hz, điều này là tốt cho việc làm mờ một đèn LED nhưng cách quá thấp cho âm thanh. Vì vậy, chúng tôi phải lập trình phần cứng đăng ký ourself.

Secrets of Arduino PWM có thêm một số thông tin và đây là detailed reference về cách triển khai DAC PWM trên Arduino.

Tôi chọn độ phân giải 7 bit, có nghĩa là đầu ra là sóng vuông 16MHz/128 = 125kHz với 128 chu kỳ nhiệm vụ có thể.

Tất nhiên, một khi bạn đã có đầu ra PWM, hoạt động vui nhộn chỉ mới bắt đầu. Với nhiều giọng nói bạn không thể dựa vào các ngắt để đặt tần số dạng sóng của bạn, bạn phải tự kéo dài chúng. Kiến thức về xử lý tín hiệu kỹ thuật số cơ bản (DSP) sẽ rất tiện dụng. Bạn sẽ cần mã chặt chẽ để tạo ra dữ liệu âm thanh từ bên trong một trình xử lý ngắt, và sau đó bạn sẽ cần một playroutine để kích hoạt các ghi chú thích hợp vào đúng thời điểm. Bầu trời là giới hạn!

Dù sao, đây là một số mã:

#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 */ 
    } 
    } 
} 

này đóng một giai điệu bốn-thoại. Tôi chỉ có một Arduino Leonardo (tốt, Pro Micro) để kiểm tra nó, vì vậy bạn có thể cần phải thay đổi PIN theo đó pin được nối với TIMER1A (nếu tôi đọc chính xác nó là pin 9 trên Uno và pin 11 trên một Mega). Bạn không nhận được sự lựa chọn pin nào để sử dụng, thật đáng buồn.

Tôi cũng đã chỉ thử nghiệm nó với tai nghe, vì vậy tôi không có ý tưởng làm thế nào nó sẽ âm thanh trên một còi piezo ...

Hy vọng rằng nó cung cấp cho bạn một số ý tưởng của possiblities, và một điểm khởi đầu tiềm năng cho giai điệu của riêng bạn. Vui lòng giải thích bất cứ điều gì không rõ ràng, và cũng cảm ơn bạn đã cho tôi lý do để viết điều này :)

+0

Tôi không hiểu làm thế nào bạn đang trộn các giọng nói từ dòng 95 đến 102. 'frac' là gì và nó được sử dụng như thế nào? Tôi hiểu [this] (https://en.wikipedia.org/wiki/Twelfth_root_of_two) và các giá trị trong 'frtab' nhưng làm thế nào để bạn đi từ một khoảng thời gian và quãng tám đến một giá trị trong sóng? Ngoài ra tôi đã thay đổi dòng 102 thành 'out + = (giọng nói [i] .off & 15) <8? 0: 31; 'bởi vì tôi không cần khối lượng và các dạng sóng khác, nó có hoạt động không? –

Các vấn đề liên quan