프로젝트

나도 메이커! 메이커스 여러분들의 작품/프로젝트를 공유하는 공간입니다.

악기 쉴드를 이용한 아두이노 전자악기 만들기

2014-08-25 15:33:41

개요

아두이노를 이용하여 음을 연주 할 수 있다는것을 아시나요?
아두이노에서는 음의 주파수를 생성하여 그 음계의 소리를 낼 수 있습니다.
(푸시버튼을 이용하여 음의 높낮이 조절하기를 참고하세요.)
하지만 소리가 풍성하지 않고 단조롭다고 느끼실겁니다.

그렇다고 아두이노 음악 프로젝트를 포기하지 마세요
아두이노를 이용하여 음악을 연주할 수 있는 방법은 엄청나게 다양합니다.


출처 : toa mata Band

실습에 앞서 프로젝트를 하나 소개 하자면,
레고 시리즈중 하나인 'toa mata'를 이용하여 아두이노 밴드를 만든 프로젝트가 있습니다.
굉장히 유쾌하고 심지어 음악도 좋습니다.
간단하게 살펴보면 위 프로젝트의 경우 아두이노와 컴퓨터를 연동하여 아두이노를 일종의 '미디 컨트롤러'로 사용했습니다.
토아마타가 움직여 사물을 두드리면, 부착된 터치센서로 움직임을 감지하여 아두이노를 거쳐 미디신호를 송출합니다.
그러면 컴퓨터가 미디신호를 받아, 응용프로그램 내에서 해당 소리를 출력하며 음악을 형성하게 되는 것입니다.

그럼 이번 프로젝트를 통해 
'MIDI' 통신에 대해 알아보고 그를 통해 간단한 전자 악기를 만들어 보겠습니다.
아두이노의 다양한 쉴드중 하나인 악기쉴드를 이용하면 그 한계를 극복할수 있습니다.
이 프로젝트에서 사용할 악기 쉴드입니다.



출처 : 악기 쉴드 - sparkfun

스파크펀에서 출시한 이 악기쉴드에는 다양한 피아노, 목관 악기, 황동, 신디사이저,
SFX과 퍼커션 사운드를 포함하여 백여가지의 다른 소리 라이브러리를 지니고 있습니다.
이러한 기능을 수행 할 수 있는 VS1053 칩이 내장되어 있기 때문입니다. 
또한 MIDI Codec IC가 내장되어 있어, 실시간 MIDI통신을 가능하게 해줍니다.

MIDI(미디)통신이란 디지털 인터페이스(Musical Instrument Digital Interface)를 줄인 말로
전자 악기끼리 디지털 신호를 주고 받기 위해 각 신호를 규칙화한 일종의 규약입니다.
다시 말해 악기와 컴퓨터, 악기와 악기끼리 주고받을 수 있는 언어와 통로의 신호 체계 표준이라 할 수 있습니다.

예를들어 어떤 전자 악기(건반, 신서사이저, 모듈 등)가 이 표준에 따라 만들어졌다면, 그 전자 악기가 미디를 지원한다고 할 수 있습니다.
피아노 모양의 악기 뿐만 아니라, 아래의 사진처럼 푸시버튼과 볼륨버튼 그리고 슬라이더가 조합된 미디컨트롤러 역시 악기로 활용될 수 있습니다.


출처: AKAI APC 40 - delamar.de

그럼 본격적으로 실습을 통해 전자악기를 만들어 보겠습니다.




미리보기 동영상


부품목록


NO 부품명 수량 상세설명
1 오렌지 보드 1 아두이노 호환보드
2 아두이노 Instrument Music Shield 1 DEV-10587
3 브레드보드 1 브레드보드
4 점퍼케이블 25 점퍼케이블
5 푸시버튼 혹은 마이크로 스위치 12 푸시버튼

부품명 오렌지 보드 브레드보드 점퍼케이블 푸시버튼 뮤직쉴드
파트 x1 x1 x25  x12 x1

하드웨어 Making

회로도

브레드보드 레이아웃



소프트웨어 Coding

아래의 코드를 아두이노에 업로드 합니다.
/*
 Music Art Robot v1.0 (2012.May.30)
 ---------------------------------------------------------
 아두이노 + MIDI악기쉴드 이용한 DIY 건반악기 예제소스 (Arduino 1.0 호환)
 ---------------------------------------------------------
 http://RoboBob.co.kr
 ---------------------------------------------------------
 */
 
