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()));
}
}