
// simple, assumes we are not going to respawn the contents,
// (so no groups)
// that the lifetime of the played and the effected are the same

AbstractSinglePlayerEffect : HasSubject {

	var sharedBus;


	makeResourcesToBundle { arg bundle;
		sharedBus = SharedBus.newFrom(patchOut.bus,this);
		AbstractPlayer.annotate(sharedBus,"AbstractSinglePlayerEffect");
		patchOut.bus = sharedBus;
	}
	prepareChildrenToBundle { arg bundle;
		subject.prepareToBundle(this.group,bundle, true,sharedBus);
	}
	preparePlayer { arg player,bus;
		^player.prepareForPlay(this.group,true,sharedBus);
	}
	freeResourcesToBundle { arg bundle;
		bundle.addFunction({
			if(status == \isStopping or: status == \isFreeing or: status == \isStopped,{
				if(sharedBus.notNil,{
					sharedBus.releaseBus(this);
				});
				sharedBus = nil;
			})
		})
	}	
}

PlayerAmp : AbstractSinglePlayerEffect {
	
	var <amp=1.0,<>spec;

	*new { arg player,amp=1.0,spec=\amp;
		^super.new(player).spec_(spec.asSpec).amp_(amp)
	}
	asSynthDef {
		^SynthDef(this.defName,{ arg i_bus=0,amp=1.0;
			ReplaceOut.ar(i_bus,
					In.ar(i_bus,this.numChannels) * amp
				)
		})
	}
	defName { ^this.class.name.asString ++ this.numChannels.asString }
	synthDefArgs { ^[\i_bus,patchOut.synthArg,\amp,amp] }

	amp_ { arg v; 
		amp = spec.constrain(v);
		if(synth.isPlaying,{
			synth.set(\amp,amp)
		})
	}
	db_ { arg db;
		this.amp = db.dbamp;
	}
	value_ { arg v;
		this.amp = v;
	}

	guiClass { ^PlayerAmpGui }
}

/*
	put an envelope on the player's output bus.
	correct numChannels of the player's output eg. from 1 to 2 if needed
*/
EnvelopedPlayer : AbstractSinglePlayerEffect {
	
	var <>env,<>numChannels;
	
	*new { arg player,env,numChannels=2;
		^super.new(player).env_(env).numChannels_(numChannels)
	}
	storeArgs { ^[subject,env,numChannels] }
	asSynthDef {
		^SynthDef(this.defName,{ arg i_bus,gate;
			var in,pnc;
			var good; 
			pnc = subject.numChannels;
			in = In.ar(i_bus,pnc);
			good = BinaryOpUGen('==', CheckBadValues.kr(in, 0, 0), 0);
			 // silence the output if freq is bad
			in = in * good * EnvGen.kr(env,gate,doneAction: 2);
			if(numChannels.notNil,{
				in = NumChannels.ar(in,numChannels,true);
			});
			ReplaceOut.ar(i_bus,in)
		})
	}
	defName { ^this.class.name.asString ++ numChannels.asString ++ 
				env.asCompileString.hash.asFileSafeString 
	}
	synthDefArgs { ^[\i_bus,patchOut.synthArg,\gate,1.0] }
	releaseToBundle { arg releaseTime,bundle;
		status = \isReleaseing;
		if(releaseTime.isNil,{ releaseTime = env.releaseTime; });
		if(synth.notNil,{
			bundle.add(synth.releaseMsg(releaseTime + (server.latency?0.05)));
		});
		if(releaseTime > 0.01,{
			bundle.addFunction({
				SystemClock.sched(releaseTime,{ 
					// want everything but the synthFree in here
					this.stop(nil,false);
					nil; 
				})
			});
		},{
			this.stop(stopSynth: false);
		});
	}
}

