[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);\
\
}