pyxen.Music
Entity에 컴포넌트로 첨부되는 스트리밍 오디오 트랙입니다.
Music은 주문형으로 디코딩됩니다. 배경 음악과 앰비언스 같은 긴 트랙에 적합합니다.
오디오 개념은 오디오를 참조하세요.
생성자
Music(filename, *, volume=1.0, pan=0.0, loop=False)
filename은 유일한 위치 인수로, 프로젝트에 있는 음악 에셋의 이름이며 확장자는 제외합니다. 나머지 세 개는 키워드 전용이며 같은 이름의 속성의 시작 값을 설정합니다(컴포넌트 속성 참고).
매개변수
| 매개변수 | 타입 | 설명 |
|---|---|---|
filename | str | 음악 에셋 이름, 확장자 제외 |
volume | float | 시작 게인. 기본값 1.0 |
pan | float | 시작 스테레오 위치(-1.0에서 1.0). 기본값 0.0 |
loop | bool | 트랙이 끝나면 다시 시작. 기본값 False |
예시
theme = Music("theme", volume=0.6, loop=True)
Music 첨부
생성 시:
bgm = world.spawn(music=Music("theme"))
또는 나중에:
entity.music = Music("theme")
음악을 할당하면 즉시 재생이 시작됩니다.
컴포넌트 속성
entity.music을 통해 접근:
| 속성 | 타입 | R/W | 설명 |
|---|---|---|---|
loop | bool | R/W | 반복 활성화 |
volume | float | R/W | 게인 (>= 0.0으로 클램프) |
pan | float | R/W | 스테레오 패닝 (-1.0에서 1.0) |
time | float | R | 현재 재생 위치 (초) |
duration | float | R | 전체 길이 (초) |
finished | bool | R | 재생이 완료되면 True |
channels | 리스트형 | R | 트랙의 각 파트. 파트마다 자체 volume이 있음 — 채널 섹션 참고 |
beat | int | R/W | 재생 시작 후의 박자 수. 값을 대입하면 해당 위치로 이동 — 음악적 시간 참고 |
bar | int | R/W | 재생 시작 후의 마디 수. 값을 대입하면 해당 위치로 이동 |
phase | float | R | 현재 박자 안에서의 진행 정도(0.0에서 1.0) |
beats_per_bar | int | R | 한 마디의 박자 수 |
frames_to_beat | int | R | 다음 박자까지 남은 프레임 수 |
tempo | float | R/W | 재생 속도 배율 — 템포 참고 |
bpm | int | R | 파일에 지정된 분당 박자 수 |
loop
entity.music.loop = True
활성화되면 트랙이 끝난 후 자동으로 다시 시작됩니다. 배경 음악에 일반적입니다.
volume
entity.music.volume = 0.5
0.0 미만의 값은 0.0으로 클램프됩니다.
pan
entity.music.pan = -1.0 # 완전히 왼쪽
entity.music.pan = 1.0 # 완전히 오른쪽
entity.music.pan = 0.0 # 가운데
-1.0에서 1.0 범위로 클램프됩니다.
time
t = entity.music.time
읽기 전용. 현재 재생 위치 (초).
duration
d = entity.music.duration
읽기 전용. 트랙의 전체 길이 (초).
finished
if entity.music.finished:
world.destroy(entity)
읽기 전용. 트랙 재생이 완료되면 (loop가 False일 때) True.
채널
music = entity.music
for i in range(len(music.channels)):
print(i, music.channels[i].volume)
music.channels[1].volume = 0.0 # 파트 1 음소거
트래커 모듈이나 MIDI 파일은 하나의 클록으로 연주되는 독립된 파트들의 집합입니다. music.channels는 파트마다 하나의 핸들을 제공하며, 각 핸들에는 다음이 있습니다.
| 속성 | 타입 | R/W | 설명 |
|---|---|---|---|
index | int | R | 채널 번호 |
volume | float | R/W | 이 파트만의 게인(0.0–1.0으로 클램프) |
모든 채널은 1.0에서 시작합니다. 모든 파트가 하나의 재생 헤드를 공유하므로, 하나의 볼륨을 올리거나 내려도 나머지와 어긋나지 않습니다. 이것이 사운드트랙의 강도를 바꾸는 방법입니다. 액션이 격해지면 파트를 넣고, 플레이어가 죽으면 뺍니다.
채널이 무엇인지는 형식에 따라 다릅니다.
| 형식 | 채널 |
|---|---|
| 트래커 모듈 | 모듈의 채널당 하나(일반적인 .mod는 4개) |
| MIDI | 항상 16개, MIDI 채널당 하나. 파일에서 사용하지 않는 채널은 그냥 무음 |
| OGG | 없음: len(music.channels)는 0 |
채널 번호는 0부터 시작합니다. 마지막을 넘어서는 번호를 요청하면 IndexError가 발생하며, music.channels[i] 자체에는 대입할 수 없고 volume에만 대입할 수 있습니다. 음수 인덱스는 파이썬 리스트처럼 끝에서부터 셉니다. music.channels[-1]은 마지막 채널입니다.
MIDI에서는 채널을 0.0으로 설정하면 그 채널에서 아직 울리고 있는 노트도 끊깁니다.
음악적 시간
if music.beat != last_beat:
last_beat = music.beat
pulse()
| 속성 | 의미 |
|---|---|
beat | 재생 시작 후의 박자 수(정수). 단조 증가합니다. 루프를 지나도 0으로 돌아가지 않고 계속 세므로, 되감김을 따로 처리하지 않고도 이전 프레임의 값과 비교할 수 있습니다 |
bar | 재생 시작 후의 마디 수(정수). 역시 단조 증가합니다 |
phase | 현재 박자 안에서의 위치. 0.0 이상 1.0 미만. beat가 단계적으로 바뀌는 반면 phase는 연속적입니다. 박자 사이에서 부드럽게 변하거나 감쇠해야 하는 것에 사용하세요 |
beats_per_bar | 한 마디의 박자 수 |
frames_to_beat | 다음 박자가 도착할 때까지의 프레임 수(올림). 박자 직후가 아니라 박자에 맞춰 무언가를 시작할 때 사용하세요 |
beat나 bar에 대입하면 해당 위치로 이동합니다. music.bar = 8은 한 번의 쓰기로 8마디의 시작으로 점프하며, 모든 채널은 동기화된 상태를 유지합니다. 다음 프레임에서 새 위치를 읽을 수 있습니다.
카운터는 파일에서 시간을 측정하므로 형식에 따라 달라집니다.
| 형식 | beat, bar, phase, frames_to_beat, beats_per_bar, bpm |
|---|---|
| 트래커 모듈 | 사용 가능. 아래 참고 |
| MIDI | 사용 가능, 파일에서 읽음. 아래 참조 |
| OGG | 사용 불가: beat, bar, beats_per_bar, frames_to_beat, bpm은 -1, phase는 0.0을 반환하며, beat나 bar에 대입해도 아무 일도 일어나지 않습니다 |
따라서 OGG일 수도 있는 음악에 맞춰 동기화하는 게임은 music.beat >= 0인지 한 번 확인하고, -1이면 자체 타이머로 대체해야 합니다.
MIDI 파일의 박자
MIDI 파일은 자신의 음악적 시간을 직접 명시하며, Pyxen은 이를 읽습니다. 한 박은 파일의 4분음표이고, bpm과 한 박의 길이는 재생 중 파일의 템포 변경을 따릅니다. beats_per_bar는 파일의 첫 번째 박자표에서 4분음표 단위로 계산합니다. 3/4는 3, 6/8도 3입니다. 박자표가 없는 파일은 4박자로 봅니다.
MIDI 트랙이 반복되면 카운트는 다음 마디의 시작으로 넘어갑니다. 그래서 beat와 bar는 계속 증가하고, 매 반복은 마디의 첫 박에서 시작합니다. 탐색은 정확히 그 박에 도착하며, 그 지점 이전부터 울리던 음표는 재생되지 않습니다.
트래커 모듈의 박자
모듈에는 박자표가 없으므로 Pyxen은 모듈이 가진 유일한 정의인 BPM을 사용합니다. 트래커의 한 틱은 2.5 / BPM초이며, 따라서 파일의 BPM은 한 박자가 24틱일 때, 즉 스피드 6에서 4행 또는 스피드 3에서 8행일 때 정확히 맞습니다. Pyxen은 이 방식으로 셉니다. 행이 아니라 틱으로 세므로 모듈이 스피드를 바꿔도 박자가 안정적입니다.
한 마디는 네 박자입니다. 이것은 데이터가 아니라 관례입니다. 3/4박자 모듈도 beats_per_bar는 4로 보고됩니다. 박자당 틱 수가 다르게 작성된 곡이라면 박자 카운터가 들리는 것과 맞지 않습니다.
템포
music.tempo = 1.5 # 50% 더 빠르게, 음높이는 그대로
music.tempo = 1.0 # 보통
tempo는 트랙이 요구하는 속도에 곱하는 배율입니다. 리샘플링하지 않으므로 음높이는 변하지 않고, 파트들이 더 빨리 또는 더 늦게 도착할 뿐입니다. tempo가 얼마이든 bpm은 파일 자체의 값을 계속 보고합니다.
제한: 최대 8.0입니다. 느린 쪽으로는 트래커 모듈이 0.25 아래로 내려가지 않으며, 거부된 값은 무시됩니다(속성은 이전 값을 유지합니다). MIDI는 0.05까지 허용합니다. OGG에서는 tempo가 효과가 없고 항상 1.0을 반환합니다.
beat, phase, frames_to_beat는 tempo를 따르므로, 음악에 동기화된 게임은 음악을 빠르게 해도 동기화가 유지됩니다.
실전 예시
박자마다 펄스
이 예시는 프로젝트에 theme.mod라는 트래커 모듈이 있다고 가정합니다.
from pyxen import *
last_beat = -1
def start():
world.spawn(name="song", music=Music("theme", loop=True))
world.spawn(name="pulse", x=160, y=90,
shape=Circle(24, fill=(1.0, 0.5, 0.2, 1.0)))
def update():
global last_beat
music = world.get(name="song").music
pulse = world.get(name="pulse")
# phase는 박자마다 0에서 1로 진행하므로, 박자에 크게 시작했다가 가라앉는다.
s = 1.0 + 0.5 * (1.0 - music.phase)
pulse.scale = (s, s)
# beat는 박자당 한 번만 바뀐다.
if music.beat != last_beat:
last_beat = music.beat
print("beat", last_beat, "bar", music.bar)
파트 음소거
클릭하면 트랙의 두 번째 파트를 끄고 켭니다.
from pyxen import *
def start():
world.spawn(name="song", music=Music("theme", loop=True))
def update():
music = world.get(name="song").music
if input.mouse.left.pressed:
part = music.channels[1]
if part.volume > 0.0:
part.volume = 0.0
else:
part.volume = 1.0
마디로 점프
from pyxen import *
def start():
world.spawn(name="song", music=Music("theme", loop=True))
def update():
music = world.get(name="song").music
if input.mouse.left.pressed:
music.bar = 4 # 4마디로 바로 이동
형식
음악 에셋은 OGG Vorbis(.ogg), MIDI(.mid, .midi) 또는 트래커 모듈 — ProTracker(.mod), FastTracker II(.xm), Impulse Tracker(.it), Scream Tracker 3(.s3m) — 일 수 있습니다.
에셋은 기본 이름으로 찾습니다. Music("theme")은 프로젝트에 존재하는 theme.ogg, theme.mod 등을 찾아냅니다. 확장자를 넘기는 것이 가장 흔한 실수입니다. Music("theme.ogg")는 theme.ogg.ogg를 찾다가 예외를 발생시킵니다.
트래커 모듈은 같은 곡을 OGG로 담으면 수 메가바이트인 것을 보통 수 킬로바이트로 담으며, 구조상 이음매 없이 반복됩니다 — 작은 게임에 잘 맞습니다.
트래커 모듈은 여기서 가장 많은 기능을 제공하는 형식이기도 합니다. 채널, 박자, 마디, 템포가 모두 모듈에서 동작합니다. MIDI에는 채널과 템포가 있고, OGG에는 둘 다 없습니다(채널과 음악적 시간 섹션 참고).
모듈은 패닝을 지원합니다. ProTracker의 8xx 패닝 설정 명령은 곡이 재생되는 동안 채널을 스테레오 필드에서 움직입니다.
Sound (WAV, 메모리에 전체 로드)와 달리, Music은 스트리밍되어 주문형으로 디코딩됩니다 — 긴 오디오에 더 적합합니다.
일반적인 에러
파일 이름 누락
Music()
ValueError: Music must have a file
잘못된 에셋
Music("nonexistent")
Music("theme.ogg") # 확장자는 파일에 붙이고 이름에는 넣지 않습니다
ValueError: Music 'nonexistent' not found. Use the asset name without its extension (.ogg, .mid, .mod, .xm, .it, .s3m).