프로젝트

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

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

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를 통해 해당 건반에 해당하는 음계음을 재생시킨다.

수박쨈

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

hne73548 2014-11-26 00:38:48

다 연결해보았는데 어떻게 스케치랑 프로세싱이랑 연동되는지 잘 모르겟네요ㅠㅠ
프로세싱을 켜서 실행시키면
myPort = new Serial(this, Serial.list()[0],9600);

이 부분에 오류가 난다고 뜨는데 어떻게 해결해야하는건지 알려주시면 감사하겠습니다ㅠ
p.s. 저는 아두이노uno r3로 <도레미파솔라시도>만 연결시켰습니다

profile

수박쨈 2014-11-26 08:39:14

println(Serial.list()); 이 구문은 현재 컴퓨터와 연결된 포트의 번호를 리스트로 나열해 주는 구문입니다. 컴퓨터와 연결된 포트의 번호들이 콘솔창에 뜰껀데 그 포트번호들을 보시고 아두이노UNO의 포트번호와 맞는 배열의 번호를 myPort = new Serial(this, Serial.list()[우노의 포트번호],9600); 이쪽에 적어 주시면 됩니다! 예를들어 아두이노의 포트번호가 COM9번일 경우 컴퓨터와 연결된 포트 번호가 COM3 COM8 COM9일 경우 2를 적어주시면 되고 COM9 COM10 COM11일 경우 그냥 0을 쓰시면 되고 COM3 COM9 COM11 COM38 이럴 경우 1을 적어주시면 됩니다.

profile

수박쨈 2014-11-26 08:48:30

본문에 이 부분에 대해 설명 추가하였습니다~

profile

4885 2014-12-02 23:57:59

종이피아노를 만들고있는 아두이노 초급자 입니다.다름이아니라 은박지를 손가락으로 누르면 약 3초의 시간이 흐른 뒤 소리가 나는데 이때 나는 소리가 정상적이지 않은 소리가 납니다. 이러한 증상에 대한 이유가 어떤것이며 해결법이 무엇인지 가르쳐 주시면 감사하겠습니다. 답변 기다리겠습니다.^^

profile

수박쨈 2014-12-03 13:52:17

증상만으로는 정보가 부족합니다. 제가 쓴 방법은 통해 만든것인지 아니면 다른 방법으로 만든것인지 모르고, 값이 정확히 전송되는 가운데서도 정상적이지 않은 소리가 나는지도 알 수가 없습니다.(예를 들어 '도'를 소리내라는 값을 전송했는데 '도'가 울리지 않고 '레'소리가 나는 경우가 있고, 전송될 때 아예 엉뚱한 값이 전송되어 엉뚱한 소리가 나는 경우도 있습니다.) 제가 올린 capacitiveSensor를 통해 만들었다면 capacitiveSensor 예제를 한번 실행시켜서 은박지에 손가락으로 터치를하여 값의 변화가 즉각적으로 이루어지는지 확인해 보시면 됩니다. 연결 방법은 제가 올린 브레드보드 이미지를 보면 저항3개가 하나의 그룹을 이루는 것을 볼 수 있는데 이것을 튜토리얼의 핀번호에 맞게 연결하여 주시면 됩니다. 튜토리얼을 실행 시켰을 때 값이 즉각적으로 이루어 진다면 원인을 아마 통신과정에서 찾아 볼 수 있을거 같습니다. 엉뚱한 소리가 나는 것은 한번 데이터의 타입에 맞춰 정확히 전송이 되는지 확인해 보시기 바랍니다.

profile

hne73548 2014-12-04 13:59:34

친절하신 설명 감사합니다^^ !!!

저는  com 으로 나오지 않고
/dev/cu.Bluetooth-Modem /dev/cu.Bluetooth-PDA-Sync /
dev/cu.iPhone-WirelessiAP /dev/cu.usbmodemfa131 /
dev/tty.Bluetooth-Modem /dev/tty.Bluetooth-PDA-Sync
/dev/tty.iPhone-WirelessiAP /dev/tty.usbmodemfa131

이렇게 뜨는데 포트번호와 맞는 배열을 쓰는곳에 1이라고 적으니
작동은 됬습니다 ㅎㅎ
근데 작동이 되니까 또