#include <SoftwareSerial.h>
#define btn1  11	// 버튼1의 아두이노 핀번호 정의
#define btn2  10	// 버튼2의 아두이노 핀번호 정의
#define btn3  9		// 버튼3의 아두이노 핀번호 정의
#define btn4  8		// 버튼4의 아두이노 핀번호 정의
#define btn5  7		// 버튼5의 아두이노 핀번호 정의
#define btn6  6		// 버튼6의 아두이노 핀번호 정의
#define btn7  5   // 버튼7의 아두이노 핀번호 정의
// 3:midi rx , 4:midi reset  아두이노 핀 3번 4번은 이미 사용중
#define btn8  2   // 버튼8의 아두이노 핀번호 정의 
                  //(SoftSerial에서 Rx핀으로 선언되지만 재 세팅 후 버튼용으로 사용)
#define btn9 A5   // 버튼9의 아두이노 핀번호 정의
#define btn10 A4  // 버튼10의 아두이노 핀번호 정의
#define btn11 A3  // 버튼11의 아두이노 핀번호 정의
#define btn12 A2  // 버튼12의 아두이노 핀번호 정의

#define defaultPatch 15 //악기 초기화 버튼 설정 악기번호

SoftwareSerial mySerial(2, 3); //SW시리얼핀 정의 D3이 MIDI신호 전송용,  D2는 미사용 

byte note = 0; //The MIDI연주될 note(음계)
byte resetMIDI = 4; // VS1053 Reset용 핀
byte ledPin = 13; //MIDI 트래픽 표시용 LED
 
boolean bs1 = false;  // 버튼1의 현재상태(눌림 or 안눌림)
boolean bs2 = false;  // 이하, 위와 유사
boolean bs3 = false;
boolean bs4 = false;
boolean bs5 = false;
boolean bs6 = false;
boolean bs7 = false;
boolean bs8 = false;
boolean bs9 = false;
boolean bs10 = false;
boolean bs11 = false;
boolean bs12 = false;

boolean br1;  // 버튼1 상태 확인용 입력값 임시저장용
boolean br2;  // 이하, 위와 유사
boolean br3;
boolean br4;
boolean br5;
boolean br6;
boolean br7;
boolean br8;
boolean br9;
boolean br10;
boolean br11;
boolean br12;

int patch = 0; //악기 대응, 연주될 악기 종류 (0~127: 기본 128 가지 선택가능)

int bn1 = 60; //버튼1의  note(음계)  가령 "도"  0~127까지 지정가능 (정확한 음계 설정은 MIDI관련정보참고)
int bn2 = 62; //버튼2의  note(음계)  가령 "레"
int bn3 = 64; //버튼3의  note(음계)  가령 "미"
int bn4 = 65; //버튼4의  note(음계)  가령 "파"
int bn5 = 67; //버튼5의  note(음계)  가령 "솔"
int bn6 = 69; //버튼6의  note(음계)  가령 "라"
int bn7 = 71; //버튼7의  note(음계)  가령 "시"
int bn8 = 72; //버튼8의  note(음계)  가령 "도~"

byte byteData;

void setup() {
  Serial.begin(31250);

  //미디컨트롤을 위한 소프트웨어 시리얼을 준비합니다.
  mySerial.begin(31250);
//  mySerial2.begin(57600);
  
  //VS1053를 초기화하고 사용할 준비를 합니다.
  pinMode(resetMIDI, OUTPUT);
  digitalWrite(resetMIDI, LOW);
  delay(100);
  digitalWrite(resetMIDI, HIGH);
  delay(100);
  
  pinMode( btn1, INPUT);      // 버튼1 입력용 핀모드를  입력모드로 전환
  digitalWrite( btn1, HIGH);  // 내부 PullUp 설정, 스위치의 나머지 한선은 GND에 물리면 됩니다.(초간단)

  pinMode( btn2, INPUT);      // 이하, 위와 유사
  digitalWrite( btn2, HIGH);
  
  pinMode( btn3, INPUT);
  digitalWrite( btn3, HIGH);
  pinMode( btn4, INPUT);
  digitalWrite( btn4, HIGH);
  pinMode( btn5, INPUT);
  digitalWrite( btn5, HIGH);
  pinMode( btn6, INPUT);
  digitalWrite( btn6, HIGH);
  pinMode( btn7, INPUT);
  digitalWrite( btn7, HIGH);
  pinMode( btn8, INPUT);
  digitalWrite( btn8, HIGH);
  
  pinMode( btn9, INPUT);
  digitalWrite( btn9, HIGH);
  pinMode( btn10, INPUT);
  digitalWrite( btn10, HIGH);
  pinMode( btn11, INPUT);
  digitalWrite( btn11, HIGH);  
  pinMode( btn12, INPUT);
  digitalWrite( btn12, HIGH);  

}

