生成AI Claude 3.5 sonnetで作曲させ、再生もしたい!

前書き

1つ前の記事で、リアルタイムボイスチェンジャーのwebアプリをClaude 3.5 sonnetで作成させ、期待通りのものを生成することができました。
Claude 3.5 sonnetはテキストのやり取りのみですが、javascriptモジュールを指定してwebのコードを生成させることで、単なるテキストを超えた成果物を生成させることができます。
たとえば、プレゼン用のjavascriptモジュールを指定すれば、入力テキストを要約してブラウザで動作するスライドショーなども実現できたりします。

ということは、生成AIで作曲させ、再生用のjavascriptモジュールを指示してHTMLコードを生成させれば、作曲から再生まで生成AIで行えることになります。

さっそく作成!

利用する再生用のjavascriptモジュールは、ボイスチェンジャーwebアプリ作成時にも利用したTone.jsを使ってもらいます。
曲調は”メタリカ風に”と指示してみました。

生成されたwebアプリ画面イメージ

生成されたHTMLをブラウザで開くと、プレイボタンが表示され、生成AIが作った曲が再生されます。(HTMLコードはページ最後に掲載)

作成された曲のHTML

さーーて、どんな曲ができましたかね。
気になる方は、以下をファイルに保存しブラウザで開くと曲を聞けます。
メタリカ風なのかといわれると、素人がMIDIで初めて作ったような感じになっちゃってますが、目的であった、自動で作曲させた曲の再生まで生成AIで実現できました!
音源がしょぼいですが、もしかするとギターやドラムのサンプル音源をダウンロードして利用するようにしたらよくなるのかもしれません。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>16-bar Melodic Metallica-style Song</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #222;
            color: #fff;
        }
        #playButton {
            font-size: 24px;
            padding: 10px 20px;
            background-color: #f00;
            color: #fff;
            border: none;
            cursor: pointer;
            margin-bottom: 20px;
        }
        #progressBar {
            width: 300px;
            height: 20px;
            background-color: #444;
            border-radius: 10px;
            overflow: hidden;
        }
        #progress {
            width: 0%;
            height: 100%;
            background-color: #f00;
            transition: width 0.1s linear;
        }
    </style>
</head>
<body>
    <button id="playButton">Play 16-bar Melodic Metallica-style Song</button>
    <div id="progressBar">
        <div id="progress"></div>
    </div>

    <script>
        const playButton = document.getElementById('playButton');
        const progressBar = document.getElementById('progress');

        // Create instruments
        const distortion = new Tone.Distortion(0.8).toDestination();
        const guitar = new Tone.PolySynth(Tone.Synth, {
            oscillator: {
                type: "sawtooth"
            },
            envelope: {
                attack: 0.01,
                decay: 0.1,
                sustain: 0.3,
                release: 0.1
            }
        }).connect(distortion);
        const bass = new Tone.Synth().toDestination();
        const kickDrum = new Tone.MembraneSynth().toDestination();
        const snareDrum = new Tone.NoiseSynth({
            noise: { type: 'white' },
            envelope: { attack: 0.005, decay: 0.1, sustain: 0.05, release: 0.1 }
        }).toDestination();

        // Create sequences
        const guitarPart = new Tone.Sequence((time, chord) => {
            guitar.triggerAttackRelease(chord, "8n", time);
        }, [
            // Verse 1 (4 bars)
            ["E4", "G4", "B4"], ["E4", "G4", "B4"], ["G4", "B4", "D5"], ["G4", "B4", "D5"],
            ["A4", "C5", "E5"], ["A4", "C5", "E5"], ["G4", "B4", "D5"], ["G4", "B4", "D5"],
            ["E4", "G4", "B4"], ["E4", "G4", "B4"], ["G4", "B4", "D5"], ["G4", "B4", "D5"],
            ["A4", "C5", "E5"], ["A4", "C5", "E5"], ["B4", "D5", "F#5"], ["B4", "D5", "F#5"],

            // Pre-Chorus (4 bars)
            ["C5", "E5", "G5"], ["C5", "E5", "G5"], ["B4", "D5", "F#5"], ["B4", "D5", "F#5"],
            ["A4", "C5", "E5"], ["A4", "C5", "E5"], ["G4", "B4", "D5"], ["G4", "B4", "D5"],
            ["C5", "E5", "G5"], ["C5", "E5", "G5"], ["B4", "D5", "F#5"], ["B4", "D5", "F#5"],
            ["A4", "C5", "E5"], ["G4", "B4", "D5"], ["F#4", "A4", "C#5"], ["F#4", "A4", "C#5"],

            // Chorus (4 bars)
            ["E5", "G5", "B5"], ["E5", "G5", "B5"], ["D5", "F#5", "A5"], ["D5", "F#5", "A5"],
            ["C5", "E5", "G5"], ["C5", "E5", "G5"], ["B4", "D5", "F#5"], ["B4", "D5", "F#5"],
            ["E5", "G5", "B5"], ["E5", "G5", "B5"], ["D5", "F#5", "A5"], ["D5", "F#5", "A5"],
            ["C5", "E5", "G5"], ["C5", "E5", "G5"], ["B4", "D5", "F#5"], ["A4", "C#5", "E5"],

            // Bridge (4 bars)
            ["F#4", "A4", "C#5"], ["F#4", "A4", "C#5"], ["E4", "G4", "B4"], ["E4", "G4", "B4"],
            ["D4", "F#4", "A4"], ["D4", "F#4", "A4"], ["C#4", "E4", "G#4"], ["C#4", "E4", "G#4"],
            ["F#4", "A4", "C#5"], ["F#4", "A4", "C#5"], ["E4", "G4", "B4"], ["E4", "G4", "B4"],
            ["D4", "F#4", "A4"], ["D4", "F#4", "A4"], ["E4", "G4", "B4"], ["E4", "G4", "B4"]
        ], "4n");