==== JavaSound Minim Error ====
==== Don't know the ID3 code TDAT
 
==== JavaSound Minim Error ====
==== Don't know the ID3 code TIME
 
==== JavaSound Minim Error ====
==== Don't know the ID3 code PRIV

콘솔에 이런 메세지가 뜨고 소리가 나지 않습니다 ㅠㅠ
어떻게 해결해야하나요?
capacitive sensor의 숫자 0은 밑에 창에 계속 뜨고 있습니다.

p.s.
AudioSample playDo; 이거 원래 전부 다 검은글씨로 나오는게 맞는건가요?
 

profile

hne73548 2014-12-04 14:16:11

찾아보니까 결국 포트연결이 제대로 안되서 그런거같은데 ㅠㅠ왜 저는 com이라고 안뜨고 블루투스 모뎀이라고 뜰까요?ㅠㅠㅠ

profile

수박쨈 2014-12-05 09:23:19

pc를 Windows가 아닌 MAC을 쓰고 계시다면 COM이라는 이름대신에 저런 dev/cu나 dev/tty로 잡히는걸로 알고 있습니다. AudioSample playDo;는 검은글씨로 뜨는 것이 맞고 Minim에서 에러가 뜨는것은 음원파일의 문제인듯 합니다. 확인해 볼 수 있는 사항으로는 1. 음원파일이 위 소스가 저장된 프로세싱파일의 디렉토리 위치와 같이 있는지 2. 위에서 지정한 음원파일 이름과 저장된 음원파일의 이름이 같은지(ex.도 음계파일이 Do.mp3로 프로세싱에서 지정되 있다면 Do.mp3로 디렉토리내에 존재해야 합니다. 3. 저는 위에서 프로세싱코드를 만들 때 도부터 Sharp음계까지 모두 지정해 주었는데 사용자 분께서는 도레미파솔라시도만 지정해서 사용하신다고 하셨습니다. 이때 그럼 사용하지 않는 Sharp음계에 관련된 소스는 모두 지워주시고 사용하셔야 합니다. 반음음계에 관련된 소스가 프로세싱내에 존재하고 그에 관련된 음계파일이 컴퓨터내에 존재하지 않을 경우 위와 관련된 에러가 뜰 수 있습니다. 제 생각에는 3번이 가장 위 에러와 관련되서 확률이 가장 높지 않을까 생각됩니다.

profile

jcsokid 2014-12-06 12:55:43

안녕하세요 아두이노를 막 시작하고있는 초보자입니다 . 

현재 실험으로 프로젝트를 좀 따라해보고있는데요 , 

아두이노는 UNO를 사용하고있고 버튼 3개정도만 연결해서 진행해보려고했습니다. 핀부분에서 13, 12, ~11, ~10

(이렇게 써있는부분 사용) 

4개만사용하여  ~10 부분에 저항 3개가 연결되게 하고 나머지는 13 12 ~11에 연결하여 브레드보드 그대로 따라했는데요,

(케이블은 일반 케이블을 호일에 테이프로 붙였습니다.)  

하지만 프로세싱이나 아두이노 코드에서 오류나는거는 없는데 호일은 누른다고 전혀 소리가 나지않습니다 ㅠㅠ...

프로세싱에서 음악파일도 제대로 넣어놨구요 .. 시리얼통신도 오류없이 일어나는거 같습니다 ..

케이블이 점퍼케이블이 아니라서 그런걸까요 ..? 아니면 저항이 제가 가진가 10k 옴이 아닌거 같기도한데

그런거때문에 그런걸까요 .. ? 도무지 해결방법을 찾지못하여 질문남깁니다... 도움주시면 정말감사하겠습니다 ㅠㅠ

profile

hne73548 2014-12-08 07:20:20

저항이 10k옴이 아니면 작동이 안되더라구요ㅎㅎ 인터넷에 저항읽는법 쳐서 한번 확인해보세요~^^ㅎㅎ

profile

수박쨈 2014-12-08 09:09:36

보통 에러가 나게 되면 전체적인 코드내에서 문제를 찾기보다는 모듈별로 계속 쪼개어서 에러가 일어나는 부분을 찾는 것이 제일 좋은 방법입니다. 여기서는 크게 안되는 이유를 찾자면 1. 아두이노 코드나 회로 연결에서의 문제(코드상에서는 문제가 없으니 회로 문제에서 찾아보세요) 2. 아두이노와 프로세싱간에 시리얼 통신과정에서 생기는 문제 3. 프로세싱 코드에서의 문제 아두이노에서 CapacitiveSensor라이브러리를 설치하게 되면 CapacitiveSensor예제가 하나 존재하는데 일단 설치한 회로도에서 이 예제를 한번 돌려보셔서 호일을 눌렀을 때 그 부분에 값의 변화가 있는지 체크부터 해보세요. 예제를 실행하게되면 시리얼 모니터에 3개의 값이 계속적으로 뜨게 되는데(왼쪽 시간 제외) 아무런 감지가 없을 경우 0이 계속 뜨다가 호일을 손으로 누르게 되면 그 부분에 해당하는 값이 60으로 증가하게 됩니다.(제 경우에는 60으로 증가하였는데, 다른 분들은 잘 모르겠습니다.) 2번 시리얼통신에서의 문제와 3번 프로세싱 코드내에서의 문제는 아두이노에서 코드를 단순화시켜서 찾아 볼 수 있습니다. 아두이노에서 loop() { Serial.print(data); delay(1000); Serial.print(data1); delay(1000); } 이런식으로 간단하게 프로세싱 코드로 데이터를 보내서 프로세싱에서 음원 소리가 나는지 확인해 보시기 바랍니다. 시리얼 통신에서 문제가 없는거 같다고 하셨으니 소리가 나지 않을 경우 프로세싱에서 문제를 찾아보시면 됩니다.

profile

수박쨈 2014-12-08 09:12:15

CapacitiveSensor의 경우 연결하는 저항의 크기가 낮을 수록 값의 변화가 둔해지고 클수록 값이 민감하게 변화합니다.
10k옴 이상의 저항으로 연결하는 것을 추천합니다.

profile

jcsokid 2014-12-10 17:16:32

답변감사합니다! 현재 계속 진행중에 있는데요 . 10k 옴을 사용하여 이제 재생도 잘되고 

연결도 다 잘되는데

누르면 여러번의 소리가뜹니다 중첩해서 ㅜㅜ... 꾹누르고있으면 끊임없이 겹쳐서 뜨고요 ..

코드를 딱히 바꾼것은 없는데 동영상에 올려주신것과 비교를 해봤을때 이런 문제가 있어서 댓글남깁니다 ㅠㅠ..

답변주시면감사하겠습니다.

profile

수박쨈 2014-12-11 08:44:19

소리 중첩 관련한 문제는 저도 그랬고 코드 구조상 계속 누르고 있으면 소리가 중첩되서 날 수 밖에 없는 구조로 되어있습니다. 스케치에서는 위의 제가 올린 코드를 중심으로 15ms마다 계속적으로 건반의 상태를 프로세싱으로 보내버립니다. 프로세싱에서는 그 상태값에 따라 눌러진 건반이 있을 경우 거기에 해당하는 음원을 계속 재생하게 되는데 그렇기 때문에 계속 누르고 있을 경우 15ms마다 그 음원을 계속 재생시켜 버려 소리가 중첩되어 들리게 됩니다. 문제를 해결하고 싶으시다면 1. 스케치에서 건반의 상태를 보내는 주기를 늘린다.(스케치 코드 맨 밑 부분 delay(15);의 값을 늘립니다.) -> 음원의 재생시간이 1초일 경우 스케치에서 프로세싱으로 보내는 건반의 상태를 1초로 할 경우 중첩되어 소리는 들리지 않지만 건반을 내가 눌렀을 때 스케치에서 읽고 있는 코드가 딜레이일 경우 눌렀을 때 소리가 밀려서 나게 됩니다. 2. 스케치내에서 프로세싱으로 건반의 상태를 보낼때 각 건반마다 전의 상태값과 현재 상태값을 비교하여 보낸다. -> 만약 '도'건반을 눌렀을 경우 스케치에서 '도' 건반에 해당하는 값이 0에서 60으로 변하였다면 현재 내가 '도'건반을 눌렀을 때 '도'건반의 전 상태값이 60이었을 경우에는 그 값을 전송하지 않으면 소리가 중첩되어 들리는 일은 없을 것입니다. ex) '도'건반을 눌렀음(60) -> '도'건반을 계속 누르고 있었음(계속 60값을 전송) 이 경우는 60값 전송을 하지 않고 '도'건반을 눌렀음(60) -> '도'건반을 한번 뗌(값이 60에서 0으로 변함) -> 다시 '도'건반을 누름(60) 이 경우 한번 뗏다가 다시 누른걸 인지하였기 때문에 '도'건반의 상태값을 프로세싱으로 전송

