Grainbuf volume fade in

hey I noticed in the Grainbuf example page, as for my project, the sample always start playing with a little volume fade in, is there a way to remove this so Grainbuf plays ,from the start, the buffer at full signal amplitude?

thank you :blush:

In the example, there is an envelop in the Synth with a 1 sec fade-in !!

(
var winenv;

b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff");
// a custom envelope
winenv = Env([0, 1, 0], [0.5, 0.5], [8, -8]);
z = Buffer.sendCollection(s, winenv.discretize, 1);

SynthDef(\buf_grain_test, { |out, gate = 1, amp = 1, sndbuf, envbuf|
    var pan, env, freqdev;
    // use mouse x to control panning
    pan = MouseX.kr(-1, 1);
	env = EnvGen.kr( // <-- ENVELOP
	 	Env([0, 1, 0], [1, 1], \sin, 1),
	 	gate,
	 	levelScale: amp,
	doneAction: Done.freeSelf);

    Out.ar(out,
        GrainBuf.ar(2, Impulse.kr(10), 0.1, sndbuf, LFNoise1.kr.range(0.5, 2),
            LFNoise2.kr(0.1).range(0, 1), 2, pan, envbuf) * env)
}).add;
)

// use built-in env
x = Synth(\buf_grain_test, [\sndbuf, b, \envbuf, -1])

So, if you want to remove/adapt that fade-in, you can play with that envelop or change it:

// env = EnvGen.kr(
// 	Env([0, 1, 0], [1, 1], \sin, 1),
// 	gate,
// 	levelScale: amp,
// doneAction: Done.freeSelf);
env=EnvGate.new(gate: gate);
1 Like

ok im gonna study the reason why im getting it in my synth

ok it seems that the dur argument affect this attack, taking the example from the help


~b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff");

({ GrainBuf.ar(
	numChannels: 1,
			trigger: 3,
			dur: 16,
			sndbuf: ~b,
			rate: 1,
			pos: 0,
			mul: 0.1
		)!2
		
}.play)

gives a slow attack and

({ GrainBuf.ar(
	numChannels: 1,
			trigger: 3,
			dur: 1,
			sndbuf: ~b,
			rate: 1,
			pos: 0,
			mul: 0.1
		)!2
		
}.play)

has no attack


I am trying to play a long slice from a sample

thanks

It’s inherent to the design of GrainBuf that the size of the grain determines the slope of the attack! The default Hann envelope resp. the passed envelope are stretched and shrinked according to the dur argument. If you want to have short attack times independent from dur you have basically two choices:

.) Prepare a number of envelopes with different attack slopes in an array of buffers. For longer dur values you’d take envelopes with sharper attacks. This requires a bit of calculation and cannot give exactly the same attack length for every possible dur arg, but it works reasonably well practically.

.) Take one of the many granulation strategies without GrainBuf. With Pattern-controlled granulation the determination and sequencing of envelopes is super-flexible: you can take different envelope shapes and params from grain to grain.

There’s another server-side approach which allows flexible envelope usage. It’s a bit trickier, though not all too complicated. I think it’s not widely used, here’s an example:

(
// the tricky part extracted
// plot multichannel envelope with static Envs

{
	var numChannels = 10;
	// multichannel trigger	
	var trigs = { |i| PulseDivider.ar(Impulse.ar(100), numChannels, numChannels-1-i) } ! numChannels;
	EnvGen.ar(Env.perc(0.01, 0.09, curve: -1), trigs);
}.plot(1)
)

(
// you could also change Env dynamically
// plot multichannel envelope with changing Envs

{
	var numChannels = 10;
	// multichannel trigger
	var attLength = SinOsc.kr(1.5).range(0.005, 0.05);
	var trigs = { |i| PulseDivider.ar(Impulse.ar(100), numChannels, numChannels-1-i) } ! numChannels;
	EnvGen.ar(Env.perc(attLength, 0.05, curve: -1), trigs);
}.plot(1)
)


~b = Buffer.read(s, Platform.resourceDir +/+ "sounds/a11wlk01-44_1.aiff");

(
SynthDef(\grains_env_trigger, {
    arg outBus = 0, buf = 0, rate = 1, grainAtt = 0.02, grainRel = 0.05, att = 0.01, rel = 0.1,
		pan = 0, pulseFreq = 10, bufPos = 0, gate = 1, amp = 0.1;
	
	// you could take more channels allowing more overlap 
	// anyway this number is fixed for the SynthDef
	var numChannels = 10;
	
	// length restriction could be handled differently
	// but we probably don't want to cut release portions
	var maxGrainDur = numChannels / pulseFreq - 0.001;
	
	var grainEnv = Env.perc(
		min(maxGrainDur/2, grainAtt),
		min(maxGrainDur/2, grainRel),
		1,
		curve: -1
	);
    var trig = Impulse.ar(pulseFreq);

	// multichannel trigger 
    var trigs = { |i| PulseDivider.ar(trig, numChannels, numChannels - 1 - i) } ! numChannels;

	// multichannel envelope for grains 
    var grainEnvGens = EnvGen.ar(grainEnv, trigs);

	// global envelope
	var envGen = EnvGen.ar(Env.asr(att, 1, rel), gate, doneAction: 2);

	var sig = PlayBuf.ar(1, buf, rate * BufRateScale.ir(buf), trigs, bufPos * BufFrames.ir(buf));
	sig = Mix(sig * grainEnvGens) * envGen * amp;
	// a more differentiated panning could be done by
	// using Splay or moving each channel in the stereo field before mixing or ...
    OffsetOut.ar(0, Pan2.ar(sig, pan));
}).add;
)

// start granulation
x = Synth(\grains_env_trigger, [bufNum: ~b.bufnum, pulseFreq: 10, bufPos: 0.2])

// change env params
x.set(\grainRel, 0.1)

x.set(\grainAtt, 0.001)

x.set(\pulseFreq, 35)

(
c = Bus.control(s, 1);
y = { Out.kr(c, MouseX.kr(0, 1).poll) }.play;
)

// control position with MouseX

x.map(\bufPos, c);

// cleanup
(
x.release;
y.free;
)

=O, thanks James it seems that Playbuf is the one to use,

({PlayBuf.ar(1, ~b, 1,1,~b.numFrames.rand)}.play)

thanks a lot