Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bar and beat #9

Open
prko opened this issue Oct 3, 2018 · 8 comments
Open

Bar and beat #9

prko opened this issue Oct 3, 2018 · 8 comments

Comments

@prko
Copy link
Contributor

prko commented Oct 3, 2018

Hello, thank you for writing the tutorial series.
I think the following melody section is excellent.
https://github.com/supercollider/learn/blob/master/src/beginner/src/ch02-01-melody.md

However, the array of melody does not contain bar and beat. I know introducing these might be more complex.
I would like to know if the concepts of bar and beat will be introduced later or not?

@cianoc
Copy link
Collaborator

cianoc commented Oct 3, 2018

Thank you, that's good to hear.

I do plan to add more stuff to this later in the tutorial, but I'm curious as to what you mean by bar and beat. Something I do plan to add is a bit on how you can write parameters that are dependent upon the beat, or bar. But I'm guessing that you're looking for something a little bit different.

If you want to clarify what you're looking for (and the scsynth.org forum might be a better place), that would help me a lot. Thanks :)

@prko
Copy link
Contributor Author

prko commented Oct 3, 2018

To show what I mean, here are two examples:

  • Example 1: the current status of my research on recursive playback of music written according to the bar structure of the piece with multiple voices. The music is the beginning of a Korean popular song.
  • Example 2: a previous version with the melody "twinkle, twinkle, little star".

I think the method of example 2 is not so good as example 1. Example 1 must be rewritten with patterns.

You need the following sample:
https://freewavesamples.com/alesis-fusion-nylon-string-guitar-c4

I am not sure if it is good to post these examples in the scsynth.org because they are too long. If you think I should post all two examples or one of them in the scsynth.org, I will post it or them there. I think the example 1 should also be rewritten with the piece "twinkle, twinkle, little star".

  • Example 1:
