- 악기와 장비가 없어도 음악을 만들 수 있는 환경으로 Sonic Pi가 소개되며, Ruby 기반 DSL로 음표·코드·스케일·타이밍을 직접 작성할 수 있음
- Ruby의 함수, 배열, 조건문, 반복, 스코프를 활용하면
play,sleep,play_pattern_timed,chord,scale을 묶어 반복 패턴과 아르페지오를 간결하게 구성 가능함 in_thread,ring,flatten, splat 연산자,with_fx를 조합하면 여러 “악기”를 동시에 실행하고 겹친 음과 다른 리듬을 레이어링할 수 있음- ADSR 엔벨로프의
attack,decay,sustain,release조절은 실제 합창 재현보다 유지 시간과 분리감을 맞추는 데 초점이 있음 live_loop는 재생 중인 코드를 갱신하며 반복 실행할 수 있어 Sonic Pi를 라이브 퍼포먼스용 도구로 만들고, 코드를 읽히는 텍스트이자 창작 행위로 보게 함
코드로 제어하는 Sonic Pi
- Sonic Pi는 일반적인 오디오 애플리케이션과 달리 코드로만 제어되는 소프트웨어 신시사이저임
- 자체 제어 언어는 Ruby를 확장한 도메인 특화 언어(DSL) 이며, 음악과 오디오 개념을 Ruby 문법 위에 매핑함
- 기본 요소는 Ruby 값과 Sonic Pi 함수로 구성됨
:c4같은 음표 이름은 해당 MIDI 코드에 대응하는 심볼로 사용됨chord(:d3, :maj7)처럼 루트 음과 코드 구조를 인자로 받아 코드를 만들 수 있음play는 음을 재생하고, Ruby의sleep과 함께 시간 간격을 제어함play_pattern_timed는 음 목록과 시간 간격을 받아 반복 재생 코드를 줄여줌
- 예제의 목표는 외부 MIDI나 샘플 없이 Sonic Pi 기본 기능만으로 데모용 반주를 코드로 만드는 것임
- Sonic Pi 앱을 설치하면 예제 코드를 붙여 넣어 따라 할 수 있고, 언어 문서와 튜토리얼도 기본 제공됨
Ruby 문법으로 반복 패턴 만들기
- 기본 예제는
use_bpm 70으로 BPM을 바꾸고,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의 버스와 비슷하게 사용할 수 있지만, 더 깊은 라우팅은 수동적이며 스코프 때문에 대체로 위에서 아래로 구성해야 함
- Sonic Pi는 정교한 프로덕션 도구라기보다 악기처럼 다루는 편이 자연스러움
ADSR 엔벨로프로 음 길이 맞추기
play_pattern_timed는sustain값을 추가하므로, 긴 코드와 짧은 코드의 유지 시간을 직접 맞출 수 있음attack,decay,sustain,release는 음량이 시간에 따라 어떻게 변하는지를 나타내는 ADSR 엔벨로프임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 예제는 그 지점을 음악 제작으로 보여줌