【ChatGPT×Python×Logic Pro】music21にAIが生成したJSONファイルを読み込ませ、MIDIファイルを生成
今回Pythonの音楽ライブラリmusic21を活用して、ChatGPTで音楽を作るプログラムを作ったので、そのログを残す。
まずはpipのインストール
pip3 install music21
pip3 install pretty_midi //今回は使用しないが、こんなのもある
リラックスできるゆっくりな音楽を、JSONファイルとして作成してください。
(以降はこのJSONファイルをそのままコピーして、フォーマットを維持してもらう)
ソースコードは記事の一番下に添付してあります。
{
"library": "music21",
"settings": {
"key": "C",
"tempo": 60
},
"repeat_count": 2,
"instruments": [
{
"name": "Piano",
"actions": [
{
"type": "add_note",
"pitch": "C3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "E3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "A3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "F3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "D3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "B2",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G2",
"quarterLength": 2.0
}
]
}
]
}
Pythonで実行するための環境ファイル設定
{
"library": "music21",
"file": "gpt.mid",
"mode": "g"
}
libraryは今後ライブラリを追加した時のために設定、fileはMIDIのファイル名。
modeをgにするとgenerateで新規作成、eはeditの編集。
最初にgで実行してみる。
VS Codeを利用。
MIDIファイルが生成された。
Logic Proで開いてみる。
音が追加されている。
{
"library": "music21",
"file": "gpt.mid",
"mode": "e"
}
次に肉付けを行う。
{
"library": "music21",
"settings": {
"key": "C",
"tempo": 60
},
"repeat_count": 2,
"instruments": [
{
"name": "Piano",
"actions": [
{
"type": "add_note",
"pitch": "C3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "E3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "A3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "F3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "D3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "B2",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G2",
"quarterLength": 2.0
}
]
},
{
"name": "Flute",
"actions": [
{
"type": "add_note",
"pitch": "C5",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "E5",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G5",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "A5",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "F5",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "D5",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "B4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G4",
"quarterLength": 2.0
}
]
},
{
"name": "Violin",
"actions": [
{
"type": "add_note",
"pitch": "C4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "E4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "A4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "F4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "D4",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "B3",
"quarterLength": 2.0
},
{
"type": "add_note",
"pitch": "G3",
"quarterLength": 2.0
}
]
}
]
}
Pythonを実行
編集ようのファイルが生成された。
ピアノ以外にもバイオリンや、フルートなども追加された。
https://www.youtube.com/shorts/T3KGYSaC7PY
現在作成段階だが、とりあえず「music21」の新規作成と修正だけは利用できるようになっている。
#main.py
import json
#import pretty_midi
from music21 import *
# JSONデータをロード
with open('type.json', 'r') as f:
type_data = json.load(f)
# ファイル名
file = type_data['file']
# 新しいMIDIファイルを生成する機能→基盤となる一つの楽器だけを設定
def generate_pretty_midi():
# MIDIファイルを読み込む
midi_data = pretty_midi.PrettyMIDI('gpt_based_magenta_output.mid')
# 楽器を設定
instrument_name = edit_data.get('instrument', 'Acoustic Grand Piano')
instrument_program = pretty_midi.instrument_name_to_program(instrument_name)
new_instrument = pretty_midi.Instrument(program=instrument_program)
for action in edit_data['actions']:
if action['type'] == 'add_note':
new_note = pretty_midi.Note(
velocity=action['velocity'],
pitch=action['pitch'],
start=action['start'],
end=action['end']
)
new_instrument.notes.append(new_note)
elif action['type'] == 'change_tempo':
# pretty_midiでのテンポ変更処理
new_tempo = action['new_tempo']
current_tempo = midi_data.estimate_tempo()
ratio = new_tempo / current_tempo
for tempo_change in midi_data.get_tempo_changes()[1]:
tempo_change *= ratio
for instrument in midi_data.instruments:
for note in instrument.notes:
note.start *= ratio
note.end *= ratio
# 新しい楽器トラックを追加
midi_data.instruments.append(new_instrument)
# MIDIファイルとして保存
midi_data.write(file)
def edit_pretty_midi():
# MIDIファイルを読み込む
midi_data = pretty_midi.PrettyMIDI('gpt_based_magenta_output.mid')
# 楽器を設定
instrument_name = edit_data.get('instrument', 'Acoustic Grand Piano')
instrument_program = pretty_midi.instrument_name_to_program(instrument_name)
new_instrument = pretty_midi.Instrument(program=instrument_program)
# 既存のノートを編集する(例:全てのノートのピッチを+1する)
for instrument in midi_data.instruments:
for note in instrument.notes:
note.pitch += 1
# 新しい楽器トラックを追加
midi_data.instruments.append(new_instrument)
# MIDIファイルとして保存
midi_data.write(file)
# 「ドレミファソラシド」で音楽を生成する機能
def generate_pretty_doremi():
# ここにドレミで音楽生成のコードを書く
pass
def generate_music21_midi(edit_data):
try:
# Create a new score
midi_stream = stream.Score()
# If a file exists, it would be parsed into midi_stream here
midi_stream = converter.parse(file)
except FileNotFoundError:
midi_stream = stream.Score()
# Setting the repeat_count
repeat_count = edit_data.get('repeat_count')
# Create a new score to hold the repeated sections
repeated_score = stream.Score()
for i in range(repeat_count):
for instr_data in edit_data.get('instruments', []):
instrument_name = instr_data.get('name', 'Piano')
new_instrument = instrument.fromString(instrument_name)
# Create a new part and add the instrument to it
new_part = stream.Part()
new_part.insert(0, new_instrument)
# Create a new measure and add a time signature to it
new_measure = stream.Measure(number=1)
new_measure.append(meter.TimeSignature('4/4'))
for action in instr_data.get('actions', []):
if action['type'] == 'add_note':
new_note = note.Note(action['pitch'])
new_note.quarterLength = action.get('quarterLength', 1.0)
new_measure.append(new_note)
elif action['type'] == 'change_tempo':
new_tempo = tempo.MetronomeMark(number=action['new_tempo'])
new_measure.insert(0, new_tempo)
# Append the measure to the part
new_part.append(new_measure)
# Append the part to the repeated_score
repeated_score.append(new_part)
# Append the repeated_score to the main score
midi_stream.append(repeated_score)
# Save as a MIDI file
midi_stream.write('midi', fp=file)
from collections import defaultdict
def edit_music21_midi(edit_data):
try:
midi_stream = converter.parse(file)
except FileNotFoundError:
print("MIDI file not found. Exiting.")
return
parts_by_instrument = defaultdict(lambda: stream.Part())
for instr_data in edit_data.get('instruments', []):
instrument_name = instr_data.get('name', 'Piano')
new_part = parts_by_instrument[instrument_name]
# Create a new measure and add a time signature to it
new_measure = stream.Measure(number=1)
new_measure.append(meter.TimeSignature('4/4'))
for action in instr_data.get('actions', []):
action_type = action.get('type')
if action_type == 'add_note':
pitch = action.get('pitch', 'C4')
new_note = note.Note(pitch)
new_note.quarterLength = action.get('duration', 1.0)
new_note.offset = action.get('start_time', 0.0)
new_measure.append(new_note)
elif action_type == 'add_rest':
new_rest = note.Rest()
new_rest.quarterLength = action.get('duration', 1.0)
new_rest.offset = action.get('start_time', 0.0)
new_measure.append(new_rest)
new_part.append(new_measure)
# Insert the instrument at the beginning of the part
new_instrument = instrument.fromString(instrument_name)
new_part.insert(0, new_instrument)
# This should only happen once, outside of the loop
for instrument_name, new_part in parts_by_instrument.items():
midi_stream.append(new_part)
# Write the MIDI file
midi_stream.write('midi', fp='edit_music21_' + file)
# 「ドレミファソラシド」で音楽を生成する機能
def generate_music21_doremi():
# ここにドレミで音楽生成のコードを書く
pass
# pretty_midiを使用する場合
if type_data['library'] == 'pretty_midi':
if type_data['mode'] == 'g':
# JSONデータをロード
with open('generate_pretty_midi.json', 'r') as f:
edit_data = json.load(f)
generate_pretty_midi()
elif type_data['mode'] == 'e':
# JSONデータをロード
with open('edit_pretty_midi.json', 'r') as f:
edit_data = json.load(f)
edit_pretty_midi()
elif type_data['mode'] == 'd':
generate_pretty_doremi()
else:
print("Invalid mode")
# music21を使用する場合
elif type_data['library'] == 'music21':
if type_data['mode'] == 'g':
# JSONデータをロード
with open('generate_music21_midi.json', 'r') as f:
edit_data = json.load(f)
generate_music21_midi(edit_data)
elif type_data['mode'] == 'e':
# JSONデータをロード
with open('edit_music21_midi.json', 'r') as f:
edit_data = json.load(f)
edit_music21_midi(edit_data)
elif type_data['mode'] == 'd':
generate_music21_doremi()
else:
print("Invalid mode")
else:
print("Invalid library in JSON")
今回は王道なJSONファイルを利用したが、他にも良い方法があったら教えてください。
今後の参考にします。
ここから先は
0字
/
1ファイル
この記事が参加している募集
メンバーシップ加入で、全ての記事が閲覧できます。