11 คะแนน โดย GN⁺ 2024-05-01 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • สำหรับนักดนตรีที่ต้องซูมไฟล์ PDF ขนาด A4 เพื่อเล่นบนหน้าจอมือถือขนาดเล็ก จำเป็นต้องมีการเรนเดอร์โน้ตเพลงบนเว็บที่ลื่นไหลและตอบสนองได้

ต้นแบบ Scribe

  • ก่อนหน้านี้ได้สร้างต้นแบบตัวเรนเดอร์เพลงชื่อ Scribe ที่สร้าง SVG จาก JSON
  • เดิมทีเป้าหมายคือการสร้างตัวเรนเดอร์เพลงแบบ responsive แต่ความคืบหน้าเป็นไปได้ยากเพราะต้องเขียนเอนจินเลย์เอาต์แบบหลายพาสที่ซับซ้อน
  • ต่อมาเมื่อได้นำ 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 จังหวะ เป็นจุดเริ่มต้นที่ดี เพราะสามารถจัดวางโน้ตเขบ็ตหนึ่งชั้น สองชั้น สามชั้น และโน้ตสามพยางค์ได้อย่างสม่ำเสมอ
  • กำหนด 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 ก็จะได้โน้ตเพลงแบบ responsive
  • ยังมีอีกหลายอย่างที่ขาดอยู่ แต่ก็เป็นฐานเริ่มต้นที่ดี
  • การตัดบรรทัดทำได้อย่างสวยงามกว่าตัวเรนเดอร์เพลงออนไลน์ที่มีอยู่แล้วมาก

ระยะห่างระหว่างตัวโน้ต

  • หัวโน้ตที่เกิดขึ้นใกล้กันมากในเวลา จะถูกเรนเดอร์ให้อยู่ใกล้กันขึ้นเล็กน้อย
  • นี่เป็นผลลัพธ์ที่ละเอียดอ่อนและตั้งใจจาก column-gap ขนาดเล็ก ซึ่งทำหน้าที่เหมือน “อีเธอร์” ของเวลาให้สัญลักษณ์ต่าง ๆ เข้าไปอยู่ในช่อง
  • ตัวคอลัมน์เองจะมีความกว้างเป็น 0 ถ้าไม่มีหัวโน้ต แต่ระหว่างเหตุการณ์ที่อยู่ห่างกันมากกว่าในเชิงจังหวะ จะมีช่องว่างระหว่างคอลัมน์มากขึ้นด้วย (24 ช่องต่อจังหวะ) จึงเกิดระยะห่างมากขึ้น
  • สามารถควบคุมระยะห่างให้สม่ำเสมอได้ด้วยการปรับ margin ของสัญลักษณ์

กุญแจและเครื่องหมายกำหนดจังหวะ

  • เหตุผลที่ใช้คลาสแยกสำหรับระยะห่างแนวตั้งและแนวนอน ก็เพื่อให้สามารถสลับอย่างใดอย่างหนึ่งได้โดยไม่ไปกระทบอีกอย่าง
  • หากต้องการแสดงทำนองเดียวกันในกุญแจฟา ก็เพียงแทนคลาส .stave ด้วยคลาส bass-stave ที่แมปแอตทริบิวต์ data-pitch เดิมไปยังแถวของบรรทัดห้าเส้นแบบกุญแจฟา
  • หากแมป data-duration="5" ด้วย CSS ไปยัง 120 คอลัมน์กริดของ .bar ก็จะกำหนดเครื่องหมายกำหนดจังหวะ 5/4 ให้กับบรรทัดห้าเส้นเดียวกันได้

คอร์ดและเนื้อร้อง

  • การใช้ CSS Grid ทำให้สามารถจัดแนวสัญลักษณ์อื่น ๆ ภายในกริดของโน้ตเพลงได้ด้วย
  • คอร์ด เนื้อร้อง ไดนามิก ฯลฯ สามารถจัดแนวและขยายตามเหตุการณ์ที่มีการกำหนดเวลาได้

ก้านโน้ต

  • ก้านโน้ต คอร์ด และเครื่องหมายพักบางตัวที่ยาว สามารถทำให้กินหลายคอลัมน์ได้โดยแมปแอตทริบิวต์ data-duration ไปยังค่า grid-column-end แบบช่วง

การปรับขนาด

  • ทั้งระบบกำหนดขนาดด้วยหน่วย em ดังนั้นจึงปรับขนาดได้เพียงแค่เปลี่ยน font-size

ข้อจำกัดของ Flex และ Grid

  • มันไม่ใช่ระบบที่สมบูรณ์แบบ ข้อจำกัดคือ:
    1. CSS ไม่สามารถวางกุญแจหรือเครื่องหมายกำหนดคีย์ใหม่โดยอัตโนมัติเมื่อมีการขึ้นบรรทัดใหม่
    2. ไม่สามารถเชื่อม beam ไปยังตัวโน้ตใหม่ในบรรทัดถัดไปได้
    3. ก้านโน้ตแบบเอียงจะรู้ตำแหน่งที่แม่นยำได้ก็ต่อเมื่อ Grid จัดวางเสร็จแล้ว จึงจัดแนวได้ยาก
  • เพื่อให้เสร็จสมบูรณ์จริง ๆ ยังต้องใช้ JavaScript สำหรับเก็บรายละเอียดเล็กน้อย แต่เพราะ CSS จัดการงานเลย์เอาต์ไปเกือบทั้งหมดแล้ว งานเลย์เอาต์ที่ JavaScript ต้องทำจึงลดลงมาก

องค์ประกอบแบบกำหนดเอง

  • ได้เขียนอินเทอร์พรีเตอร์ขึ้นมาบนระบบ CSS ใหม่นี้ และห่อไว้ในองค์ประกอบ
  • แม้จะยังไม่พร้อมใช้งานระดับ production แต่สามารถเรนเดอร์ลีดชีตแบบ responsive และเขียนโน้ตกลองได้ จึงทั้งน่าสนใจและมีประโยชน์
  • เรนเดอร์โน้ตเพลงจากข้อมูลของคอนเทนต์ จากไฟล์ที่ดึงผ่านแอตทริบิวต์ src หรือจากอ็อบเจ็กต์ JS ที่ตั้งไว้ในพร็อพเพอร์ตี .data ขององค์ประกอบ
  • ขณะนี้สามารถนำ development build ไปใส่ในหน้าเว็บแล้วทดลองใช้งานได้

แผนต่อไป

  • นอกจากการปรับปรุงใน Scribe 0.3 แล้ว ฟีเจอร์ที่อยากศึกษาในระยะยาวมีดังนี้:
    • รองรับฟอนต์ SMuFL - เปลี่ยนฟอนต์ที่ใช้กับสัญลักษณ์โน้ตเพลง
    • รองรับลำดับแบบซ้อน - เปิดใช้งานเพลงหลายพาร์ต
    • เรนเดอร์บรรทัดห้าเส้นแบบแบ่ง - วางหลายพาร์ตในบรรทัดห้าเส้นเดียว
    • เรนเดอร์หลายบรรทัดห้าเส้น - วางหลายพาร์ตบนหลายบรรทัดห้าเส้นที่จัดแนวตรงกัน

ความเห็นของ GN⁺

  • การเรนเดอร์โน้ตเพลงบนเว็บให้ลื่นไหลและตอบสนองได้ น่าจะมีประโยชน์มากทั้งกับนักดนตรีและคนรักดนตรี เพราะช่วยลดความลำบากจากการต้องซูมเข้าออกไฟล์ PDF ของโน้ตเพลงบนหน้าจอเล็ก
  • แนวทางที่ใช้เลย์เอาต์ Grid และ Flex ของ CSS นั้นน่าสนใจ เป็นตัวอย่างที่ดีว่าการใช้ CSS อย่างเดียวก็แก้ปัญหาได้มากพอสมควรโดยไม่ต้องมีเอนจินเลย์เอาต์ที่ซับซ้อน
  • อย่างไรก็ตาม ด้วยธรรมชาติของโน้ตเพลง ก็ยังมีข้อจำกัดที่ CSS เพียงอย่างเดียวทำไม่ได้ เช่น การวางกุญแจหรือเครื่องหมายกำหนดคีย์อัตโนมัติเมื่อขึ้นบรรทัดใหม่ หรือการเชื่อม beam แบบอัตโนมัติ ซึ่งเป็นส่วนที่ต้องอาศัย JavaScript มาช่วยเข้าใจบริบททางดนตรี
  • มีการทำไปได้ไกลพอสมควรแล้ว ทั้งการเรนเดอร์ลีดชีตและการรองรับโน้ตกลอง จึงดูมีโอกาสที่จะพัฒนาไปจนใช้งานได้ดีในเวลาไม่นาน หากเปิดซอร์สและพัฒนาต่อเนื่อง ก็อาจเป็นทางเลือกที่ดีแทนโปรแกรมเขียนโน้ตเพลงเดิมอย่าง MuseScore ได้
  • หากฟีเจอร์ที่วางแผนไว้ เช่น การรองรับฟอนต์ SMuFL การรองรับหลายพาร์ต และการเรนเดอร์หลายบรรทัดห้าเส้น ถูกทำสำเร็จ ความสมบูรณ์ของการแสดงผลโน้ตเพลงก็น่าจะเพิ่มขึ้นอย่างมาก เป็นโปรเจกต์ที่น่าจับตา

2 ความคิดเห็น

 
roxie 2024-05-06

