3 คะแนน โดย GN⁺ 2024-08-11 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • มีการแนะนำ Sonic Pi ในฐานะสภาพแวดล้อมที่สามารถสร้างดนตรีได้แม้ไม่มีเครื่องดนตรีหรืออุปกรณ์ โดยใช้ DSL ที่อิงกับ Ruby เพื่อเขียนโน้ต คอร์ด สเกล และจังหวะเวลาได้โดยตรง
  • หากใช้ฟังก์ชัน อาร์เรย์ เงื่อนไข การวนซ้ำ และสโคปของ Ruby ก็สามารถนำ play, sleep, play_pattern_timed, chord, scale มาประกอบกันเพื่อสร้างแพตเทิร์นซ้ำและอาร์เพจจิโอได้อย่างกระชับ
  • เมื่อผสาน in_thread, ring, flatten, ตัวดำเนินการ splat และ with_fx ก็สามารถรัน “เครื่องดนตรี” หลายชิ้นพร้อมกัน และเลเยอร์เสียงซ้อนกับจังหวะที่ต่างกันได้
  • การปรับ attack, decay, sustain, release ของ ADSR envelope เน้นที่การปรับระยะคงเสียงและความรู้สึกแยกจากกันของโน้ต มากกว่าการจำลองคอรัสจริงให้สมจริง
  • live_loop สามารถรันซ้ำพร้อมอัปเดตโค้ดที่กำลังเล่นอยู่ได้ ทำให้ Sonic Pi เป็นเครื่องมือสำหรับการแสดงสด และทำให้มองโค้ดได้ทั้งในฐานะข้อความที่อ่านได้และการกระทำเชิงสร้างสรรค์

Sonic Pi ที่ควบคุมด้วยโค้ด

  • Sonic Pi แตกต่างจากแอปเสียงทั่วไป เพราะเป็นซอฟต์แวร์ซินธิไซเซอร์ที่ควบคุมด้วยโค้ดเท่านั้น
  • ภาษาควบคุมของมันคือ ภาษาจำเพาะโดเมน (DSL) ที่ขยายจาก Ruby และแมปแนวคิดด้านดนตรีและเสียงเข้ากับไวยากรณ์ของ Ruby
  • องค์ประกอบพื้นฐานประกอบด้วยค่าของ Ruby และฟังก์ชันของ Sonic Pi
    • ชื่อโน้ตอย่าง :c4 ถูกใช้เป็นสัญลักษณ์ที่สอดคล้องกับรหัส MIDI นั้น
    • สามารถสร้างคอร์ดได้โดยส่งโน้ตรากและโครงสร้างคอร์ดเป็นอาร์กิวเมนต์ เช่น chord(:d3, :maj7)
    • play ใช้เล่นโน้ต และควบคุมช่วงเวลาด้วย sleep ของ Ruby
    • play_pattern_timed รับรายการโน้ตและช่วงเวลา เพื่อย่อโค้ดเล่นซ้ำให้สั้นลง
  • เป้าหมายของตัวอย่างคือการสร้างแบ็กกิงแทร็กสำหรับเดโมด้วยโค้ด โดยใช้เพียงความสามารถพื้นฐานของ Sonic Pi โดยไม่พึ่ง MIDI ภายนอกหรือซัมเปิล
  • เมื่อติดตั้งแอป Sonic Pi แล้ว ก็สามารถวางโค้ดตัวอย่างและลองทำตามได้ และยังมีเอกสารภาษาและบทสอนมาให้ในตัว

สร้างแพตเทิร์นซ้ำด้วยไวยากรณ์ของ Ruby

  • ตัวอย่างพื้นฐานเปลี่ยน BPM ด้วย use_bpm 70 และใช้ use_synth :pulse เพื่อเลือกซินธ์แบบ pulseแทนคลื่นไซน์เริ่มต้น
  • การทำ play :c2, sleep 0.25 ซ้ำจะเล่นโน้ตตามลำดับได้ และสามารถเขียนโครงสร้างเดียวกันให้สั้นลงด้วย play_pattern_timed [ :c3, :d3, :e3, :g3 ], 0.25
  • เพราะภาษาควบคุมสร้างอยู่บน Ruby จึงสามารถนามธรรมแพตเทิร์นด้วยฟังก์ชันและการวนซ้ำได้
    • ฟังก์ชัน arpeggiate รับโน้ตรากและตัวเลือกว่าจะเป็นไมเนอร์หรือไม่ เพื่อสร้างแพตเทิร์นไล่เสียงขึ้นและลง
    • การวนซ้ำ 4.times ใช้รันแพตเทิร์นเดียวกันข้ามหลายอ็อกเทฟ
    • ใช้เงื่อนไขเพื่อเปลี่ยนโน้ตตัวที่ 3 เมื่อเป็นไมเนอร์ และใช้ map ของอาร์เรย์เพื่อสร้างรายการโน้ตโดยบวกช่วงเสียงเข้ากับโน้ตราก
  • สามารถตั้งค่าเริ่มต้นของซินธ์ล่วงหน้าได้ด้วย use_synth_defaults amp: 0.1 และก็ส่งออปชันให้ play หรือ play_pattern_timed โดยตรงได้เช่นกัน
  • ใน Ruby วงเล็บของการเรียกฟังก์ชันเป็นทางเลือก แต่ในกรณีอย่าง play chord(:a3, maj7) อาจต้องเขียนให้ชัดเพื่อแยกขอบเขตให้เข้าใจง่าย