(
fork{
	var baseFolder, snd, gtrSmpl, aLetter_kjKim, player, reverb;

	s.freeAll;

	baseFolder = thisProcess.nowExecutingPath.dirname;
	snd = "Alesis-Fusion-Nylon-String-Guitar-C4.wav";
	gtrSmpl = Buffer.read(s, baseFolder+/+snd);

	s.sync;

	SynthDef(\gtr5, {
		|ptch=60, mul=0.25, ads=0.5, ade=0.25, rls=0.05, gate=1, vfs=4, vfe=5, vds=0.25, vde=0.25, len=1|
		var smplPtch, bRate, vShp, vDpth, rate, sig, env, envGen, vAmp;
		smplPtch = 60;
		bRate = 2 ** (ptch - smplPtch / 12);
		vShp = {|phs| SinOsc.kr(XLine.kr(vfs + 1, vfe + 1, len) - 1, phs)};
		vAmp = vShp.(pi/2).range(XLine.kr(ads + 1, ade + 1, len) - 1, 1);
		vDpth = vShp.(3pi/2).range(1, 2 ** (XLine.kr(vds + 1, vde + 1, len) - 1 / 12));
		rate = bRate * vDpth;
		env = Env.asr(0.01, 1, rls);
		envGen = EnvGen.kr(env, gate, doneAction:2);
		sig = PlayBuf.ar(gtrSmpl.numChannels, gtrSmpl, rate);
		sig = sig * envGen * mul * vAmp * [-1, 1].choose;
		OffsetOut.ar(0, sig)
	}
	).add;

	SynthDef(\melody, {
		|ptch=60, mul=0.25, ads=0.5, ade=0.25, rls=0.05, gate=1, vfs=4, vfe=5, vds=0.25, vde=0.25, len=1|
		var vShp, vDpth, sig, env, envGen, vAmp;
		vShp = {|phs| SinOsc.kr(XLine.kr(vfs + 1, vfe + 1, len) - 1, phs)};
		vAmp = vShp.(pi/2).range(XLine.kr(ads + 1, ade + 1, len) - 1, 1);
		vDpth = vShp.(3pi/2).range(1, 2**(XLine.kr(vds + 1, vde + 1, len) - 1 / 12));
		env = Env.adsr(0.12, 0.3, 0.6, rls + 0.05, 1, 4);
		envGen = EnvGen.kr(env, gate, doneAction:2);
		sig = LFTri.ar([1, 1.013]*ptch.midicps * vDpth);
		sig = sig * envGen * mul * vAmp * [-1, 1].choose;
		OffsetOut.ar(0, sig)
	}
	).add;

	SynthDef(\reverb2Ch, {
		|oCh, mix = 0.25, room = 0.15, damp = 0.5, amp = 1.0|
		var signal;
		signal = In.ar(oCh, 2);
		ReplaceOut.ar(oCh,
			FreeVerb2.ar( // FreeVerb2 - true stereo UGen
				signal[0], // Left channel
				signal[1], // Right Channel
				mix, room, damp, amp
			)
		); // same params as FreeVerb 1 chn version
	}).add;

	s.sync;

	aLetter_kjKim = [
		[ // 1st bar
			[ // voice 1
				[[69,60],65,8], // entry: [aChord, velocity, length.reciprocal]
				[[70,62],60,8],
				[[70,62],65,8],
				[[69,60],60,8],
				[[69,60],57,2]
			],
			[ // voice 2
				[53,55,1] // entry: [aNote, velocity, length.reciprocal]
			]
		],
		[ // 2nd bar
			[ // voice 1
				[67,62,8],
				[69,60,8],
				[69,65,8],
				[67,61,8],
				[67,58,2]
			],
			[ // voice 2
				[62,40,4],
				[59,35,4],
				[61,40,2]
			],
			[ // voice 3
				[52,50,2],
				[45,45,2]
			]
		],
		[ // 3rd bar
			([
				[65,50,8],
				[67,47,8],
				[67,48,8],
				[65,52,8]
			]!2).flatten,
			[
				[57,48,4],
				[62,43,4],
				[63,0,8],
				[63,53,8],
				[61,48,4]
			],
			[
				[50,47,2],
				[49,45,2]
			]
		],
		[ // 4th bar
			[
				[[65,60,58],50,2],
				[[64,60,55],45,2]
			],
			[
				[48,50,1]
			]
		]
	];

	player = {
		|music, synth, tempo=60, nthBar=0, vol=1|
		var tF, entries, voiceItems, maxItemVoice, lastVoice;
		tF = 60 / tempo * 4;
		entries = music[nthBar];
		voiceItems = entries.size.collect{
			|nth|
			entries[nth].size.collect{
				|ith|
				entries[nth][ith][2].reciprocal
			}
		};
		maxItemVoice = voiceItems.size.collect{
			|nth|
			var items, range;
			items = voiceItems[nth];
			range = (0..items.size - 2);
			(items.size == 1).if{ 0 }{ items[range].sum }
		};
		maxItemVoice.postln;
		lastVoice = maxItemVoice.maxIndex;
		("Bar"+nthBar++"\nThe voice which will be played lastly:" + lastVoice).postln;
		{
			|vce|
			fork{
				("\t\t\t\t\t\t\tVoice"+vce+"numItems:" + entries[vce].size

				).postln;
				("\t\t\t\t\t\t\t"++entries[vce]).postln;
				entries[vce].do{
					|entry,idxEntry|
					var aChord, entryPlayer, len, untilNext, rAmp, rRls;
					aChord = entry[0].asArray;
					entryPlayer = Array.newClear(10);
					len = entry[2];
					untilNext = len.reciprocal;
					rAmp = -0.1.rand;
					rRls = rrand(0.01, 0.03);
					aChord.size.do{
						|i|
						var aNote, amp;
						aNote = aChord[i];
						amp = entry[1]/127 * (2**(-3 - (i - 1.0.rand) + rAmp));
						s.makeBundle(
							0.2,
							{
								entryPlayer[i] = Synth(
									synth,
									[
										\ptch, aNote,
										\mul, amp * vol,
										\ads, 0.9,
										\ade, 0.85,
										\rls, rRls,
										\gate, 1,
										\vfs, 3,
										\vfe, 4,
										\vds, 2 ** -4,
										\vde, 2 ** -5,
										\len, 1
									],
									1,
									0) });
						(
							[
								"voice:" + vce,
								"MIDI note:" + aNote,
								"rhythm:" + untilNext.asString.padRight(8, "0"),
								"dbFS:" + amp.ampdb.round(0.01).asString.padRight(6, "0")+"dB"
							]
						).postln;
						rrand(256, 384).reciprocal.wait;
					};

					( tF * untilNext + (128.reciprocal.rand2) - rRls).wait;
					aChord.size.do{
						|i|
						entryPlayer[i].release
					};
					rRls.wait;

					if (
						(nthBar != (music.size - 1))
						&&
						vce == lastVoice
						&&
						(idxEntry == (entries[vce].size - 1))
					)
					{
						nthBar = nthBar+1;
						player.(music, synth, tempo, nthBar, vol);
					}
				}
			};
		}!entries.size;
	};

	// start the reverb
	reverb = Synth(\reverb2Ch,
		[
			\oCh, 0,
			\mix, 0.7,
			\room, 0.5,
			\damp, 0.6,
			\amp, 1
		],
		addAction:\addToTail
	);

	// play music
	player.(aLetter_kjKim ++ aLetter_kjKim, \gtr5, 75, 0, 1);
	player.(
		(aLetter_kjKim * [[[[[1],0,1]]]] ++ aLetter_kjKim).collect{
			|item|
			[
				item[0].collect{
					|itm|
					[if(itm[0].isArray){ itm[0][0] }{ itm[0] }, itm[1], itm[2]]
				}
			]
			+ [[[[-12],0,0]]]
		},
		\melody,
		75,
		0,
		0.4);
}
)
  • Example 2:
