Web Audio APIのOscillatorNodeの音をphyphoxで計測

Web Audio APIのOscillatorNodeの音をphyphox(Physical Phone Experiments)で計測しました。

OscillatorNodeの波形パラメータとして、正弦波(sine)、矩形波(square)、のこぎり波(sawtooth)、三角波(triangle)の4種類のパラメータをセットして計測しました。周波数には全て440Hzを指定しました。

下記のボタンをクリックすると音が流れます。音量(特に矩形波)に注意してください。
ボタンをもう一度クリックすると音が消えます。

1. 正弦波(440Hz)

2. 矩形波(440Hz)

3. のこぎり波(440Hz)

4. 三角波(440Hz)

上記の4つのボタンをクリックしたときのオーディオスペクトルをphyphoxで計測しました。データはこちらのブログと同様の方法で取得しました。FFTのサンプル数は2,048にしています。

音は、下記の写真のFujitsu LIFEBOOK UH77/C3から出力しました。計測にはiPhone SE (第1世代)を使用しました。ノートパソコンのスピーカーで音を出力したときには、正弦波以外の波形が期待される波形と異なっていたため、外付けのスピーカーから音を出力して再計測しました。

1. 正弦波(440Hz):ノートパソコンのスピーカー

音圧データの波形はサインカーブを描いています。

オーディオスペクトルは440Hzに近い445.3125Hzで鋭いピークを取っています。

補足:
phyphoxの設定でFFTのサンプル数を32,768にして計測したところ、より440Hzに近い439.453125Hzと440.91796875Hzで大きな値を取りました。サンプル数を32,768にするとWebページの表示に時間を要するため、今回はサンプル数2,048の結果をプロットすることにしました。

2. 矩形波(440Hz):ノートパソコンのスピーカー

音圧データの波形は下のグラフのようになりました。

オーディオスペクトルは440Hzに近い445.3125Hzに小さなピークを持ちますが、ピーク周波数は1,312.5Hzとなっています。phyphoxの設定でFFTのサンプル数を32,768にして計測しても1,312.5Hzに近い1,319.82421875Hzがピーク周波数となっていました。

3. のこぎり波(440Hz):ノートパソコンのスピーカー

音圧データの波形は下のグラフのようになりました。

オーディオスペクトルは下のグラフのようになりました。ピーク周波数ではありませんが、一番周波数の小さいピークは440Hzに近い445.3125Hzとなっています。

4. 三角波(440Hz):ノートパソコンのスピーカー

音圧データの波形は下のグラフのようになりました。

オーディオスペクトルは下のグラフのようになりました。ピーク周波数ではありませんが、一番周波数の小さいピークは440Hzに近い445.3125Hzとなっています。

音源に外部スピーカーを用いた計測結果

5. 正弦波(440Hz):外付けスピーカー

音圧データの波形はサインカーブを描いています。

オーディオスペクトルは440Hzに近い445.3125Hzで鋭いピークを取っています。

6. 矩形波(440Hz):外付けスピーカー

音圧データの波形は下のグラフのようになりました。矩形波とは波形が異なりますが、ノートパソコンのスピーカーから音を出力した2. の矩形波(440Hz)のときよりは矩形波に近い波形になっています。

波形は音量やスマートフォンとスピーカーの位置関係によって変化しました。一方のスピーカーを伏せ、他方のスピーカーのすぐ裏側で計測すると比較的安定した波形を計測することができました。

オーディオスペクトルは440Hzに近い445.3125Hzで鋭いピークを取っています。

7. のこぎり波(440Hz):外付けスピーカー

音圧データの波形は下のグラフのようになりました。のこぎり波とは波形が異なりますが、ノートパソコンのスピーカーから音を出力した3. ののこぎり波(440Hz)のときよりはのこぎり波に近い波形になっています。

波形は音量やスマートフォンとスピーカーの位置関係によって変化しました。一方のスピーカーを伏せ、他方のスピーカーのすぐ裏側で計測すると比較的安定した波形を計測することができました。

オーディオスペクトルは440Hzに近い445.3125Hzで鋭いピークを取っています。

8. 三角波(440Hz):外付けスピーカー

音圧データの波形は下のグラフのようになりました。三角波の波形ははっきりとは表れていませんが、ノートパソコンのスピーカーから音を出力した4. の三角波(440Hz)のときよりは三角波に近い波形になっています。

