clickless additive synthesis

hi

making additive synthesis from blob data, and is clicking a bit.
how to solve? am using the audioOutputExample as basis.

this is what i have:
a monobuffer called sonicoutput[BufSize];
every sonic voice has phase, phaseadder, targetfreq vars.

the sound synthesis callback function, where the clicks are happening, is defined as this:

  
  
  
void testApp::audioRequested(float * output, int bufferSize, int nChannels){  
  
	if(bLowFreq) {  
	  
	float volume = 0.7f;  
	float numblobs_rec = CLAMP(cv.contourFinder.nBlobs,0,MAX_SOUND_VOICES);  
	if(numblobs_rec > 0.)  
		numblobs_rec = 1.0 / numblobs_rec ;  
	  
	//1. zero buffer  
	for(int i=0; i<bufferSize; i++)  
		sonicoutput[i]=0.;  
	  
	//2. process  
	for(int n=0; n < MAX_SOUND_VOICES; n++) {		  
  
		while(sonic[n].phase>TWO_PI){  
			sonic[n].phase-=TWO_PI;  
		}  
				  
		bool playing = (n<=cv.contourFinder.nBlobs);  
		  
		for(int i=0; i<bufferSize; i++){  
			sonic[n].phase+=sonic[n].phaseadder;  
			if(playing){  
				float sample = sin(sonic[n].phase);				  
				sonicoutput[i] += sample*volume	 * numblobs_rec; // * reciprocal numblobs to try compensate overflowing sonic buffer  
			}  
		}  
	  
	}  
	  
	//3. output  
	for(int i=0; i<bufferSize; i++){  
		output[i*nChannels] = output[i*nChannels+1] = sonicoutput[i];  
	}  
	  
  
	} //end if bLowFreq  
	else {  
		for(int i=0; i<bufferSize; i++)  
			output[i*nChannels] = output[i*nChannels+1] = sonicoutput[i]=0.;  
	  
	}  
}  
  
  
  

when different blobs enter, and in some num blobs combinations, there are a lot of clicks, sound is aliasing somehow…

wondering if anyone knows a way to bypass this clicks…

thanks!

so placing a double sonic buffer, and lowpassing the output with a filter helps a bit, not perfect yet, perhaps there are better ways?

samples get accumulated in a temp buffer and the main buffer is never zero’ed, continually lowpassed.

here’s what i changed:

  
  
  
  
	//2. playing sample values accumulate sonicoutputtemp  
		  
	//3. output  
	for(int i=0; i<bufferSize; i++){  
		float filter = 0.9;  
		sonicoutput[i] = (1.0-filter) * sonicoutput[i] + filter * sonicoutputtemp[i];  
		output[i*nChannels] = output[i*nChannels+1] = sonicoutput[i];  
	}  
	  
  
  

Hi,

Clicks generally happen when there is an abrupt change in sound sample amplitude. This often can be when there are gaps in the audio buffer. In your case its probably because you are not zeroing the phase of your sine waves when you start them playing and not fading them out when they stop playing. Also, I don’t see you clamping the audio amplitude of the samples to 1.0f which you need to do to avoid clipping (which can also sound like a click).
Hope this helps…

hi grimus,

thanks for your help. i was aware that the clicks were by abrupt sound changes, that’s why i tried the one pole filter. the one solution i think can zero the clicks is fading in/out, that will envolve adding more vars to the class, and making a fading engine for each additive voice, i was trying to bypass that, but might be worth implementing…

zeroing the phase of the sound makes also some gaps in frequency content, that’s why i am using the while loop to try make it continuous, while the phase is above TWO_PI, subtract TWO_PI (because @44.1khz the phase rapidly goes up and sin doesnt cope well with it)

the problem with the one pole filter is that is needs to be very low damping, otherwise the frequency content of the sound gets changed a lot. even with a low param to the filter it gets changed. perhaps i need to dig in better filters, like a two pole, or other stuff…

thanks for your response.

yay, clickless :slight_smile:

a simple lowpass filter in the volume of the sonic voice gets it just right.

in the voice i added volume and targetvolume vars; volume keeps current state, and targetvolume keeps future state, the filter goes smoothly from one to the other.

the code that gets this right is this:

  
  
	//2. process  
	for(int n=0; n < MAX_SOUND_VOICES; n++) {		  
  
		while(sonic[n].phase>TWO_PI){  
			sonic[n].phase-=TWO_PI;  
		}  
			  
		bool playing = ( n <  cv.contourFinder.nBlobs);  
		(playing>0) ? sonic[n].targetvolume = volume : sonic[n].targetvolume = 0.;  
		float filter1 = 0.021;  
		  
		for(int i=0; i<bufferSize; i++){  
		  
			sonic[n].phase+=sonic[n].phaseadder;  
			float sample = sin(sonic[n].phase);				  
			sonic[n].volume = (1.0-filter1)*sonic[n].volume + filter1*sonic[n].targetvolume;  
			sonicoutputtemp[i] += sample*sonic[n].volume;  
		}  
  
		  
	}