■再生機能付き 完全版(あなたのコードに追加済み)
!pip install mido numpy scipy
import numpy as np
from mido import MidiFile
from scipy.io.wavfile import write
fs = 44100
# ==========================================
# Game Boy DMG Duty Table (Pulse)
# ==========================================
GB_DUTY = {
0: [0,1,0,0,0,0,0,0], # 12.5%
1: [0,1,1,0,0,0,0,0], # 25%
2: [0,1,1,1,1,0,0,0], # 50%
3: [1,0,0,1,1,1,1,1], # 75%
}
# ==========================================
# 4bit DAC (DMG)
# ==========================================
def dmg_dac_4bit(w):
q = np.floor((w + 1) * 7.5) / 7.5 - 1
return q
# ==========================================
# DMG Pulse Channel
# ==========================================
def gb_pulse(freq, dur, duty=2, amp=1.0):
n = int(fs * dur)
t = np.arange(n)
phase = ((t * freq) / fs * 8).astype(int) % 8
base = np.array(GB_DUTY[duty])[phase] * 2 - 1
wave = dmg_dac_4bit(base) * amp
return wave
# ==========================================
# MIDI load
# ==========================================
from google.colab import files
uploaded = files.upload()
midi_path = list(uploaded.keys())[0]
midi = MidiFile(midi_path)
tpb = midi.ticks_per_beat
tempo = 120 # default BPM
notes = []
note_on_time = {}
for track in midi.tracks:
t = 0
for msg in track:
t += msg.time
if msg.type == "set_tempo":
tempo = 60_000_000 / msg.tempo
if msg.type == "note_on" and msg.velocity > 0:
start = t / tpb * 60 / tempo
note_on_time[msg.note] = (start, msg.velocity)
if msg.type in ("note_off","note_on") and msg.velocity == 0:
if msg.note in note_on_time:
st, vel = note_on_time[msg.note]
et = t / tpb * 60 / tempo
dur = max(0.05, et - st)
freq = 440 * 2 ** ((msg.note - 69) / 12)
notes.append((st, freq, dur, vel))
del note_on_time[msg.note]
bass = [n for n in notes if n[1] < 220]
lead = [n for n in notes if n[1] >= 220]
# ==========================================
# Synthesis
# ==========================================
total_len = max(st + d for st, f, d, v in notes) + 1
out = np.zeros(int(fs * total_len))
def synth(part, duty):
w = np.zeros_like(out)
for st, f, d, v in part:
wave = gb_pulse(f, d, duty=duty, amp=(v/127))
a = int(st * fs)
b = a + len(wave)
w[a:b] += wave[:len(w)-a]
return w
pulse1 = synth(lead, duty=2)
pulse2 = synth(bass, duty=1)
out = pulse1 + pulse2
out /= np.max(np.abs(out) + 1e-9)
out *= 0.95
# ==========================================
# Save
# ==========================================
wav_name = "gb_dmg_midi.wav"
write(wav_name, fs, (out * 32767).astype(np.int16))
print("Saved:", wav_name)
# ==========================================
# ★ Google Colab 再生
# ==========================================
from IPython.display import Audio, display
display(Audio(wav_name, rate=fs))
これで MIDI をアップロードすると:
- 初代ゲームボーイの Pulse1/Pulse2 音源で自動再生し
- WAV 生成し
- その場で Colab で再生
できます。