profile

또또시겡 2014-12-11 22:45:15

프로세싱 코드를 살짝 수정했습니다. 피아노 같이 일정 주파수를 계속 내는 장치라면 상관없는 이야기지만 만약 다른 사운드를 넣고 싶을 때는 문제가 생기더군요. 한 번 누르면 한 번 소리를 내게 해야 되는데, 저 코드로는 누르고 있는 시간만큼 소리를 내기 때문에 한 번 눌렀다 때도 그 동안 1을 보낸 수 만큼 소리를 내더군요. 그래서 변수를 좀 추가해서 코드를 수정해보았습니다.

Last_계이름(ex Last_Do) 를 추가하여 setup을 0으로 지정해두고.(이제부턴 Last_Do를 예시로 설명합니다.)
if문으로 Do 변수가 1이 될 경우(은박지를 만짐) 소리를 내고 Last_Do를 1로 바꿔줍니다. 그리고 Last_Do 가 1일 경우에는 소리가 나지 않습니다. 그러다가 Do 변수가 0보다 작거나 같을 경우(은박지에서 손을 땜) Last_Do가 다시 0이 되어 다음 번에 다시 은박지를 만질 경우에는 소리가 다시 납니다. 대신 쭉 누르고 있는 경우에는 소리가 한 번 만 나게 됩니다.

