3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「目」と 「口」を持った人工生命体関数が、ユーチューブを見て歌を歌う。Webカメラ映像を音にするゲーム。

Last updated at Posted at 2024-10-07

スクリーンショット 2024-10-07 205851.png

ショートストーリー: 「光と音のシンフォニー」

東京の高層ビルの一室、夜の街を見下ろすオフィスに、プログラマの翔太は座っていた。

「やっとこれで形になったか…」

彼はふと手を止め、目の前に表示されているコードを見つめた。Webカメラを通じて光のデータを音に変換するプログラム。

Webカメラで映し出された映像の各ピクセルをスキャンし、その明るさを音に変換する仕組みを作ろうと決めた。ピクセルの明るさ、つまり光の強さを数字に変え、それを音の周波数に結びつける。彼の頭の中で、数学的なロジックと音の波形が交差した。

「さて、これが動くかどうか…」

翔太はビデオストリームを起動し、自分の手をカメラの前にかざした。モニターには手の映像が映し出され、それが瞬く間にキャンバスに描かれた。彼はコードがピクセルごとに光の明るさを計算する様子を思い浮かべた。4096個のピクセル。それぞれがRGBの値を持ち、その平均値から光の強さ、つまり「明るさ」が算出される。

「1ピクセルごとの光の強さ…それが僕の音符か。」

彼は微笑んだ。プログラムは、4096のデータポイントを取り出し、それを周波数に変換する。そして、ランダムに選ばれた波形タイプで音を生成する。正弦波、矩形波、三角波、そして鋸歯状波。それぞれの音が微妙に異なるリズムを刻み、複雑なハーモニーを生み出すのだ。

そして、実行ボタンを押すと、室内に優しい音が流れ始めた。彼は手を動かし、カメラに映る光の量を変えるたびに、音のトーンが変わっていくのを楽しんだ。

image.png

Webカメラ映像を音に変換するゲーム。ユーチューブを見せてます。

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

キャンバスの解像度を64×64にし、4096ピクセル(64×64)のデータポイントから明るさを取得しています。

複雑な音の生成:

各フレームから取得した複数の明るさデータに基づき、複数の周波数で音を生成するようにしました。
ランダムに音波タイプ(sine, square, triangle, sawtooth)を設定し、複雑な音を作り出します。

周波数の組み合わせ:

5つの異なる明るさレベルを使って、それぞれ異なる周波数を設定し、複数の音を重ね合わせています。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Webカメラ映像を音に変換</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin-top: 50px;
        }
        video {
            display: block;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    <h1>Webカメラ映像を音に変換</h1>
    <!-- Webカメラの映像を表示するビデオ要素 -->
    <video id="video" width="640" height="480" autoplay></video>
    <!-- Webカメラ映像を一時的に描画し、ピクセルデータを取得するための隠れたキャンバス -->
    <canvas id="canvas" width="64" height="64" style="display:none;"></canvas>

    <script>
        // Webカメラ映像を取得するビデオ要素と、データを取得するためのキャンバス要素
        const video = document.getElementById('video');
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');  // キャンバスの描画コンテキストを取得

        // 音を生成するためのオーディオコンテキスト(古いブラウザ対応のためにwindow.webkitAudioContextも使用)
        const audioContext = new (window.AudioContext || window.webkitAudioContext)();

        // 音を生成する関数
        // 複数の周波数(frequencies)と持続時間(duration)を引数に取る
        function playComplexTone(frequencies, duration) {
            // 音量を制御するためのゲインノードを作成し、出力に接続
            const gainNode = audioContext.createGain();
            gainNode.connect(audioContext.destination);
            gainNode.gain.setValueAtTime(0.05, audioContext.currentTime); // 音量を低めに設定(0.05)

            // 複数のオシレーター(音波生成装置)を周波数ごとに作成し、複数の音を同時に再生
            frequencies.forEach(frequency => {
                const oscillator = audioContext.createOscillator(); // オシレーター(音波生成器)を作成
                oscillator.type = ['sine', 'square', 'triangle', 'sawtooth'][Math.floor(Math.random() * 4)]; // 音波のタイプをランダムに設定
                oscillator.frequency.value = frequency;  // 周波数を設定
                oscillator.connect(gainNode); // ゲインノードに接続
                oscillator.start();  // オシレーターを開始
                oscillator.stop(audioContext.currentTime + duration); // 指定された持続時間(duration)で停止
            });
        }

        // Webカメラ映像をキャンバスに描画し、データを取得・処理する関数
        function processVideo() {
            // Webカメラ映像をキャンバスに描画
            ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

            // キャンバスからピクセルデータを取得
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const data = imageData.data; // RGBAのピクセルデータが入った配列

            // 4096カ所(64x64)のデータポイントを使用して明るさを計算
            let totalBrightness = 0;
            let brightnessValues = []; // 各ピクセルの明るさを格納する配列

            // 各ピクセルのRGB値から明るさを計算し、配列に追加
            for (let i = 0; i < 4096; i++) {
                const index = i * 4;  // ピクセルのデータはRGBAの4つの値で構成されているため、i*4でそのピクセルの最初の値を取得
                const r = data[index];     // 赤の値
                const g = data[index + 1]; // 緑の値
                const b = data[index + 2]; // 青の値
                const brightness = (r + g + b) / 3; // 明るさを計算(RGBの平均値)
                brightnessValues.push(brightness); // 計算した明るさを配列に格納
                totalBrightness += brightness;     // 総明るさを加算
            }

            // 明るさに基づいて複数の周波数を生成
            // ここでは明るさの最初の5つのデータポイントを使って異なる周波数を計算
            const frequencies = brightnessValues.slice(0, 5).map(brightness => (brightness / 255) * 1000 + 200);

            // 音を再生(5つの周波数で複雑な音を生成)
            playComplexTone(frequencies, 0.2); // 0.2秒間の音を再生
        }

        // Webカメラの映像を取得し、処理を開始
        navigator.mediaDevices.getUserMedia({ video: true }) // Webカメラのアクセスを要求
            .then(stream => {
                video.srcObject = stream; // 取得した映像ストリームをビデオ要素に設定
                setInterval(processVideo, 200); // 200msごとに映像を処理
            })
            .catch(err => {
                console.error("Webカメラのアクセスに失敗しました: ", err); // エラー処理
            });
    </script>
</body>
</html>

3
5
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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?