オーディオスペクトルは440Hzに近い445.3125Hzで鋭いピークを取っています。

9. Web Audio APIのOscillatorNodeを使った下記のJavaScriptを記述し、ボタンをクリックした際に音を出すようにしました。

<script type="text/javascript">

// create web audio api context
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();

const oscillatorMap = new Map();
const isPlayingMap = new Map();

// sine, 440Hz
CreateAudioOscillator('audio (sine, 440Hz)', 'start-stop-sine', 'sine', 440);

// square, 440Hz
CreateAudioOscillator('audio (square, 440Hz)', 'start-stop-square', 'square', 440);

// sawtooth, 440Hz
CreateAudioOscillator('audio (sawtooth, 440Hz)', 'start-stop-sawtooth', 'sawtooth', 440);

// triangle, 440Hz
CreateAudioOscillator('audio (triangle, 440Hz)', 'start-stop-triangle', 'triangle', 440);

function CreateAudioOscillator(buttonText, buttonID, oscillatorType, frequency) {

    const button = document.getElementById(buttonID);
    button.textContent = buttonText;

    button.addEventListener("click", () => {

        let oscillator = oscillatorMap.get(buttonID);
        let isPlaying = isPlayingMap.get(buttonID);

        if (isPlaying === undefined) {
            isPlaying = false;
        }

        if (isPlaying === false) {
            oscillator = audioCtx.createOscillator();
            oscillator.type = oscillatorType;
            oscillator.frequency.setValueAtTime(frequency, audioCtx.currentTime);
            oscillator.connect(audioCtx.destination);
            oscillator.start();
            isPlaying = true;

            button.textContent = buttonText + " - playing";
        } else {
            oscillator.stop();
            isPlaying = false;

            button.textContent = buttonText;
        }

        oscillatorMap.set(buttonID, oscillator);
        isPlayingMap.set(buttonID, isPlaying);

    });
}
</script>

10. phyphoxで記録したデータはこちらのブログと同様の手順でGoogle Sheetsにアップロードし、下記のJavaScriptでWebページにプロットしました。

<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">

google.charts.load('current', {packages:['corechart']});
google.charts.setOnLoadCallback(Spreadsheet);


