회원과 곡 보관함 기능을 추가한다
회원 가입과 로그인 및 등급별 접근과 공개 범위를 구현했다.`nMIDI 업로드와 검색 및 같은 곡의 버전 묶음을 추가했다.`n비밀번호 변경과 회원 탈퇴 및 관리자 회원 관리를 포함했다.`n직접 만든 공개 샘플 MIDI 네 곡과 운영 문서를 추가했다.
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
OUTPUT = Path(__file__).resolve().parents[1] / "sample_midi"
|
||||
|
||||
|
||||
def variable_length(value: int) -> bytes:
|
||||
data = [value & 0x7F]
|
||||
value >>= 7
|
||||
while value:
|
||||
data.insert(0, (value & 0x7F) | 0x80)
|
||||
value >>= 7
|
||||
return bytes(data)
|
||||
|
||||
|
||||
def track(events: list[tuple[int, bytes]], tempo: int = 500_000, program: int = 0) -> bytes:
|
||||
body = bytearray()
|
||||
body.extend(b"\x00\xff\x51\x03" + tempo.to_bytes(3, "big"))
|
||||
body.extend(b"\x00\xff\x58\x04\x04\x02\x18\x08")
|
||||
body.extend(bytes([0, 0xC0, program]))
|
||||
for delta, message in events:
|
||||
body.extend(variable_length(delta))
|
||||
body.extend(message)
|
||||
body.extend(b"\x00\xff\x2f\x00")
|
||||
return b"MTrk" + len(body).to_bytes(4, "big") + body
|
||||
|
||||
|
||||
def midi_file(events: list[tuple[int, bytes]], tempo: int = 500_000) -> bytes:
|
||||
header = b"MThd" + (6).to_bytes(4, "big") + (0).to_bytes(2, "big") + (1).to_bytes(2, "big") + (480).to_bytes(2, "big")
|
||||
return header + track(events, tempo)
|
||||
|
||||
|
||||
def melody(notes: list[int], beats: int = 1, velocity: int = 82) -> list[tuple[int, bytes]]:
|
||||
result: list[tuple[int, bytes]] = []
|
||||
for note in notes:
|
||||
result.append((0, bytes([0x90, note, velocity])))
|
||||
result.append((480 * beats, bytes([0x80, note, 0])))
|
||||
return result
|
||||
|
||||
|
||||
def chord_progression(chords: list[list[int]], melody_notes: list[int]) -> list[tuple[int, bytes]]:
|
||||
result: list[tuple[int, bytes]] = []
|
||||
for index, note in enumerate(melody_notes):
|
||||
chord = chords[(index // 4) % len(chords)]
|
||||
for chord_note in chord:
|
||||
result.append((0, bytes([0x90, chord_note, 52])))
|
||||
result.append((0, bytes([0x90, note, 86])))
|
||||
result.append((480, bytes([0x80, note, 0])))
|
||||
for chord_note in chord:
|
||||
result.append((0, bytes([0x80, chord_note, 0])))
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
OUTPUT.mkdir(parents=True, exist_ok=True)
|
||||
first_snow = [64, 67, 69, 67, 64, 62, 60, 62, 64, 67, 72, 71, 69, 67, 64, 60]
|
||||
blue_breeze = [60, 62, 64, 67, 69, 67, 64, 62, 64, 65, 67, 72, 69, 67, 64, 60]
|
||||
morning_waltz = [64, 67, 72, 71, 67, 64, 62, 65, 69, 67, 64, 62, 60, 64, 67, 72]
|
||||
(OUTPUT / "first-snow-melody.mid").write_bytes(midi_file(melody(first_snow), 560_000))
|
||||
(OUTPUT / "first-snow-accompaniment.mid").write_bytes(
|
||||
midi_file(chord_progression([[48, 52, 55], [45, 48, 52], [41, 45, 48], [43, 47, 50]], first_snow), 560_000)
|
||||
)
|
||||
(OUTPUT / "blue-breeze-walk.mid").write_bytes(midi_file(melody(blue_breeze), 470_000))
|
||||
(OUTPUT / "little-morning-waltz.mid").write_bytes(
|
||||
midi_file(chord_progression([[48, 52, 55], [43, 47, 50], [45, 48, 52]], morning_waltz), 620_000)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user