void loop() {
    br1 = digitalRead(btn1);
    br2 = digitalRead(btn2);
    br3 = digitalRead(btn3);
    br4 = digitalRead(btn4);
    br5 = digitalRead(btn5);
    br6 = digitalRead(btn6);
    br7 = digitalRead(btn7);
    br8 = digitalRead(btn8);
    br9 = digitalRead(btn9);
    br10 = digitalRead(btn10);
    br11 = digitalRead(btn11);
    br12 = digitalRead(btn12);    
    
   if( !bs1 && !br1 ){
     noteOn(0, bn1,100);
     bs1 = true;
   }else if(bs1 && br1){
     noteOff(0, bn1,0);   
     bs1 = false;
   }
   if( !bs2 && !br2 ){
     noteOn(0, bn2,100);
     bs2 = true;
   }else if(bs2 && br2){
     noteOff(0, bn2,0);   

     bs2 = false;
   }   
   if( !bs3 && !br3 ){
     noteOn(0, bn3,100);
     bs3 = true;
   }else if(bs3 && br3){
     noteOff(0, bn3,0);   
     bs3 = false;
   }
   if( !bs4 && !br4 ){
     noteOn(0, bn4,100);
     bs4 = true;
   }else if(bs4 && br4){
     noteOff(0, bn4,0);   
     bs4 = false;
   }
   if( !bs5 && !br5 ){
     noteOn(0, bn5,100);
     bs5 = true;
   }else if(bs5 && br5){
     noteOff(0, bn5,0);   
     bs5 = false;
   }   
   if( !bs6 && !br6 ){
     noteOn(0, bn6,100);
     bs6 = true;
   }else if(bs6 && br6){
     noteOff(0, bn6,0);   
     bs6 = false;
   }   
   
   if( !bs7 && !br7 ){
     noteOn(0, bn7,100);
     bs7 = true;
   }else if(bs7 && br7){
     noteOff(0, bn7,0);   
     bs7 = false;
   }   
   if( !bs8 && !br8 ){
     noteOn(0, bn8,100);
     bs8 = true;
   }else if(bs8 && br8){
     noteOff(0, bn8,0);   
     bs8 = false;
   }   
   
   if( !bs9 && !br9 ){ //patch up (max:127)
     patch++;
     if(patch >127) patch = 0;
     talkMIDI(0xc0, patch, 0);     
     bs9 = true;
   }else if(bs9 && br9){
     bs9 = false;
   }   
   if( !bs10 && !br10 ){ //patch down (min:0)
     patch--;
     if(patch < 0) patch = 127;
     talkMIDI(0xc0, patch, 0);       
     bs10 = true;
   }else if(bs10 && br10){
     bs10 = false;
   }   
   
   if( !bs11 && !br11 ){
     //bank 0x78(drum)
     talkMIDI(0xb0, 0, 0x78);
     talkMIDI(0xb0,20, 0);
     talkMIDI(0xc0, patch, 0);     
     bs11 = true;
   }else if(bs11 && br11){
     bs11 = false;
   }   
   
   if( !bs12 && !br12 ){
     //bank MSB 0, default instruments
     patch = defaultPatch;
     talkMIDI(0xb0, 0, 0);
     talkMIDI(0xb0,20, 0);
     talkMIDI(0xc0, patch, 0);       
     bs12 = true;
   }else if(bs12 && br12){
     bs12 = false;
   }   
            

  //*************** MIDI LOOPBACK ******************//
  if(Serial.available() > 0)
  {
    byteData =  Serial.read();
    mySerial.write( byteData);
  }  
     

}

//노트 온 미디 메세지를 송출합니다. 버튼이 눌린상태와 같습니다.
//채널 범위는 0-15 입니다.
void noteOn(byte channel, byte note, byte attack_velocity) {
  talkMIDI( (0x90 | channel), note, attack_velocity);
}

