はじめに:音楽とプログラミングの新たな融合
「コード進行をプログラムで自動生成できたら、作曲がもっと効率的になるのに…」
そんな音楽制作者の願いを叶えるのが、JavaScriptを使ったMIDIファイル自動生成技術です。近年のWeb技術の進歩により、ブラウザ上で高品質な音楽生成が可能になりました。この記事では、Web MIDI APIとTone.jsを活用して、コード進行データから実際に演奏可能なMIDIファイルを生成する方法を、実装可能なコード例とともに詳しく解説します。
この記事で学べること
• Web MIDI APIの基本的な使い方
• Tone.jsを使った音楽生成
• コード進行データの構造化
• MIDIファイル生成の実装方法
• 実際に動作するサンプルコード
音楽理論の知識とプログラミング技術を組み合わせることで、従来では不可能だった創造的な音楽制作が可能になります。
必要な技術とライブラリの概要
Web MIDI API
Web MIDI APIの特徴
- ブラウザネイティブ:追加のプラグイン不要
- リアルタイム処理:即座にMIDI信号を送受信
- デバイス連携:外部MIDI機器との接続可能
- 標準準拠:W3C標準仕様
Tone.js音楽ライブラリ
Tone.jsの主要機能
- 音源生成:シンセサイザー、サンプラー
- エフェクト:リバーブ、ディストーション
- シーケンサー:パターン再生、タイミング制御
- 録音機能:オーディオ出力のキャプチャ
開発環境の準備
必要なファイル構成
1 2 3 4 5 6 7 8 9 | project/ ├── index.html # メインHTML ├── js/ │ ├── chord-data.js # コード進行データ │ ├── midi-generator.js # MIDI生成エンジン │ └── audio-engine.js # 音声処理 ├── css/ │ └── style.css # スタイルシート └── samples/ # 音源サンプル |
コード進行データの構造化
基本データ構造の設計
chord-data.js – コード進行データベース
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | // コード進行データベース const chordProgressions = { "456進行": { key: "C", chords: [ { name: "F", roman: "IV", notes: ["F", "A", "C"] }, { name: "G", roman: "V", notes: ["G", "B", "D"] }, { name: "Am", roman: "vi", notes: ["A", "C", "E"] } ], tempo: 120, timeSignature: [4, 4] }, "2516進行": { key: "C", chords: [ { name: "Dm7", roman: "ii7", notes: ["D", "F", "A", "C"] }, { name: "G7", roman: "V7", notes: ["G", "B", "D", "F"] }, { name: "C", roman: "I", notes: ["C", "E", "G"] }, { name: "Am", roman: "vi", notes: ["A", "C", "E"] } ], tempo: 100, timeSignature: [4, 4] }, "カノン進行": { key: "C", chords: [ { name: "C", roman: "I", notes: ["C", "E", "G"] }, { name: "G", roman: "V", notes: ["G", "B", "D"] }, { name: "Am", roman: "vi", notes: ["A", "C", "E"] }, { name: "Em", roman: "iii", notes: ["E", "G", "B"] }, { name: "F", roman: "IV", notes: ["F", "A", "C"] }, { name: "C", roman: "I", notes: ["C", "E", "G"] }, { name: "F", roman: "IV", notes: ["F", "A", "C"] }, { name: "G", roman: "V", notes: ["G", "B", "D"] } ], tempo: 80, timeSignature: [4, 4] } }; |
拡張データ構造
高度なコード情報の追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | // より詳細なコード情報 const advancedChordData = { name: "FM7", roman: "IVM7", notes: ["F", "A", "C", "E"], bassNote: "F", inversion: 0, duration: "1n", // 全音符 velocity: 0.8, voicing: "close", // close, open, drop2, drop3 function: "subdominant", tensions: ["9", "11"], alterations: [] }; |
Web MIDI APIの実装
MIDI API初期化
midi-generator.js – MIDI API初期化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class MIDIGenerator { constructor() { this.midiAccess = null; this.outputs = []; this.inputs = []; this.isConnected = false; } // MIDI API初期化 async init() { try { if (navigator.requestMIDIAccess) { this.midiAccess = await navigator.requestMIDIAccess(); this.setupDevices(); this.isConnected = true; console.log('MIDI API初期化完了'); } else { throw new Error('Web MIDI API未対応ブラウザ'); } } catch (error) { console.error('MIDI初期化エラー:', error); this.isConnected = false; } } // MIDI デバイス設定 setupDevices() { // 出力デバイス for (let output of this.midiAccess.outputs.values()) { this.outputs.push(output); console.log('MIDI出力:', output.name); } // 入力デバイス for (let input of this.midiAccess.inputs.values()) { this.inputs.push(input); input.onmidimessage = this.handleMIDIMessage.bind(this); console.log('MIDI入力:', input.name); } } // MIDIメッセージ処理 handleMIDIMessage(message) { const [command, note, velocity] = message.data; console.log('MIDIメッセージ:', { command, note, velocity }); } } |
MIDIメッセージ送信
MIDI信号の送信メソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | // MIDIノート送信 sendMIDINote(note, velocity = 127, duration = 500, channel = 0) { if (!this.isConnected || this.outputs.length === 0) { console.warn('MIDI出力デバイスが見つかりません'); return; } const output = this.outputs[0]; // 最初の出力デバイス使用 const noteNumber = this.noteToMIDI(note); // Note On const noteOnMessage = [0x90 + channel, noteNumber, velocity]; output.send(noteOnMessage); // Note Off (指定時間後) setTimeout(() => { const noteOffMessage = [0x80 + channel, noteNumber, 0]; output.send(noteOffMessage); }, duration); } // 音名からMIDI番号への変換 noteToMIDI(note) { const noteMap = { 'C': 0, 'C#': 1, 'Db': 1, 'D': 2, 'D#': 3, 'Eb': 3, 'E': 4, 'F': 5, 'F#': 6, 'Gb': 6, 'G': 7, 'G#': 8, 'Ab': 8, 'A': 9, 'A#': 10, 'Bb': 10, 'B': 11 }; const match = note.match(/([A-G][#b]?)(\d+)/); if (!match) return 60; // デフォルトC4 const [, noteName, octave] = match; return noteMap[noteName] + (parseInt(octave) + 1) * 12; } |
Tone.jsによる音声生成
基本音源の設定
audio-engine.js – Tone.js音声エンジン
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | class AudioEngine { constructor() { this.synth = null; this.polySynth = null; this.isInitialized = false; this.reverb = null; this.compressor = null; } // 音声エンジン初期化 async init() { try { // Tone.js開始 await Tone.start(); // ポリフォニックシンセサイザー作成 this.polySynth = new Tone.PolySynth(Tone.Synth, { oscillator: { type: "triangle" }, envelope: { attack: 0.02, decay: 0.1, sustain: 0.3, release: 1 } }); // エフェクト設定 this.reverb = new Tone.Reverb({ decay: 2.0, wet: 0.3 }); this.compressor = new Tone.Compressor({ threshold: -30, ratio: 3 }); // エフェクトチェーン this.polySynth.chain(this.reverb, this.compressor, Tone.Destination); this.isInitialized = true; console.log('Audio Engine初期化完了'); } catch (error) { console.error('Audio Engine初期化エラー:', error); } } // コード演奏 playChord(notes, duration = "2n") { if (!this.isInitialized) { console.warn('Audio Engineが初期化されていません'); return; } const now = Tone.now(); notes.forEach(note => { this.polySynth.triggerAttackRelease(note + "4", duration, now); }); } // シーケンス再生 playSequence(chordProgression, chordDuration = "1n") { let time = Tone.now(); chordProgression.chords.forEach((chord, index) => { Tone.Transport.schedule(() => { this.playChord(chord.notes, chordDuration); }, time); time += Tone.Time(chordDuration).toSeconds(); }); // トランスポート開始 Tone.Transport.start(); } } |
高度な音源とエフェクト
多様な音色の実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | // 楽器別音源の作成 createPianoSound() { return new Tone.Sampler({ urls: { C4: "C4.mp3", "D#4": "Ds4.mp3", "F#4": "Fs4.mp3", A4: "A4.mp3", }, release: 1, baseUrl: "./samples/piano/" }); } createStringSound() { return new Tone.PolySynth(Tone.Synth, { oscillator: { type: "sawtooth" }, filter: { frequency: 1000, rolloff: -24 }, envelope: { attack: 0.1, decay: 0.2, sustain: 0.6, release: 1.5 } }); } createOrganSound() { return new Tone.PolySynth(Tone.Synth, { oscillator: { type: "square" }, envelope: { attack: 0.01, decay: 0.1, sustain: 0.9, release: 0.5 } }); } |
コード進行の自動生成
確率的コード生成
chord-generator.js – 自動コード進行生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | class ChordProgressionGenerator { constructor() { // コード進行の確率テーブル this.transitionMatrix = { "I": { "ii": 0.2, "iii": 0.1, "IV": 0.3, "V": 0.25, "vi": 0.15 }, "ii": { "V": 0.6, "iii": 0.1, "IV": 0.2, "vi": 0.1 }, "iii": { "IV": 0.3, "vi": 0.4, "ii": 0.2, "V": 0.1 }, "IV": { "V": 0.4, "I": 0.3, "ii": 0.2, "vi": 0.1 }, "V": { "I": 0.7, "vi": 0.2, "IV": 0.1 }, "vi": { "IV": 0.3, "ii": 0.25, "V": 0.25, "iii": 0.2 } }; this.chordSymbols = { "C": { "I": "C", "ii": "Dm", "iii": "Em", "IV": "F", "V": "G", "vi": "Am", "vii°": "Bdim" } }; } // ランダムコード進行生成 generateProgression(key = "C", length = 4, startChord = "I") { const progression = [startChord]; let currentChord = startChord; for (let i = 1; i < length; i++) { const nextChord = this.selectNextChord(currentChord); progression.push(nextChord); currentChord = nextChord; } return this.convertToChordData(progression, key); } // 次のコードを確率的に選択 selectNextChord(currentChord) { const transitions = this.transitionMatrix[currentChord]; const random = Math.random(); let cumulative = 0; for (const [chord, probability] of Object.entries(transitions)) { cumulative += probability; if (random <= cumulative) { return chord; } } return "I"; // フォールバック } // ローマ数字を実際のコードに変換 convertToChordData(progression, key) { const symbols = this.chordSymbols[key]; return { key: key, chords: progression.map(roman => ({ name: symbols[roman], roman: roman, notes: this.getChordNotes(symbols[roman]) })), tempo: 120, timeSignature: [4, 4] }; } // コードの構成音を取得 getChordNotes(chordName) { const chordMap = { "C": ["C", "E", "G"], "Dm": ["D", "F", "A"], "Em": ["E", "G", "B"], "F": ["F", "A", "C"], "G": ["G", "B", "D"], "Am": ["A", "C", "E"], "Bdim": ["B", "D", "F"] }; return chordMap[chordName] || ["C", "E", "G"]; } } |
ジャンル別生成ルール
スタイル別コード進行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // ジャンル別コード進行パターン const genrePatterns = { "pop": { common: ["I", "V", "vi", "IV"], variations: [ ["vi", "IV", "I", "V"], ["I", "vi", "IV", "V"] ] }, "jazz": { common: ["ii7", "V7", "IM7", "VIM7"], variations: [ ["IM7", "VI7", "ii7", "V7"], ["iii7", "VI7", "ii7", "V7"] ] }, "rock": { common: ["I", "♭VII", "IV", "I"], variations: [ ["I", "IV", "V", "I"], ["vi", "IV", "I", "V"] ] } }; |
MIDIファイル出力機能
MIDI データ構造
midi-export.js – MIDIファイル生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | class MIDIFileGenerator { constructor() { this.ticksPerQuarter = 480; this.tracks = []; this.format = 1; // Multi-track MIDI } // コード進行からMIDIファイル生成 generateMIDIFile(chordProgression) { const midiData = { format: this.format, tracks: [], ticksPerQuarter: this.ticksPerQuarter }; // テンポトラック midiData.tracks.push(this.createTempoTrack(chordProgression.tempo)); // コードトラック midiData.tracks.push(this.createChordTrack(chordProgression)); // MIDIバイナリデータに変換 return this.encodeToMIDI(midiData); } // テンポトラック作成 createTempoTrack(tempo) { const microsecondsPerQuarter = Math.round(60000000 / tempo); return [ { deltaTime: 0, type: 'setTempo', microsecondsPerQuarter }, { deltaTime: 0, type: 'timeSignature', numerator: 4, denominator: 4 }, { deltaTime: 0, type: 'endOfTrack' } ]; } // コードトラック作成 createChordTrack(chordProgression) { const track = []; let currentTime = 0; const chordDuration = this.ticksPerQuarter; // 4分音符 chordProgression.chords.forEach((chord, index) => { const deltaTime = index === 0 ? 0 : chordDuration; // Note On events chord.notes.forEach((note, noteIndex) => { const noteNumber = this.noteToMIDI(note + "4"); track.push({ deltaTime: noteIndex === 0 ? deltaTime : 0, type: 'noteOn', channel: 0, noteNumber: noteNumber, velocity: 80 }); }); // Note Off events chord.notes.forEach((note, noteIndex) => { const noteNumber = this.noteToMIDI(note + "4"); track.push({ deltaTime: noteIndex === 0 ? chordDuration : 0, type: 'noteOff', channel: 0, noteNumber: noteNumber, velocity: 0 }); }); }); // End of track track.push({ deltaTime: 0, type: 'endOfTrack' }); return track; } // MIDIバイナリエンコード encodeToMIDI(midiData) { // 簡略化された実装 // 実際にはより複雑なバイナリエンコーディングが必要 return JSON.stringify(midiData); // 開発用 } // ファイルダウンロード downloadMIDI(midiData, filename = 'generated_chord_progression.mid') { const blob = new Blob([midiData], { type: 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } } |
ユーザーインターフェースの実装
HTML構造
index.html – メインインターフェース
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript MIDI Generator</title> <script src="https://unpkg.com/tone@14.7.77/build/Tone.js"></script> <link rel="stylesheet" href="css/style.css"> </head> <body> <div class="container"> <h1>コード進行 MIDI ジェネレーター</h1> <div class="controls"> <div class="control-group"> <label for="progression-select">コード進行:</label> <select id="progression-select"> <option value="456進行">456進行 (IV-V-vi)</option> <option value="2516進行">2516進行 (ii7-V7-I-vi)</option> <option value="カノン進行">カノン進行</option> </select> </div> <div class="control-group"> <label for="key-select">キー:</label> <select id="key-select"> <option value="C">C Major</option> <option value="G">G Major</option> <option value="F">F Major</option> <option value="Am">A minor</option> </select> </div> <div class="control-group"> <label for="tempo-slider">テンポ: <span id="tempo-value">120</span> BPM</label> <input type="range" id="tempo-slider" min="60" max="180" value="120"> </div> </div> <div class="buttons"> <button id="play-btn">再生</button> <button id="stop-btn">停止</button> <button id="generate-btn">ランダム生成</button> <button id="download-btn">MIDI ダウンロード</button> </div> <div class="chord-display"> <h3>現在のコード進行</h3> <div id="chord-list"></div> </div> <div class="visualizer"> <canvas id="piano-roll"></canvas> </div> </div> <script src="js/chord-data.js"></script> <script src="js/midi-generator.js"></script> <script src="js/audio-engine.js"></script> <script src="js/chord-generator.js"></script> <script src="js/midi-export.js"></script> <script src="js/app.js"></script> </body> </html> |
メインアプリケーション
app.js – アプリケーション制御
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | class MIDIGeneratorApp { constructor() { this.midiGenerator = new MIDIGenerator(); this.audioEngine = new AudioEngine(); this.chordGenerator = new ChordProgressionGenerator(); this.midiExporter = new MIDIFileGenerator(); this.currentProgression = null; this.isPlaying = false; } async init() { try { // 各エンジン初期化 await this.midiGenerator.init(); await this.audioEngine.init(); // イベントリスナー設定 this.setupEventListeners(); // 初期コード進行設定 this.loadProgression('456進行'); console.log('アプリケーション初期化完了'); } catch (error) { console.error('初期化エラー:', error); } } setupEventListeners() { // 再生ボタン document.getElementById('play-btn').addEventListener('click', () => { this.playProgression(); }); // 停止ボタン document.getElementById('stop-btn').addEventListener('click', () => { this.stopProgression(); }); // ランダム生成ボタン document.getElementById('generate-btn').addEventListener('click', () => { this.generateRandomProgression(); }); // ダウンロードボタン document.getElementById('download-btn').addEventListener('click', () => { this.downloadMIDI(); }); // コード進行選択 document.getElementById('progression-select').addEventListener('change', (e) => { this.loadProgression(e.target.value); }); // テンポスライダー document.getElementById('tempo-slider').addEventListener('input', (e) => { document.getElementById('tempo-value').textContent = e.target.value; if (this.currentProgression) { this.currentProgression.tempo = parseInt(e.target.value); } }); } loadProgression(progressionName) { this.currentProgression = chordProgressions[progressionName]; this.displayChords(); this.drawPianoRoll(); } playProgression() { if (!this.currentProgression || this.isPlaying) return; this.isPlaying = true; this.audioEngine.playSequence(this.currentProgression); // MIDI出力も同時送信 if (this.midiGenerator.isConnected) { this.sendMIDISequence(); } } stopProgression() { this.isPlaying = false; Tone.Transport.stop(); Tone.Transport.cancel(); } generateRandomProgression() { const randomProgression = this.chordGenerator.generateProgression(); this.currentProgression = randomProgression; this.displayChords(); this.drawPianoRoll(); } downloadMIDI() { if (!this.currentProgression) return; const midiData = this.midiExporter.generateMIDIFile(this.currentProgression); this.midiExporter.downloadMIDI(midiData); } displayChords() { const chordList = document.getElementById('chord-list'); chordList.innerHTML = ''; this.currentProgression.chords.forEach(chord => { const chordElement = document.createElement('div'); chordElement.className = 'chord-item'; chordElement.innerHTML = ` <span class="chord-name">${chord.name}</span> <span class="chord-roman">${chord.roman}</span> <span class="chord-notes">${chord.notes.join(', ')}</span> `; chordList.appendChild(chordElement); }); } drawPianoRoll() { // ピアノロール描画(簡略化) const canvas = document.getElementById('piano-roll'); const ctx = canvas.getContext('2d'); // キャンバスクリア ctx.clearRect(0, 0, canvas.width, canvas.height); // コード進行の可視化 // 実装は複雑になるため省略 } } // アプリケーション開始 const app = new MIDIGeneratorApp(); app.init(); |
応用と拡張機能
AI による進行分析
機械学習との連携
既存の楽曲データを機械学習で分析し、より自然なコード進行を生成することも可能です。TensorFlow.jsを使用して、ブラウザ上で学習済みモデルを実行し、リアルタイムでコード進行を予測・生成できます。
リアルタイム演奏対応
ライブ演奏機能の追加
- MIDI キーボード入力:リアルタイムでコード認識
- 自動伴奏生成:演奏に合わせてベースライン生成
- ハーモナイザー:メロディに対するコード提案
- ループ機能:作成した進行の繰り返し再生
ジャンル特化機能
スタイル別生成エンジン
- ジャズ進行:複雑なテンションコード、代理コード
- クラシック:機能和声に基づく厳密な進行
- ポップス:キャッチーで覚えやすい進行
- EDM:ループに適した構造
- アニソン:感情的な盛り上がりを重視
トラブルシューティングと最適化
パフォーマンス最適化
よくある問題と解決法
- 音声遅延:Web Audio APIのバッファサイズ調整
- CPU負荷:Web Workerでの並列処理
- メモリリーク:不要なオーディオノードの適切な破棄
- ブラウザ互換性:Polyfillとフォールバック実装
ブラウザ対応状況
対応ブラウザ
- Chrome:Web MIDI API、Web Audio API完全対応
- Firefox:Web Audio API対応、MIDI API部分対応
- Safari:制限付き対応、ユーザー操作が必要
- Edge:Chrome同等の対応
実践的な活用例
作曲支援ツールとしての活用
作曲ワークフローへの統合
- アイデア生成:ランダム進行からインスピレーション獲得
- ハーモニー検証:理論的に正しい進行の確認
- デモ作成:クライアントへのプレゼンテーション用
- 練習用:楽器練習のバッキングトラック生成
教育ツールとしての可能性
音楽教育での活用
視覚的なコード進行表示と音声再生により、音楽理論の学習効果が大幅に向上します。特に、ローマ数字記法と実際の音との関係を理解するのに非常に効果的です。
まとめ:音楽とテクノロジーの融合が切り開く未来
JavaScriptによるMIDI生成技術は、音楽制作の新たな可能性を切り開きます。
この技術で実現できること
- ✅ ブラウザ上での本格的な音楽生成
- ✅ リアルタイムでのコード進行作成
- ✅ 音楽理論の視覚化と体感
- ✅ クロスプラットフォーム対応
- ✅ 外部機器との連携
- ✅ データドリブンな作曲支援
Web技術の進歩により、従来は専門的なソフトウェアでしか実現できなかった音楽生成が、誰でもアクセス可能なWebアプリケーションとして提供できるようになりました。この技術は、音楽教育、作曲支援、ライブ演奏など、様々な分野での活用が期待されます。
次のステップ
この記事のサンプルコードを基に、ぜひ実際にMIDI生成アプリケーションを作成してみてください。Web MIDI APIとTone.jsの組み合わせにより、想像以上に豊かな音楽体験を創造できるはずです。また、機械学習ライブラリと組み合わせることで、より高度なAI作曲システムの構築も可能になります。
関連記事


