Tone Generator - clicking and popping

I am following @admsyn 's great sound tutorial in ofBook. I have a basic tone generator working, but getting clicks and pops when it plays.

I have tried messing with the volume setting using ofLerp, but am getting the problem even with gradual changes. It’s subtle, but definitely there.

Is it something with my sound card? I am on Win 8.1 in Bootcamp on a MacBook Pro 15". I get the same results in Mac OS. Is there anything else that I should try?

Here’s the code…

ofApp.h

#pragma once

#include "ofMain.h"

class ofApp : public ofBaseApp{

public:
	void setup();
	void update();
	void draw();
	
	void keyPressed(int key);

	void updateWaveform(int waveformResolution);
	void audioOut(float * output, int bufferSize, int nChannels);

	float startTime;

	std::vector<float> waveform; // this is the lookup table
	double phase;
	float frequency;
	float volume;

	ofMutex waveformMutex;
	ofPolyline waveLine;
	ofPolyline outLine;
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup() {

	ofSetFrameRate(60);

	phase = 0;
	volume = 0;
	frequency = 0;

	updateWaveform(512);
	ofSoundStreamSetup(1, 0); // mono output

	startTime = ofGetElapsedTimef();
}

//--------------------------------------------------------------
void ofApp::update() {

	ofSetWindowTitle(ofToString(ofGetFrameRate(), 1));

	ofScopedLock waveformLock(waveformMutex);

	float timePassed = ofGetElapsedTimef() - startTime;
	float attack = 0.8;
	float release = 0.1;

	if (timePassed > 0 && timePassed < 0.5) {
		frequency = 261.63; // C4
		volume = ofLerp(volume, 1, attack);
	}
	else if (timePassed > 1 && timePassed < 1.5) {
		volume = ofLerp(volume, 1, attack);
	}
	else if (timePassed > 2 && timePassed < 2.5) {
		volume = ofLerp(volume, 1, attack);
	}
	else if (timePassed > 3 && timePassed < 4) {
		frequency = 523.25; // C5
		volume = ofLerp(volume, 1, attack);
	}
	else {
		volume = ofLerp(volume, 0, release);
	}
}

//--------------------------------------------------------------
void ofApp::draw() {
	ofBackground(ofColor::black);
	ofSetLineWidth(5);
	ofSetColor(ofColor::lightGreen);
	outLine.draw();
	ofSetColor(ofColor::cyan);
	waveLine.draw();
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key) {
	if (key == ' ') {
		startTime = ofGetElapsedTimef();
	}
}

void ofApp::updateWaveform(int waveformResolution) {
	waveform.resize(waveformResolution);
	waveLine.clear();

	// "waveformStep" maps a full oscillation of sin() to the size 
	// of the waveform lookup table
	float waveformStep = (PI * 2.) / (float)waveform.size();

	for (int i = 0; i < waveform.size(); i++) {
		waveform[i] = sin(i * waveformStep);
		waveLine.addVertex(ofMap(i, 0, waveform.size() - 1, 0, ofGetWidth()),
			ofMap(waveform[i], -1, 1, 0, ofGetHeight()));
	}
}

void ofApp::audioOut(float * output, int bufferSize, int nChannels) {

	ofScopedLock waveformLock(waveformMutex);

	float sampleRate = 44100;
	float phaseStep = frequency / sampleRate;

	outLine.clear();

	for (int i = 0; i < bufferSize * nChannels; i += nChannels) {
		phase += phaseStep;
		int waveformIndex = (int)(phase * waveform.size()) % waveform.size();
		output[i] = waveform[waveformIndex] * volume;
		outLine.addVertex(ofMap(i, 0, bufferSize - 1, 0, ofGetWidth()),
			ofMap(output[i], -1, 1, 0, ofGetHeight()));
	}
}

Can you put up a recording? Might be easier to diagnose that way. Also, is the popping constant or is it triggered by certain events?

The artifact is noticeable as the volume is changing. It’s like a flutter or glitter behind the desired tone. It does not seem to be there at full volume.

Here’s a recording. Its not great since I had to use the internal microphone.
WARNING - It’s a bit loud.
https://drive.google.com/file/d/0B5pd3uMoNX8XOGx5bEh5TUpyTWc/view?usp=sharing

Ah ok, what’s happening there is that each time you change the volume it’s creating a tiny discontinuity in the waveform, which sounds like a pop.

When you change the volume in update(), the next time audioOut() happens it’ll create a waveform with a greater amplitude. So, there’ll probably be a little jump from the last sample of the last buffer and the first sample of the next buffer.

One way to get around this is by introducing a new variable for volume and ofLerp-ing it towards the volume value you set it in update() while writing samples in the for-loop portion of audioOut(). You can get away with a pretty low lerp rate of something like 0.05.

There are more robust ways of doing this, like keeping queuing up a few buffers in advance so you can stitch together the buffer boundaries a bit more intelligently, but that’s overly complicated for this :).

Hi,

I have a similar issue, tried the ofLerping inside update() and inside audioOut(), but I still have a little bit discontinuity in the waveform.

I even tried with a lerp rate of 0.02.

My fastest volume change is from 0 to 1 to 0 (silence to max to silence) within 1 second.

My ofLerp implementation inside audioOut():

finalVol = ofLerp(finalVol, 0.0025 * (1 - sin(1.5 - (1 / (float)volTime) * TWO_PI * ofGetElapsedTimef())) * volume), 0.02);

phaseAdder = 0.95f * phaseAdder + 0.05f * phaseAdderTarget;

for (int i = 0; i < bufferSize; i++) {
	phase += phaseAdder;
	float sample = sin(phase);
	output[i*nChannels] = sample * (float)finalVol;
	output[i*nChannels + 1] = sample * (float)finalVol;
}

The lerping helped a bit, it takes the “noise” down, but also smoothens out the volume change alot.

Thanks in advance
Jaan

Hi Jaan,

I was never able to figure this out and ended up using a pre recorded tone. What I needed was really simple. I would ping Adam and see if he can help.

Good luck!
Rob