|
-글을 시작하기 전에-
예전부터 작성하고 싶은 글이었는데.. 어떤 형식으로 해야 좋을지 고민고민 하다가 결국 '무형식이 형식' 이란 형식으로... (귀찮아서 이런거 절대 아니라곤 못하지만..) 글을 작성하게 되었습니다.
이 글은
MATLAB을 일단 막 인스톨 하였으나 이걸로 대체 뭘 할 수 있는지 고민하는 분들
신호처리 플젝을 하는데 이론적인 내용은 알겠으나 MATLAB으로 어떻게 처리해야 할 지 잘 모르겠는 전기전자공학과 학생
너무나 무료한 일상에 지쳐 강렬한 예술적 욕구에 불타는 나머지 자신만의 음악을 만들어 보고자 하고 있으나 쓸만한 도구가 매틀랩밖에 없는 분들
에게 조금이나마 도움이 되어보고자 작성합니다.
그.러.나. 반드시 짚고 넘어가야 하는 부분이 있으니.. 저도 MATLAB이라는 툴에 대해 잘 알고 있는게 아니라는 사실입니다.
제가 알고 있는 몇 가지 사실은... 데이터를 어디에 어떻게 올려놓고 쓰길래 속도가 이래 느려 터졌는지...(동일한 알고리즘을 C로 짜서 돌려보면 차이가 후덜덜입니다. 물론 어떻게 보면 거의 당연한거긴 한데.. 아무튼.)
그럼에도 불구하고 웬 함수가 이리 많아서 마음먹은걸 몇줄안에 간단히 명령할 수 있는지.. (역시 C와 비교하게 되는데.. 필요한 라이브러리가 생기면 C는 직접 짜거나 헤더찾아 삼만리를 해야 하는 반면.. 매틀랩은 무한의 help 연타와 도움말 검색으로 정말 웬만한 기능의 함수는 다 찾을 수 있습니다.)
그리고 약간의 명령어들.. 정도입니다. 그럼에도 불구하고, MATLAB을 정말 처음 시작하는 분들에게는 어느정도 도움이 되리라고 생각합니다.
-----이제 글 시작-----
원래는 MATLAB에 대한 간단한 소개 등으로 글을 시작하려 했으나.. 머리말이 생각보다 길어졌으므로 이 내용은 위키페디아에 패스하기로 하고..
http://en.wikipedia.org/wiki/MATLAB
그럼... 자세한 사항을 언급하고 넘어가면 이야기가 우구장창 길어지니까 우선 냅다 코드부터 보고 시작하죠.
매틀랩은 기본적으로 '매우 좋은 계산기' 의 역할을 수행합니다.
그래서 처음 시작했을 때 깜박깜박 거리는 창에다
>> 1+1 (그리고 엔터)
ans
= 2 (1+1을 수행하여 그 결과를 ans라는 일종의 버퍼 변수에 저장함과 동시에 출력한다.)
>> x=1
x
= 1 (x 라는 변수에 1을 저장하고 결과를 표시한다.)
이런 식으로 사용할 수도 있지만 위에서 표시된 네모난 종이버튼을 누르면 m파일이란 것을 작성할 수 있습니다.(확장자가 m이에요0_ 0)
여기에는 내가 쓴 매틀랩 코드를 저장해 놓거나(이건 좀 있다 보여드리겠숨) 우리가 지금 하려는 것 처럼 function 이라는 명령어를 사용하여 새로운 함수를 만들 수 있습니다.
위의 캡쳐화면은 len, fq, a 라는 세 함수를 받아서 note 라는 변수를 출력하는 mknote 라는 함수를 만드는 중인 장면을 생동감있게 캡쳐한 장면입니다. 이런 함수를 만들기 위해서 맨 첫 줄에 다음과 같은 명령어를 작성합니다.
function [출력변수] = 함수이름(입력변수1,입력변수2,...)
물론! 출력변수가 1개일 필요가 없지요. 출력변수를 여러개 지정해 주어도 상관 없지만 일단 우리는 하나만 쓰면 되므로 자세한 내용은
>> hlep function
을 통해 익혀보도록 하세요. >_</
암튼 함수의 내용을 따라가다 보면
fs=44000;
이런 명령어가 나옵니다. 이건 뭐 명백하게 fs 라는 변수(샘플링 주파수. 1/fs 초당 샘플 1개를 만듭니다.) 에 44000 이라는 값을 저장하는 구문임에는 틀림 없으나, 뒤에' ; ' 의 존재가 거슬리는 분들이 분명 계실거라 믿습니다. fs=44000 과 fs=44000; 의 차이는 결과값을 표시하느냐? 표시하지 않느냐? 의 차이라고 보시면 됩니다.
근데.. 중요합니다. -_ - 지금 fs라는 변수는 그냥 항 한개짜리 몇 번 등장하지도 않는 변수라서 실행을 시켰을 때 ; 가 없으면 그냥 '미관상' 안좋게 계속 fs = 44000 을 알려주는 정도지만... 이게 길게 생긴 변수거나 자주 등장하는 변수면 이 출력을 하고 안하고 차이 때문에 코드가 돌아가는 시간이 압도적으로 차이가 날 수 있습니다. 0_ 0(경험담입니다..-_-)
따라서 꼭 표시해야 하는 변수가 아니면 대입을 한 뒤에 ; 을 써주는 센스를 꼭 지켜주시기 바랍니다.
아.. 그리고 여기서 44000은 크게 의미 없습미다.. 가청주파수의 2배보다 조금 높게 잡은건데.. -_ - 이런 음질의 음원을 캐치해서 분석하는 것도 아니고 대충.. 우리가 다루고자 하는 최고로 높은 주파수보다 2배정도 높게 잡으면 됩니다.
Nyquist frequency <--궁금하신 분은 여기 참고
http://en.wikipedia.org/wiki/Nyquist_frequency
그리고.. 대부분 눈치를 채셨겠지만 % 는 매틀랩에서 주석을 의미합니다. 매틀랩을 돌리면 %이후 줄이 바뀔동안은 모두 주석처리합니다. C에서.. %를 이용하여 나머지를 구하던 그 기억을 되살리시면 안됩니다..0_ 0(다행히, 나머지를 구하는 함수도 포함되어 있습니다. mod라는 함수인데.. 역시 help mod 해보시길..)
다음의 문장에서.. 또 요상한 기호가 등장합니다. 바로
t=[1:X]/X;
이렇게 생긴 대입문인데.. 이것은 꽤나 자주 쓰이는 명령어입미다. 하나씩 뜯어보면
1:X 라는 것은 1부터 X까지 1씩 늘어나는 행벡터를 의미합니다. 따라서 당연히 X가 정수여야 하고 아니면 매틀랩이 버럭 화를 내겠죠?(근데 이상하게 꼭 화를 내진 않고 알아서 교정을 하기도 하더군요.)
따라서 1:X 는
[1 2 3 4 5 .... X] 라는 벡터를 만드는데 사용됩니다. 이 벡터에다가 +X 를 하면 모든 원소에 X를 더하고, *를 하면 모든 원소에 곱하고 /를 하면 모든 원소를 나눕니다. 즉!
t=[1:X]/X;
라는 명령어는 1/X부터 (X가 충분히 크면 거의 0부터) 1까지 길이가 X인 행벡터를 만들어서 t에 저장하렴! 하는 명령이인것입니다.
참고로 : 를 두번 이용하여 벡터간 간격을 자동으로 조정하는 방법도 있습니다. 다음과 같이 명령하면 됩니다.
입력당할 변수=시작:간격:끝
t=1/X:1/X:1;
따라서 이라고 하면 1/X부터 1까지 간격이 1/X인 열벡터를 만들어서 t에 저장하센! 이런 명령이 됩니다. 어덯게 생각해보면 이게 더 빠르겠지만..-_ - 크게 차이가 안나는 듯 하여 그냥 전자의 표현을 썻습니다.(그냥 개인적 습관이라고 보시면 될듯..)
그리고 이건 코드 내용과는 상관없는 내용인데.. 매틀랩에서 행렬의 연산을 할 경우 각 성분에 접근하여 연산하는 방법은 왜 그런지 정확히는 잘 모르겠지만(아마 처리과정을 한두단계 더 거치겠지요) 굉장히 느려집니다. (for 문을 돌리면 다운된다! 는 소문이 여기서 나옵니다.) 따라서 매틀랩을 사용할 때에는 가능하면 행렬 전체에 연산을 해서 한꺼번에 결과가 나올 수 있도록 알고리즘을 짜는 것이 중요합니다.
하아하아.. 코드 3줄에 엄청난 설명이 필요하군요. -_ - (이래서 .. 그동안 시작할 엄두를 못냈습니다.) 잠깐 커피좀 마시고 업데이트 하겠습니다.
---- 자 2라운드. 커피를 다 마셨습니다. -_ -v ----
이제 첫 번째 캡쳐화면의 마지막 문장이군요. 그냥 주석처리된 부분은 wav만들기 with MATLAB -02 에서 다룰 내용이므로 일단 넘어가고,
마지막으로 우리의 출력변수에 사인함수를 그려넣는 장면입니다!
note=a*sin(t*2*pi*fq);
와우! sin 이 그냥 함수로 있습니다! 무려 sin! 을 그냥 치면 사인함수가 나옵니다! 워어어어어어!!! (다른 사람에게 어떨지 모르겟지만 'sin이 필요하군. 이까짓거 c로 구현해 보겠어!' 하고 대판 삽질하다 실패한 저로써는... 이건 대단한 혁명이었습니다.-_ -; 아직도 알고리즘이 궁금해요 ㅎㅎ )
여기선 특별할 것은 없고.. sin 에 오직 하나의 값인 변수가 들어가는 것이 아니라 행벡터(1행 여러 열 짜리 행렬) 가 들어가서 값이 나온다는 사실이 중요합니다. 이 때 출력되는 결과는
note(1) = a*sin(t(1)*2*pi*fq), note(2) = a*sin(t(2)*2*pi*fq)...
이런 식으로 note에도 길이 t짜리 변수가 저장되는 것입니다!
그리고.. 한 가지 더.. pi 라는 상수.. 짐작하시겠지만 이건 우리가 잘 아는 파이입니다. 3.141592.... 매틀랩 안에 기본적으로 정의되어있는 상수라서 어느때고 빼서 쓸 수 있습니다.
그리고 자주 나타나는 실수가.. 우리는 곱하기 기호를 안쓰기도 하지만.. 예를들어 2pi 하면 2곱하기 pi 인줄 알지만 컴퓨터는 이게 뭔소린지 모릅니다. 더하라는건지 빼라는건지... 따라서 꼭 곱하기를 써 줘야만 작동을 합니다.
후어.. 후어.. 이제 첫 캡쳐그림이 끝났군요. 채팅하느냐 딴짓하느냐.. 진도가 좀 느린데 서둘러 나가봅시다.
아무튼 저렇게 코드를 다 작성한 뒤에는 꼭! 함수이름과 동일한 이름으로 매틀랩이 저장된 work폴더에 저장해야 합니다. (꼭 이렇게 안해도 나중에 경로 지정해주면 되긴 하는데.... 귀찮죠.-_-)
자.. 저 위치에 저장이 완료 되면! 우리는 mknote라는 함수를 사용할 수 있습니다.
뜸들이지 말고 바로 실행을 해 보죠!
아래는 testnote 라는 변수에 mknote(16,2,1) 의 값으로 출력되는 note 라는 변수를 입력하는 장면을 박진감 넘치게 캡쳐한 화면입니다.
유후?! 물론! mknote라는 함수를 실행하더라도 mknote안에서 정의된 변수를 (예를들어 t) 사용할 수 없습니다. 이건 뭐.. 거의 모든 언어에 공통이겠지만 함수 안에 있던 변수들은 지역변수니깐 함수가 실행된 뒤 자동으로 다 삭제되겠지요? (물론.. 이 상황에서는 삭제 안해도 도저히 호출할 방법이 없으므로 무효..)
따라서 t 가 뭐니? 라고 물어보면 '정의 안했어요.' 라고 대답합니다. 하지만.. 그래프는 보고싶고 해서 mknote에서 2줄 긁어다가 len=16을 넣고 붙여넣기해서 실행을 하는 방법으로 t를 다시 정의했습니다.
그 뒤에! 바로 plot이라는 명령어를 사용합니다. plot(x,y) 는 다음과 같은 점들을 그래프로 주욱~ 표시하는거라고 보시면 됩니다.
(x(1),y(1)), (x(2),y(2)),....(x(n),y(n))
이 값들이 꽤 촘촘하게 있으면 부드러~ 운 그래프를 그릴 수 있겠지요?
아.. 그리고 만약 plot(y) 를 하면
(1,y(1)), (2,y(2)),....(n,y(n)) 을 출력합니다.
plot에는 많은 세팅이 존재하므로 그래프를 예쁘게 그리기 위해서는 반드시 help plot을 참고하여 공부하셔야 합니다. !! (그리고 이건 사실 심미적 요소 그 이상의 가치를 지니고 있습니다. 여러개의 그래프를 한 창에 그렸는데 몽땅 파란색에 실선이면 구별이 안되겠죠? 물론... 여기서는 구별이 필요 없는 관계로..디폴트 셋팅으로 고고싱~!)
뭐.. 계속 원래 주제로 넘어가서... 거의 당연한 결과지만 그렇기 때문에 x와 y의 길이가 같지 않으면 그래프를 출력하는데 어려움을 겪습니다. (어려움을 겪는 정도가 아니라 버럭! 화를 냅니다.. 길이가 다르다고.. 그리고 그래프를 못그리죠) 이럴땐 당황하지 말고 길이가 어떻게 다른지 확인을 해 보면 됩니다.
다행히도, 왼쪽에 워크스페이스를 보면 지금까지 정의한 변수들이 나타나는데 이 변수들의 데이터 타입과(기본적으로 숫자로 된건 거의 double로 줍니다. ) 이들의 디멘젼이 나타납니다. (1*44000 .. 기본적으로 매틀랩은 변수들을 거의 행렬이라고 생각합니다.) 이 수치를 보고 어떤 변수의 길이가 원하던 것과 달라졌는지 찾아서 코드를 수정할 수 있겠지요?
만약.. 이걸 보기 귀찮거나 변수를 너무 많이 정의해서 헷갈린다! 싶으면 length 라는 명령어를 사용합니다. m*n 행렬변수 x로 length(x) 를 실행하면 max(m,n) 이 결과로 튀어나옵니다.
테스트 결과를 보면.. 우리가 원한대로 길이는 총 1초, 주파수는 2이고 진폭이 1인 사인 그래프가 나왔습니다. 이것으로 우리의 mknote 가 잘 작동한다는 것을 확인할 수 있겠군요.
이제 이를 가지고 노래를 만들어 봅시다!
sin을 가지고 노래를 만들기 위해서는 각 음계별 주파수를 알아야 하는데 이건 검색하면 금방금방 나옵니다 ㄷㄷ (아니면 자신만의 음계를 만드셔도.....)
여기부터는 사실 그냥 명령창에 그대로 입력하셔도 무방하지만! 편집작업을 수월하게 하기 위해 m파일로 만드는 방법도 좋습니다.
아아 이제 대망의 끝이 보이는군요! 음의 길이를 다르게 해서 적절히 연주를 하면 좋겠지만 귀찮은 관계로..-_ -모두 0.5초짜리 음으로 만들었습니다.(slnc는 silence 로 쉼표입니다. 진폭을 0으로 줘서 만들었습니다.) 여기서.. song 이라는 변수를 저장할 때 행렬들을 그대로 연결하고 있음에 주목! 이 방법은 정말 막강한 방법인데.. 작용되는 방법은 다음과 같습니다.
예를들어 두 행렬 a=[1 2] 와 b=[3 4] 가 있다고 할 때 [1 2 3 4] 를 만들고 싶다! 하면 그냥 단순히
c=[a b] 를 해주면 되는 엄청난 획기적인 방법인 것입니다!
너무 급하게 뛰어오느라 .. 여기에 행렬을 만드는 몇 가지 요소를 설명을 안했는데.. [1 2] 이런건 주구장창 설명을 햇고..
그럼
┌1┐
└2┘
이런 놈들은 어떻게 만드느냐?
; 기호를 사용하면 됩니다. 즉, a=[1;2] 라고 써주면
a=
┌1┐
└2┘
이렇게나 우아하고 멋지게 저장이 됩니다.
물론 a=[1 2] 와 b=[3 4] 에서
c=[a;b] 를 해주면
c=
┌1 2┐
└3 4┘
를 얻습니다.
추가로 좀더 설명을 하면 ' (; 왼쪽의 ') 를 쓰면 전치행렬을 얻습니다.
a=[1 2] 일 때 a'은
┌1┐
└2┘
입니다.
c=
┌1 2┐
└3 4┘
에서 2행 2열의 원소에 직접 접근하고 싶다! 고 한다면 c(2,2)를 사용합니다.
c(2,2)
= 4
1행에만 접근하고 싶다! 이런 경우에는 : 를 사용하여 c(1,:) 를 사용합니다.
c(1,:)
= [1 2]
물론 열에도 적용이 되겠지요? c(:,1)은
c(:,1) =
┌1┐
└3┘
그렇다면, c(3)은 뭘까요? 호출이 안될까요?
컴터가 배열을 저장하는 방식을 떠올려 보면.. 쉽게 알 수 있는데 c(3)를 호출하면 1행의 1열 원소부터 2열 원소까지 접하고 다음에 2행 1열의 원소로 바로 접근하게 됩니다. 따라서 이런 방법을 이용해서 코드를 짤 수도 있지만, 헷갈릴 수 있으므로 가능하면 사용하지 않는 것이 좋습니다.
자.. 이제 대망의 wavwrite 명령어를 사용합니다.
이 함수는 배열을 받아서 .wav의 확장자를 가지는 파일을 생성하는 함수입니다.
구체적인 스펙은 help wavwrite 를 보면 나오고(모듈레이션 방식으로 PCM을 사용한다든가.. 몇 비트로 모듈레이션 한다든가..) 오버로딩 되어 있어서 입력하는 변수의 숫자로 디폴트로 부를 수도 있고 옵션을 조절할 수도 있습니다. (plot에서도 비슷한걸 봤지요?.)
물론! 여기선 디폴트 설정을 사용합니다. 입력해야 할 변수는 wavwrite(소리데이터,샘플링 주파수,파일 생성 위치) 가 되겠습니다.
자.. 명령어 작성이 끝났습니다. 이걸 긁어 붙여서 명령을 내려도 되고! F5 버튼의 save & run을 작동시켜서 주욱~ 실행이 되도록 해도 됩니다. 보통은 후자를 추천하지만..... 저는 코드를 많이 틀려서.... -_ - 어느샌가 긁어붙이기가 습관이 되어버렸는데 긴 코드를 작성할 때에는 굉장히 안좋은 습관입니다. 디버거도 있으므로 적극 활용합시다.
특히 이런 m파일은 함수로 사용되는게 아니기 때문에 저장할 때 아무 이름으로도 저장해도 됩니다.
그리고... 실행을 하면!
우왕~! 몇가지 경고가 나오지만 이건 PCM을 수행할 때 quantization error에 관한거 아니면 같은이름의 파일을 저장했을 때 나오는 내용이므로 무시하고 (더 많은 의미를 포함하고 있을지도 모르는데 ;ㅅ; 사실 저도 더 자세하게는 모르겠습니다.) 넘어가도 좋습니다.
이것으로 testsound.wav 파일을 완성하는데 성공했습니다!
기념으로 song이 어떻게 생겼는지 관찰을 해 봅시다.. 물론, 시간을 만들때는 아까 언급했던 1:X 를 적극 활용합니다.
그런데.. 여기서 X값을 어떻게 찾느냐? length(song) 으로 찾으면 됩니다. 특히 우리는 시간간격도 알고 있으므로 (샘플링 주파수의 역수 1/fs) 이를 이용해서 시간-song그래프를 그리면!!!!!
기대와는 달리(?) 시퍼런 화면을 볼 수 있습니다. ㅋㅅㅋ
사실 당연한게.. 가장 낮은 음이 200헤르쯔가 넘기 때문에 위아래로 엄청나게 요동을 칠 수 밖에 없고 그걸 다 점찍다 보면 시퍼렇게...-_ - 가운데 쉼표구간만 0으로 알아볼 수 있군요 . ㅋㅅㅋ
이렇게 완성된 학교종이 땡땡땡~! 들어봅시다.
그런데 일반적으로 wav라는 포맷은 잘 안쓰기 때문에 ..(bmp를 잘 안쓰듯..) 확장자를 변환해주는 프리웨어 컨버터를 사용해서 wma 포맷으로 변경했습니다.
자.. 기나긴 글 읽느라 수고하셨습니다. 다음 후속편을 쓰면서 코드 일체와 캡쳐사진을 모두 공개할 생각입니다.(물론 그림이랑 코드 작업은 다 되어있는데... 설명이..설명이..할게너무많어 ;ㅅ;) 근데.. 이거 쓰는데 워낙 오래 걸려서..-_ - 다음이 언제가 될런지... 휴우...
그럼! 읽어주셔서 감사하고 나만의 매틀랩 세계에 빠져보시길!!
첫댓글 저도 매틀랩 잘하고싶어요..ㅠㅠ 근데 현실은 메스메티카도 안습..;; 책사서 공부좀해야할듯..
킬러님 진짜로 존경함. ㅠㅠ
오오!!! 매트랩 팁이군요 ~! 저도 매트랩 사랑합니다 ㅜㅡ
완소죠 ;ㅅ; 필요한 함수가 있으면 help 치면 다나오는 우리의 매트랩!