본문 바로가기
개발(Development)/Media(오디오&비디오 개발)

[자바스크립트] Oscillator Node 사용 방법 & 예제코드

by 카레유 2022. 1. 18.

자바스크립트 Web Audio API를 이용하면 다양한 소스의 음원을 다룰 수 있다.

이번 글에서는 개발자가 직접 소리를 만들 수 있는 Oscillator Node에 대한 기초 사용 방법을 정리한다.

 

구체적으로 아래와 같이 특정 파형/주파수의 소리를 만들어 낼 수 있다.

(실제로 작동한다. 재생 버튼을 눌러보자. 코드 전문은 글 하단에 작성해두었다.)


OscillatorNode

현재 주파수: 440 Hz


Web Audio API 자체에 대한 기초는 아래 글을 참고하자.

[자바스크립트] Web Audio API 기본 원리와 예제 코드


# OscillatorNode 란?

Oscillator의 사전적 의미는 "전기적으로 진동을 만들어내 음을 출력하는 장치"이다

Web Audio API 에서의 OscillatorNode는 "컴퓨터를 이용해 특정 주파수, 파형의 음원을 만들어내는 소스 Node"이다.

즉, 별도의 음원을 입력받지 않고 스스로 음원을 만들어내는 "소스Node" 객체다.

 

이에 반해,

AudioBufferSourceNode는 로우 오디오 데이터인 AudioBuffer를 음원으로 입력 받으며,

MediaElementAudioSourceNode는 mp3, wav 등이 로드되어 있는 Audio객체나 <audio>엘리먼트를 음원으로 입력 받고,

MediaStreamAudioSourceNode는 마이크 등을 통해 입력되는 MediaStream을 음원으로 입력 받는다.

 

OscillatorNode는 스스로 음원을 만들어내는 "소스 Node"이며,

AudioNode > AudioScheduledSourceNode 순으로 상속받는 객체이기 때문에,

AudioScheduledSourceNode에 정의된 메서드인 start(), stop()을 통해 직접 음원의 재생/정지를 컨트롤 할 수 있다.

 

단, AudioScheduledSourceNode는 1회만 재생되고 가비지 컬렉터에 의해 제거된다.

따라서 여러 번 재생하려면, 매번 새로운 "소스Node" 객체를 생성해서 start()메서드를 호출해 주어야 한다.

(글 아래에서 다루는 예제코드를 보면 바로 이해가 될 것 같다.)

 

 

# OscillatorNode로 "음원을 다루는" 방법

1. AudioContext생성

const audioContext = new (window.AudioContext || window.webkitAudioContext)();

* 크로스 브라우저 지원을 위해 단축평가를 활용해 AudioContext를 생성했다.

 

 

2. 소스 Node 생성

: OscillatorNode 객체 생성(2가지 방법)

 

1) 메서드 방식

const oscillator = audioContext.createOscillator();

 

2) 생성자 방식

const oscillatorNode = new OscillatorNode(audioContext);

* 생성자 방식은 일부 브라우저에서 지원되지 않을 수 있다.

 

 

3. 작업 Node 생성

: 일반적으로 볼륨 설정 역할을 하는 GainNode는 기본으로 생성하는 편이다.(2가지 방법)

 

1) 메서드 방식

const gainNode = audioContext.createGain();
gainNode.gain.value = 0.5; // 볼륨 설정(0~1사이)

 

2) 생성자 방식

const gainNode = new GainNode(audioContext, {gain: 0.5});

 

* 생성자 방식은 일부 브라우저에서 지원되지 않을 수 있다.

 

 

4. Audio Graph 연결

: 소스Node ~ 작업Node ~ 목적지Node 연결

oscillatorNode.connect(gainNode).connect(audioContext.destination);

 

 

5. 재생

: OscillatorNode의 부모인 AudioScheduledSourceNode에 정의된 start() 메서드를 호출(상속) 

oscillatorNode.start();

 

 

# OscillatorNode로 다양한 "음원을 만드는" 방법