การทำงานขนานและการเลเยอร์เสียง

  • บล็อก in_thread do ใช้ใน Sonic Pi เพื่อรันโค้ดแบบขนานกัน
  • หากมี in_thread คนละบล็อก ก็สามารถรัน “เครื่องดนตรี” แยกกันสองชิ้นพร้อมกันได้
    • เธรดหนึ่งอาจรับหน้าที่เล่นแพตเทิร์นอาร์เพจจิโอ
    • อีกเธรดหนึ่งอาจใช้ use_synth :saw ร่วมกับอาร์เรย์เมโลดี้และอาร์เรย์จังหวะเพื่อเล่นทำนองอีกเส้น
  • play_pattern_timed ไม่ได้รับแค่รายการโน้ต แต่รับรายการช่วงเวลาได้ด้วย
    • [1] * 2 จะขยายอาร์เรย์และทำซ้ำค่าด้านในให้กลายเป็น [1, 1]
    • อาร์เรย์ที่ซ้อนกันไม่สามารถส่งให้ play_pattern_timed ตรง ๆ ได้ จึงต้องใช้ flatten เพื่อคลี่ให้เป็นอาร์เรย์ชั้นเดียว
  • หากต้องการสร้างเสียงซ้อนแบบคอรัส ก็สามารถรวมหลายโน้ตไว้ด้วยกัน เช่น ring(:c5, :e5)
  • แต่ flatten อาจทำให้รายการภายใน ring ถูกคลี่รวมไปด้วย ดังนั้นในกรณีนี้จึงใช้ตัวดำเนินการ splat * เพื่อกระจายรายการลงในอาร์เรย์แม่แทน

สโคป เอฟเฟกต์ และการจัดส่วนคอรัส

  • หากใช้ in_thread แบบซ้อนกัน ก็สามารถแชร์ชิ้นส่วนจังหวะจากสโคปภายนอกได้ ขณะเดียวกันแต่ละเธรดด้านในก็ยังใช้ชื่อตัวแปรเดิมซ้ำได้
  • เมื่อแยกพาร์ตอย่างโซปราโนและอัลโตออกเป็นคนละเธรด แต่ละบล็อกจะมีสโคปของตัวเอง จึงไม่เข้าถึงตัวแปรของอีกฝั่งโดยตรง
  • with_fx :reverb, mix: 0.2 ใช้สร้างบล็อกเอฟเฟกต์ และการไหลของสัญญาณจะเริ่มจากเลเยอร์ด้านในสุดแล้วเคลื่อนไปตามเชนเอฟเฟกต์ด้านนอก
  • mix หมายถึงสัดส่วนการผสมระหว่างสัญญาณ wet และ dry
    • สัญญาณ wet คือสัญญาณที่ผ่านเอฟเฟกต์แล้ว
    • สัญญาณ dry คือสัญญาณที่ยังไม่ผ่านเอฟเฟกต์
  • การครอบทั้งกลุ่มด้วยเอฟเฟกต์เดียวสามารถใช้งานคล้ายบัสใน DAW ได้ แต่การทำ routing ที่ลึกกว่านั้นต้องจัดการเองมากกว่า และเพราะข้อจำกัดด้านสโคป จึงมักต้องประกอบจากบนลงล่าง
  • Sonic Pi จึงดูเป็นธรรมชาติกว่าเมื่อใช้งานมันเหมือนเครื่องดนตรี มากกว่าจะเป็นเครื่องมือโปรดักชันแบบละเอียด

ปรับความยาวโน้ตด้วย ADSR envelope

  • play_pattern_timed สามารถเพิ่มค่า sustain ได้ จึงปรับระยะคงเสียงของคอร์ดยาวและคอร์ดสั้นให้ตรงกันได้โดยตรง
  • attack, decay, sustain, release คือ ADSR envelope ที่อธิบายว่าความดังของเสียงเปลี่ยนไปตามเวลาอย่างไร
    • attack คือเวลาที่ใช้จาก 0 ไปถึงยอดแรก เปรียบได้กับแรงปะทะตอนดีดสายกีตาร์หรือค้อนเปียโนกระทบสาย
    • decay คือช่วงเวลาที่ลดลงจากยอดแรก ซึ่งคล้ายกับเสียงที่ค่อย ๆ เบาลงแม้ยังค้างคีย์เปียโนหรือกดสายกีตาร์ไว้
    • sustain คือเวลาที่คงเสียงไว้ที่ระดับความดังเสถียร คล้ายเสียงไวโอลินที่สีค้างไว้หรือเสียงร้องที่ลากยาว
    • release คือเวลาหลังจากปล่อยเสียงแล้วจนกว่าจะเงียบจริง
  • ไม่ใช่ทุกเสียงที่จะมีองค์ประกอบ ADSR ครบทั้งหมด
    • โทนจากซินธิไซเซอร์เป็นเสียงที่ถูกสร้างขึ้น จึงไม่จำเป็นต้องมีช่วงก่อตัวแรกหรือเสียงค้างเสมอไป
    • เครื่องดนตรีธรรมชาติจะมีสัดส่วนขององค์ประกอบเหล่านี้ต่างกันตามวิธีเล่น
  • การจำลองคอรัสในตัวอย่างนี้ไม่ได้มุ่งความสมจริงเป็นหลัก แต่เน้นให้โน้ตคงอยู่ตามเวลาที่ต้องการอย่างแม่นยำ และมีช่วงแยกระหว่างคอร์ดเล็กน้อย
  • ฟังก์ชัน choral_rings notes, sus รับรายการโน้ตและเวลาคงเสียง จากนั้นเล่นพร้อมกันด้วย ring(*notes) คำนวณ sustain และ release แล้วจึง sleep sus เพื่อรอถึงอีเวนต์ถัดไป

ไลฟ์โค้ดดิ้งด้วย live_loop

  • เมื่อใช้ live_loop ก็สามารถรันโค้ดซ้ำไปได้ไม่สิ้นสุด
  • Sonic Pi เป็นเครื่องมือที่คำนึงถึงการแสดงสดอย่างมาก และโค้ดภายใน live_loop จะทำงานต่อเนื่องจนกว่าจะหยุดโปรแกรม
  • แม้ระหว่างเล่นอยู่ก็ยังแก้เนื้อหาของลูปได้ และเมื่อสั่งเริ่มใหม่อีกครั้ง ลูปที่อัปเดตแล้วจะถูกนำไปใช้ตั้งแต่รอบถัดไป
  • ลูป in_thread ด้านนอกแบบเดิมสามารถเปลี่ยนเป็นรูปแบบ live_loop :some_unique_name ได้
  • เพราะเชนเอฟเฟกต์จะถูกสร้างใหม่ทุกครั้งที่ลูปหมุน การวางเอฟเฟกต์ไว้ด้านนอก live_loop จึงประหยัดทรัพยากรมากกว่าเมื่อมีการซ้อนเอฟเฟกต์หลายชั้น
  • ตัวอย่างสุดท้ายประกอบด้วย live_loop :harp และ live_loop :choir
    • ลูป :harp ใช้ซินธ์ :square และแพตเทิร์นอาร์เพจจิโอที่มีความยาว 0.25
    • ลูป :choir ใช้ซินธ์ :saw, sleep 64, with_fx :reverb, mix: 0.75 และการเรียกฟังก์ชัน choral_rings เพื่อเล่นคอร์ดคอรัส

