2
3

Web Audio APIを使って、音源が大きな3次元の楕円軌道上を動いた場合の、音の変化をシミュレーション。

Last updated at Posted at 2024-10-05

スクリーンショット 2024-10-06 045912.png

スクリーンショット 2024-10-06 050145.png

コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。

音源が大きな3次元の楕円軌道上を動き、その時の音の変化をシミュレーションしてます。

Web Audio APIを使って、移動する音源をシミュレート

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3次元のランダムな楕円軌道で動く音源とカラフルなキューブ</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
    <!-- three.jsのスクリプトをCDNから読み込む -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
    <h2>3次元のランダムな楕円軌道で動く音源とカラフルなキューブのシミュレーション</h2>
    <script>
        // Web Audio APIの初期化
        let audioContext = null;
        let oscillator = null;
        let panner = null;
        let gainNode = null;

        // シーン、カメラ、レンダラーの初期化
        const scene = new THREE.Scene();
        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
        const renderer = new THREE.WebGLRenderer();
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        // 楕円の半径をランダムに設定
        const radiusX = Math.random() * 300 + 200;  // 楕円の長半径 (200〜500)
        const radiusY = Math.random() * 200 + 100;  // 楕円の短半径 (100〜300)
        const radiusZ = Math.random() * 150 + 50;   // 楕円の高さ (50〜200)
        let angle = 0;

        // 音源の球体を作成
        const geometry = new THREE.SphereGeometry(10, 32, 32);
        const material = new THREE.MeshBasicMaterial({ color: 0x0000ff });
        const soundSource = new THREE.Mesh(geometry, material);
        scene.add(soundSource);

        // カラフルなキューブをランダムで10個作成
        function createColorfulCubes() {
            for (let i = 0; i < 10; i++) {
                const cubeSize = Math.random() * 20 + 10; // ランダムなサイズ
                const cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
                const cubeMaterial = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffffff }); // ランダムな色
                const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
                
                // ランダムな位置を設定
                cube.position.set(
                    Math.random() * 600 - 300,  // X座標
                    Math.random() * 400 - 200,  // Y座標
                    Math.random() * 600 - 300   // Z座標
                );

                scene.add(cube);
            }
        }

        // カメラの位置を設定
        camera.position.z = 500;

        // 音源の位置更新と描画のループ
        function updatePosition() {
            const x = radiusX * Math.cos(angle);
            const y = radiusY * Math.sin(angle);
            const z = radiusZ * Math.sin(angle * 0.5);  // Z軸の動きを追加

            // 音源の位置を更新
            soundSource.position.set(x, y, z);

            // 音源の位置に基づくステレオ効果
            if (panner && gainNode) {
                const panningX = x / radiusX;  // 左右のパン
                const distance = Math.sqrt(x * x + y * y + z * z) / radiusX;  // 音量調整のための距離

                panner.setPosition(panningX, 0, -0.5);  // 前後方向は固定
                gainNode.gain.setValueAtTime(1 - distance, audioContext.currentTime);  // 距離に基づいて音量を調整
            }

            angle += 0.02;
            if (angle > 2 * Math.PI) {
                angle = 0;
            }

            requestAnimationFrame(updatePosition);
            renderer.render(scene, camera);
        }

        // ユーザーのクリックで音を開始
        document.body.addEventListener('click', () => {
            if (!audioContext) {
                // 初回クリック時にAudioContextと音源の初期化
                audioContext = new (window.AudioContext || window.webkitAudioContext)();
                panner = audioContext.createPanner();  // 音の位置を制御する
                gainNode = audioContext.createGain();  // 音量を制御する

                oscillator = audioContext.createOscillator();
                oscillator.type = 'sine'; // 正弦波
                oscillator.frequency.setValueAtTime(440, audioContext.currentTime); // 440Hz (A4の音)
                oscillator.connect(panner).connect(gainNode).connect(audioContext.destination);

                oscillator.start();
            }
            audioContext.resume();  // AudioContextの再開
        });

        createColorfulCubes();  // カラフルなキューブを作成
        updatePosition();  // 位置更新のループ開始
    </script>
</body>
</html>


参考。

空間オーディオで移動する音源のサンプル

// オーディオコンテキストを作成
const audioContext = new (window.AudioContext || window.webkitAudioContext)();

// 音源(オシレーター)を作成
const oscillator = audioContext.createOscillator();

// ゲインノードを作成(音量調整)
const gainNode = audioContext.createGain();

// PannerNode(パンニングノード)を作成
const panner = audioContext.createPanner();
panner.panningModel = 'HRTF';  // 空間オーディオモデルの設定
panner.distanceModel = 'linear';  // 距離による音量の減衰モデル
panner.positionX.setValueAtTime(0, audioContext.currentTime); // X座標の初期位置
panner.positionY.setValueAtTime(0, audioContext.currentTime); // Y座標の初期位置
panner.positionZ.setValueAtTime(-1, audioContext.currentTime); // Z座標の初期位置(リスナーの前)

// ノードを接続
oscillator.connect(gainNode);
gainNode.connect(panner);
panner.connect(audioContext.destination);

// オシレーターの設定
oscillator.frequency.value = 440; // 440Hz(A4の音)
oscillator.type = 'sine'; // サイン波

// 音声を開始
oscillator.start();

// 音源の移動をシミュレートする関数
function moveSource() {
    let startTime = audioContext.currentTime;
    let duration = 5;  // 音源が移動する時間(秒)
    let endTime = startTime + duration;

    // X軸の位置を時間に応じて変化させる
    panner.positionX.linearRampToValueAtTime(5, endTime);  // 右に移動

    // 移動が終了したら音声を停止
    oscillator.stop(endTime);
}

// 音源の移動を開始
moveSource();

説明
PannerNode: PannerNodeは、音を3D空間に配置するために使用します。このノードのpositionX, positionY, positionZプロパティを使って、音源の位置をリアルタイムで変更できます。
HRTF: パンニングモデルとしてHRTF(Head Related Transfer Function)を使用しています。これは、人間の耳で音を聞くときの立体感をシミュレートするモデルで、よりリアルな3D音響を体験できます。
音源の移動: panner.positionX.linearRampToValueAtTime()を使用して、音源が一定の時間内に徐々に右側に移動するようにしています。
このコードでは、音源が5秒かけてリスナーの前から右側に移動します。これにより、音源が動いているような効果をシミュレートできます。また、positionYやpositionZも変更すれば、上下や前後方向の移動も可能です。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3