프로젝트

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

프로세싱을 사용하여 종이피아노 만들기

2014-09-12 14:57:17

개요

MakeyMakey라는 것을 아두이노를 조금이라도 만져본 사람이라면 한번쯤은 들어봤을 것이다.
아래는 MakeyMakey의 Youtube동영상이다.



어떤 물체라도 연결될 경우 특정값을 출력하는 역할을 하는 물체로 바뀌어서 여러가지를 만들 수 있게 하는 제품이다.
이번에는 위의 MakeyMakey의 역할과 비슷하게 종이의 쿠킹호일이 피아노 건반역할을 하게 하는 종이피아노를 만들것이다.




아두이노로 다양한 모양과 다양한 소리를 내는 악기나 작품을 만들 수 있지만 이번에는 그 중에서 재료가 적게 들고 선 꽂는게 매우 귀찮기는 하지만 만들기는 쉬운편에 속하는 종이 피아노를 만들어 보자. 
종이피아노는 사람의 손터치를 전기적신호로 인식하여 이를 이용하여 건반음을 재생한다. 전자피아노의 원리와 비슷한 minor버전을 만든다고 생각하면 이해가 쉬울 것이다. 
사람의 접촉을 인지하기 위해서는 전기가 통하는 도체가 필요한데 이번 프로젝트에서는 주변에서 쉽게 구하기 쉽고 이리저리 공작이 간편한 쿠킹호일을 사용하여 만들 었다.
소리를 재생하는 방법은 뮤직쉴드나 mp3쉴드, 웨이브쉴드등이 있지만 이번에는 프로세싱의 내장 라이브러리를 이용할 것이다.



동영상


필요한 사전지식

프로세싱
라이브러리



부품 목록

부품 목록

NO 부품명 수량 상세설명
1 아두이노 maga 1 아두이노
2 점퍼케이블 30-50 건반의 개수에 따라 유동적
3 10k옴 저항 10-15 건반의 개수에 따라 유동적
4 쿠킹 호일    


부품명 아두이노 우노 R3 점퍼케이블 저항 쿠킹호일
파트


