기본 콘텐츠로 건너뛰기

Web Audio로 악기를 만들자.

잉여력이 넘치던 꼬꼬마 시절. 10년전쯤이었나. 2000년 이전이었는지 이후였는지 가물가물.
친구 taekie군과 함께 Lazy Bird Cook Book 이란 프로젝트명을 걸고 wb303이라는 TB-303 머신을 만든 적이 있다.
생긴건 대충 이런 식.

구조는 건반을 찍고 나면 Music Macro Language(http://en.wikipedia.org/wiki/Music_Macro_Language) 비슷한 일련의 문자열을 생성하고 wav 파일을 생성하도록 인자를 POST하는 구조였는데
MSX에서 play 명령문으로 음악을 만들어보거나 마비노기로 연주를 만들어본 사람이라면 익숙한 구조일것이다.

C 1 ! _

첫번째는 C D E F G A B (도레미파솔라시도) 로 음계를
두번째는 옥타브를
그 뒤부터 붙는 !와 _ , x는 각각 accent와 slide, mute 여부를 표기하는 방식으로 극히 단순하지만 나름 효율적인 구조여서 게시판이나 메일, 메신저 등등으로 간편하게 주고 받을 수 있었는데 아무튼 나중엔 Propellerhead 사의 rb338 파일 저장형식인 rbs 포멧을 분석. rbs import 와 song pattern 도 가능했지만 당시에 호스팅했던 하나포스가 망해버리는 바람에 소스가 엉망이 되어버리긴했지만서도.

암튼 요즘은 세상이 좋아져서 Native Application에서나 가능했던 영역들이 속속 html5란 이름 아래 모여들고 있는데
Web Audio라는 기술이 생겨 관심있게 지켜보고 있어 뭐라도 안할 수가 없다.

뚜껑을 열어보니 생각보다 진지하게 만든 구조라서 좀 놀랐음.
https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
풀 스펙은 위의 링크를 참조하면 됨.
low-pass filter, send effect, pan 등을 routing 하는 구조가 잡혀있음. 심지언 impulse response 까지 지원. 뭐야 무섭쟎아!
일단 빨리 뭐라도 소리를 내보고 싶어서 좀 쉬운 라이브러리가 없나 구글링을 해보니
FireFox와 Chrome 간의 구현방법이 달라서 jQuery 같은 표준라이브러리로 쓸만한 놈이 필요했다.
Sink.js(https://github.com/jussi-kalliokoski/sink.js)가 있고
거기에 FFT(고속푸리에변환)용 라이브러리랑, Wav codec 지원, binary data 처리 라이브러를 합쳐놓은 audiolib.js(https://github.com/jussi-kalliokoski/audiolib.js)가 있어 둘 다 써보기로 결정하고.

삐익하고 소리를 내보았다.

http://jsbin.com/uculiz/edit#javascript,html,live
먼저 audiolib.js를 써서



var dev, sampleRate, osc, buf, fx, filter;
var p = {
  onReady : function(){
    $("#play").click(p.onPlay);
  },
  onPlay : function() {
    setTimeout(function(){
      dev = audioLib.AudioDevice(null, 2);
      sampleRate = dev.sampleRate;
      buf = new Float32Array(sampleRate * 8);
      osc = new audioLib.Oscillator(sampleRate);
      osc.waveShape = 1;
      osc.append(buf.subarray(0, sampleRate * 1), 2);
      dev.writeBuffer(buf);
    }, 0);
  }
};

$().ready(p.onReady);

헉 html 파일에 오타있네.
요정도로 일단.
채널이 2개(stereo)인 AudioDevice를 생성하고
square 파형인 1초 분량의 440hz 음인 buffer 를 생성 후 writeBuffer로 소리를 출력하는 구조.

해놓고보니 sink.js 를 포함하고 있어서 그냥 이걸 쓰는게 낫겠다는 생각이 들었음.

waveShape따위를 쓰고 싶은 생각은 없어서 sink를 사용해서 직접 low-level로 버퍼를 핸들링함.
http://jsbin.com/uculiz/3/edit

var p = {
  channelCount : 1,
  preBufferSize : 0,
  sampleRate : 0,
  onReady : function(){
    $("#play").click(p.onPlay);
  },
  onPlay : function() {
    var dev = audioLib.Sink(function(sampleBuffer){
      // make some noise
      for (i=0; i<sampleBuffer.length; i++){
        sampleBuffer[i] = Math.random() - 0.5;
      }
    }, p.channelCount, p.preBufferSize, p.sampleRate);
  }
};

$().ready(p.onReady);


channelCount 를 제외한 나머지 preBufferSize나 sampleRate는 값을 바꿔도 별 효과가 없음.
-0.5~0.5 사이의 값을 무작위로 생성하여 화이트 노이즈를 생성했다.
play를 하면 무한 재생이 되는데 지금은 일단 모르겠고
의미있는 음도 재생해봐야겠다 싶어서 sin파를 생성해본다.

http://jsbin.com/uculiz/4/edit

var p = {
  channelCount : 2,
  frequency : 440,
  preBufferSize : 0,
  sampleRate : 44100,
  onReady : function(){
    $("#play").click(p.onPlay);
  },
  onPlay : function() {
    var dev = audioLib.Sink(function(sampleBuffer){
      // sampleBuffer length:8192
      for (i=0; i<sampleBuffer.length; i++){
        sampleBuffer[i] = Math.sin(i * Math.PI * p.frequency / p.sampleRate);
      }
    }, p.channelCount, p.preBufferSize, p.sampleRate);
  }
};

$().ready(p.onReady);

440hz의 sin파를 생성하려고 했는데 8192 버퍼마다 끊어짐.
음 뭔가 잘못했나 싶어서 여기저기 기웃기웃.

http://jsbin.com/uculiz/5/edit
Gain 을 달아주면 음량을 조절할 수 있다.
최대값이 1이므로 0이면 무음. 1이상이면 overdrive가 걸린다.

http://jsbin.com/uculiz/9/edit
녹음은 잘 되나?
아 record는 현재 장비에서 나는 음을 캡쳐하는 것이구나.
실시간 mixdown 같은 개념으로 보면 되겠네.


var p = {
  channelCount : 2,
  frequency : 440,
  preBufferSize : 0,
  sampleRate : 44100,
  gain:1.0,
  rec:null,
  dev:null,
  audioElement:null,
  onReady : function(){
    $("#rec").click(p.onRecord);
    $("#play").click(p.onPlay);
  },
  onRecord : function() {
    p.dev = audioLib.Sink(function(sampleBuffer){
      // make some noise
      for (i=0; i<sampleBuffer.length; i++){
        sampleBuffer[i] = Math.floor(i*440/this.sampleRate) % 2 ? 1:-1;
      }
    }, p.channelCount, p.preBufferSize, p.sampleRate);
    p.rec = p.dev.record();
    $("#log").text("recording....");
    /* To stop */
    setTimeout(p.recordStop, 4000);
  },
  recordStop: function() {
    p.rec.stop();
    p.dev.kill();
    // To export wav
    p.audioElement = new Audio(
      'data:audio/wav;base64,' + btoa( p.rec.toWav() )
    );
    p.audioElement.play();
    $("#log").text(p.rec.toWav().length);
  },
  onPlay: function() {
    p.audioElement.play();
  }
};

$().ready(p.onReady);


악! 크롬 다운되서 다날렸다;;
다음에 계속(...할 수 있으려나)




이 블로그의 인기 게시물

React-Native App 개발 시 Expo 선택 전에 고려해야할 것.

Expo는 지옥같은 React-Native 개발 환경 아래 섬광처럼 빛나는 훌륭한 도구지만 빛이 있으면 어둠이 있는 법.
https://docs.expo.io/versions/latest/introduction/why-not-expo.html 에선 이런 경우에 Expo를 사용을 고려하라고 전하고 있다.


독립형 앱에 이미지 같은 정적 자원(Assets)들을 함께 묶어 배포할 수 없다.background 혹은 장치가 잠자기 모드일때 코드 실행을 지원하지 않는다.일부 API를 미지원. Bluetooth, WebRTC 같은 건 아직.오버헤드가 꽤 크다. iOS 25MB, Android 20MBPush Notification의 경우 OneSignal 같은 외부서비스를 활용하려면 ExpoKit을 사용하거나 Expo가 아닌 React-Native를 사용해야한다.Staging/Production같은 다양한 디플로이 환경. 하지만 이 경우 https://github.com/oliverbenns/expo-deploy 같은 선택지도 있으니 참조할 것

MQTT Broker Mosquitto 설치 후 설정

우분투 기준
$ sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
$ sudo apt-get update
하고

$ sudo apt-get install mosquitto
으로 설치하면 서비스까지 착실하게 올라간다.

설치는 간단한데 사용자를 만들어야한다.

/etc/mosquitto/mosquitto.conf 파일에서 권한 설정을 변경하자.
allow_anonymous false
를 추가해서 아무나 못들어오게 하자.
$ service mosquitto restart
서비스를 재시작.
이제 사용자를 추가하자. mosquitto_passwd <암호파일 경로명> <사용자명> 하면 쉽게 만들 수 있다.
# mosquitto_passwd /etc/mosquitto/passwd admin Password:  Reenter password: 
암호 넣어준다. 두번 넣어준다.
이제 MQTT 약을 열심히 팔아서 Broker 사글세방 임대업을 하자.

ESP32 DevBoard 개봉기

오늘 드디어 손에 넣었다. ESP32 DevBoard!
Adafruit 에서 15개 한정 재입고 트윗을 보고 광속 결제.
그리고 1주일의 기다림. 사랑해요 USPS <3
알리를 이용하다보니 1주일 정도는 광속 배송임.
물론 배송비도 무자비함 -_ㅜ
15개 한정판 adafruit 발 dev board
그놈이 틀림없으렸다.
오오 강려크한 포스
ESP32_Core_board_V2라고 적혀있군요.
ESP32 맞구요. 네네. ESP32-D0WDQ6 라고 써있는데 D → Dual-core 0 → No internal flash W → Wi-Fi D → Dual-mode Bluetooth Q → Quad Flat No-leads (QFN) package 6 → 6 mm × 6 mm package body size 라고 함.
길이는 이정도
모듈크기는 이정도
코어는 6mm밖에 안해! 여기에 전기만 넣으면 BLE+WIFI!
밑에 크고 발 8개 달린 놈은 FM25Q32라고 32Mbit 플래시메모리
ESP8266 DevBoard 동생이랑 비교 크고 아름다운 레귤레이터랑 CP2102 USB Driver가 붙어있음.
ESP8266 DevBoard엔 CH340G 인데 확 작아졌네.
머리를 맞대어 보았음.
모듈크기는 아주 약간 ESP32가 더 큰데 워낙에 핀이 많고 촘촘함. ESP8266인 ESP12는 핀 간격이 2.00mm인데 비해
ESP32는 1.27mm 밖에 안함.
딱봐도 비교가 될 정도.
https://www.sparkfun.com/news/2017 크고 아름다운 Pinouts

ESP8266 보드랑 별로 안달라보인다.
http://www.silabs.com/products/mcu/pages/usbtouartbridgevcpdrivers.aspx#mac
에서 CP2102 드라이버를 설치하고
screen 으로 연결해보자.
내 경우엔 tty.SLAB_USBtoUART 로 잡혔다.
어디서 기본 속도가 115200bps 라고 들은 적이 있어서
screen /dev/tty.SLAB_USBtoUART …