function Spreadsheet() {

    // Note PC speaker
    const sine_sheetdata = 'https://docs.google.com/spreadsheets/d/1WnHJhNyvOwXlX-nqhNwRbjFlY53pPYtQcVu-Zjo6fbo/edit?usp=sharing';
    const square_sheetdata = 'https://docs.google.com/spreadsheets/d/12ciF19pUXNmgcMzS2X0zgr3nAaLUNepECbu2Y3wERHE/edit?usp=sharing';
    const sawtooth_sheetdata = 'https://docs.google.com/spreadsheets/d/1hZFBLbzkOPBK50O7EJwi9jE7RNfevDO3N43iXX0FIos/edit?usp=sharing';
    const triangle_sheetdata = 'https://docs.google.com/spreadsheets/d/1XwCTs6-N8zr_FfnLiHUxs1XagUTmrg_vxT_6348Goj4/edit?usp=sharing';


    const query_sine_raw_data = new google.visualization.Query(sine_sheetdata + '&gid=11974291&range=A1:B501');
    drawRawDataWithSpecifiedId(query_sine_raw_data, 'audio_raw_data_sine_440Hz');

    const query_sine = new google.visualization.Query(sine_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_sine, 'audio_spectrum_sine_440Hz_logx');


    const query_square_raw_data = new google.visualization.Query(square_sheetdata + '&gid=297086323&range=A1:B501');
    drawRawDataWithSpecifiedId(query_square_raw_data, 'audio_raw_data_square_440Hz');

    const query_square = new google.visualization.Query(square_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_square, 'audio_spectrum_square_440Hz_logx');


    const query_sawtooth_raw_data = new google.visualization.Query(sawtooth_sheetdata + '&gid=1334885699&range=A1:B501');
    drawRawDataWithSpecifiedId(query_sawtooth_raw_data, 'audio_raw_data_sawtooth_440Hz');

    const query_sawtooth = new google.visualization.Query(sawtooth_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_sawtooth, 'audio_spectrum_sawtooth_440Hz_logx');


    const query_triangle_raw_data = new google.visualization.Query(triangle_sheetdata + '&gid=841364434&range=A1:B501');
    drawRawDataWithSpecifiedId(query_triangle_raw_data, 'audio_raw_data_triangle_440Hz');

    const query_triangle = new google.visualization.Query(triangle_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_triangle, 'audio_spectrum_triangle_440Hz_logx');


    // ----------------------------------------------------------------------------------
    // with external speaker
    const sine_with_speaker_sheetdata = 'https://docs.google.com/spreadsheets/d/1ue2hClO51djXHrDVlIyBimSGTRTOXWPSVNhcqESw_zk/edit?usp=sharing';
    const square_with_speaker_sheetdata = 'https://docs.google.com/spreadsheets/d/1Z0fe3hLOH0u-bA4epkQgMfAig6yCQCImZ20_tJ_zxuQ/edit?usp=sharing';
    const sawtooth_with_speaker_sheetdata = 'https://docs.google.com/spreadsheets/d/1L1U2zRE-Q7ZA9EUc3PzFgCM81e3YdO965v35fOhEtsA/edit?usp=sharing';
    const triangle_with_speaker_sheetdata = 'https://docs.google.com/spreadsheets/d/1MLv5dNoEKK_y8m90ac3cxi0LQ6IC6Y39aK98QlyWE1o/edit?usp=sharing';


    const query_sine_with_speaker_raw_data = new google.visualization.Query(sine_with_speaker_sheetdata + '&gid=573933332&range=A1:B501');
    drawRawDataWithSpecifiedId(query_sine_with_speaker_raw_data, 'audio_raw_data_sine_440Hz_with_speaker');

    const query_sine_with_speaker = new google.visualization.Query(sine_with_speaker_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_sine_with_speaker, 'audio_spectrum_sine_440Hz_logx_with_speaker');


    const query_square_with_speaker_raw_data = new google.visualization.Query(square_with_speaker_sheetdata + '&gid=1774282809&range=A1:B501');
    drawRawDataWithSpecifiedId(query_square_with_speaker_raw_data, 'audio_raw_data_square_440Hz_with_speaker');

    const query_square_with_speaker = new google.visualization.Query(square_with_speaker_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_square_with_speaker, 'audio_spectrum_square_440Hz_logx_with_speaker');


    const query_sawtooth_with_speaker_raw_data = new google.visualization.Query(sawtooth_with_speaker_sheetdata + '&gid=1119483708&range=A1:B501');
    drawRawDataWithSpecifiedId(query_sawtooth_with_speaker_raw_data, 'audio_raw_data_sawtooth_440Hz_with_speaker');

    const query_sawtooth_with_speaker = new google.visualization.Query(sawtooth_with_speaker_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_sawtooth_with_speaker, 'audio_spectrum_sawtooth_440Hz_logx_with_speaker');


    const query_triangle_with_speaker_raw_data = new google.visualization.Query(triangle_with_speaker_sheetdata + '&gid=116127029&range=A1:B501');
    drawRawDataWithSpecifiedId(query_triangle_with_speaker_raw_data, 'audio_raw_data_triangle_440Hz_with_speaker');

    const query_triangle_with_speaker = new google.visualization.Query(triangle_with_speaker_sheetdata);
    drawFFTSpectrumWithSpecifiedId(query_triangle_with_speaker, 'audio_spectrum_triangle_440Hz_logx_with_speaker');
}


function drawFFTSpectrumWithSpecifiedId(query, graph_id_logx) {

    query.send( function(response) {
        const data = response.getDataTable();

        // audio spectrum : horizontal axis is logarithmic scale
        const options_logx = {title: 'phyphox audio spectrum : horizontal axis is logarithmic scale',
                              hAxis: {title: 'Frequency [Hz]', scaleType: 'log'},
                              vAxis: {title: 'FFT Magnitude'}};
        const chart_logx = new google.visualization.LineChart(document.getElementById(graph_id_logx));
        chart_logx.draw(data, options_logx);
    });
}


function drawRawDataWithSpecifiedId(query, graph_id) {

    query.send( function(response) {
        const data = response.getDataTable();

        // Peak History
        const options = {title: 'phyphox audio - raw data',
                         hAxis: {title: 'Time [s]'},
                         vAxis: {title: 'Sound Pressure'}};
        const chart = new google.visualization.LineChart(document.getElementById(graph_id));
        chart.draw(data, options);
    });
}
</script>

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA