[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [sc-users] Patterns framework to SMF
Wiadomość napisana w dniu 2009-05-06, o godz. 09:19, przez blackrain:
again, Patterns to SMF =)
I did something in that spirit, only for note events though (just a
hack for some algorithmic stuff).
ak
{\rtf1\ansi\ansicpg1250\cocoartf949\cocoasubrtf430
{\fonttbl\f0\fnil\fcharset0 Inconsolata;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red191\green0\blue0;\red96\green96\blue96;
\red0\green0\blue191;\red0\green115\blue0;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\f0\fs36 \cf2 (\
\cf3 // needs 'wslib' dependency:\cf2 \
\cf4 "wslib"\cf2 .include;\
\
\
~funcs = (\
\cf3 // midi filename template:\cf2 \
midifileTempl: \cf4 "sc_algo_*.mid"\cf2 ,\
\
\cf3 // generate new midi file path:\cf2 \
midifilename: \{ \cf5 |ev|\cf2 \
\cf5 var\cf2 mfDir = (\
\cf5 Document\cf2 .current.dir \
? \cf4 "~/Desktop"\cf2 .standardizePath\
);\
\cf5 var\cf2 mfFile = (((mfDir +/+ ev.midifileTempl)\
.pathMatch\
.maxItem(\{\cf5 |pn|PathName\cf2 (pn).realEndNumber\}))\
? (mfDir +/+ ev.midifileTempl.replace(\cf4 "*"\cf2 ,\cf4 "0"\cf2 )))\
.realNextName;\
mfFile;\
\},\
\
\cf3 // allows 4th division to binary-3lvl, ternary and 5-nary\cf2 \
midifileDivision: [2,2,2,3,5].product,\
\
\cf3 // returns new midifile object:\cf2 \
midifile: \{ \cf5 |ev, tracks = 10|\cf2 \
\cf5 var\cf2 mf = \cf5 SimpleMIDIFile\cf2 (ev.midifilename);\
mf.init1(tracks, 120, \cf4 "4/4"\cf2 );\
mf.timeMode = \cf6 \\ticks\cf2 ;\
mf.division = ev.midifileDivision;\
mf;\
\},\
\
\cf3 // default max duration of midi file:\cf2 \
midifileMaxDur: 400, \cf3 // in beats\cf2 \
\
\cf3 // convertion pattern -> array of events:\cf2 \
pattern2array: \{ \cf5 |ev, pattern, maxDur|\cf2 \
\cf5 var\cf2 arr=[];\
\cf5 var\cf2 curEv=\cf5 nil\cf2 ;\
\cf5 var\cf2 generator = \cf5 Pfindur\cf2 (maxDur ? ev.midifileMaxDur,\
pattern).asStream;\
while \{ \cf3 // LOOP //\cf2 \
curEv = generator.next(~defaultEv); \
curEv.notNil.if \{ \
curEv.use \{ \
~midinote = (~detunedFreq.value.cpsmidi)\
.round(1).asInteger;\
~sustain = ~sustain.value;\
~miditrack = ~miditrack.value;\
~velocity = ~velocity.value; \
\};\
arr=arr.add(curEv);\
\cf5 true\cf2 ;\
\} \{\
\cf5 false\cf2 ;\
\}\
\};\
arr;\
\},\
\
\cf3 // write file to disk:\cf2 \
write: \{ \cf5 |ev, mus, mf, multitrack = true|\cf2 \
\cf5 var\cf2 ntrax, m, timep=0, arr, file;\
\
arr = case \
\{ mus.isKindOf(\cf5 SequenceableCollection\cf2 ) \} \{ mus \}\
\{ mus.isKindOf(\cf5 Pattern\cf2 ) \} \{ ev.pattern2array(mus) \}\
\{ \cf5 true\cf2 \} \{ [] \};\
arr.notEmpty.if \{\
ntrax = arr[0].instrProps.size.max(1);\
\
m = mf ?? \{ev.midifile(ntrax)\};\
\cf3 // add dummy event at the beginning of every track:\cf2 \
multitrack.if \{\
ntrax.do \{ \cf5 |tnum|\cf2 \
m.addCC(1,0,0,0,tnum);\
\};\
\};\
arr.do \{ \cf5 |evv|\cf2 \
(evv.midinote > 0).if \{ \cf3 // filter out rests //\cf2 \
m.addNote(\
evv.midinote, \cf3 //midi note\cf2 \
evv.velocity, \cf3 //midi vel\cf2 \
(timep \
* ev.midifileDivision).asInt, \cf3 //pos\cf2 \
(evv.sustain \
* ev.midifileDivision).asInt, \cf3 //dur\cf2 \
0, \cf3 //midi noteoff vel\cf2 \
evv.midichannel, \cf3 //midi channel\cf2 \
evv.miditrack \cf3 //midi file track number\cf2 \
);\
\};\
timep = timep + evv.delta;\
\};\
\cf3 // write file to disk:\cf2 \
m.write;\
\cf4 "Saved as '%'\\n"\cf2 .postf(m.pathName.basename);\
m;\
\};\
\}, \cf3 // end of 'write'\cf2 \
\
\cf3 // mini-lilypond syntax (sequence)\cf2 \
notenames: #[c,d,e,f,g,a,b],\
notenamesteps: #[0,2,4,5,7,9,11],\
durnames: \cf5 Dictionary\cf2 [\
4 -> 1.0, 2 -> 2.0, 4 -> 1.0,\
8 -> 0.5, 16 -> 0.25,\
32 -> 0.125, 64 -> 0.0625\
],\
accidentalnames: \cf5 Dictionary\cf2 [\
\cf6 \\s\cf2 -> 1, \cf6 \\f\cf2 -> -1, \
\cf6 \\b\cf2 -> -1, \cf6 '#'\cf2 -> 1, \
\cf6 '-'\cf2 -> -1, \cf6 '+'\cf2 -> 1,\
\cf6 \\x\cf2 -> 2\
],\
octavesymbols: \cf5 Dictionary\cf2 [ \
\cf6 '\\''\cf2 -> 1, \cf6 ','\cf2 -> -1\
],\
firstnote: 0,\
str2dur: \{ \cf5 arg\cf2 ev,str;\
\cf5 var\cf2 resarr = str\
.findRegexp(\cf4 "([0-9]+)|([\\\\.]+)|(\\\\*[0-9,.]+)"\cf2 )\
.asSet\
.reject(\{\cf5 |el|\cf2 el.at(1).isEmpty\})\
.asArray\
.sort(\{\cf5 |a,b|\cf2 a[0] < b[0]\})\
.collect(\cf5 _\cf2 .at(1));\
\cf5 var\cf2 dur = ev.durnames[resarr.first.asInteger];\
(resarr.size > 1) && dur.notNil.if \{ \
resarr[1..].do \{ \cf5 |elm|\cf2 \
case \
\{ elm[0] == $. \} \
\{ dur = \cf5 Array\cf2 .geom(elm.size+1,dur, 0.5).sum \}\
\cf3 //--\cf2 \
\{ elm[0] == $* \} \
\{ dur = dur * (elm.drop(1).asFloat) \};\
\};\
\};\
dur; \cf3 // return\cf2 \
\},\
str2pchoct: \{ \cf5 arg\cf2 ev, str, deg = \cf5 false\cf2 ;\
\cf5 var\cf2 resarr = str.findRegexp(\cf4 "([a-z#\\\\+\\\\-]+)|([,']*)"\cf2 )\
.asSet\
.reject(\{\cf5 |el|\cf2 el[1].isEmpty\})\
.asArray\
.sort(\{\cf5 |a,b|\cf2 a[0] < b[0]\})\
.collect(\cf5 _\cf2 .at(1));\
\cf5 var\cf2 pch = ev.notenames\
.indexOf(resarr.first.first.asSymbol);\
\cf5 var\cf2 accarr = resarr.first.copyToEnd(1);\
\cf5 var\cf2 acc = ( accarr !? \
\{ accarr.sum \
\{ \cf5 |elm|\cf2 ev.accidentalnames.at(elm.asSymbol) ? 0 \}\
\}) ? 0;\
\cf5 var\cf2 octave = ( resarr.at(1) !?\
\{ resarr.at(1).sum \
\{ \cf5 |elm|\cf2 ev.octavesymbols.at(elm.asSymbol) ? 0 \}\
\}) ? 0;\
deg.booleanValue.if \
\{ [pch + (0.1 * acc), octave] \}\
\{ [ev.notenamesteps@@(pch) + acc, octave] \};\
\},\
\
str2notedur: \{ \cf5 arg\cf2 ev, str;\
\cf5 var\cf2 tokens = str.split($ );\
\cf5 var\cf2 curNote = 0, curDur = 1, lastPitch=0;\
\cf5 var\cf2 notedurArr = [];\
tokens.do \{\cf5 |tok,i|\cf2 \
\cf5 var\cf2 tokp, tokd, po, d, o, diff;\
tokd = tok.findRegexp(\cf4 "[0-9\\\\.\\\\*]+"\cf2 ).first;\
tokd.notNil.if \{ \
tokp = tok[..(tokd[0]-1)];\
tokd = tokd.at(1);\
\}\{\
tokp = tok;\
\};\
case \
\cf3 /* [a-g] NOTE*/\cf2 \
\{ ($a<=tok[0]) && (tok[0]<=$g) \} \
\{ \
po = ev.str2pchoct(tokp);\
diff = (po[0] - (lastPitch%12)); \cf3 // difference\cf2 \
(diff.abs >6).if \{\
diff = (12 - diff.abs) * diff.sign.neg\};\
\
lastPitch = curNote = lastPitch + diff + (12*po[1]); \
tokd !? \{ curDur = ev.str2dur(tokd) ? curDur\};\
notedurArr = notedurArr.add([curNote, curDur]); \
\}\
\
\cf3 /* [r] REST*/\cf2 \
\{ tok[0] == $r \}\
\{ \
curNote = \\ ;\
tokd !? \{ curDur = ev.str2dur(tokd) ? curDur\};\
\
notedurArr = notedurArr.add([curNote, curDur]); \
\}\
\
\cf3 /* [/] REPEAT LAST ONE*/\cf2 \
\{ tok[0] == $/ \}\
\{ \
tokd !? \{ curDur = ev.str2dur(tokd) ? curDur\};\
notedurArr = notedurArr.add([curNote, curDur]); \
\}\
\
\cf3 /* [0-9] REPEAT WITH NEW DURATION*/\cf2 \
\{ ($0<=tok[0]) && (tok[0]<=$9) \} \
\{ \
tokd !? \{ curDur = ev.str2dur(tokd) ? curDur\};\
notedurArr = notedurArr.add([curNote, curDur]); \
\};\
\};\
\cf3 // return\cf2 \
notedurArr;\
\},\
pbind: \{ \cf5 arg\cf2 ev, str;\
\cf5 var\cf2 notedur = ev.str2notedur(str);\
\cf5 var\cf2 notes, durs;\
#notes,durs = notedur.flop;\
\cf5 Pbind\cf2 (\cf6 \\note\cf2 , \cf5 Pseq\cf2 (notes,1), \cf6 \\dur\cf2 , \cf5 Pseq\cf2 (durs,1));\
\},\
notes: \{ \cf5 arg\cf2 ev, str;\
ev.str2notedur(str).flop.at(0);\
\},\
durs: \{ \cf5 arg\cf2 ev, str;\
ev.str2notedur(str).flop.at(1);\
\}\
\
);\
\
\
\cf3 // slightly modified Default Event:--------------------\cf2 \
~defaultEv = \cf5 Event\cf2 .default.copy;\
~defaultEv.parent.putAll((\
// default midifile track\
miditrack: 0,\
\
\cf3 // calculate velocity from amp / db\cf2 \
velocity: \{\cf5 |ev|\cf2 ((~amp.value * 127).clip(0, 127)).asInt \},\
\
\cf3 // default midi channel\cf2 \
midichannel: 0\
));\
\
)}
{\rtf1\ansi\ansicpg1250\cocoartf949\cocoasubrtf430
{\fonttbl\f0\fnil\fcharset0 Inconsolata;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;\red0\green0\blue191;\red96\green96\blue96;
\red191\green0\blue0;\red0\green115\blue0;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\f0\fs36 \cf2 (\cf3 Document\cf2 .current.dir +/+ \cf4 "midifile-kit v2.rtf"\cf2 ).load;\
\cf5 /*\
// lilypond-like notation to pattern:\
~funcs.str2notedur("c4 d c f g8 e c'");\
~funcs.str2notedur("c16 c' c, df' c, g'8 fs c");\
~funcs.str2notedur("c4 a' e d b' c g d' e fis g");\
~funcs.pbind("c16 d b' c eb,8 16 / r8 f e2").play;\
~funcs.pbind("c16 d eb d eb8. b'16 cs c d, es c g'4").play;\
*/\cf2 \
\
\
p = \cf3 Pbind\cf2 (\cf6 \\note\cf2 , \cf3 Pseq\cf2 (\{rrand(0,15).round\}!20, 1));\
p.play\
\
\cf5 // obtaining array of events\cf2 \
q = ~funcs.pattern2array(p);\
\
~funcs.write(q);\
\
\cf5 // or directly from pattern:\cf2 \
~funcs.write(p);\
\
}