(
// score[nth bar][nth voice][nth note information][pitch, velocity, length to next note]
~twinkle_a=
[
	[ // bar 1, 17
		[ // voice 1
			[72,64,4], [72,64,4] // [pitch, velocity, length to next note]
		],
		[ // voice 2
			[48,52,4],[60,52,4]
		]
	],
	[ // bar 2, 18
		[
			[79,64,4], [79,64,4]
		],
		[
			[64,52,4],[60,52,4]
		]
	],
	[ // bar 3, 19
		[81,64,4].dup(2),
		[
			[65,52,4],[60,52,4]
		]
	],
	[ // bar 4, 20
		[
			[79,58,2]
		],
		[
			[64,46,4],[60,38,4]
		]
	],
	[ // bar 5, 21
		[77,64,4]!2,
		[
			[62,52,4],[59,52,4]
		]
	],
	[ // bar 6, 22
		[76,64,4]!2,
		[
			[60,52,4],[52,52,4]
		]
	],
	[ // bar 7, 23
		[74,64,4]!2,
		[
			[53,52,4],[55,52,4]
		]
	],
	[ // bar 8, 24
		[
			[72,64,2]
		],
		[
			[48,52,2]
		]
	]
];

~twinkle_b=
[
	[ // bar 9, 13
		[79,48,4]!2,
		[ // voice 2
			[64,48,4],[55,48,4]
		]
	],
	[ // bar 10, 14
		[77,48,4]!2,
		[
			[62,48,4],[55,48,4]
		]
	],
	[ // bar 11, 15
		[76,64,4].dup(2),
		[
			[60,48,4],[55,48,4]
		]
	],
	[ // bar 12, 16
		[
			[74,48,2]
		],
		[
			[59,48,4],[55,48,4]
		]
	]
];

~twinkle=~twinkle_a++(~twinkle_b!2).flatten(1)++~twinkle_a;
)

/* bus */

(
SynthDef(\reverb, {
	arg oCh, iCh, rs=100, rt=2.5, mul=0.5;
	var sig= GVerb.ar(In.ar(iCh), rs, rt, mul:mul);
	Out.ar(oCh, sig)
}
).add
)

~gtrC4m= Buffer.readChannel(s, "Alesis-Fusion-Nylon-String-Guitar-C4.wav".resolveRelative, channels:0);

(
SynthDef(\gtrM, {
	arg len=1, oCh=0, pit=60, vSpd=3.5, vDepth=0.3, mul=1;
	var env, atk=0.01, rls=0.05, rate, fm, am, sig;
	env=Env([0,1,1,0],[atk,len-atk-rls,rls]).kr(doneAction:2);
	fm= SinOsc.kr(vSpd,1.5pi).range(1, 2**(vDepth/12));
	rate= 2**(pit-60/12) * BufRateScale.kr(~gtrC4m.bufnum) * fm;
	am= SinOsc.kr(vSpd, 0.5pi).range(0.5.sqrt, 1);
	sig= PlayBuf.ar(~gtrC4m.numChannels, ~gtrC4m.bufnum, rate);
	sig= sig * am * env * mul;
	Out.ar(oCh, sig)
}
).add
)

(
~gtrM={
	arg score, voice=0, temp=2, offset=0, leg=1;
	fork{
		score.do{
			arg barItms, barIdx;
			barItms[voice].do{
				arg item, idx;
				var wait, len, notes, mul, phr;
				if (idx==0 && voice==0) {barIdx.postln};
				phr= if ((barIdx/2).asInteger.even==3) {idx} {barItms.size-idx} - barItms.size;
				wait= 2**(phr/20) * temp * item[2].reciprocal;
				len= wait * leg;
				notes= item[0].asArray;
				mul= exprand(2**2.5.neg, 2**1.neg) * (item[1]/127).sqrt;
				[voice, idx, item].postln;
				notes.size.do{
					arg i;
					var deTune, phrMul;
					deTune= rrand( -0.02, 0.02);
					phrMul= 2**(phr/3);
					Synth.before(
						~reverb,
						\gtrM,
						[
							oCh:~bus_reverb,
							pit:notes[i]+deTune+offset,
							len:len,
							mul:mul*(2**(1/(i+1)).neg).sqrt * phrMul,
							vSpd:2,
							vDepth:0.2
						]
					);
					(rrand(1/256, 1/128)).wait
				};
				( wait + rrand(-1/256, 1/256) ).wait
			}
		}
	}
}
)

