예제 소스
SGF 기보로 학습하는 바둑 LLM 인공지능 · 파일 169개
generate_stone_sound.py
#!/usr/bin/env python3
"""실제 바둑돌 소리 생성 - 나무와 돌이 부딪치는 소리.
사용법:
python scripts/generate_stone_sound.py
"""
import numpy as np
import subprocess
import sys
from pathlib import Path
def generate_wood_stone_click(duration_ms: int = 15, sample_rate: int = 44100) -> bytes:
"""바둑돌 소리 - 짧고 건조한 '딱' 클릭음."""
duration_s = duration_ms / 1000.0
samples = int(sample_rate * duration_s)
# 매우 짧은 임펄스 (충격음)
# 대역 제한된 노이즈 (200-800Hz) - 나무 + 돌의 충격
t = np.linspace(0, duration_s, samples, False)
# 짧은 버스트 노이즈
noise = np.random.normal(0, 0.7, samples)
# 밴드패스 필터 효과 (간단한 방법)
# 200Hz와 600Hz 혼합
tone1 = 0.3 * np.sin(2 * np.pi * 200 * t)
tone2 = 0.2 * np.sin(2 * np.pi * 600 * t)
sound = noise + tone1 + tone2
# 매우 급격한 감쇠 (공명 없는 건조한 소리)
envelope = np.exp(-30 * t / duration_s) # 극도로 빠른 감쇠
sound = sound * envelope
# 정규화
sound = sound / np.max(np.abs(sound))
# 16-bit PCM 변환
sound_int = (sound * 32767).astype(np.int16)
return sound_int.tobytes()
def create_stone_sounds():
"""흑돌/백돌/잡기 소리 생성."""
try:
import numpy as np
except ImportError:
print("numpy가 설치되지 않았습니다.")
print("설치: pip install numpy")
return False
sounds_dir = Path("sounds")
sounds_dir.mkdir(exist_ok=True)
# 흑돌 (낮은 톤)
print("🪨 흑돌 소리 생성 중...")
black_sound = generate_wood_stone_click(150, 44100)
# 백돌 (약간 높은 톤)
print("⚪ 백돌 소리 생성 중...")
# 주파수를 약간 높여서
white_sound = generate_wood_stone_click(140, 44100)
# 잡기 (더 크고 긴 소리)
print("💥 잡기 소리 생성 중...")
capture_sound = generate_wood_stone_click(250, 44100)
# WAV 파일 생성 (간단한 헤더 + 데이터)
def save_wav(filename: Path, data: bytes, sample_rate: int = 44100):
"""간단한 WAV 파일 저장."""
import struct
# WAV 헤더
channels = 1
bits_per_sample = 16
byte_rate = sample_rate * channels * bits_per_sample // 8
block_align = channels * bits_per_sample // 8
data_size = len(data)
with open(filename, 'wb') as f:
# RIFF 헤더
f.write(b'RIFF')
f.write(struct.pack('<I', 36 + data_size))
f.write(b'WAVE')
# fmt 청크
f.write(b'fmt ')
f.write(struct.pack('<I', 16)) # 청크 크기
f.write(struct.pack('<H', 1)) # PCM
f.write(struct.pack('<H', channels))
f.write(struct.pack('<I', sample_rate))
f.write(struct.pack('<I', byte_rate))
f.write(struct.pack('<H', block_align))
f.write(struct.pack('<H', bits_per_sample))
# data 청크
f.write(b'data')
f.write(struct.pack('<I', data_size))
f.write(data)
save_wav(sounds_dir / "stone_black.wav", black_sound)
save_wav(sounds_dir / "stone_white.wav", white_sound)
save_wav(sounds_dir / "stone_capture.wav", capture_sound)
print(f"\n✅ 소리 파일 생성 완료: {sounds_dir}/")
print(" - stone_black.wav")
print(" - stone_white.wav")
print(" - stone_capture.wav")
return True
def main() -> int:
if not create_stone_sounds():
return 1
# 테스트 재생
print("\n🔊 소리 테스트 중...")
sounds_dir = Path("sounds")
for sound_file in ["stone_black.wav", "stone_white.wav", "stone_capture.wav"]:
path = sounds_dir / sound_file
if path.exists():
print(f" 재생: {sound_file}")
subprocess.run(["afplay", str(path)], check=False)
return 0
if __name__ == "__main__":
sys.exit(main())