※건반 음원파일 요청이 있어서 링크로 올려드립니다.(음원파일에는 #음계도 있는데 기본 건반음이랑 소리가 똑같습니다.)

피아노 단음 소리 파일

하드웨어 making

브레드 보드




전자 회로도

소프트웨어 coding

스케치

#include <CapacitiveSensor.h>

//CapacitiveSensor라이브러리를 사용하여 좌표값을 지정하여 객체생성 CapacitiveSensor cs_2_3 = CapacitiveSensor(2,3); CapacitiveSensor cs_2_4 = CapacitiveSensor(2,4); CapacitiveSensor cs_2_5 = CapacitiveSensor(2,5); CapacitiveSensor cs_6_7 = CapacitiveSensor(6,7); CapacitiveSensor cs_6_8 = CapacitiveSensor(6,8); CapacitiveSensor cs_6_9 = CapacitiveSensor(6,9); CapacitiveSensor cs_10_11 = CapacitiveSensor(10,11); CapacitiveSensor cs_10_12 = CapacitiveSensor(10,12); CapacitiveSensor cs_10_13 = CapacitiveSensor(10,13); CapacitiveSensor cs_30_31 = CapacitiveSensor(30,31); CapacitiveSensor cs_30_32 = CapacitiveSensor(30,32); CapacitiveSensor cs_30_33 = CapacitiveSensor(30,33); CapacitiveSensor cs_40_41 = CapacitiveSensor(40,41); int Do; int Re; int Mi; int Pa; int Sol; int Ra; int Si; int Do_1; int DoSharp; int ReSharp; int PaSharp; int SolSharp; int RaSharp; byte ch = '9'; void setup() { cs_2_4.set_CS_AutocaL_Millis(0xFFFFFFFF); Serial.begin(9600); } void loop() {
//손가락이 접촉되었을때 값을 측정. 접촉이 없을 경우 0을 반환하고 접촉이 있을 경우 0보다 큰값을 반환한다 long total1 = cs_2_3.capacitiveSensor(30); long total2 = cs_2_4.capacitiveSensor(30); long total3 = cs_2_5.capacitiveSensor(30); long total4 = cs_6_7.capacitiveSensor(30); long total5 = cs_6_8.capacitiveSensor(30); long total6 = cs_6_9.capacitiveSensor(30); long total7 = cs_10_11.capacitiveSensor(30); long total8 = cs_10_12.capacitiveSensor(30); long total9 = cs_10_13.capacitiveSensor(30); long total10 = cs_30_31.capacitiveSensor(30); long total11 = cs_30_32.capacitiveSensor(30); long total12 = cs_30_33.capacitiveSensor(30); long total13 = cs_40_41.capacitiveSensor(30);
//각 건반마다 특정값 이상으로 출력될 경우 그 건반 상태를 1로 변경(신체와 접촉 상태)
//접촉이 없을 경우 건반 상태를 0으로 변경 if(total1 > 20) Do = 1; else Do = 0; if(total2 > 20) Re = 1; else Re = 0; if(total3 > 20) Mi = 1; else Mi = 0; if(total4 > 20) Pa = 1; else Pa = 0; if(total5 > 20) Sol = 1; else Sol = 0; if(total6 > 20) Ra = 1; else Ra = 0; if(total7 > 20) Si = 1; else Si = 0; if(total8 > 20) Do_1 = 1; else Do_1 = 0; if(total9 > 20) DoSharp = 1; else DoSharp = 0; if(total10 > 20) ReSharp = 1; else ReSharp = 0; if(total11 > 20) PaSharp = 1; else PaSharp = 0; if(total12 > 20) SolSharp = 1; else SolSharp = 0; if(total13 > 20) RaSharp = 1; else RaSharp = 0;
//시리얼 통신을 통해 프로세싱으로 전송 Serial.write(ch); Serial.write(Do); Serial.write(Re); Serial.write(Mi); Serial.write(Pa); Serial.write(Sol); Serial.write(Ra); Serial.write(Si); Serial.write(Do_1); Serial.write(DoSharp); Serial.write(ReSharp); Serial.write(PaSharp); Serial.write(SolSharp); Serial.write(RaSharp); delay(15); //안정성을 위해 지연시간을 준다 }
스케치에서는 CapacitiveSensor라이브러리를 사용하는데 이 라이브러리는 아래 링크에서 받을 수 있다.

CapacitiveSensor라이브러리 받기

외부라이브러리 설치하는 방법

CapacitiveSensor라이브러리는 신체의 접촉이 생기는 순간 연결된 물체의 정전용량의 변화를 감지하여 접촉을 인식하는 라이브러리로 연결된 저항값이 클수록 더 민감하게 반응한다.



이 라이브러리를 사용하면 쿠킹호일에 터치하는 것을 감지할 수 있다. 스케치에서는 각 건반마다 변수값을 지정하고(도 건반에는 도, 레 건반에는 레 등)
건반에 신체접촉이 일어나 특정값이 발생할 경우 그 건반의 상태값을 1로 변경하여 그 값을 프로세싱으로 전송하는 역할을 한다. 

건반의 상태는 2가지로 나뉜다. 눌렸을 경우 1로 표현되고, 아무런 상태변화가 없을 경우에는 0으로 표현된다.
프로세싱으로 1이 전송될 경우에는 그 건반이 눌렸다는 신호이고 0이 전송될 경우에는 아무런 변화가 없다는 신호로 보면 된다.

프로세싱

import processing.serial.*;
import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;

Minim minim = new Minim(this);

AudioSample playDo;
AudioSample playRe;
AudioSample playMi;
AudioSample playPa;
AudioSample playSol;
AudioSample playRa;
AudioSample playSi;
AudioSample playDo_1;
AudioSample playDoSharp;
AudioSample playReSharp;
AudioSample playPaSharp;
AudioSample playSolSharp;
AudioSample playRaSharp;
int wait;

int Do;
int Re;
int Mi;
int Pa;
int Sol;
int Ra;
int Si;
int Do_1;
int DoSharp;
int ReSharp;
int PaSharp;
int SolSharp;
int RaSharp;

Serial myPort;

void setup(){
  println(Serial.list());
  myPort = new Serial(this, Serial.list()[0],9600);
  playDo = minim.loadSample("do.mp3");
  playRe = minim.loadSample("re.mp3");
  playMi = minim.loadSample("mi.mp3");
  playPa = minim.loadSample("pa.mp3");
  playSol = minim.loadSample("sol.mp3");
  playRa = minim.loadSample("ra.mp3");
  playSi = minim.loadSample("si.mp3");
  playDo_1 = minim.loadSample("do_1.mp3");
  playDoSharp = minim.loadSample("dosharp.mp3");
  playReSharp = minim.loadSample("resharp.mp3");
  playPaSharp = minim.loadSample("pasharp.mp3");
  playSolSharp = minim.loadSample("solsharp.mp3");
  playRaSharp = minim.loadSample("rasharp.mp3");

}

void draw(){
  
  if(myPort.available() >=1) {      
    if(myPort.read() == '9') { 
      Do = myPort.read();
      Re = myPort.read();
      Mi = myPort.read();
      Pa = myPort.read();
      Sol = myPort.read();
      Ra = myPort.read();
      Si = myPort.read();
      Do_1 = myPort.read();
      DoSharp = myPort.read();
      ReSharp = myPort.read();
      PaSharp = myPort.read();
      SolSharp = myPort.read();
      RaSharp = myPort.read();
    } 
  }

  background(255);
  print(Do);
  print(" ");
  print(DoSharp);
  print(" ");
  print(Re);
  print(" ");
  print(ReSharp);
  print(" ");
  print(Mi);
  print(" ");
  print(Pa);
  print(" ");
  print(PaSharp);
  print(" ");
  print(Sol);
  print(" ");
  print(SolSharp);
  print(" ");
  print(Ra);
  print(" ");
  print(RaSharp);
  print(" ");
  print(Si);
  print(" ");
  print(Do);
  println(" ");

  if(Do > 0) 
    playDo.trigger();
  if(Re > 0) 
    playRe.trigger();
  if(Mi > 0) 
    playMi.trigger();  
  if(Pa > 0) 
    playPa.trigger();
  if(Sol > 0) 
    playSol.trigger();
  if(Ra > 0)
    playRa.trigger();
  if(Si > 0)
    playSi.trigger();
  if(Do_1 > 0)
    playDo_1.trigger();
  if(DoSharp > 0) 
    playDoSharp.trigger();
  if(ReSharp > 0) 
    playReSharp.trigger();
  if(PaSharp > 0) 
    playPaSharp.trigger();  
  if(SolSharp > 0) 
    playSolSharp.trigger();
  if(RaSharp > 0) 
    playRaSharp.trigger();
}
아두이노와 프로세싱의 연결은
myPort = new Serial(this, Serial.list()[0],9600);

이 구문을 통해 이루어 진다.
Serial.list()[0]은 현재 컴퓨터와 연결된 포트 중에서 첫번째 포트와 연결하겠다는 의미이다.

현재 컴퓨터와 연결된 포트의 리스트들은 아래 사진처럼 실행시킬때 하단부에 뜨게 된다.




위 사진에서는 COM7과 COM37이 떴는데 이 중에 COM7이 아두이노 포트일 경우 리스트 배열안에
Serial.list()[0] 0을 그냥 쓰면 되고, COM37이 아두이노일 경우 0이 아닌 1을 쓰면 된다.
(순서에 따라 배열안의 숫자를 늘려주면 된다. 첫번째는 1이 아닌 0부터 시작인점만 유의하면 된다.)
프로세싱에서는 minim이라는 내장 라이브러리를 사용한다. minim은 프로세싱에서 음악파일을 재생할 수 있게 해주는 라이브러리이다. 이 라이브러리를 사용한다면 피아노음계파일을 재생시켜 피아노처럼 보이게 할 수 있다.

음악파일을 재생할 때에는 아래와 같이 음악파일이 프로세싱파일이 존재하는 폴더 안에 존재해야 한다.



스케치에서는 건반의 상태를 확인하고 그 상태의 값을 프로세싱으로 전송한다고 했는데
프로세싱에서는 간단하게 그 상태값을 가지고 해당 건반에 맞는 음원파일을 재생시키는 역할을 한다.
스케치에서 하지 못하는 음원파일 재생을 프로세싱을 통하여서 한다고 보면 된다. 

음원파일은 setup()에서 loadSample()을 통해 불러오게 되고, draw()에서는 스케치에서 보낸 값을 비교하여 trigger를 통해 해당 건반에 해당하는 음계음을 재생시킨다.

수박쨈

아두이노, 프로세싱, 라이브러리