//노트 오프 미디 메세지를 송출합니다. 버튼이 눌리지 않은 상태와 같습니다.
void noteOff(byte channel, byte note, byte release_velocity) {
  talkMIDI( (0x80 | channel), note, release_velocity);
}


void talkMIDI(byte cmd, byte data1, byte data2) {
  digitalWrite(ledPin, HIGH);
  mySerial.write(cmd );
  mySerial.write(data1 );

  //모든 명령은 1바이트를 지니며, 모든 cmds는   0xBn보다  2 데이터 바이트를 덜 지닙니다.  
  //(sort of: http://253.ccarh.org/handout/midiprotocol/)
  if( (cmd & 0xF0) <= 0xB0)
    mySerial.write(data2 );

  digitalWrite(ledPin, LOW);
}





소프트웨어 & 하드웨어 설명 


1. 하드웨어 설명
아두이노 D3,D4핀은 MIDI악기쉴드 제어용이므로 다른용도로 사용하지 마세요. 
아두이노 D3핀을 통해 MIDI악기쉴드에 MIDI신호를 전송합니다.
D2핀도 SoftSerial 수신용으로 선언은됐지만, 재세팅하고 버튼 입력용으로 사용됩니다.

버튼1 ~ 8 : 도/레/미/파/솔/라/시/도 임의 note(음계)로 변경하시면 됩니다.
버튼9: 다른 악기 선택(현재 악기번호 + 1) 0~127
버튼10: 다른 악기 선택(현재 악기번호 - 1) 0~127
버튼11: 악기를 드럼셋으로 설정합니다.(리셋버튼을 통해 초기화 할수 있습니다.)
버튼12: defaultPatch 에 정의된 악기로 변경(리셋 기능을 담당합니다.)

외부스위치 연결방법: 버튼의 양쪽선 중 한쪽은 아두이노 해당 핀에 연결하고, 나머지 한쪽은 GND에 공통 연결하시면 됩니다.
참고사항, 전원을 켠 후 소리가 나지않을 경우 리셋버튼을 눌러서 초기화 해주면 작동됩니다.

뮤직쉴드는 아래의 핀을 사용합니다.
5V : VS1053 VCC
GND : VS1053 GND
D3 (SoftSerial TX) : VS1053 RX
D4 : VS1053 RESET

2.소프트웨어 설명

#include <SoftwareSerial.h>
#define btn1  11	// 버튼1의 아두이노 핀번호 정의
#define btn2  10	// 버튼2의 아두이노 핀번호 정의
#define btn3  9		// 버튼3의 아두이노 핀번호 정의
#define btn4  8		// 버튼4의 아두이노 핀번호 정의
#define btn5  7		// 버튼5의 아두이노 핀번호 정의
#define btn6  6		// 버튼6의 아두이노 핀번호 정의
#define btn7  5   // 버튼7의 아두이노 핀번호 정의
// 3:midi rx , 4:midi reset  아두이노 핀 3번 4번은 이미 사용중
#define btn8  2   // 버튼8의 아두이노 핀번호 정의 
                  //(SoftSerial에서 Rx핀으로 선언되지만 재 세팅 후 버튼용으로 사용)
#define btn9 A5   // 버튼9의 아두이노 핀번호 정의
#define btn10 A4  // 버튼10의 아두이노 핀번호 정의
#define btn11 A3  // 버튼11의 아두이노 핀번호 정의
#define btn12 A2  // 버튼12의 아두이노 핀번호 정의

#define defaultPatch 15 //악기 초기화 버튼 설정 악기번호

SoftwareSerial mySerial(2, 3); //SW시리얼핀 정의 D3이 MIDI신호 전송용,  D2는 미사용 

byte note = 0; //The MIDI연주될 note(음계)
byte resetMIDI = 4; // VS1053 Reset용 핀
byte ledPin = 13; //MIDI 트래픽 표시용 LED

이 스케치에서는 소프트웨어 시리얼을 사용합니다.
 
Built-in 된 Serial 통신용 0, 1번 핀 외에 다른 디지털 핀으로 Serial 통신을 원할 경우 Software Serial 을 사용하며,
Serial 통신을 두 개의 디지털 핀으로 가능하게 해주는 라이브러리를 사용하는 방식입니다. 
뮤직 쉴드는 2,3번 핀을 사용하며 3번 핀을 통해 미디 신호를 전송합니다.

