Ruby ในฐานะเครื่องมือแต่งเพลง: Sonic Pi
(bhmt.dev)- มีการแนะนำ 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ของ Rubyplay_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 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ถ้าสนใจไลฟ์โค้ดดิ้ง Glicol(https://glicol.org) ก็น่าลองใช้เหมือนกัน
ยังมี TidalCycles ด้วย และความสามารถของ Haskell กับ Ruby ในการสร้าง DSL นั้นน่าชื่นชมจริง ๆ
Glicol เน้นไวยากรณ์ที่ได้แรงบันดาลใจจากซินธิไซเซอร์ ความเร็วในการแต่งเพลง และความสะดวกในการออกแบบเสียง แนวคิดคือการออกแบบ DSL ที่นำความเคยชินจากการเขียนโปรแกรมเดิม ๆ มาใช้ได้ แต่ไม่ถูกผูกติดอยู่กับภาษาใดภาษาหนึ่ง
https://github.com/tidalcycles/strudel
เมื่อหลายปีก่อนเคยเห็นวิดีโอบน YouTube ที่มีคนใช้ Sonic Pi สร้าง THX Deep Note ขึ้นมาใหม่
ตอนแรกเขาไม่ได้อธิบายว่าจะทำอะไร แต่เริ่มจากสร้าง “โน้ต” เสียงเดี่ยว ๆ แล้วเรียกมันว่าผึ้ง จากนั้นก็ค่อย ๆ ปรับเสียงผึ้งนั้นและเปิดให้ฟัง แล้วตอนท้ายก็ประมาณว่า “ลองสร้างผึ้งแบบนี้สักหลายร้อยตัว แล้วดูว่ามันจะฟังเป็นอย่างไรในรังผึ้ง” พอถึงจังหวะนั้น Deep Note ก็ปรากฏขึ้นมา
วิดีโอนั้นดีมากจริง ๆ แต่หลังจากนั้นก็หาไม่เจออีกเลย ถ้าใครรู้ลิงก์ช่วยแชร์ให้หน่อยก็ดี
ผมมองว่านี่เป็นหนึ่งในกรณีที่ Ruby เปล่งประกายได้ดี ด้วย เมตาโปรแกรมมิง ทำให้ไวยากรณ์ดูแทบจะเหมือนภาษาอังกฤษได้
เมื่อหลายปีก่อนเคยเห็น Sam Aaron หรือก็คือ ผู้สร้าง Sonic Pi แสดงสดด้วย Sonic Pi
ทุกครั้งที่โค้ดเปลี่ยนแบบเรียลไทม์ ดนตรีก็พัฒนาไปเรื่อย ๆ และมันดีจริง ๆ
ถ้าอยากดูตัวอย่างศิลปินที่ใช้ 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/
จริง ๆ แล้วบางครั้งเราก็คิดประมาณว่า “ต่อไปเป็นอาร์เปจจิโอ C minor ต่อไปเป็นอาร์เปจจิโอ F major” และผมชอบที่สามารถแต่งเพลงโดยเขียนเจตนานั้นเป็นฟังก์ชันได้อย่างชัดเจนมาก
แก่นของมันน่าจะอยู่ที่การสร้างด้วยอัลกอริทึม มากกว่าการเล่นโน้ตทีละตัว
ถึงอย่างนั้น การนำเครื่องมือที่ปกติคงไม่ใช้มาใช้อย่างน่าสนใจเพราะข้อจำกัดแปลก ๆ ก็เข้ากับความสนใจของ 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 หนึ่งตัว—ขอแนะนำ alsoknownasrox ไลฟ์โค้ดเดอร์ Sonic Pi ที่ผมชอบที่สุดด้วย
https://www.youtube.com/watch?v=wXIvXrBQFkE