Spectral mapping implementation supercollider

greetings

i am trying to implement a large time scale granular synthesis thing. here’s the specification that my friend chat gpt, helped me to write, based upon my first commitment, which can also be found below:

i am striving to implement a definition of a class for spectral mapping in supercollider. my main goal is to map mid-large scale grains in supercollider using large scale grains. i am not being meticulous in relation to the way as i am implementing the features in the buffer, but i am sort of striving to achieving something which can be modularized and so on so forth. so, the goal of this specific module, is to mape the mappings using either a controller, which can be a sensor, an HID or a webcam, and using it to make some general mappings. so, here is my first module guess.:

  • an osc func reasembling the top hyericacal funcion and receing input parameters of a xy vector, and x,y,z toggle, and floating 0-1 parameters
  • a mix fill parameter and a warp1 synth, a collection, of buffers, and a warp1 synthesis definition which would theoreticall look something like:
(
~myBuffers=[
	~buffer1,~buffer2,~buffer3,~buffer4,~buffer5,~buffer6,~buffer7,~buffer8,~buffer9,~buffer10,
	~buffer11,~buffer12,~buffer13,~buffer14,~buffer15,~buffer16,~buffer17,~buffer18,~buffer19,~buffer20,
	~buffer21,~buffer22,~buffer23,~buffer24,~buffer25,~buffer26,~buffer27,~buffer28,~buffer29,~buffer30,
	~buffer31,~buffer32,~buffer33,~buffer34,~buffer35,~buffer36,~buffer37,~buffer38,~buffer39,~buffer40,
	~buffer41,~buffer42,~buffer43,~buffer44,~buffer45,~buffer46,~buffer47
];
)

(
{Mix.fill(n:3,function:{
	Warp1.ar(
		numChannels:2,
		bufnum:~myBuffers.choose,
	pointer:[LFDNoise0,LFDNoise1,LFDNoise3,LFDClipNoise].choose.kr(rrand(5,30))+TRand.kr(0,0.01,Impulse.kr(200)),
		freqScale:1,
		windowSize: 21,
		envbufnum:-1,overlaps:1,windowRandRatio:1,interp:[4,2].choose,mul:1
)})}.play;
);

on the top of that, i want the mix fill, to be allocated, with the length of the blobs or sensor data, and ­­­­­­I want the warp 1, to play just a single grain, and a single envelope of that grain (which should be large scale)
maybe it won’t be a bad idea at all to dynamically load the module using smoothing of the values
maybe it is everything by now
so bearing this in mind how could i technically achieve this, and could you please draft me an implementation of all of this

this is the specification that chatgpt helped me to write.
any form of help to this would be highly appreciated

Example usage:
create a spectral mapping object
 var specMap = SpecMap.new(numChannels: 2, mixFill: 0.5, buffers: ~myBuffers, warpDef: {|bufnum, pointer| Warp1.ar(numChannels: 2, bufnum: bufnum, pointer: pointer, freqScale: 1, windowSize: 21, envbufnum: -1, overlaps: 1, windowRandRatio: 1, interp: [4, 2].choose, mul: 1)}, outputBus: Bus.audio(s, 2), smoothing: 0.1, inputFunc: {|xy, toggle, param| [xy.x, xy.y, toggle, param]});