OscillatorNode 객체를 생성하여 start()메서드로 재생하면 디폴트로 주파수 440 Hz인 sine파의 음을 출력한다.

440Hz는 피아노 건반의 정가운데 있는 "라"와 같은 음정이며, 순수한 sine파의 음만 출력하므로 "순음: Pure Tone"이라고도 한다.

 

*주파수, sine 파 등의 용어가 익숙하지 않다면 아래 글을 참고하자.

소리의 진폭, 주기, 주파수, 파형 (볼륨, 음정, 음색의 결정 원리)

 

OscillatorNode가 출력하는 음의 높낮이와 파형은 개발자가 직접 변경할 수 있다.

이를 위해 OscillatorNode는 3개의 소리 관련 프로퍼티와 1개의 메서드를 보유하고 있다.

 

1. frequency 프로퍼티

- Hz 설정 : 디폴트 440 

- 음정(높낮이)를 Hz 단위(주파수)로 조정하는 프로퍼티이다.

 

 

2. detune 프로퍼티

- Cent 설정: 디폴트 0

- 음정(높낮이)을 cent 단위(=1/100반음)로 조정하는 프로퍼티이다.

 

 

3. type 프로퍼티

- 파형 설정 : "sine(디폴트)", "square", "sawtooth", "triangle", "custom"

- 진동에 따른 공기 밀도의 변형 형태, 즉 파형을 선택할 수 있다. 파형에 따라 음색이 달라진다.

 

 

4. OscillatorNode.setPeriodicWave(PeriodicWave) 메서드

- 커스텀 파형 설정 메서드로 개발자가 원하는 파형을 가진 소리를 만들어낼 수 있다.

- 레퍼런스: https://developer.mozilla.org/en-US/docs/Web/API/OscillatorNode/setPeriodicWave

 

 

위의 프로퍼티/메서드에서 사용된 용어들을 정말 간단하게 정리하면 아래와 같다.

 

※ 소리, 주파수, 음정(Pitch)에 대하여

- 소리는 공기의 떨림, 즉 진동에 의해 만들어진다.

- 1초 동안의 진동수를 주파수(HZ)라고하며, 주파수가 높아질 수록 음정(Pitch)이 높아진다.

- 즉, 음정 높낮이(Pitch)는 Frequency(주파수) 진동수가 높아지면 높아지고, 낮아지면 낮아진다.

- 위의 frequency 프로퍼티는 는 Hz 단위의 주파수 값으로 음의 높낮이를 조정하고, 

- detune 프로퍼티는 cent단위(1/100반음)로 음의 높낮이를 조정한다.

 

※ 주파수, 옥타브에 대하여

- 주파수(Hz)가 2배가 되면 음정은 1 옥타브(도~ 다음 도)씩 올라간다.

- 1옥타브는 12개의 반음으로 구분되며, 미~파, 시~도는 반음, 나머지는 온음이다.

- 반음은 주파수가 2.pow(1/12)만큼 높아지는 정도. 온음은 주파수가 2.pow(2/12)만큼 높아지는 정도이다.

- 그래서 12반음(1옥타브)이 높아지면, 주파수가 2.pow(1/12) * 2.pow(1/12) * ... 를 12번 해서 2.pow(12/12)배가 되어 정확히 2배가 된다.

- 즉, 주파수 2배 == 1옥타브 == 12반음 이 된다.

 

※ cent 란?

- cent는 1/100 반음 단위를 의미한다.

- 100cent는 1반음이 되고, 1200cent는 12반음이 된다. 

- 따라서,  1200cent == 12반음 == 1옥타브 == 주파수 2배 가 된다.

 

※ 파형이란?

- 파형이란 소리가 진동하는 모양으로 소리의 음색을 결정한다.

- 즉 같은 볼륨, 같은 음정(높낮이)라도 파형에 따라 전혀 다른 소리로 인식된다.

- 예를 들어 피아노의 "라"와 리코더의 "라"가 다른 소리로 들리는 이유는 파형이 다르기 때문이다.

 

 

