2019年1月26日 星期六

播放聲音頻率 HTML5 Web Audio API

之前在讀書時,誤打誤撞選擇選修科時,選譯了音樂欣賞
最後音樂就不太懂得欣賞,反而增加對聲音的興趣,便尋找更多關於聲音的資料
發現並開始學習使用 HTML5 Web Audio API



在試用 HTML5 Web Audio API 前,需要先了解一些基本的聲音概念及技術
樂理音譜有8度共12律:
  • C
  • C# / Db
  • D
  • D#/ Eb
  • E
  • F
  • F#/ Gb
  • G
  • G#/ Ab
  • A
  • A#/ Bb
  • B
12個現今常用的音標名稱
根據 十二平均律 Equal temperament 單音倍率為 2^(n / 12)
以 Middle A 為 440 Hz,所以頻率換算方程式為 440 * 2^(n / 12) ,當 n = 0 時,就是 Middle A
因此可以以 88鍵鋼琴 作為基礎,計算出每個琴鍵對應的頻率
音標名稱Midi值(十進制)Midi值(十六進制)n頻率
A02115-4827.500000000000
A#0 / Bb02216-4729.135235094881
B02317-4630.867706328508
C12418-4532.703195662575
C#1 / Db12519-4434.647828872109
D1261A-4336.708095989676
D#1 / Eb1271B-4238.890872965260
E1281C-4141.203444614109
F1291D-4043.653528929126
F#1 / Gb1301E-3946.249302838954
G1311F-3848.999429497719
G#1 / Ab13220-3751.913087197493
A13321-3655.000000000000
A#1 / Bb13422-3558.270470189761
B13523-3461.735412657016
C23624-3365.406391325150
C#2 / Db23725-3269.295657744218
D23826-3173.416191979352
D#2 / Eb23927-3077.781745930520
E24028-2982.406889228218
F24129-2887.307057858251
F#2 / Gb2422A-2792.498605677909
G2432B-2697.998858995437
G#2 / Ab2442C-25103.826174394986
A2452D-24110.000000000000
A#2 / Bb2462E-23116.540940379522
B2472F-22123.470825314031
C34830-21130.812782650299
C#3 / Db34931-20138.591315488436
D35032-19146.832383958704
D#3 / Eb35133-18155.563491861040
E35234-17164.813778456435
F35335-16174.614115716502
F#3 / Gb35436-15184.997211355817
G35537-14195.997717990875
G#3 / Ab35638-13207.652348789973
A35739-12220.000000000000
A#3 / Bb3583A-11233.081880759045
B3593B-10246.941650628062
C4 (Middle C)603C-9261.625565300599
C#4 / Db4613D-8277.182630976872
D4623E-7293.664767917408
D#4 / Eb4633F-6311.126983722081
E46440-5329.627556912870
F46541-4349.228231433004
F#4 / Gb46642-3369.994422711634
G46743-2391.995435981749
G#4 / Ab46844-1415.304697579945
A4 (Middle A)69450440.000000000000
A#4 / Bb470461466.163761518090
B471472493.883301256124
C572483523.251130601197
C#5 / Db573494554.365261953744
D5744A5587.329535834815
D#5 / Eb5754B6622.253967444162
E5764C7659.255113825740
F5774D8698.456462866008
F#5 / Gb5784E9739.988845423269
G5794F10783.990871963499
G#5 / Ab5805011830.609395159890
A5815112880.000000000000
A#5 / Bb5825213932.327523036180
B5835314987.766602512248
C68454151046.502261202390
C#6 / Db68555161108.730523907490
D68656171174.659071669630
D#6 / Eb68757181244.507934888320
E68858191318.510227651480
F68959201396.912925732020
F#6 / Gb6905A211479.977690846540
G6915B221567.981743927000
G#6 / Ab6925C231661.218790319780
A6935D241760.000000000000
A#6 / Bb6945E251864.655046072360
B6955F261975.533205024500
C79660272093.004522404790
C#7 / Db79761282217.461047814980
D79862292349.318143339260
D#7 / Eb79963302489.015869776650
E710064312637.020455302960
F710165322793.825851464030
F#7 / Gb710266332959.955381693080
G710367343135.963487853990
G#7 / Ab710468353322.437580639560
A710569363520.000000000000
A#7 / Bb71066A373729.310092144720
B71076B383951.066410048990
C81086C394186.009044809580
function getFrequencies(){
    var baseFrequency = 440;
    var frequencyNames = ["A0", "As0", "B0", "C1", "Cs1", "D1", "Ds1", "E1", "F1", "Fs1", "G1", "Gs1", "A1", "As1", "B1","C2", "Cs2", "D2", "Ds2", "E2", "F2", "Fs2", "G2", "Gs2", "A2", "As2", "B2", "C3", "Cs3", "D3", "Ds3", "E3", "F3", "Fs3", "G3", "Gs3", "A3", "As3", "B3", "C4", "Cs4", "D4", "Ds4", "E4", "F4", "Fs4", "G4", "Gs4", "A4", "As4", "B4", "C5", "Cs5", "D5", "Ds5", "E5", "F5", "Fs5", "G5", "Gs5", "A5", "As5", "B5", "C6", "Cs6", "D6", "Ds6", "E6", "F6", "Fs6", "G6", "Gs6", "A6", "As6", "B6", "C7", "Cs7", "D7", "Ds7", "E7", "F7", "Fs7", "G7", "Gs7", "A7", "As7", "B7", "C8"];
    var frequencies = {};
    for (var i in frequencyNames){
        frequencies[frequencyNames[i]] = baseFrequency * Math.pow(2, (i - 48) / 12);
    }
    return frequencies;
}
便可以隨時獲得音標頻率