ก็คงต้องมีเหตุผลที่ทำแบบนี้อยู่แล้วไม่ใช่เหรอ

 
GN⁺ 2024-05-01
ความคิดเห็นบน Hacker News
  • มีคำชื่นชมจากนักพัฒนาซอฟต์แวร์โน้ตเพลงต่อวิธีเรนเดอร์โน้ตดนตรีด้วย CSS Grid
    • เขาพัฒนา Soundslice ซึ่งเป็นบริการเรนเดอร์โน้ตเพลงบนเว็บมานานกว่า 10 ปี และเป็นผู้ทำระบบเรนเดอร์โน้ตเพลงบนเว็บแบบ "responsive" เป็นครั้งแรกในปี 2014
    • รายละเอียดเชิงเทคนิคดูได้จากวิดีโอบรรยายที่ลิงก์ไว้: https://www.youtube.com/watch?v=XH5EtQge_Bg
    • ลิงก์ตัวอย่างโน้ตเพลงแบบ responsive ของ Soundslice: https://www.soundslice.com/slices/zzNlc/
    • มีเครื่องมือหลากหลาย เช่น ตัวแก้ไขบนเว็บ ฟีเจอร์สำหรับฝึกซ้อม และฟังก์ชันสแกนเพื่อดึงข้อมูลโน้ตเพลงจากภาพ/PDF
    • วิธีแบบ CSS Grid อาจมีประโยชน์กับโปรเจ็กต์ขนาดเล็ก แต่คงยากที่จะรองรับรายละเอียดซับซ้อนและความละเอียดอ่อนทั้งหมดของฟูลสกอร์ได้
  • น่าจะลองเสนอให้ชุมชน CSS พิจารณาด้วย เพื่อให้ทำสิ่งนี้ได้ด้วย CSS ล้วนโดยไม่ต้องใช้ JavaScript
    • ตัวอย่างเช่น การแสดงเครื่องหมายกุญแจซ้ำเมื่อขึ้นบรรทัดใหม่ คล้ายกับ sticky table header และน่าจะประยุกต์ใช้นอกเหนือจากโน้ตเพลงได้ด้วย
  • ประทับใจกับไวยากรณ์ของ CSS attribute selector([...]) เช่น .stave > [data-pitch^="A"][data-pitch$="5"] { grid-row-start: A5; }
  • ในมุมมองของนักจัดพิมพ์โน้ตเพลง ยังดูต้องปรับปรุงด้านภาพอีกมาก และคงทำได้ยากด้วย CSS อย่างเดียวเพราะมีข้อจำกัดด้านความแม่นยำ
    • มีปัญหาในการแสดงก้านโน้ต สเลอร์ และ ties
    • โน้ตเพลงบนเบราว์เซอร์ส่วนใหญ่มักใช้ SVG หรือ Canvas เพื่อเรนเดอร์เวกเตอร์ให้ได้ความแม่นยำระดับจุด
    • นอกจาก CSS แล้ว ยังมีเครื่องมืออื่นสำหรับทำโน้ตเพลงแบบสเกลได้ในเบราว์เซอร์อยู่แล้ว เช่น Soundslice และ Sibelius Cloud Publishing
  • ตอนแรกคิดว่าการแสดงโน้ตเพลงด้วย CSS คงไปไม่รอด แต่คุณภาพด้าน typography ที่ทำได้ด้วยวิธีเรียบง่ายนี้น่าประทับใจมาก ขอชื่นชมผู้เขียน
    • อย่างไรก็ดี ยังน่ากังวลว่ากรณีพิเศษอย่างคอร์ด ระยะห่างของโน้ตเขบ็ต 8/16 และการจัดแนวระหว่างพาร์ตจะทำงานได้ดีแค่ไหน โดย Lilypond ได้พิสูจน์ความยืดหยุ่นในงานซับซ้อนลักษณะนี้แล้ว
  • CSS Grid น่าสนใจมาก เคยใช้มันกับ pure frontend JS เพื่อทำเครื่องมือออกแบบเฟอร์นิเจอร์มาก่อน: https://alnvdl.github.io/2023/01/07/designing-furniture-using-the-css-grid.html
  • ค่อนข้างคาดหวังกับ custom element <scribe-music> ด้วย
    • เมื่อหลายปีก่อน มีอินเทิร์นเคยทำโปรเจ็กต์ห่อ VexFlow เป็น web component แต่ไม่ได้รับการดูแลต่อ: https://github.com/PolymerLabs/vexflow-elements/blob/web-components/demo/index.html
    • ถ้ามีไลบรารีที่ดูแลดีและใช้งานง่าย จะเป็นประโยชน์มากต่อวงการ web music notation
  • เป็นเรื่องดีที่มีทางเลือกนอกจาก Lilypond(lilypond.org) แต่เนื่องจากระบบโน้ตมีความซับซ้อนสูง ข้อดีด้านความกระชับอาจอยู่ได้ไม่นาน
    • ถ้าเป็นแฟน Asciidoc ก็น่าจะผนวก Lilypond เข้ากับ toolchain ของ Asciidoc ได้ง่าย ใช้อยู่ใน DocBook PDF pipeline และผลลัพธ์ก็ค่อนข้างดี คล้าย TeX
  • ทำให้นึกถึง https://www.musicxml.com และ https://opensheetmusicdisplay.org ซึ่งเป็นโซลูชันที่สมบูรณ์กว่าแต่มีต้นทุนสูงกว่ามาก
  • สงสัยว่าจะเอาสิ่งนี้มาแทนฟังก์ชันโน้ตเพลงที่ยังไม่ค่อยดีของ Impro-Visor(https://github.com/Impro-Visor/Impro-Visor) ได้ไหม
  • ให้อารมณ์เหมือน CSS benchmark