아래 코드가 핵심 수정 부분입니다.

if(Do > 0) {
    if(Last_Do <= 0) {
      playDo.trigger();
      Last_Do = 1;
    }
  }
  else{
    Last_Do = 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;
  int Last_Do;
  int Last_Re;
  int Last_Mi;
  int Last_Pa;
  int Last_Sol;
  int Last_Ra;
  int Last_Si;
  int Last_Do_1;
  int Last_DoSharp;
  int Last_ReSharp;
  int Last_PaSharp;
  int Last_SolSharp;
  int Last_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");
  Last_Do = 0;
  Last_Re = 0;
  Last_Mi = 0;
  Last_Pa = 0;
  Last_Sol = 0;
  Last_Ra = 0;
  Last_Si = 0;
  Last_Do_1 = 0;
  Last_DoSharp = 0;
  Last_ReSharp = 0;
  Last_PaSharp = 0;
  Last_SolSharp = 0;
  Last_RaSharp = 0;
}
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) {
    if(Last_Do <= 0) {
      playDo.trigger();
      Last_Do = 1;
    }
  }
  else{
    Last_Do = 0;
  }
 
if(Re > 0) {
    if(Last_Re <= 0) {
      playRe.trigger();
      Last_Re = 1;
    }
  }
  else{
    Last_Re = 0;
  }
 
   if(Mi > 0) {
    if(Last_Mi <= 0) {
      playMi.trigger();
      Last_Mi = 1;
    }
  }
  else{
    Last_Mi = 0;
  }
 
    if(Pa > 0) {
    if(Last_Pa <= 0) {
      playPa.trigger();
      Last_Pa = 1;
    }
  }
  else{
    Last_Pa = 0;
  }
 
 if(Sol > 0) {
    if(Last_Sol <= 0) {
      playSol.trigger();
      Last_Sol = 1;
    }
  }
  else{
    Last_Sol = 0;
  }
 
  if(Ra > 0) {
    if(Last_Ra <= 0) {
      playRa.trigger();
      Last_Ra = 1;
    }
  }
  else{
    Last_Ra = 0;
  }
 
   if(Si > 0) {
    if(Last_Si <= 0) {
      playSi.trigger();
      Last_Si = 1;
    }
  }
  else{
    Last_Si = 0;
  }
 
   if(Do_1 > 0) {
    if(Last_Do_1 <= 0) {
      playDo_1.trigger();
      Last_Do_1 = 1;
    }
  }
  else{
    Last_Do_1 = 0;
  }
 
   if(DoSharp > 0) {
    if(Last_DoSharp <= 0) {
      playDoSharp.trigger();
      Last_DoSharp = 1;
    }
  }
  else{
    Last_ReSharp = 0;
  }
 
  if(ReSharp > 0) {
    if(Last_ReSharp <= 0) {
      playReSharp.trigger();
      Last_ReSharp = 1;
    }
  }
  else {
    Last_ReSharp = 0;
  }
 if(PaSharp > 0) {
    if(Last_PaSharp <= 0) {
      playPaSharp.trigger();
      Last_PaSharp = 1;
    }
  }
  else{
    Last_PaSharp = 0;
  }
 