boolean bs1 = false;  // 버튼1의 현재상태(눌림 or 안눌림)
boolean bs2 = false;  // 이하, 위와 유사
boolean bs3 = false;
boolean bs4 = false;
boolean bs5 = false;
boolean bs6 = false;
boolean bs7 = false;
boolean bs8 = false;
boolean bs9 = false;
boolean bs10 = false;
boolean bs11 = false;
boolean bs12 = false;

boolean br1;  // 버튼1 상태 확인용 입력값 임시저장용
boolean br2;  // 이하, 위와 유사
boolean br3;
boolean br4;
boolean br5;
boolean br6;
boolean br7;
boolean br8;
boolean br9;
boolean br10;
boolean br11;
boolean br12;

boolean는 참, 거짓을 의미하는 논리값을 저장하는 데이터타입으로 true(1)나  false(0)의 값이 들어가게 됩니다.
따라서 버튼이 눌리거나 눌리지 않은경우 모두를 포함합니다.

int patch = 0; //악기 대응, 연주될 악기 종류 (0~127: 기본 128 가지 선택가능)

int bn1 = 60; //버튼1의  note(음계)  가령 "도"  0~127까지 지정가능 (정확한 음계 설정은 MIDI관련정보참고)
int bn2 = 62; //버튼2의  note(음계)  가령 "레"
int bn3 = 64; //버튼3의  note(음계)  가령 "미"
int bn4 = 65; //버튼4의  note(음계)  가령 "파"
int bn5 = 67; //버튼5의  note(음계)  가령 "솔"
int bn6 = 69; //버튼6의  note(음계)  가령 "라"
int bn7 = 71; //버튼7의  note(음계)  가령 "시"
int bn8 = 72; //버튼8의  note(음계)  가령 "도~"

patch는 127여가지 소리를 선택적으로 사용할수 있도록 해줍니다.
다른 음계를 설정하고 싶으실땐 아래의 그림을 참고하여 해당 음계의 미디노트 번호를 대입해 주시면 됩니다.



  if( !bs1 && !br1 ){
     noteOn(0, bn1,100);
     bs1 = true;
   }else if(bs1 && br1){
     noteOff(0, bn1,0);   
     bs1 = false;

만약 버튼1이 눌렸을때 노트 ON 신호를 미디 신호로 보내고  
그렇지 않으면 노트 OFF 신호를 미디 신호로 보냅니다.

if( !bs9 && !br9 ){ //patch up (max:127)
     patch++;
     if(patch >127) patch = 0;
     talkMIDI(0xc0, patch, 0);     
     bs9 = true;
   }else if(bs9 && br9){
     bs9 = false;
   }   
   if( !bs10 && !br10 ){ //patch down (min:0)
     patch--;
     if(patch < 0) patch = 127;
     talkMIDI(0xc0, patch, 0);       
     bs10 = true;
   }else if(bs10 && br10){
     bs10 = false;

만약 9번 버튼이 눌리면, patch값을 증가시키고,
patch가 127보다 클때 patch값을 0으로 초기화 시킵니다.

만약 10번 버튼이 눌리면, patch값을 감소시키고
patch값이 0보다 작아지면, patch값을 127로 설정합니다.

 if( !bs11 && !br11 ){
     //bank 0x78(drum)
     talkMIDI(0xb0, 0, 0x78);
     talkMIDI(0xb0,20, 0);
     talkMIDI(0xc0, patch, 0);     
     bs11 = true;
   }else if(bs11 && br11){
     bs11 = false;
 
   if( !bs12 && !br12 ){
     //bank MSB 0, default instruments
     patch = defaultPatch;
     talkMIDI(0xb0, 0, 0);
     talkMIDI(0xb0,20, 0);
     talkMIDI(0xc0, patch, 0);       
     bs12 = true;
   }else if(bs12 && br12){
     bs12 = false;
   }   
 

만약 11번 버튼이 눌리면 미디신호를 통해, drum 소리를 불러옵니다.
12번 버튼이 눌리면 미디신호를 통해, 초기설정 악기로 돌아갑니다.

기술문서

  • 부품목록
  • 회로도
  • 브레드보드 레이아웃
  • 스케치

hihyo

아두이노,초급,악기,음악,악기쉴드,전자악기,피아노,MIDI