獲得音標頻率開始了解 HTML5 Web Audio API
使用 HTML5 Web Audio API ,需要建立 AudioContext(音訊內容) 物件,但 AudioContext 未統一建立方法
一些瀏覽器需要使用 webkitAudioContext ,因此建立使用
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext();
確保不同瀏覽器的都能建立 AudioContext

  1. 聲音來源 (Audio Source)
  2. 處理器 (Processor) ,非必要
  3. 輸出目的地 (Output Destination)
要令聲音輸出最簡單就是載入來源並選擇目的地輸出,不需要任何加工

最簡單的聲音來源可以透過 HTMLAudioElement 建立 或 Oscillator (振湯器) 產生

HTMLAudioElement 就很簡單,建立 HTMLAudioElement 再設定 src
var audio = new window.Audio();
audio.src = "/path/of/audio";

Oscillator 則有多種設定內容
Oscillator.type 有四種,預設為 sine
  • sine (正弦波) ,預設種類,聲音比較柔和
  • square (方波) ,只有高音或低音頻率,非常吵耳
  • triangle (三角波) ,與 sine 類似,但比較響亮
  • sawtooth (鋸齒波) ,與 sqaure 類似,有反覆振盪感覺,連腦都一同振盪的感覺

Oscillator.detune.value 能夠微調各種頻率聲音
數值越大頻率越高, detune 能夠設定為負數,越小頻率越低,預設為 0

Oscillator.frequency.value 就是振盪時頻率的聲音
預設為 440 ,即是 Middle A 的頻率

var oscillator = audioContext.createOscillator();
oscillator.type = "sine";
oscillator.detune.value = 0;
oscillator.frenquency.value = 440;

兩者完成設定後都使用 connect 連接到 audioCotext.destination
audio.connect(audioContext.destination);
oscillator.connect(audioContext.destination);

由於 Oscillator 及 HTMLAudioElement 都實作了 AudioNode 的 start() 及 stop() 功能,連接後可以使用
/*
設定 Oscillator 在執行 0 秒後開始,預設為 0
*/
oscillator.start(0);
/*
設定 Oscillator 在執行 1 秒結果,預設為 0
*/
oscillator.stop(1);

除了聲音來源,還有不同的處理器

  • Gain (增益)
    var gain = audioContext.createGain();
    /*
    1 為 100% 音量, 0 即係沒有音量
    但在下認為 1 非常響亮,因此使用 0.1
    */
    gain.gain.value = 0.1;
    oscillator.connect(gain);
    gain.connect(audioContext.destination);
  • Delay (延遲)
    var delay = audioContext.createDelay();
    /*
    延遲 1 秒,可以使用小數
    */
    delay.delayTime.value = 1;
    oscillator.connect(delay);
    delay.connect(audioContext.destination);

簡單例子
  • Javascript
    function change(){
        document.getElementById("showFrequency").value = document.getElementById("frequency").value;
        document.getElementById("showDetune").value = document.getElementById("detune").value;
        document.getElementById("showGain").value = document.getElementById("gain").value;
    }
    
    function test(element){
        var AudioContext = window.AudioContext || window.webkitAudioContext;
        if (AudioContext){
            element.disabled = true;
            var audioContext = new AudioContext();
            var gain = audioContext.createGain();
            gain.gain.value = document.getElementById("gain").value;
            var oscillator = audioContext.createOscillator();
            oscillator.addEventListener("ended", function(){
                element.disabled = false;
            });
            oscillator.type = document.getElementById("type").value;
            oscillator.frequency.value = document.getElementById("frequency").value;
            oscillator.detune.value = document.getElementById("detune").value;;
            oscillator.connect(gain);
            gain.connect(audioContext.destination);
            oscillator.start(0);
            oscillator.stop(1);
        }
    }
  • HTML
    <table border="1" width="95%">
        <colgroup> 
            <col width="40"/> 
        </colgroup> 
        <tbody>
            <tr valign="top">
                <td>波紋</td>
                <td><select id="type">
                    <option value="sine">Sine</option>
                    <option value="square">Square</option>
                    <option value="triangle">Triangle</option>
                    <option value="sawtooth">Sawtooth</option>
                </select></td>
            </tr>
            <tr valign="top">
                <td>頻率</td>
                <td>
                    <input id="frequency" type="range" min="0" max="4200" step="1" value="440" style="width: 400px;" oninput="change();"/>
                    <input id="showFrequency" type="text" value="440" style="width: 40px;" readonly="true"/>
                </td>
            </tr>
            <tr valign="top">
                <td>音調</td>
                <td>
                    <input id="detune" type="range" min="-1000" max="1000" step="1" value="0" style="width: 400px;" oninput="change();"/>
                    <input id="showDetune" type="text" value="0" style="width: 40px;" readonly="true"/>
                </td>
            </tr>
            <tr valign="top">
                <td>增幅</td>
                <td>
                    <input id="gain" type="range" min="0" max="1" step="0.01" value="0.1" style="width: 400px;" oninput="change();"/>
                    <input id="showGain" type="text" value="0.1" style="width: 40px;" readonly="true"/>
                </td>
            </tr>
            <tr valign="top">
                <td colspan="2">
                    <input type="button" value="播放" onclick="test(this);"/>
                </td>
            </tr>
        </tbody> 
    </table>
  • 效果
    警告:
    播放時,請先將閣下電腦的音量調低,避免影響聽覺
    如果閣下有曾經因為聽到頻率產生頭暈、想嘔的感覺
    閣下便不要播放測試頻率

    波紋
    頻率
    音調
    增幅

沒有留言 :

張貼留言