GN⁺: CSS Grid로 악보 출력 하기
(cruncher.ch)- 작은 모바일 화면에서 A4 PDF를 확대하며 연주하는 음악가들을 위해, 웹에서 유동적이고 반응형인 악보 렌더링이 필요함
Scribe 프로토타입
- 과거에 JSON에서 SVG를 출력하는 Scribe라는 음악 렌더러를 프로토타입으로 제작했음
- 원래 목표는 반응형 음악 렌더러를 제작하는 것이었으나 복잡한 다중 패스 레이아웃 엔진을 작성해야 해서 진전이 어려웠음
- 이후 CSS Grid를 프로젝트에 도입하면서 Scribe에서 다루던 레이아웃 문제의 해답이 될 수 있을 것 같았음
.stave 클래스
- 오선지는 그리드와 비슷함. 수직 축은 음높이, 수평 축은 시간임
- .stave 클래스에서는 수직 축인 그리드 행을 정의
- 표준 음높이 이름으로 고정 크기 그리드 행을 만들고 오선지를 그리는 배경 이미지를 사용
- 트레블 음자리 오선에 대한 행 맵 예시:
.stave { display: grid; row-gap: 0; grid-template-rows: [A5] 0.25em [G5] 0.25em [F5] 0.25em [E5] 0.25em [D5] 0.25em [C5] 0.25em [B4] 0.25em [A4] 0.25em [G4] 0.25em [F4] 0.25em [E4] 0.25em [D4] 0.25em [C4] 0.25em ; background-image: url('/path/to/stave.svg'); background-repeat: no-repeat; background-size: 100% 2.25em; background-position: 0 50%; }
- 각 오선과 간격에 음높이 이름의 그리드 선이 생김
오선에 음높이 배치
- 오선의 각 행에는 여러 음높이가 위치할 수 있음
- DOM 요소가 올바른 행에 위치하도록 하기 위해 data-pitch 속성에 음높이 이름을 넣고 CSS로 data-pitch 값을 오선 행에 매핑
.stave > [data-pitch^="G"][data-pitch$="4"] { grid-row-start: G4; }
- 이 규칙은 'G'로 시작하고 '4'로 끝나는 음높이를 캡처해서 'G♭4', 'G4', 'G♯4' 등을 G4 행에 할당
- 모든 오선 행에 대해 이를 수행해야 함
- 이제 몇 가지 기호를 오선에 배치할 수 있음
.bar 클래스와 박자
- 리듬을 처리하는 것은 좀 더 까다로움
- 모든 종류의 리듬을 지원하는 명확한 최소 리듬 분할이 없음
- 24열 당 1박자 접근법은 8분 음표, 16분 음표, 32분 음표 및 3연음표를 균등하게 배치할 수 있어 좋은 출발점임
- 4박자를 4 × 24 = 96 그리드 열로 정의하고 시작과 끝에 열을 추가:
.bar { column-gap: 0.03125em; grid-template-columns: [bar-begin] max-content repeat(96, minmax(max-content, auto)) max-content [bar-end]; }
- ::before와 ::after로 마디선을 추가하고 data-pitch="B4"로 음자리 기호를 중앙에 배치
박자에 기호 배치
- 이번에는 data-beat 속성을 사용하여 요소에 박자를 할당하고 CSS 규칙을 사용하여 박자를 그리드 열에 매핑
- CSS 맵은 1/24박자마다 하나의 규칙으로 구성
- 속성 ^= 시작 선택기를 사용하면 규칙이 오차 허용이 됨
- .stave 클래스와 함께 사용하면 data-beat를 1~5 사이의 박자로, data-pitch를 음 이름으로 설정하여 박자와 음높이별로 기호를 배치할 수 있음
유동적이고 반응형인 악보
- 이러한 여러 마디를 flexbox 컨테이너에 넣으면 반응형 악보를 볼 수 있음
- 아직 누락된 것들이 많지만 시작하기에 좋은 기반임
- 이미 온라인 음악 렌더러보다 훨씬 우아하게 줄 바꿈이 됨
음표 사이의 공간
- 서로 더 가까운 시간에 발생하는 음표 머리는 약간 더 가깝게 렌더링됨
- 작은 column-gap에 의해 만들어진 미묘하고 의도적인 효과로, 기호 요소가 슬롯에 들어가는 일종의 시간 '에테르' 역할을 함
- 열 자체는 음표 머리가 없으면 너비가 0이지만 박자가 더 멀리 떨어진 이벤트 사이에는 더 많은 열 간격(박자당 24개)이 있어 더 많은 거리가 생김
- 기호의 여백을 조정하여 일정한 간격을 제어할 수 있음
음자리와 박자 기호
- 수직 및 수평 간격에 대해 별도 클래스를 사용한 이유는 다른 것을 건드리지 않고 하나를 교체할 수 있기 때문
- 같은 멜로디를 베이스 음자리에 표시하려면 .stave 클래스를 같은 data-pitch 속성을 베이스 오선 행에 매핑하는 bass-stave 클래스로 교체하면 됨
- CSS로 data-duration="5"를 .bar의 120 그리드 템플릿 열에 매핑하면 같은 오선에 5/4 박자 기호를 줄 수 있음
코드와 가사
- CSS Grid를 사용하면 악보 그리드 내에서 다른 기호도 정렬할 수 있음
- 코드와 가사, 다이내믹 등을 시간이 지정된 이벤트와 정렬하고 확장할 수 있음
음표 꼬리
- 음표 꼬리, 코드, 일부 긴 쉼표는 data-duration 속성을 grid-column-end 범위 값에 매핑하여 열에 걸치게 만듦
크기 조정
- 전체 시스템은 em 단위로 크기가 지정되므로 font-size를 변경하는 것만으로 크기를 조정할 수 있음
Flex와 Grid의 한계
- 완벽한 시스템은 아님. 한계점:
- CSS는 줄 바꿈시 새 음자리/조표를 자동으로 배치할 수 없음
- 새 줄의 새 음표에 묶음줄을 연결할 수 없음
- 기울어진 음표 꼬리는 Grid가 배치한 후에야 정확한 위치를 알 수 있어 정렬이 어려움
- 완전히 마무리하려면 약간의 정리 JavaScript가 필요하지만 CSS가 레이아웃 작업의 대부분을 처리하므로 JavaScript에서 할 레이아웃 작업이 훨씬 줄어듦
사용자 정의 요소
- 이 새로운 CSS 시스템을 중심으로 인터프리터를 작성하고 요소에 래핑함
- 아직 프로덕션 준비는 안되었지만 반응형 리드 시트를 렌더링하고 드럼을 표기할 수 있어 흥미롭고 유용함
- 콘텐츠의 데이터, src 속성으로 가져온 파일, 요소의 .data 속성에 설정된 JS 객체에서 악보를 렌더링함
- 현재 개발 빌드는 웹 페이지에 파일을 가져와서 사용해 볼 수 있음
앞으로 계획
- Scribe 0.3의 개선사항 외에 장기적으로 조사하고 싶은 기능:
- SMuFL 글꼴 지원 - 악보 기호에 사용되는 글꼴 변경
- 중첩 시퀀스 지원 - 다중 파트 곡 활성화
- 분할 오선 렌더링 - 한 오선에 여러 파트 배치
- 다중 오선 렌더링 - 여러 개의 정렬된 오선에 여러 파트 배치
GN⁺의 의견
- 웹에서 악보를 유동적이고 반응형으로 렌더링하는 것은 음악가와 음악 애호가 모두에게 매우 유용할 것 같음. PDF 악보를 작은 화면에서 확대/축소하며 보는 불편함을 해소해 줄 수 있을 것
- CSS의 Grid와 Flex 레이아웃을 활용한 접근 방식이 흥미로움. 복잡한 레이아웃 엔진 없이도 CSS만으로 꽤 많은 부분을 해결할 수 있다는 것을 보여주는 좋은 예시
- 하지만 악보의 특성상 CSS만으로는 한계가 있는 부분들도 있음. 줄 바꿈 시 자동으로 음자리나 조표를 배치한다든지, 묶음줄을 자동 연결하는 것 같이 음악적 문맥을 이해해야 하는 부분은 자바스크립트의 도움이 필요할 것
- 리드 악보 렌더링과 드럼 악보 지원 등 이미 꽤 많은 부분을 구현했다고 하니, 조만간 충분히 쓸만한 수준으로 개선될 수 있을 것 같음. 오픈소스화 해서 개발이 지속된다면 MuseScore 같은 기존 악보 편집기의 좋은 대안이 될 수 있을 것
- 앞으로 계획하고 있는 SMuFL 글꼴 지원, 다중 파트 및 다중 오선 렌더링 지원 등의 기능이 구현된다면 악보 표현의 완성도가 크게 높아질 것 같음. 기대가 되는 프로젝트
Hacker News 의견
- Sheet music 소프트웨어 개발자로부터 CSS 그리드를 이용한 악보 렌더링 방식에 대해 찬사가 있음
- Soundslice라는 웹 기반 악보 렌더링 서비스를 10년 넘게 개발해 왔으며, 2014년에 최초로 "반응형" 웹 악보 렌더링을 구현했음
- 관련 기술 상세 내용은 발표 영상 링크 참고: https://www.youtube.com/watch?v=XH5EtQge_Bg
- Soundslice의 반응형 악보 예시 링크: https://www.soundslice.com/slices/zzNlc/
- 웹 기반 편집기, 연습 기능, 사진/PDF에서 악보 데이터 추출하는 스캔 기능 등 다양한 툴을 제공함
- CSS 그리드 방식은 가벼운 프로젝트에는 유용할 수 있지만, 풀 스코어의 복잡하고 미묘한 표현을 다 구현하기는 어려울 것임
- JavaScript 없이 CSS만으로 구현할 수 있도록 CSS 커뮤니티에 제안해보는 것도 좋을 듯함
- 예를 들어 줄바꿈 시 음자리표 반복 표시는 sticky table header와 유사한데, 악보 외에도 활용될 수 있음
- CSS의 attribute selector(
[...]
) 문법이 인상 깊었음. 예:.stave > [data-pitch^="A"][data-pitch$="5"] { grid-row-start: A5; }
- 음악 조판사 입장에서는 시각적으로 개선이 많이 필요해 보임. CSS만으로는 정밀도에 한계가 있어 어려울 듯함
- 음표 기둥, 활, 붙임줄 등의 표현에 문제가 있음
- 대부분의 브라우저 악보는 SVG나 Canvas로 벡터 렌더링하여 핀포인트 정밀도를 구현함
- CSS 외에 이미 Soundslice, Sibelius Cloud Publishing 등 브라우저에서 스케일러블한 악보를 구현하는 다른 도구들이 있음
- 처음에는 CSS로 악보를 표현하는 게 잘 안 될 것 같았는데, 간단한 방식으로 타이포그래피 품질이 인상적임. 작성자에게 찬사를 보냄
- 다만 화음, 8/16분음표 간격, 파트 간 정렬 등 특수한 경우 잘 작동할지 우려됨. Lilypond는 이런 복잡한 표현에서 유연성이 입증됨
- CSS 그리드가 흥미로움. 예전에 가구 디자이너를 pure frontend JS로 CSS 그리드 활용해 구현한 적 있음: https://alnvdl.github.io/2023/01/…
-
<scribe-music>
커스텀 엘리먼트도 기대됨- 몇 년 전 인턴이 VexFlow를 웹 컴포넌트로 래핑한 프로젝트 진행했었는데 유지보수 안 됨: https://github.com/PolymerLabs/vexflow-elements/…
- 잘 관리되고 사용하기 쉬운 라이브러리가 웹 음악 표기법에 큰 도움이 될 것임
- Lilypond(lilypond.org)의 대안이 나온 것은 좋지만, 표기법이 매우 복잡해서 간결성의 이점은 오래가지 않을 듯함
- Asciidoc 매니아라면 Lilypond를 Asciidoc 툴체인에 통합하기 쉬움. DocBook PDF 파이프라인에서 사용 중이며, 출력물이 꽤 괜찮음. TeX과 유사함
- https://www.musicxml.com과 https://opensheetmusicdisplay.org 를 상기시켜 줌. 훨씬 큰 비용이 들지만 완전한 솔루션임
- Impro-Visor(https://github.com/Impro-Visor/Impro-Visor)의 어설픈 악보 기능을 이것으로 대체할 수 있을지 궁금함
- CSS 벤치마크 같은 느낌