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?

ファミコン実機っぽいかえるのうた

Posted at

!pip install mido numpy soundfile

import mido
import numpy as np
import soundfile as sf
from IPython.display import Audio, display

#---------------------------------------------
# パラメータ設定 / Parameters
#---------------------------------------------
sample_rate = 44100
amplitude = 0.35
duty_cycle = 0.25   # 矩形波デューティ比(ファミコンらしさ)
decay_time = 0.15   # 音の減衰時間(短く)
tempo_bpm = 100
ticks_per_beat = 480

#---------------------------------------------
# ① メロディ+休符 / Melody with spacing
#---------------------------------------------
# 「かえるのうたが〜」+ 0.5拍の間を追加
notes = [
    "C4","D4","E4","F4","E4","D4","C4",
    None,  # 間(休符)
    "E4","F4","G4","A4","G4","F4","E4","C4"
]
note_length = 480  # 1拍
rest_length = 240  # 0.5拍
note_map = {"C4":60,"D4":62,"E4":64,"F4":65,"G4":67,"A4":69,"B4":71,"C5":72}

#---------------------------------------------
# ② MIDIシーケンス生成 / Build simple sequence
#---------------------------------------------
mid = mido.MidiFile(ticks_per_beat=ticks_per_beat)
track = mido.MidiTrack()
mid.tracks.append(track)

tempo = mido.bpm2tempo(tempo_bpm)
track.append(mido.MetaMessage('set_tempo', tempo=tempo))

for n in notes:
    if n is None:
        # 休符
        track.append(mido.Message('note_off', note=60, velocity=0, time=rest_length))
    else:
        pitch = note_map[n]
        track.append(mido.Message('note_on', note=pitch, velocity=90, time=0))
        track.append(mido.Message('note_off', note=pitch, velocity=90, time=note_length))

#---------------------------------------------
# ③ MIDIを矩形波音声に変換 / Convert MIDI to 8bit square-wave audio
#---------------------------------------------
current_time = 0
active_notes = {}
events = []

for msg in mid.play():
    current_time += msg.time
    if msg.type == 'note_on' and msg.velocity > 0:
        active_notes[msg.note] = current_time
    elif msg.type in ['note_off', 'note_on'] and msg.note in active_notes:
        start = active_notes.pop(msg.note)
        duration = current_time - start
        events.append((msg.note, start, duration))

duration_total = max(s + d for _, s, d in events)
t = np.linspace(0, duration_total, int(sample_rate * duration_total), endpoint=False)
wave = np.zeros_like(t)

for pitch, start, dur in events:
    f = 440 * (2 ** ((pitch - 69) / 12))
    start_idx = int(start * sample_rate)
    end_idx = int((start + dur) * sample_rate)
    n = end_idx - start_idx
    phase = np.arange(n) * f / sample_rate
    sq = np.where((phase % 1) < duty_cycle, 1.0, -1.0)
    env = np.exp(-np.linspace(0, decay_time, n) * 5)
    tone = amplitude * sq * env
    wave[start_idx:end_idx] += tone

wave /= np.max(np.abs(wave))
sf.write("kaeru_famicom_spaced.wav", wave, sample_rate)

#---------------------------------------------
# ④ Colabで再生 / Play inside Colab
#---------------------------------------------
print("✅ ファミコン風『かえるのうた』(間あり・ピコピコ版)")
display(Audio("kaeru_famicom_spaced.wav", rate=sample_rate))

🎧 改良ポイント

要素 内容
休符追加 音と音の間に0.5拍(120ms程度)の「間」を挿入
減衰短縮 チップチューン特有のパッと消える音を再現
音量少し上げ 実機ファミコンのクリッピング気味サウンドに近づけた
矩形波25% NES矩形波チャンネル1・2の代表的デューティ比

実行すると、
「ポッ・ポッ・ポッ」と間のある、より本物のファミコン感ある“かえるのうた”が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?