0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

初代ゲームボーイのMIDI

Posted at

■再生機能付き 完全版(あなたのコードに追加済み)

!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 で再生

できます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?