Upgrade to Pro — share decks privately, control downloads, hide ads and more …

音楽プログラミング言語mimium 理論と実装の詳細

Avatar for Tomoya Matsuura Tomoya Matsuura
April 15, 2026
210

音楽プログラミング言語mimium 理論と実装の詳細

2026/04/15 音楽技術勉強会

Avatar for Tomoya Matsuura

Tomoya Matsuura

April 15, 2026

More Decks by Tomoya Matsuura

Transcript

  1. • দӜ ஌໵ • 2019~2022೥ɹ۝भେֶେֶӃ ܳज़޻ֶ෎ म࢜ɾത࢜՝ఔ • 2022೥͔Βɹ౦ژ᥁ज़େֶ ܳज़৘ใηϯλ

    ʔ(AMC) ಛ೚ॿڭ • 2026೥͔Βɿແ৬ʢnew!ʣ • ݚڀྖҬɿԻָ౔໦޻ֶʢCivil Engineering of Musicʣ ࣗݾ঺հ
  2. ྺ࢙ #BDLHSPVOECZl-BOHVBHFTGPS$PNQVUFS.VTJD 3PHFS#%BOOFOCFSHz 'SPOUJFSTJO%JHJUBM)VNBOJUJFT /PW Overtone Sam Aaron (2011?) Sonic

    PI Sam Aaron (2012) TidalCycles Alex Mclean (2006~) Chronic Eli Blandt (2002) Kronos Versa Norilo (2015) LC Hiroki Nishino (2013) ixi lang Thor Magnusson (2012) Gibber Charlie Roberts (2012) FoxDot Jean-François Parent (2015) Extempore Andrew Sorensen (2012) impromptu Andrew Sorensen (2010) Common Lisp Music Bill Schottstaedt (1989) SuperCollider- Clients SOUL ROLI (2018) Vult Leonardo Laguna Ruiz (2016) mimium Tomoya Matsuura (2020) Gwion Jérémie Astor (2017) ϙΠϯτɿ90೥୅ʹ
 ύʔιφϧίϯϐϡʔλʔ΍ Protoolsͷલ਎ͱͳΔGUIϕ ʔεͷϫʔΫϑϩʔ͕ొ৔ ͦΕҎޙͷݴޠ͸ศརͳGUI ͕͋ΔதͰ׶͑ͯϓϩάϥϛ ϯάݴޠͱ͍͏UXΛ
 બΜͩͱ͍͏จ຺͕͋Δ
  3. McCartney, James. ʮRethinking the computer music language: SuperColliderʯ. Computer Music

    Journal 26, no. 4 (2002೥): 61–68. https://doi.org/10.1162/014892602320991383. “Is a specialized computer music language even necessary? In theory at least, I think not. The set of abstractions available in computer languages today are sufficient to build frameworks for conveniently expressing computer music. Unfortunately, in practice, some pieces are missing in the implementations of languages available today. Often, the garbage collection is not performed in real time, and often argument passing is not very flexible. If lazy evaluation is not included, then implementing Patterns and Streams becomes more complicated.”
  4. طଘݴޠ΁ͷෆຬ ಈతܕ෇͚ ίʔυϕʔε͕ڊେ Ҡ২ੑ͕௿͍ UGenΛίϯϐϡʔςʔγϣφϧ
 ʹѻ͑ͳ͍ ϥΠϒίʔσΟϯά࣌ʹσΟϨΠ ͱϦόʔϒ్͕੾ΕΔ SuperCollider ChucK

    Faust ه๏͕ಛघ͗͢ ৴߸ॲཧͷ֎ͷੈք΁ग़ΒΕͳ͍ ه๏ɺදݱྗͷॊೈ͞ αϯϓϧϨϕϧͰͷਖ਼֬ͳ੍ޚ จ๏ͷݫີͳܗࣜԽ
  5. λmmm base on a simply typed, call by value lambda

    calculus e ::= x x ∈ vp [value] | λx.e [lambda] | let x = e1 in e2 [let] | fix x.e [fixpoint] | e1 e2 [app] | if (ec ) et else ee [if ] | delay n e1 e2 n ∈ ℕ [delay] | feed x.e [feed] | ... τ ::= R | In n ∈ ℕ | τ → τ 7BMVFT 5FSNT v ::= | R R ∈ ℝ[number] | cls(e, E) [closure] 5ZQFT
  6. BiquadϑΟϧλͷྫ fn _biquad_inner (x,a1,a2){ let (ws, wss, _wsss) = self

    let w = x - a1*ws - a2*wss (w, ws, wss) } pub fn biquad(x,coeffs){ let (a1,a2,b0,b1,b2) = coeffs; let (w,ws,wss) = _biquad_inner(x,a1,a2); b0*w + b1*ws + b2*wss }
  7. ϑΟʔυόοΫσΟϨΠͷྫ fn fbdelay(input, time, fb, mix){ input * mix +

    1.0 - mix * delay(40001.0, input + self * fb, time) } • delay͸૊ΈࠐΈؔ਺ɻόοϑΝͷαΠζɺೖྗ৴߸ɺ஗Ԇͷ௕͞ͷ3Ҿ਺
  8. SinewaveΦγϨʔλͷྫ use math::* fn phasor_zero(freq){ (self + freq/samplerate)%1.0 } pub

    fn phasor(freq,phase_shift=0){ (phasor_zero(freq) + phase_shift)%1.0 } pub fn sinwave(freq,phase=0){ phasor(freq,phase)*2.0*PI() |> sin } • samplerate͸ϥϯλΠϜఆٛͷ؀ڥม਺ɻ • ύΠϓ(|>)͸a(b)Λb |> aͱॻͨ͘Ίͷه๏ɻ
 ؔ਺ܕͰ৴߸ॲཧͷσʔλϑϩʔͱ૬ੑ͕͍͍
  9. ߴ֊ؔ਺Λ࢖͏ fn cascade(n,gen){ let g = gen() if (n>0){ let

    c = cascade(n - 1.0 ,gen) let multiplier = 1.0-(1.0/(n*2.0)) |rate| { rate + g(rate/2.0)* 0.5 * rate * multiplier |> c } }else{ |rate| g(rate) } } let myosc = cascade(20, | | osc) fn dsp(){ let f = 200 let r = f |> myosc (r,r) }
  10. ϥϯλΠϜ্ͷදݱ Virtual Machine Program Counter State_Ptr Stack Audio Driver Call

    Stack ... State Storage Closure Storage Base Pointer State Position State for self 1 Ring Buffer for delay 1 State for self 2 Ring Buffer for delay 2 ... Program Function Prototype0 Static Variables ... ... Function Prototype1 OP A B C OP A B C OP A B C OP A B C OP A B C Upvalue List Program State Size Local(N1) Upvalue(N2) Open Closure Function Prototype State Storage Upvalues Open(Local(N1)) Open(Upvalue(N2)) State Position Escaped Closure Function Prototype State Storage Upvalues State Position Closed Upvalue 1 Closed Upvalue 2 Somewhere on the Heap Memory (Maybe Shared with other closures) Delay΍selfͷϝϞϦ͸ɺάϩʔόϧۭؒ ্ͷҰ࣍ݩ഑ྻͱɺಡΈॻ͖ҐஔΛද͢ ϙΠϯλͰදݱ VM໋ྩ͕ϙΠϯλΛద੾ʹಈ͔͠ɺ ಡΈॻ͖Ґஔʹ͋ΔηάϝϯτΛ ϦϯάόοϑΝ΍ϝϞϦͱͯ͠ղऍ
  11. MOVE A B R(A) := R(B) MOVECONST A B R(A)

    := K(B) GETUPVALUE A B R(A) := U(B) (SETUPVALUE does not exist) GETSTATE* A R(A) := SPtr[SPos] SETSTATE* A SPtr[SPos] := R(A) SHIFTSTATE* sAx SPos += sAx DELAY* A B C R(A) := update_ringbuffer(SPtr[SPos],R(B),R(C)) *(SPos,SPtr)= vm.closures[vm.statepos_stack.top()].state (if vm.statepos_stack is empty, use global state storage.) JMP sAx PC +=sAx JMPIFNEG A sBx if (R(A)<0) then PC += sBx CALL A B C R(A),...,R(A+C-2) := program.functions[R(A)](R(A+1),...,R(A+B-1)) CALLCLS A B C vm.statepos_stack.push(R(A)) R(A),...,R(A+C-2) := vm.closures[R(A)].fnproto(R(A+1),...,R(A+B-1)) vm.statepos_stack.pop() CLOSURE A Bx vm.closures.push(closure(program.functions[R(Bx)])) R(A) := vm.closures.length - 1 CLOSE A close stack variables up to R(A) RETURN A B return R(A), R(A+1)...,R(A+B-2) ADDF A B C R(A) := R(B) as float + R(C) as float SUBF A B C R(A) := R(B) as float - R(C) as float MULF A B C R(A) := R(B) as float * R(C) as float DIVF A B C R(A) := R(B) as float / R(C) as float ADDI A B C R(A) := R(B) as int + R(C) as int ...Other basic arithmetic continues for each primitive types... (࣮ࡍͷίϯύΠϥͰ͸߹੒ܕΛѻ͏ ͨΊɺଟ͘ͷ໋ྩͰϫʔυαΠζΛࣔ ͢௥ՃΦϖϥϯυ͕ಋೖ͞Ε͍ͯΔ)
  12. ྫɿϑΟʔυόοΫ෇͖σΟϨΠΛෳ਺࢖༻͢Δ fn fbdelay(x,fb,dtime){ x + delay(1000,self,dtime)*fb } fn twodelay(x,dtime){ fbdelay(x,dtime,0.7)

    +fbdelay(x,dtime*2,0.8) } fn dsp(x){ twodelay(x,400)+twodelay(x,800) } "fbdelay" ؔ਺͸࠷େͰ1000αϯϓϧͷ delayͱ selfΛ࢖༻ "twodelay"͸"fbdelay"2ճ࢖༻ "dsp"͸"twodelay"Λ͞Βʹ2ճ࢖͏
  13. fn fbdelay(x,fb,dtime) state_size:1004 MOVE 3 0 //load x GETSTATE 4

    SHIFTSTATE 1 DELAY 4 4 2 MOVE 5 1 MULF 4 4 5 ADDF 3 3 4 SHIFTSTATE -1 GETSTATE 4 SETSTATE 3 RETURN 4 1 State for Self Ring Buffer for Delay SPos ... ...
  14. fn twodelay(x,dtime) state_size:2008 MOVECONST 2 5 MOVE 3 0 MOVE

    4 1 MOVECONST 5 0 CALL 2 3 1 SHIFTSTATE 1004 MOVECONST 3 5 MOVE 4 0 MOVECONST 5 1 //load 2 MULF 4 4 5 MOVECONST 5 0 //load 0.7 CALL 3 3 1 ADDF 3 3 4 SHIFTSTATE -1004 RETURN 3 1 State for Self Ring Buffer for Delay ... ... State for Self Ring Buffer for Delay 0 1 2 SPos 3 4 5 6
  15. fn dsp (x) state_size:4016 MOVECONST 1 6 //load twodelay MOVE

    2 0 MOVECONST 3 3 //load 400 CALL 1 2 1 SHIFTSTATE 2008 MOVECONST 2 6 //load twodelay MOVE 2 3 MOVE 3 0 MOVECONST 3 4 //load 400 CALL 2 2 1 ADD 1 1 2 SHIFTSTATE -2008 RETURN 1 1 State for Self Ring Buffer for Delay State for Self Ring Buffer for Delay 0 1 2 SPos 3 4 5 6 State for Self Ring Buffer for Delay State for Self 7 8 9 10 11 12 13 14 SPosͷҠಈΛ૬ରతͳΦϑηοτͰ දݱ͢ΔͷͰɺ֤ؔ਺ఆٛ͸ݺͼग़͠ ݩʹ͍ͭͯ஌Βͳͯ͘Α͍
  16. ϥϯλΠϜ্ͷදݱ Virtual Machine Program Counter State_Ptr Stack Audio Driver Call

    Stack ... State Storage Closure Storage Base Pointer State Position State for self 1 Ring Buffer for delay 1 State for self 2 Ring Buffer for delay 2 ... Program Function Prototype0 Static Variables ... ... Function Prototype1 OP A B C OP A B C OP A B C OP A B C OP A B C Upvalue List Program State Size Local(N1) Upvalue(N2) Open Closure Function Prototype State Storage Upvalues Open(Local(N1)) Open(Upvalue(N2)) State Position Escaped Closure Function Prototype State Storage Upvalues State Position Closed Upvalue 1 Closed Upvalue 2 Somewhere on the Heap Memory (Maybe Shared with other closures) ߴ֊ؔ਺Λ࢖͏Α͏ͳෳࡶͳύλʔϯͰ͸ɺ Ϋϩʔδϟʢؔ਺ͷΠϯελϯεʣ ࣗମʹ಺෦ঢ়ଶετϨʔδ͕࡞ΒΕͯɺ ࣮ߦ࣌ʹ੾Γସ͑
  17. ߴ֊ؔ਺Λ࢖͏ fn cascade(n,gen){ let g = gen() if (n>0){ let

    c = cascade(n - 1.0 ,gen) let multiplier = 1.0-(1.0/(n*2.0)) |rate| { rate + g(rate/2.0)* 0.5 * rate * multiplier |> c } }else{ |rate| g(rate) } } let myosc = cascade(20, | | osc) fn dsp(){ let f = 200 let r = f |> myosc (r,r) } άϩʔόϧ؀ڥͰॳظԽ͠ͳ͍ͱ ຖαϯϓϧ৽͍͠ΦγϨʔλΛੜ੒͢Δ͜ͱʹͳΔ ʹಈ͖͸͢Δ͚Ͳεέʔϧ͠ͳ͍
  18. λMmmm base on a multi-stage simply typed, call by value

    lambda calculus τ ::= R | In n ∈ ℕ | τ → τ | ⟨τ⟩ 7BMVFT 5FSNT v ::= | R R ∈ ℝ[number] | cls(e, E) [closure] | ⟨v⟩ [code] 5ZQFT e ::= R R ∈ ℝ[number] | λx . e [abs] | let x = e in e [let] | fix x . e [fixpoint] | e e [app] | if (ec ) et else ee [if ] | delay(x, etime , vbound ) [delay] | mem(e) [mem] | feed x . e [feed] | quote(e) [quote] | splice(e) [splice] | . . .
  19. ଟஈ֊ܭࢉͰͷίʔυ #stage(macro) fn cascade(n,gen){ if (n>0.0){ let multiplier = 1.0-(1.0/(n*3))

    |> lift_f `{|rate| rate + ($gen)(rate/3)* 0.5 * rate* $multiplier |> $cascade(n - 1.0 ,gen) } }else{ `{|rate| ($gen)(rate)} } } #stage(main) fn dsp(){ let f = 200 let r = f |> cascade!(20,`osc) (r,r) } cascadeؔ਺͸ίϯύΠϧ࣌ʹ࣮ߦ͞Εɺ ίʔυΛຒΊࠐΉͷͰঢ়ଶ͸Ҿ͖ܧ͕ΕΔ
  20. FnCall(dsp) FnCall(osc) FnCall(osc2) Feed( fl oat) Feed( fl oat) FnCall(dsp)

    FnCall(osc) Feed( fl oat) Delay(4000) FnCall(fbdelay) Feed( fl oat) fn dsp(){ osc(440) + osc2(220) } fn dsp(){ osc(440) ||> fbdelay(_, 1000) }
  21. FnCall(dsp) FnCall(osc) FnCall(osc2) Feed( fl oat) Feed( fl oat) FnCall(dsp)

    FnCall(osc) Feed( fl oat) Delay(4000) FnCall(fbdelay) Feed( fl oat) fn dsp(){ osc(440) + osc2(220) } ໦ಉ࢜ͷࠩ෼ΛͱͬͯɺมԽͯ͠ͳ͍෦෼͸಺෦ঢ়ଶΛίϐʔͯ͠Ҿ͖ܧ͛Δ fn dsp(){ osc(440) ||> fbdelay(_, 1000) }
  22. from: ... to: ... size: ... from: ... to: ...

    size: ... from: ... to: ... size: ... from: ... to: ... size: ... dsp dsp fmosc fmosc fmosc fmosc osc osc osc osc osc osc osc osc osc phasor phasor phasor phasor phasor phasor phasor phasor phasor phasor phasor phasor phasor feed feed feed feed feed feed feed feed feed feed feed feed feed Generate Patches Apply Patches Swap VMs Compare State Trees Version 1-a Previous Storage Compiler Thread Audio Thread Upcoming Storage Version 2-b VM1 VM2