if(SolSharp > 0) {
    if(Last_SolSharp <= 0) {
      playSolSharp.trigger();
      Last_SolSharp = 1;
    }
  }
  else{
    Last_SolSharp = 0;
  }
 
   if(RaSharp > 0) {
    if(Last_RaSharp <= 0) {
      playRaSharp.trigger();
      Last_RaSharp = 1;
    }
  }
  else{
    Last_RaSharp = 0;
  }
}


profile

또또시겡 2014-12-11 23:32:24

근데 전 다 이렇게 해놓고도 피아노 소리를 못구해서 게임 사운드 이펙트 넣어 놓은 상태네요 ㅠㅜ 사운드 좀 올려주시면 안될까요? 지금 Do 누르면 데마시아! 소리 나요.

profile

수박쨈 2014-12-12 09:33:28

전 댓글에서의 문의가 중복되어 소리나는 것에 대한 질문이었는데 소스 수정 감사합니다. 바로 전 댓글에도 제가 달았듯이 현재 상태값과 전 상태값을 비교하여 같은 경우에는 소리가 나지 않게 하여 중복되어 소리나는 것을 방지할 수 있습니다. 음원파일은 위에 부품목록쪽에 Dropbox링크로 올려드렸습니다.(저도 인터넷에서 구해서 받은거지만 #음계의 소리가 일반 음계랑 소리가 같더군요. 그래서 #음계 소리는 필요하시다면 다른 방법으로 구하셔야 할거 같습니다.)

profile

dlslzmal 2014-12-14 20:02:36

아두이노 우노를 이용하여 만들려고 하는데 회로도에서처럼 연결할수 있는 input 단자가 maga 처럼 많지 않은데 어떻게 해야 하나요???ㅜㅜㅜ

profile

dlslzmal 2014-12-14 21:57:31

==== JavaSound Minim Error ==== ==== Don't know the ID3 code TSSE 프로세싱에서 이렇게 뜨면서 소리가 안나는데 이유가 뭔지 아시는분 알려주세요 ㅜㅜ

profile

수박쨈 2014-12-15 08:23:24

출력포트 같은 경우에는 74HC595같은 시프트레지스터나 여러 멀티플렉싱으로 소수의 핀으로 다수의 센서나 LED를 제어할 수 있는 확장이 가능하지만 이런 피아노 같이 데이터를 읽어오는 입력포트의 경우에는 정해진 핀 번호로 데이터를 읽어와야 하기 때문에 확장이 어렵습니다. 많은수의 건반을 제어하기 위해서는 Mega의 사용이 용이하기는 하지만 Mega가 없어도 없는대로 3개의 건반을 제어하기 위해 4개의 핀을 사용한다 했을 때 UNO의 디지털 핀 2번부터 13번까지 12개의 핀으로 9개의 건반을 제어할 수 있습니다. 9개의 건반이면 #음계를 배제하고 1옥타브의 음계는 표현할 수 있는 수 입니다.

profile

수박쨈 2014-12-15 08:37:19

Minim에러는 사운드파일쪽 i/o에서 문제가 있어서 뜨는 듯 합니다.
아래 부분을 확인하시고 찾아보세요!
1. 사운드파일의 위치가 저장된 프로세싱파일 디렉토리 내에 존재하는지 확인
2. 사운드파일의 이름과 프로세싱내에 지정한 파일의 이름이 같은지 확인
   (minim.loadSample("do.mp3");일 경우 '도'음계의 파일은 do.mp3가 되어야 합니다.)
3. 소스내에서 읽어오려는 음계의 파일이 존재하지 않을 경우
   (예를 들어 소스내에서 '솔'음계의 파일을 불러오려 할때 파일이 없을 경우 에러)
4. 아두이노에서 전송하는 데이터가 올바른지 확인
   (위 소스에서는 아두이노에서 모든 음계에 대한 데이터를 전송합니다. 모든 음계에 대하여 건반 터치가 없을 경우 0, 터치할 경우 1을 전송하는데 예를 들어 '미'건반에 대한 데이터를 아두이노에서 전송하였는데 프로세싱에서 데이터를 받아도 그것에 대한 처리를 할 수 없는 경우 에러가 뜨게 됩니다. 프로세싱 내에서 현재 사용하고 있지 않은 음계에 대한 소스코드는 삭제해주시고 스케치에서도 프로세싱과 데이터에서 싱크를 맞춰주어야 합니다.)

profile

핸섬맨 2015-06-17 21:49:39

눌르면 한~참 뒤에 소리가 남니다 그리고 코일이랑 붙혀놧는데 코일말고 핀을 눌려야 소리가 나는데 어떻게 해야 하나요?

profile

서동현 2015-12-08 21:52:37

println(Serial.list()); 이분에서 자꾸 오류가 뜨는대, type string [] of the last argument to method println(Object...) doesn't exactly match the vararg parameter type.Cast to object[] to confirm the non-varargs invocation,or pass individual argument of type Object for a varargs invocation. 이렇게 뜨는데 왜그런가요 ㅠㅠ 스케치 업로드도 하고 음원 파일도 내문서에 있는 프로세싱 폴더 안에 minim 폴더에 넣었는대 ㅠㅠ 왜 오류가 생기는지 모르겠어요 참고로 아두이노는 메가 2560 호환보드를 사용했습니다

profile

서동현 2015-12-09 13:12:19


COM3
==== JavaSound Minim Error ====
==== java.io.FileNotFoundException: do.mp3

Exception in thread "Animation Thread" java.lang.NullPointerException
at ddf.minim.Minim.addSource(Minim.java:308)
at ddf.minim.Minim.loadSample(Minim.java:476)
at ddf.minim.Minim.loadSample(Minim.java:458)
at sketch_151209a.setup(sketch_151209a.java:70)
at processing.core.PApplet.handleDraw(PApplet.java:2374)
at processing.awt.PSurfaceAWT$12.callDraw(PSurfaceAWT.java:1527)
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:316)
이렇게 갑자기 뜨는데 이건 왜그런건가요? ㅠㅠㅠ