Define the SpectMap class
SpectMap {
	var numChannels, mixFill, buffers, warpDef, outputBus, smoothing, inputFunc, smoothedInput;
	
	*new { arg numChannels = 2, mixFill = 0.5, buffers = [], warpDef = {|bufnum, pointer| Warp1.ar(numChannels: 2, bufnum: bufnum, pointer: pointer, freqScale: 1, windowSize: 21, envbufnum: -1, overlaps: 1, windowRandRatio: 1, interp: [4, 2].choose, mul: 1)}, outputBus, smoothing = 0.1, inputFunc;
		^super.new.init(numChannels, mixFill, buffers, warpDef, outputBus, smoothing, inputFunc)
	}
	
	 Initialize the object
	*init { arg numChannels, mixFill, buffers, warpDef, outputBus, smoothing, inputFunc;
		this.numChannels = numChannels;
		this.mixFill = mixFill;
		this.buffers = buffers;
		this.warpDef = warpDef;
		this.outputBus = outputBus;
		this.smoothing = smoothing;
		this.inputFunc = inputFunc;
		this.smoothedInput = Array.fill(numChannels+2, 0.0);
	}
	
	 Map the input to the output
	*map {
		var input = inputFunc.value, output;
		smoothedInput = smoothedInput.interpolate(input, smoothing);
		output = Mix.fill(mixFill, {
			warpDef.value.(buffers.choose, [LFDNoise0,LFDNoise1,LFDNoise3,LFDClipNoise].choose.kr(rrand(5,30))+TRand.kr(0,0.01,Impulse.kr(200)))
		});
		Out.ar(outputBus, output);
	}

Define a class for spectral mapping
This class takes an input XY vector, X/Y/Z toggle, and floating 0-1 parameters
It also has a mix fill parameter, a collection of buffers, and a Warp1 synthesis definition
The class has a method called “map” that maps the input to the output using the Warp1 synthesis
The output is written to a designated output buffer
The output buffer can then be used for further processing or playback
The module can be controlled by various input sources such as sensors, HID or webcam
The input data can be smoothed to avoid abrupt changes in the output

Define the spectral mapping class
The class takes the following arguments:

  • numChannels: number of audio channels
  • mixFill: mix fill parameter
  • buffers: a collection of buffers to choose from
  • warpDef: a Warp1 synthesis definition
  • outputBus: the output bus to write to
  • smoothing: the smoothing factor for input data
  • inputFunc: the input function to get the input data
    The class has a single method called “map” that takes no arguments and maps the input to the output
    The output is written to the designated output buffer
    The output buffer can then be used for further processing or playback
    The module can be controlled by various input sources such as sensors, HID or webcam
    The input data can be smoothed to avoid abrupt changes in the output
Example usage:
create a spectral mapping object
 var specMap = SpecMap.new(numChannels: 2, mixFill: 0.5, buffers: ~myBuffers, warpDef: {|bufnum, pointer| Warp1.ar(numChannels: 2, bufnum: bufnum, pointer: pointer, freqScale: 1, windowSize: 21, envbufnum: -1, overlaps: 1, windowRandRatio: 1, interp: [4, 2].choose, mul: 1)}, outputBus: Bus.audio(s, 2), smoothing: 0.1, inputFunc: {|xy, toggle, param| [xy.x, xy.y, toggle, param]});
specMap.map;

Define the spectral mapping class
The class takes the following arguments:

  • numChannels: number of audio channels
  • mixFill: mix fill parameter
  • buffers: a collection of buffers to choose from
  • warpDef: a Warp1 synthesis definition
  • outputBus: the output bus to write to
  • smoothing: the smoothing factor for input data
  • inputFunc: the input function to get the input data
SpecMap {
	arg numChannels, mixFill, buffers, warpDef, outputBus, smoothing, inputFunc;
	var input, lastInput, output;
	var idx = 0;
	var warpSynth = SynthDef("warp", warpDef);

Define a function to get the input data
The function takes the following arguments:

  • xy: an XY vector
  • toggle: an X/Y/Z toggle
  • param: a floating 0-1 parameter
    The function returns an array of input data
    The input data is smoothed to avoid abrupt changes in the output

Approach
Here are some steps you could follow to implement the class:

Define the class: I’ll need to define a SuperCollider class that encapsulates the functionality you’re looking to achieve. I can create a new file in the SuperCollider editor, and start by defining your class:

Define a new class for spectral mapping

SpectralMapping : Object {
	 add instance variables and methods here
}

Define instance variables: You’ll need to define instance variables for the various buffers, synths, and other parameters that you’ll use in the class. Based on your initial code, it seems like you’ll need buffers to hold the grains, a synth to play the grains, and a controller to map the grains. Here’s an example of how you could define the instance variables:

Define instance variables for the spectral mapping class

SpectralMapping : Object {
	var myBuffers;        an array of buffers to hold the grains
	var mySynth;          a synth to play the grains
	var myController;     a controller to map the grains
	var mixFill;          a mix fill parameter to control the density of grains
	var warpDef;          a warp1 synthesis definition
	var warpBuf;          a buffer to hold the warp1 synthesis waveform
	var warpEnv;          an envelope to shape the grains
	 add more instance variables here as needed
}

Define the constructor: You’ll need to define a constructor method for your class that initializes the instance variables. Here’s an example of how you could define the constructor:

Define the constructor method for the spectral mapping class

SpectralMapping : Object {
	*new { |buffers, synth, controller|
		var obj = this.allocate;
		obj.init(buffers, synth, controller);
		obj
	}
	
	init { |buffers, synth, controller|
		myBuffers = buffers;
		mySynth = synth;
		myController = controller;
		mixFill = 0.5;
		warpDef = {
			arg freq, pointer;
			Warp1.ar(
				numChannels: 2,
				bufnum: warpBuf,
				pointer: pointer,
				freqScale: freq,
				windowSize: 21,
				envbufnum: warpEnv.bufnum,
				overlaps: 1,
				windowRandRatio: 1,
				interp: [4, 2].choose,
				mul: 1
			);
		};
		warpBuf = Buffer.alloc(s, 88200, 1);
		warpEnv = Env.linen(0.01, 1, 0.01);
	}
}

In this example, the constructor takes three arguments: an array of buffers to hold the grains, a synth to play the grains, and a controller to map the grains. The init method initializes the instance variables, including creating a new warpDef synthesis definition that uses the Warp1 UGen with some default parameters, and creates a new warpBuf buffer and warpEnv envelope to be used in the synthesis definition.

Define methods: You’ll need to define methods for your class that implement the functionality you’re looking to achieve.

 here's a draft of the class
	 SpectralMap class definition
(
Class.new(\SpectralMap, {
	var myBuffers;  array of buffers
	var mixFill;  mix fill value
	var warpSynth;  warp1 synth definition
	
	*new { |buffers, mixFill, warpSynth|
		var instance;
		instance = super.new;
		instance.myBuffers = buffers;
		instance.mixFill = mixFill;
		instance.warpSynth = warpSynth;
		^instance;
	}
	
	 method to generate a single grain
	 using a buffer from the myBuffers array
	 with a random pointer and envelope
	 specified by the length parameter
	 and return the synthdef and arguments
	 for the Warp1 synth
	*generateGrain { |length|
		var buf = myBuffers.choose;
		var pointer = [LFDNoise0,LFDNoise1,LFDNoise3,LFDClipNoise].choose.kr(rrand(5,30))+TRand.kr(0,0.01,Impulse.kr(200));
		var envelope = EnvGen.kr(Env.linen(rrand(0.1,0.5),1,rrand(0.1,0.5)),doneAction:2);
		var args = [			\bufnum, buf,			\pointer, pointer,			\envbufnum, -1,			\windowSize, 21,			\overlaps, 1,			\windowRandRatio, 1,			\interp, [4,2].choose,
			\mul, 1,
			\envelope, envelope
		];
		^[\Warp1, args];
	}
 method to generate a list of grains
 with the length of the blobs or sensor data
 and return a Pbind with the synthdef
 and arguments for the Warp1 synth
	*generateGrainList { |length|
		var grains = (0..length-1).collect { |i|
			generateGrain(rrand(0.1, 0.5));
		};
		^Pbind(*grains).play;
	}

method to generate and play a list of grains
with the length of the blobs or sensor data
and mix them using Mix.fill
and return the resulting mix

	*map { |length|
		var grainList = generateGrainList(length);
		var mix = Mix.fill(length, mixFill, { grainList });
		mix.play;
		^mix;
	}
})

furthermore:

  • how can this technically be mapped into WFS (wave field synthesis), using a set of stereo mixers, perharps using the WFSCollider class?

I cannot make heads nor tails of what you are after.

Are you wanting sequence grains of on audio file based on some input parameter, e.g., using an x-y pad (or some other 2d vector) to move between different grains, where small movements in the input result in small sonic changes in the output? If so, the answer is to use flucoma.

flucoma is a library. i want to achieve this from scratch, because i want to develop something that suits my needs. and under which i have achieved perfect control

Okay so are asking from the perspective of wanting to know how something like flucoma would implement this, or because you want to achieve some kind of musical result/relationship, or to achieve some type of total mastery over ones tools?

I am asking because the help that you get will be very different depending, and I’d genuinely like to help because these sort of spectral technique are super cool and I’ve used them quite a bit. But the code you’ve posted is basically unreadable and I’m not too sure how to help… not to mention all the obvious errors chatGPT has made.

If you want to know how flucoma might implement this, there is a lot of theory to talk about, but little point in actually writing the code as it would take a long time. Flucoma is a well respected multi-person project that has taken many years to develop. It is also incredibly flexible and allows for many meaningful musical choices. It’s a framework that takes most of the tedious and difficulty away from a range of modern statistically and generative methods, allowing for focus on the music decisions. (In case you didn’t notice by my tone, I think it’s great!).

If you just want the sound, I’m pretty sure there is an excellent tutorial doing exactly what (I think) you have described.

If you are after some kind of enlightenment through technique, why stop there? why not implement supercollider? or the operating system from scratch? why not make hardware? I’m being pretty absurd here, but I think the point stands, technology is like an infinite onion. That’s not to say learning how to implement kd-trees and spectral measurements isn’t worth while. Just that being in control isn’t a good reason, IMHO. Also, you might be better off asking elsewhere for more general computing/ai question.

1 Like

great. i already downloaded flucoma for both MaxMSP, Supercollider, PD, and command line. can you please help me to define some of the above stated things, using flucoma in supercollider?

Hi, can you edit your original post to surround the SC code with triple backticks ```? That way the forum will format it as code blocks and will be much easier to read/copy/paste etc…

dear eric
i made my effort towards improving that. still some stuff i will want to fix. but we will get to that. the post was written in less then one hour with the help of my dear friend chat gpt, so i didn’t have much time to edit and fix it and polish it. but i will get to that along the way

I’m closing this thread because there is no substance to this question other than asking the community to clean up a mess made by a language model, which isn’t a reasonable request.

2 Likes