~bus_reverb= Bus.audio(s, 1);

~reverb= Synth(\reverb, [iCh:~bus_reverb, oCh:0]);

(
~gtrM.(score:~twinkle, voice:0, leg:1);
~gtrM.(score:~twinkle, voice:1, leg:0.4)
)

~reverb.set(\rs, 10, \rt,1.15, \mul,2)

@cianoc
Copy link
Collaborator

cianoc commented Oct 3, 2018

I probably won't do this, no, though I may augment the melodic stuff a little (for example showing how you can specify the notes and duration together using nested arrays). I will also show a way to use environments so that you can use note names, rather than numbers (e.g. \cs4 for C sharp 4) - though that will probably go into the cookbook. I'm also going to talk about importing midi files, and using those with patterns.

If you want to discuss this further though, the SCcode forum is the place to do it. Other people may have views, and I would like to discuss this more generally. I think it's a shame that there isn't an easy way to do melodies in SuperCollider, though this looks pretty promising:
https://github.com/shimpe/panola

You don't get the bar lines, but there may be other ways to achieve that (e.g. using the current time in a pbind).

@prko
Copy link
Contributor Author

prko commented Oct 3, 2018

Thank you for your precious opinion!

In February, 2018, I posted it in the mailing list, but nobody gave me any feedback:
https://www.listarc.bham.ac.uk/lists/sc-users/msg59085.html

I am a bit confused with the term SCcode forum. Do you mean scsynth.org, sccode.org or the mailing list?

I probably make code simpler with Pbind, then should post it. What do you think about it?

I do not use bar lines as an element of arrays. However, I can easily accentuate the first note or chord of each bar with altering velocity or altering duration in Example 1 because the playback firstly detects the size of the array of a bar then plays each element then go to next bar if the all notes in the current bar are played. In Example 1, I also make a rubato, accelerando, ritardando, crescendo and decrescendo within a bar. By using a global variable, I can do such things for some bars in Example 1.

I am not sure if pitch name is good because pitch names are diverse according to lands: In Germany, they use the Helmholtz-system; in the US, they use American Standard Pitch Notation (ASPN); In France, the octave number is lower than ASPN. Yamaha follows the French system, and Rolland follows the American system. Most software programmes provide an option to choose middle C as C3 or C4. Thus, I think that fixing middle C as C4 would be not good.

Anyway, Panola is cool! Is it your Quarks?

@cianoc
Copy link
Collaborator

cianoc commented Oct 3, 2018

Sorry I meant scsynth.org

There is a way to detect which beat of a bar you're in from within a pattern, and I plan to show how you can use that to create more realistic playback (it's a trick I use quite a bit). You can also do rubato/crescendo etc using patterns relatively easily - perhaps I should also show how to do that so it relates to bars/beats as well.

As far as notation goes - this is for an example, so the user can change it if they want. However, it is far easier to support ASPN in SuperCollider, than any of the others:

\C0;   \\ No problems
\C-1; \\ Not going to work.

Panola isn't mine - but I plan to use it.

@prko
Copy link
Contributor Author

prko commented Oct 4, 2018

I expect to read your method in the tutorial!
I understand now ASPN is simpler in SC, but the system of the pitch names should be explained in the tutorial.

Twinkle ... is too simple, and the Korean song is good enough to discuss on notation method, but it is not well-known.
I will search for another short and well-known example for the post in scsynth.org.

Thank you a lot!

@prko
Copy link
Contributor Author

prko commented Oct 4, 2018

Is the first part of the following piece interesting enough for a transcription example?
https://www.youtube.com/watch?v=ffZnrJpQmZY
the number of voice is fixed but mixed with rests.
There is a note exceeds a bar.

@cianoc
Copy link
Collaborator

cianoc commented Oct 4, 2018

Ha, I remember learning that piece for piano.

Yes, that's pretty good. Multiple voices, a tied note, rests, repeats. Post it on the forum and see what people make of it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants