«Graphic illustration of granular time-stretching» by jamshark70

on 26 Apr'15 09:39 in granularsynthesis techniquespedagogy

Graphically represents a small set of granular windows, with variable offset. Move the "ratio" slider, and you can see and hear time stretching.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
(
var tick = 4,
grainSamps = 100,
grainHeight = 50,
grainIOI = 50,  // samples between grain onsets
grainStep = 50,  // step size between source windows
linkI = 0;

var p = Platform.resourceDir +/+ "sounds/a11wlk01.wav", f, u, w, ratioSl, d, indexSl;
var viewWidth = 500;
var buf, synth;

f = SoundFile.openRead(p);
f.seek(86965);
f.readData(d = Signal.newClear((f.sampleRate * 0.333).roundUp));
f.close;

w = Window("granular windows", Rect(800, 200, viewWidth + 4, 450));

u = UserView(w, Rect(2, 2, viewWidth, 400)).front;
u.background = Color.gray(0.1);

indexSl = EZSlider(w, Rect(2, u.bounds.bottom + 2, viewWidth, 20),
	"Grain index",
	[0, 7, \lin, 1, 0],
	{ |view|
		if(view.value != linkI) {
			linkI = view.value;
			u.refresh;
		};
	},
	linkI,
	labelWidth: 120
);

ratioSl = EZSlider(w, Rect(2, indexSl.bounds.bottom + 2, viewWidth, 20),
	"Step ratio",
	[0.25, 2, \exp],
	{ |view|
		grainStep = grainIOI * view.value;
		synth.set(\stepRatio, view.value);
		u.refresh;
	},
	1,
	labelWidth: 120
);

d = d.resamp1(viewWidth);
d = d / (d.maxValue(_.abs));

f = { |view|
	var grainStart = 0,
	grainX = 0,
	grainY = 0,
	grainCt = 0;

	var bounds = view.bounds.moveTo(0, 0),
	width = bounds.width,
	height = bounds.height,
	waveTop = height * 0.25,
	waveMid = 0.5 * (waveTop + height),
	mapY = { |x, y| Point(x, y.linlin(-1, 1, height-1, waveTop)) };

	var fg = Color.gray(0.8),
	scoreColor = Color(1, 1, 0.4, 0.5);

	var oneGrain = { |start = 0, samps = 100, plotX = 0, plotY = 0, height = 50, drawLink = false|
		var data = d[start.asInteger .. (start + samps - 1).asInteger],
		env = Env.sine(1).discretize(samps),
		top = plotY+height,
		mapY = { |x, y| Point(plotX + x, y.linlin(-1, 1, top, plotY)) };

		// envelope
		Pen.color_(fg)
		.moveTo(mapY.(0, 0));
		(1 .. samps-1).do { |i|
			Pen.lineTo(mapY.(i, env[i]));
		};
		// signal
		Pen.moveTo(mapY.(0, 0));
		(1 .. samps-1).do { |i|
			Pen.lineTo(mapY.(i, data[i] * env[i]));
		};
		Pen.stroke;
		if(drawLink) {
			Pen.color_(scoreColor)
			.moveTo(mapY.(0, 0))
			.lineTo(Point(start, waveMid))
			.moveTo(mapY.(samps-1, 0))
			.lineTo(Point(start + samps, waveMid))
			.stroke;
		};
	};

	Pen.color_(fg)
	.moveTo(Point(tick, waveTop))
	.lineTo(Point(0, waveTop))
	.lineTo(Point(0, height-1))
	.lineTo(Point(tick, height-1))
	.moveTo(Point(0, waveMid))
	.lineTo(Point(width-1, waveMid))
	.moveTo(Point(width-1, waveMid - (0.75 * tick)))
	.lineTo(Point(width-1, waveMid + (0.75 * tick)))
	.stroke;

	Pen.moveTo(mapY.(0, d[0]));
	(1 .. d.size - 1).do { |i|
		Pen.lineTo(mapY.(i, d[i]));
	};
	Pen.stroke;

	while { grainStart + grainSamps < width and: {
		grainX + grainSamps < width
	} } {
		oneGrain.(grainStart, grainSamps, grainX, grainY, grainHeight, grainCt == linkI);
		grainStart = grainStart + grainStep;
		grainX = grainX + grainIOI;
		grainY = grainHeight - grainY;
		grainCt = grainCt + 1;
	};
};
u.drawFunc = { |view| f.value(view) };
u.refresh;

w.front;
w.onClose = {
	synth.free;
	buf.free;
};

s.waitForBoot {
	buf = Buffer.read(s, p, 86965, (44100 * 0.333).roundUp);
	SynthDef(\grains, { |out = 0, bufnum, retrigFreq = 0.8, trigFreq = 20, stepRatio = 1|
		var phase = Sweep.ar(Impulse.ar(retrigFreq), stepRatio),
		grainHalfDur = trigFreq.reciprocal,
		grainDur = grainHalfDur * 2,
		trig = Impulse.ar(trigFreq) * (phase + grainDur < BufDur.kr(bufnum)),
		sig = TGrains.ar(2, trig, bufnum, 1, phase + grainHalfDur, grainDur);
		Out.ar(out, sig);
	}).add;
	s.sync;
	synth = Synth(\grains, [bufnum: buf]);
};
)
raw 3630 chars (focus & ctrl+a+c to copy)
comments
alln4tural user 08 May'15 10:05

pretty! another reminder of how much more there is to explore with sc. the grain index slider has no effect on the sound, is that intentional? (i'm on windows, 3.6. atm, so maybe that's reason, but i don't see in the code or synth args where it might ..)