มองการเขียนโปรแกรมเป็นการสร้างสรรค์

  • งานเทคนิคมักถูกพูดถึงจากมุมมองของวิทยาการคอมพิวเตอร์ แต่การเขียนโปรแกรมก็สามารถเป็นศิลปะได้
  • มันไม่ได้เป็นศิลปะเฉพาะตอนที่สร้างผลลัพธ์เชิงสร้างสรรค์อย่างจงใจ เช่น การสร้างเสียงหรือจัดเลย์เอาต์ CSS เท่านั้น
  • โค้ดไม่ได้เป็นเพียงคำสั่งที่เหมาะกับเครื่องจักร แต่ยังเป็นข้อความที่มีการจัดระเบียบซึ่งผู้อื่นและตัวเราในอนาคตต้องอ่าน
  • โค้ดที่ดีคือโค้ดที่เขียนให้คนอื่น หรือแม้แต่ตัวเราเองในอีก 6 เดือนข้างหน้า เข้าใจได้ทันทีเมื่อมอง
  • บางครั้งความเป็นศิลปะของโค้ดก็เป็นเป้าหมายในตัวเอง และตัวอย่างของ Sonic Pi ก็แสดงจุดนั้นผ่านการสร้างดนตรี

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

 
GN⁺ 2024-08-11
ความคิดเห็นจาก Hacker News
  • ถ้าสนใจไลฟ์โค้ดดิ้ง Glicol(https://glicol.org) ก็น่าลองใช้เหมือนกัน
    ยังมี TidalCycles ด้วย และความสามารถของ Haskell กับ Ruby ในการสร้าง DSL นั้นน่าชื่นชมจริง ๆ
    Glicol เน้นไวยากรณ์ที่ได้แรงบันดาลใจจากซินธิไซเซอร์ ความเร็วในการแต่งเพลง และความสะดวกในการออกแบบเสียง แนวคิดคือการออกแบบ DSL ที่นำความเคยชินจากการเขียนโปรแกรมเดิม ๆ มาใช้ได้ แต่ไม่ถูกผูกติดอยู่กับภาษาใดภาษาหนึ่ง

    • ยังมี strudel ซึ่งเป็นพอร์ต JavaScript ของ TidalCycles ด้วย ค่อนข้างสนุกทีเดียว
      https://github.com/tidalcycles/strudel
  • เมื่อหลายปีก่อนเคยเห็นวิดีโอบน YouTube ที่มีคนใช้ Sonic Pi สร้าง THX Deep Note ขึ้นมาใหม่
    ตอนแรกเขาไม่ได้อธิบายว่าจะทำอะไร แต่เริ่มจากสร้าง “โน้ต” เสียงเดี่ยว ๆ แล้วเรียกมันว่าผึ้ง จากนั้นก็ค่อย ๆ ปรับเสียงผึ้งนั้นและเปิดให้ฟัง แล้วตอนท้ายก็ประมาณว่า “ลองสร้างผึ้งแบบนี้สักหลายร้อยตัว แล้วดูว่ามันจะฟังเป็นอย่างไรในรังผึ้ง” พอถึงจังหวะนั้น Deep Note ก็ปรากฏขึ้นมา
    วิดีโอนั้นดีมากจริง ๆ แต่หลังจากนั้นก็หาไม่เจออีกเลย ถ้าใครรู้ลิงก์ช่วยแชร์ให้หน่อยก็ดี

  • ผมมองว่านี่เป็นหนึ่งในกรณีที่ Ruby เปล่งประกายได้ดี ด้วย เมตาโปรแกรมมิง ทำให้ไวยากรณ์ดูแทบจะเหมือนภาษาอังกฤษได้

    • ใช่ แต่ Sonic Pi DSL ให้ความรู้สึกใกล้ Lisp นิด ๆ มากกว่าภาษาธรรมชาติ
    • ถูกแล้ว นั่นคือเมตาโปรแกรมมิง
  • เมื่อหลายปีก่อนเคยเห็น Sam Aaron หรือก็คือ ผู้สร้าง Sonic Pi แสดงสดด้วย Sonic Pi
    ทุกครั้งที่โค้ดเปลี่ยนแบบเรียลไทม์ ดนตรีก็พัฒนาไปเรื่อย ๆ และมันดีจริง ๆ

    • ผมก็เคยเจอ Sam เหมือนกัน ราวปี 2008~2010 หลังจากเขาพูดที่ Clojure Conj แล้วได้ไปกินมื้อเย็นด้วยกัน ตอนนั้นเขาก็เป็นคนดีมากจริง ๆ และการนำเสนอก็ยอดเยี่ยม
  • ถ้าอยากดูตัวอย่างศิลปินที่ใช้ Sonic Pi ก่อนหน้านี้ผมรู้จัก DJ_Dave ซึ่งถือเป็นตัวอย่างที่ดีของนักดนตรีที่มีพรสวรรค์และชำนาญซึ่งใช้เครื่องมือนี้ได้ดี
    ก่อนหน้านี้เธอเคยทำ AMA บน Reddit ด้วย และดูเหมือนว่าจะมีผลงานอยู่บน GitHub ด้วย
    [1] https://www.youtube.com/watch?v=JiQHclg_648
    [2] https://www.reddit.com/r/audius/comments/19dxmy6/hey_audius_...
    [3] https://github.com/algorave-dave

  • ในฐานะนักพัฒนา Ruby มันดูเท่มาก น่าจะสนุกถ้าเอาไปใช้กับ ดนตรีเชิงอัลกอริทึม และผมเคยอยากลองเปลี่ยนอีเวนต์ใน git repository ที่มีเสียงรบกวนเยอะ ๆ ให้กลายเป็นดนตรีอัลกอริทึมบางแบบ ซึ่งดูเหมือนจะเหมาะกับสิ่งนี้
    แต่ในฐานะนักดนตรีที่มีคีย์บอร์ดหลายตัว ถ้าจะประพันธ์เพลง ก็แค่เล่นด้วยคีย์บอร์ด MIDI หรืออัดเสียงเครื่องดนตรีอะคูสติกแล้วทำเป็นลูปก็ได้ ผมเลยไม่ค่อยเห็นเหตุผลว่าทำไมต้องเขียนซีเควนซ์ดนตรีด้วย Ruby รู้ว่าเราพิมพ์สิ่งที่ต้องการด้วย Ruby DSL ได้ แต่คงพูดได้ยากว่าประสบการณ์ผู้ใช้ดีกว่าคีย์เปียโน
    พูดอีกอย่างคือ ผมคงไม่ใช้สิ่งนี้เพื่อแทนที่ Logic แน่นอนว่าเป็นความชอบส่วนตัว
    อ้างอิง: http://listen.hatnote.com/

    • แต่ในบางแง่ Ruby DSL แสดงเจตนาได้ชัดกว่า MIDI sequence
      จริง ๆ แล้วบางครั้งเราก็คิดประมาณว่า “ต่อไปเป็นอาร์เปจจิโอ C minor ต่อไปเป็นอาร์เปจจิโอ F major” และผมชอบที่สามารถแต่งเพลงโดยเขียนเจตนานั้นเป็นฟังก์ชันได้อย่างชัดเจนมาก
    • Sonic Pi ดูเหมือนจะเน้น ดนตรีที่สร้างด้วยอัลกอริทึม โดยเฉพาะไลฟ์โค้ดดิ้ง มากกว่าจะเป็นเครื่องมือแต่งเพลงทั่วไป
      แก่นของมันน่าจะอยู่ที่การสร้างด้วยอัลกอริทึม มากกว่าการเล่นโน้ตทีละตัว
      ถึงอย่างนั้น การนำเครื่องมือที่ปกติคงไม่ใช้มาใช้อย่างน่าสนใจเพราะข้อจำกัดแปลก ๆ ก็เข้ากับความสนใจของ Hacker News ค่อนข้างดี
    • อาจเหมาะกับ นักทฤษฎีดนตรี ที่ขาดการประสานงานของร่างกายและทักษะการเคลื่อนไหวละเอียด
    • อย่างที่บทความต้นฉบับบอกเป็นนัย ส่วนใหญ่เป็นเพราะ ข้อจำกัดในโลกจริง
      ผมมีคีย์บอร์ดที่ชอบอยู่ และในทางทฤษฎีก็ใช้กับงานนี้ได้ แต่ส่วนตัวมีเรื่องซับซ้อนบางอย่าง ทำให้เครื่องดนตรีทั้งหมดต้องอยู่ในโกดังไปอีกสักพัก
  • เมื่อไม่กี่วันก่อนเพิ่งนึกถึง Sonic Pi แล้วได้ดูวิดีโอออนไลน์อยู่สองสามอัน
    ที่เคยลองเล่นเองก็แค่เมื่อหลายปีก่อนนิดหน่อย ตอนนั้นลองกดเพลงคลาสสิกอย่าง Old MacDonald กับ Hot Cross Buns

  • ถ้าอยากดูแนวทางอื่นของการเขียนโปรแกรมดนตรี แม้จะไม่ใช่แบบไลฟ์ แต่ scamp[0] สำหรับ Python ก็น่าดู
    ผู้สร้างอัปโหลดวิดีโอผลงานที่ทำด้วยสิ่งนี้ไว้บน YouTube เยอะมาก เช่นวิดีโอ Fourier Elise[1]
    [0] http://scamp.marcevanstein.com/
    [1] https://www.youtube.com/watch?v=zq32bIud3OM

  • ดูเหมือนว่า HTML comment จะไม่ทำงาน เหมือนตัวแก้ไขจะเปลี่ยนขีดกลางสองตัว -- ให้เป็น em dash หนึ่งตัว

    • จับได้ดี จริง ๆ แล้วตอนรัน find/replace รีบเกินไป เลยพลาดแบบนี้ไปหลายจุด XD
  • ขอแนะนำ alsoknownasrox ไลฟ์โค้ดเดอร์ Sonic Pi ที่ผมชอบที่สุดด้วย
    https://www.youtube.com/watch?v=wXIvXrBQFkE