profile

수박쨈 2015-12-09 13:52:33

do.mp3파일을 찾지 못하는거 같은데요..

profile

조재영 2015-12-19 01:03:25

서동현님 처럼 println 쪽에서 오류가 발생해서, 이름도 다 맞추었는데 계속 오류가나는건 무엇일까요??,,

profile

김수현 2016-02-15 00:30:27

참고하겠습니다~

profile

강지훈 2016-05-22 17:21:56

혹시 부저를 연결하지 않았는데 소리가 어떻게 나는지 알 수 있나요????

profile

마지석 2016-06-09 17:46:28


processing.app.SketchException: unexpected char: 'i'
at processing.mode.java.JavaBuild.preprocess(JavaBuild.java:399)
at processing.mode.java.JavaBuild.preprocess(JavaBuild.java:193)
at processing.mode.java.JavaBuild.build(JavaBuild.java:152)
at processing.mode.java.JavaBuild.build(JavaBuild.java:131)
at processing.mode.java.JavaMode.handleLaunch(JavaMode.java:153)
at processing.mode.java.JavaEditor$36.run(JavaEditor.java:1101)
at java.lang.Thread.run(Thread.java:745)

이런식으로 에러가 뜨는데 어쩌나요....ㅠㅠㅠㅠㅠㅠ 살려주세요..