設計したプロンプト
このままコピー&ペーストして、ChatGPT(GPT-4o推奨)やGeminiに入力してください。
# 命令書:
あなたは「熟練したクラシック作曲家」かつ「優秀なPythonエンジニア」です。
以下の要件に基づき、美しいピアノソロ曲のMIDIデータを生成するための**実行可能なPythonコード**を作成してください。
# 目的:
Pythonライブラリ(`midiutil` または `mido` 推奨)を使用して、聴く人の心を動かすような「シンプルで美しいピアノ・インストゥルメンタル」のMIDIファイルを生成すること。
# 楽曲の要件:
1. **ジャンル**: クラシック / 現代音楽(久石譲や坂本龍一のような、ミニマルで叙情的なスタイル)
2. **楽器**: Acoustic Grand Piano (General MIDI Program #0)
3. **構成**: イントロ → Aメロ → Bメロ(サビ) → アウトロ の展開を持たせること。
4. **音楽理論の適用**:
* ランダムな音の生成は禁止します。
* 特定のキー(例: イ短調 A Minor または ト長調 G Major)を設定すること。
* 左手(伴奏)はアルペジオやコード進行(王道の進行:IV-V-iii-viなど)を使用すること。
* 右手(メロディ)は、スケールに基づいた自然なフレージングにすること。
* ベロシティ(音の強弱)に変化をつけ、人間味のある演奏をシミュレートすること。
# 技術的要件:
1. Google Colabやローカル環境でエラーなく動作するコードであること。
2. 必要なライブラリのインストールコマンド(`!pip install midiutil` など)を冒頭に含めること。
3. 生成されたMIDIファイル(output.mid)を保存する処理を含めること。
# 出力形式:
1. **Pythonコード**: コピーしてすぐに実行可能なコードブロック。
2. **楽曲解説**: コード内でどのような音楽理論(キー、コード進行、リズム)を使用したかの簡潔な説明。
# トーン:
クリエイティブ、論理的、専門的
# 必要なライブラリのインストール(Colab等で初回のみ必要)
# !pip install midiutil
import random
from midiutil import MIDIFile
class SentimentalComposer:
def __init__(self, filename="sentimental_piano.mid"):
self.filename = filename
self.midi = MIDIFile(1) # 1トラック
self.track = 0
self.time = 0 # 現在の小節位置
self.tempo = 76 # ゆったりとしたテンポ
# 楽器設定 (0: Acoustic Grand Piano)
self.midi.addTrackName(self.track, 0, "Grand Piano")
self.midi.addTempo(self.track, 0, self.tempo)
self.midi.addProgramChange(self.track, 0, 0, 0)
# 音楽理論設定: Key = A Minor (イ短調) / C Major (ハ長調)
# スケールノート (A Minor Natural)
self.scale_notes = [57, 59, 60, 62, 64, 65, 67, 69, 71, 72, 74, 76, 77]
# 57=A3, 60=C4, 69=A4...
# コード構成音 (Bass + Chord tones)
# 進行: FM7 -> G7 -> Em7 -> Am (王道進行 IV-V-iii-vi)
self.chord_progression = [
{"root": 41, "type": "F", "notes": [53, 57, 60, 64]}, # FM7 (F2ベース)
{"root": 43, "type": "G", "notes": [55, 59, 62, 65]}, # G7 (G2ベース)
{"root": 40, "type": "Em", "notes": [52, 55, 59, 62]}, # Em7 (E2ベース)
{"root": 45, "type": "Am", "notes": [57, 60, 64, 67]}, # Am7 (A2ベース)
]
def add_note_humanized(self, note, duration, velocity_base=60):
"""人間味のある演奏のためにベロシティとタイミングを微調整"""
# ベロシティのゆらぎ
vel = max(40, min(127, int(random.gauss(velocity_base, 5))))
# タイミングの微細なズレ (0.01拍〜0.03拍)
offset = random.uniform(0, 0.03)
self.midi.addNote(self.track, 0, note, self.time + offset, duration, vel)
def play_arpeggio(self, chord, duration=4):
"""左手: アルペジオ伴奏 (1小節分)"""
# パターン: Root -> 5th -> Octave -> 3rd/5th...
notes = chord["notes"]
# 8分音符で分散和音
pattern = [0, 1, 2, 3, 1, 2, 3, 1] # インデックス
step = duration / len(pattern)
current_local_time = self.time
for idx in pattern:
note = notes[idx % len(notes)]
# 左手は少し弱めに
self.add_note_humanized(note, step * 1.1, velocity_base=50)
self.time += step
self.time = current_local_time # 時間ポインタを戻さない(和音とメロディを並行処理するため呼び出し元で管理すべきだが、今回は簡易化)
def generate_section(self, section_name, num_bars, intensity="low"):
"""セクションごとの生成"""
print(f"Generating {section_name}...")
start_time = self.time
for i in range(num_bars):
# コード進行の選択 (王道進行をループ)
chord_idx = i % len(self.chord_progression)
chord = self.chord_progression[chord_idx]
# -------------------------------
# 左手: アルペジオ
# -------------------------------
# アルペジオ用の時間を進めるが、メロディ用に開始時間を保持しておく
bar_start = self.time
# アルペジオパターンの生成
notes = chord["notes"]
bass_note = chord["root"]
# ベース音 (全音符)
self.midi.addNote(self.track, 0, bass_note, bar_start, 4, 55)
# 分散和音 (8分音符 x 8)
for j in range(8):
# 音の選び方: 下から上へ、揺らぎを持たせる
if j < 4:
note_idx = j % len(notes)
else:
note_idx = (7 - j) % len(notes) # 折り返し
note_num = notes[note_idx]
self.add_note_humanized(note_num, 0.5, velocity_base=45 if intensity=="low" else 55)
self.time += 0.5
# -------------------------------
# 右手: メロディ (スケールから選択)
# -------------------------------
# 時間を小節の頭に戻してメロディを重ねる
current_melody_time = bar_start
remaining_duration = 4.0
while remaining_duration > 0:
# 音価の決定 (intensityによって変化)
if intensity == "low":
durations = [1.0, 2.0] # ゆったり
vel_base = 65
octave_offset = 0
else: # high (サビ)
durations = [0.5, 1.0, 1.5] # 動きがある
vel_base = 80
octave_offset = 12 # 1オクターブ上げる
dur = random.choice(durations)
if dur > remaining_duration:
dur = remaining_duration
# 音高の決定 (前の音に近い音を選びやすくする「ランダムウォーク」)
# コードトーンを優先すると綺麗に響く
is_chord_tone = random.random() > 0.3
if is_chord_tone:
# その瞬間のコードに合う音
candidates = [n + octave_offset for n in chord["notes"]]
else:
# スケール内の音(経過音)
candidates = [n + octave_offset for n in self.scale_notes if 60 <= n + octave_offset <= 84]
if not candidates: candidates = [69] # フォールバック(A4)
note = random.choice(candidates)
# 休符を入れる確率 (サビは少なく、Aメロは適度に)
rest_prob = 0.1 if intensity == "high" else 0.2
if random.random() > rest_prob:
self.add_note_humanized(note, dur, velocity_base=vel_base)
current_melody_time += dur
remaining_duration -= dur
# 次の小節へ
self.time = bar_start + 4
def compose(self):
# 1. Intro (4小節): 静かで、ルート音中心
self.generate_section("Intro", 4, intensity="low")
# 2. Aメロ (8小節): 物語の始まり、中音域
self.generate_section("Verse (A)", 8, intensity="low")
# 3. Bメロ/サビ (8小節): 感情の高まり、高音域、強め
self.generate_section("Chorus (B)", 8, intensity="high")
# 4. Outro (4小節): フェードアウト、主音(Am)で終わる
print("Generating Outro...")
# 最後はAmのアルペジオで終わる
chord = self.chord_progression[3] # Am
self.midi.addNote(self.track, 0, 45, self.time, 4, 50) # Bass A
self.midi.addNote(self.track, 0, 57, self.time, 4, 45) # A
self.midi.addNote(self.track, 0, 60, self.time+0.5, 3.5, 45) # C
self.midi.addNote(self.track, 0, 64, self.time+1.0, 3.0, 45) # E
self.midi.addNote(self.track, 0, 69, self.time+1.5, 2.5, 40) # A (High)
# ファイル保存
with open(self.filename, "wb") as output_file:
self.midi.writeFile(output_file)
print(f"Done! Saved to {self.filename}")
# 実行部
if __name__ == "__main__":
composer = SentimentalComposer()
composer.compose()
# Google Colabの場合、自動ダウンロードさせるコード
try:
from google.colab import files
files.download('sentimental_piano.mid')
except ImportError:
print("Local environment detected. File is saved in the current directory.")