# OscillatorNode 예제 코드

- 재생버튼을 누르면 기본 440Hz의 Sine파를 재생한다.

- 라디오 버튼으로 파형을 Sine, Square, Sawtooth, Traingle로 조정할 수 있다.

- 슬라이드바로 주파수를 변경하여 음정 높낮이(Pitch)를 조정할 수 있다.

* 글의 맨 첫 부분에서 직접 테스트해볼 수 있다.

 

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OscillatorNode</title>
</head>
<body>
    <!-- 재생/정지 버튼 -->
    <button id="btn">재생/정지</button>
    
    <!-- 파형 설정 raido -->
    <input type="radio" name="wave" id="wave_sine" class="radio_wave" checked>
    <label for="wave_sine">Sine파</label>
    <input type="radio" name="wave" id="wave_square" class="radio_wave">
    <label for="wave_square">square파</label>
    <input type="radio" name="wave" id="wave_sawtooth" class="radio_wave">
    <label for="wave_sawtooth">sawtooth파</label>
    <input type="radio" name="wave" id="wave_triangle" class="radio_wave">
    <label for="wave_triangle">triangle파</label>

    <br><br>
    <!-- 주파수 조정 슬라이드바 -->
    <label for="bar_frequency">주파수 조정</label>
    <input type="range" name="bar_frequency" id="bar_frequency" min="1" max="2000" value="440">
    <p>현재 주파수: <span id="span_frequency">440</span> Hz</p>

</body>

<script>
    // 1. AudioContext 생성
    const audioContext = new (window.AudioContext || window.webkitAudioContext)();

    // 2. 소스 Node(OscillatorNode) 변수 생성
    let oscillatorNode = null;
    
    // 3. 작업Node(GainNode: 볼륨 설정) 생성: 볼륨을 0.5로 설정 (0~1 사이)
    const gainNode = audioContext.createGain();
    gainNode.gain.value = 0.5;

    // 재생여부 변수
    let isPlaying = false; 
    
    // 재생/정지 버튼 이벤트
    document.getElementById("btn").onclick = (event)=>{
        
        // 사용자 제스처가 있어야만 audioContext가 작동하기 때문에, 여기서 재가동해준다.
        audioContext.resume(); 
     
        if(!isPlaying){
            // OscillatorNode는 1회만 재생 가능하기 때문에
            // 새로 재생할 때마다 새로 OscillatorNode를 생성해주어야 한다.
            oscillatorNode = audioContext.createOscillator();

            // 오디오 그래프 연결
            oscillatorNode.connect(gainNode).connect(audioContext.destination);
            
            // 재생
            oscillatorNode.start();
            isPlaying = true;
        }else{
            // 정지
            oscillatorNode.stop();
            isPlaying = false;
        }
    }

    // 주파수 조정 이벤트
    document.getElementById("bar_frequency").oninput = (event)=>{
        const inputVolume = parseInt(event.currentTarget.value); // 0~1사이로 변환
        oscillatorNode.frequency.value = inputVolume;

        // 현재 주파수 표시
        document.getElementById("span_frequency").textContent = oscillatorNode.frequency.value;
    }

    // 파형 조정 이벤트
    document.querySelectorAll("input.radio_wave").forEach((el, idx) => {
        el.addEventListener("click", (event)=>{
            switch (idx) {
                case 0:
                    oscillatorNode.type = "sine";
                    break;
                case 1:
                    oscillatorNode.type = "square";
                    break;
                case 2:
                    oscillatorNode.type = "sawtooth";
                    break;
                case 3:
                    oscillatorNode.type = "triangle";
                    break;
                default:
                    oscillatorNode.type = "sine";
                    break;
            }
        })
    });


</script>
</html>

이 것으로 Web Audio API의 Oscillaotor Node에 대한 기초 정리를 마무리한다.

다른 Node들에 대해서도 계속 다루어볼 생각이다.

 

 

 

댓글