const bassPart = new Tone.Sequence((time, note) => {
    if (Array.isArray(note)) {
        note.forEach((n, index) => {
            bass.triggerAttackRelease(n, "16n", time + index * Tone.Time("64n"));
        });
    } else {
        bass.triggerAttackRelease(note, "8n", time);
    }
}, [
    // Verse 1
    "E2", ["E2", "G2"], "A2", ["G2", "A2"],
    "E2", ["E2", "G2"], "A2", "B2",
    "E2", ["E2", "G2"], "A2", ["G2", "A2"],
    "E2", ["E2", "G2"], "A2", "B2",
    // Pre-Chorus
    "C3", ["C3", "B2"], "A2", ["A2", "G2"],
    "C3", ["C3", "B2"], "A2", ["G2", "F#2"],
    "C3", ["C3", "B2"], "A2", ["A2", "G2"],
    "C3", ["C3", "B2"], "A2", ["G2", "F#2"],
    // Chorus
    "E2", ["E2", "F#2"], "G2", ["G2", "A2"],
    "B2", ["B2", "A2"], "G2", ["G2", "F#2"],
    "E2", ["E2", "F#2"], "G2", ["G2", "A2"],
    "B2", ["B2", "A2"], "G2", "F#2",
    // Bridge
    "F#2", ["F#2", "G2"], "A2", ["A2", "B2"],
    "C#3", ["C#3", "B2"], "A2", "G2",
    "F#2", ["F#2", "G2"], "A2", ["A2", "B2"],
    "C#3", ["C#3", "B2"], "A2", "G2"
], "4n");

const drumPart = new Tone.Sequence((time, pattern) => {
    if (pattern.kick) kickDrum.triggerAttackRelease("C2", "8n", time);
    if (pattern.snare) snareDrum.triggerAttackRelease("8n", time);
    if (pattern.hihat) {
        const hihat = new Tone.NoiseSynth({
            noise: { type: 'white' },
            envelope: { attack: 0.001, decay: 0.05, sustain: 0.005, release: 0.05 }
        }).toDestination();
        hihat.triggerAttackRelease("32n", time);
    }
}, [
    { kick: true, snare: false, hihat: true }, { kick: false, snare: false, hihat: true },
    { kick: false, snare: true, hihat: true }, { kick: false, snare: false, hihat: true },
    { kick: true, snare: false, hihat: true }, { kick: false, snare: false, hihat: true },
    { kick: false, snare: true, hihat: true }, { kick: false, snare: false, hihat: true },
    { kick: true, snare: false, hihat: true }, { kick: false, snare: false, hihat: true },
    { kick: false, snare: true, hihat: true }, { kick: false, snare: false, hihat: true },
    { kick: true, snare: false, hihat: true }, { kick: false, snare: true, hihat: true },
    { kick: true, snare: false, hihat: true }, { kick: false, snare: true, hihat: true }
], "8n");
        // Set up the transport
        Tone.Transport.bpm.value = 140;
        Tone.Transport.loop = true;
        Tone.Transport.loopEnd = "16m";

        // Update progress bar
        function updateProgressBar() {
            const progress = (Tone.Transport.ticks / (16 * 4 * 4 * 128)) * 100;
            progressBar.style.width = `${progress}%`;
            requestAnimationFrame(updateProgressBar);
        }

        playButton.addEventListener('click', async () => {
            await Tone.start();
            if (Tone.Transport.state !== "started") {
                guitarPart.start(0);
                bassPart.start(0);
                drumPart.start(0);
                Tone.Transport.start();
                playButton.textContent = "Stop";
                updateProgressBar();
            } else {
                Tone.Transport.stop();
                playButton.textContent = "Play 16-bar Melodic Metallica-style Song";
                progressBar.style.width = "0%";
            }
        });
    </script>
</body>
</html>



この記事が気に入ったらサポートをしてみませんか?