2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

画像から、8ビット風の音楽制作を楽しむゲーム。画像からの歌声を聴いてみましょう。

Last updated at Posted at 2024-10-18

スクリーンショット 2024-10-18 212411.png

image.png

8ビット風の音楽をよりダイナミックにカスタマイズし、音楽制作を楽しむゲーム。

画像のピクセルデータに基づいた多様な音が生成され、曲として再生されます。

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

スライダーによる調整:

基本周波数 (Hz) を調整することで、生成される音の全体的な高さを変えられます。
周波数変動幅を調整することで、音程の変動の強さを調整可能。
再生速度も調整でき、音のテンポを変えることができます。
インタラクティブな操作: 画像のサイズや音のパラメータをリアルタイムで変更し、結果を即座に反映させて再生できます。

がんばれば、8ビット風の音楽をよりダイナミックにカスタマイズし、音楽制作を楽しむことができます。

モールスのほうが落ち着く。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>8ビット音楽生成</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin: 20px;
        }
        #canvas, #uploadedImage {
            display: block;
            margin: 20px auto;
        }
        #canvas {
            border: 1px solid black;
        }
        input[type="range"] {
            width: 300px;
        }
    </style>
</head>
<body>

    <h1>画像から8ビット音楽生成</h1>
    <input type="file" id="imageInput" accept="image/*"><br>

    <label for="sizeSlider">画像サイズ: <span id="sizeValue">256</span> x <span id="sizeValue2">256</span></label><br>
    <input type="range" id="sizeSlider" min="16" max="512" value="256"><br>

    <label for="frequencyBaseSlider">基本周波数 (Hz): <span id="frequencyBaseValue">100</span></label><br>
    <input type="range" id="frequencyBaseSlider" min="50" max="500" value="100"><br>

    <label for="frequencyRangeSlider">周波数変動幅: <span id="frequencyRangeValue">200</span></label><br>
    <input type="range" id="frequencyRangeSlider" min="50" max="1000" value="200"><br>

    <label for="speedSlider">再生速度: <span id="speedValue">256</span></label><br>
    <input type="range" id="speedSlider" min="50" max="1000" value="256"><br>

    <button id="playSound">8ビット音楽を再生</button>
    <img id="uploadedImage" alt="アップロードされた画像">
    <canvas id="canvas"></canvas>

    <script>
        const imageInput = document.getElementById('imageInput');
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        const uploadedImage = document.getElementById('uploadedImage');
        const playSoundButton = document.getElementById('playSound');
        const sizeSlider = document.getElementById('sizeSlider');
        const sizeValue = document.getElementById('sizeValue');
        const sizeValue2 = document.getElementById('sizeValue2');
        const frequencyBaseSlider = document.getElementById('frequencyBaseSlider');
        const frequencyBaseValue = document.getElementById('frequencyBaseValue');
        const frequencyRangeSlider = document.getElementById('frequencyRangeSlider');
        const frequencyRangeValue = document.getElementById('frequencyRangeValue');
        const speedSlider = document.getElementById('speedSlider');
        const speedValue = document.getElementById('speedValue');
        let pixelData = [];
        let imgWidth = 256;
        let imgHeight = 256;

        // スライダーの値で画像サイズを変更
        sizeSlider.addEventListener('input', function() {
            imgWidth = imgHeight = sizeSlider.value;
            sizeValue.textContent = imgWidth;
            sizeValue2.textContent = imgHeight;
            canvas.width = imgWidth;
            canvas.height = imgHeight;
            if (uploadedImage.src) {
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(uploadedImage, 0, 0, imgWidth, imgHeight);
                pixelData = ctx.getImageData(0, 0, imgWidth, imgHeight).data;
            }
        });

        // 周波数とスピードのスライダーに応じて値を更新
        frequencyBaseSlider.addEventListener('input', function() {
            frequencyBaseValue.textContent = frequencyBaseSlider.value;
        });
        frequencyRangeSlider.addEventListener('input', function() {
            frequencyRangeValue.textContent = frequencyRangeSlider.value;
        });
        speedSlider.addEventListener('input', function() {
            speedValue.textContent = speedSlider.value;
        });

        // 画像を読み込み、スライダーに応じてリサイズし、ピクセルデータを取得
        imageInput.addEventListener('change', function(event) {
            const file = event.target.files[0];
            const img = new Image();
            const reader = new FileReader();

            reader.onload = function(e) {
                img.src = e.target.result;
                uploadedImage.src = e.target.result; // アップロードされた画像を表示
            };

            img.onload = function() {
                canvas.width = imgWidth;
                canvas.height = imgHeight;
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.drawImage(img, 0, 0, imgWidth, imgHeight);
                pixelData = ctx.getImageData(0, 0, imgWidth, imgHeight).data;
                alert('ピクセルデータを取得しました');
            };

            reader.readAsDataURL(file);
        });

        // 8ビット風音楽生成
        playSoundButton.addEventListener('click', function() {
            if (pixelData.length === 0) {
                alert('まず画像をアップロードしてください');
                return;
            }

            const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
            const sampleRate = audioCtx.sampleRate;
            const buffer = audioCtx.createBuffer(1, sampleRate * 10, sampleRate); // 10秒間のバッファ
            const channelData = buffer.getChannelData(0);
            let time = 0;
            const baseFrequency = parseFloat(frequencyBaseSlider.value);
            const frequencyRange = parseFloat(frequencyRangeSlider.value);
            const playbackSpeed = parseInt(speedSlider.value);

            // ピクセルデータを基に音を生成(8ビット風の矩形波と音程の急激な変化)
            for (let i = 0; i < pixelData.length; i += 4) {
                const r = pixelData[i];
                const g = pixelData[i + 1];
                const b = pixelData[i + 2];

                // RGB値を基に周波数を急激に変化させる
                let frequency = baseFrequency + (r + g + b) / 3 + Math.sin(i / 500) * frequencyRange; // 音程をダイナミックに変化

                // 矩形波を生成(8ビットスタイル)
                for (let j = 0; j < sampleRate / playbackSpeed; j++) {
                    const t = j / sampleRate;
                    const squareWave = Math.sign(Math.sin(2 * Math.PI * frequency * t));
                    channelData[time++] = squareWave;

                    if (time >= channelData.length) break;
                }
                if (time >= channelData.length) break;
            }

            // ループ再生用にバッファを作成
            const bufferSource = audioCtx.createBufferSource();
            bufferSource.buffer = buffer;
            bufferSource.loop = true; // ループ再生
            bufferSource.connect(audioCtx.destination);
            bufferSource.start();
        });
    </script>

</body>
</html>

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?