I am trying to make brown noise in C++, and to play the sound of it. You can hear the brown noise, but I constantly hear clicking in the background and I don't know why.
Here is my code:
#include <xaudio2.h>
#include <iostream>
#include <random>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
XAUDIO2_BUFFER buffer;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
BYTE pDataBuffer[2*l];
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
w[0] = w[l - 1] + dis(gen)*ampl;
for (int t = 1; t < l; t++) {
tt = (float)(t + frame*l); //total time
w[t] = w[t - 1] + dis(gen)*ampl;
if (w[t] > ampl) {
cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16*(intw/pow16[k]));
intw -= bytw[k]*(pow16[k]/16);
bytw[k] += (BYTE)(intw/(pow16[k]/16));
intw -= (intw/(pow16[k]/16))*pow16[k]/16;
}
pDataBuffer[2*t] = bytw[0];
pDataBuffer[2*t + 1] = bytw[1];
}
cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) {}
}
buffer.AudioBytes = 2*l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer;
buffer.Flags = XAUDIO2_END_OF_STREAM;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = {0};
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
cout << "start";
while (loop()) {}
return 0;
}
The line before the for-loop in loop()
is to make sure that the first element nicely attaches itself to the last element of the previous iteration.
To make sure that w
doesn't go over ampl
or under -ampl
, I have added a couple lines that make them bounce back, and I make it output "upper edge" or "lower edge" respectively so that you know when this is happening. As you notice, the clicking also happens when the w
is not near the edges.
As a test to make sure it isn't because of XAudio2 being implemented wrongly, you can comment the first line in loop()
that defines the first element of w
; make the for-loop (in the next line) start from 0
; comment the lines that create the brown noise; and uncomment one of the two lines after that: the first line to hear a sine wave sound, the second line to hear a square wave sound (both with a frequency of about 44100/1000 = 44.1 Hz, which you can change around by changing how p
is initialized in init()
). You will (hopefully) hear a clean sine/square wave sound.
So what is going wrong?
You have two issues in your code:
BuffersQueued
to reduce the CPU usage.pDataBuffer[0]
or pDataBuffer[1]
so they will always be 0
.This code works:
#include <xaudio2.h>
#include <iostream>
#include <random>
#include <array>
#include <thread>
using namespace std;
#define PI2 6.28318530717958647692f
#define l 2205 //0.05 seconds
bool init();
bool loop();
random_device rd;
mt19937 gen(rd());
uniform_real_distribution<> dis(-.01, .01);
IXAudio2MasteringVoice* pMasterVoice;
IXAudio2* pXAudio2;
IXAudio2SourceVoice* pSourceVoice;
const size_t bufferCount = 64;
std::array<XAUDIO2_BUFFER, bufferCount> buffers;
WAVEFORMATEX wfx;
XAUDIO2_VOICE_STATE state;
std::array<std::array<BYTE,2 * l>, bufferCount> pDataBuffers;
BYTE bytw[2];
int pow16[2];
float w[l];
int frame, p;
float tt, ampl;
bool loop() {
float prevW = w[l - 1];
auto& pDataBuffer = pDataBuffers[frame & (bufferCount-1)];
auto& buffer = buffers[frame & (bufferCount - 1)];
for (int t = 0; t < l; t++) {
tt = (float)(t + frame * l); //total time
w[t] = prevW + dis(gen) * ampl;
if (w[t] > ampl) {
//cout << "upper edge ";
w[t] = ampl - fmod(w[t], ampl);
}
if (w[t] < -ampl) {
//cout << "lower edge ";
w[t] = -fmod(w[t], ampl) - ampl;
}
//w[t] = sin(PI2*tt/p)*ampl;
//w[t] = (fmod(tt/p, 1) < .5 ? ampl : -ampl)*(.5f - 2.f*fmod(tt/p, .5f));
prevW = w[t];
int intw = (int)w[t];
if (intw < 0) {
intw += 65535;
}
bytw[0] = 0; bytw[1] = 0;
for (int k = 1; k >= 0; k--) {
//turn integer into a little endian byte array
bytw[k] += (BYTE)(16 * (intw / pow16[k]));
intw -= bytw[k] * (pow16[k] / 16);
bytw[k] += (BYTE)(intw / (pow16[k] / 16));
intw -= (intw / (pow16[k] / 16)) * pow16[k] / 16;
}
pDataBuffer[2 * t] = bytw[0];
pDataBuffer[2 * t + 1] = bytw[1];
}
//cout << endl << endl;
if (frame > 1) {
//wait until the current one is done playing
while (pSourceVoice->GetState(&state), state.BuffersQueued > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(1); }
}
buffer.AudioBytes = 2 * l; //number of bytes per buffer
buffer.pAudioData = pDataBuffer.data();
buffer.Flags = 0;
pSourceVoice->SubmitSourceBuffer(&buffer);
if (frame == 1) {
pSourceVoice->Start(0, 0);
}
frame++;
return true;
}
bool init() {
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
pXAudio2 = nullptr;
XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
pMasterVoice = nullptr;
pXAudio2->CreateMasteringVoice(&pMasterVoice);
wfx = { 0 };
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = (WORD)1; //mono
wfx.nSamplesPerSec = (DWORD)44100; //samplerate
wfx.wBitsPerSample = (WORD)16; //16 bit (signed)
wfx.nBlockAlign = (WORD)2; //2 bytes per sample
wfx.nAvgBytesPerSec = (DWORD)88200; //samplerate*blockalign
wfx.cbSize = (WORD)0;
pSourceVoice = nullptr;
pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx);
tt = 0, p = 1000, ampl = 10000;
pow16[0] = 16;
pow16[1] = 4096;
frame = 0;
return true;
}
int main() {
if (!init()) return 1;
while (loop()) {}
return 0;
}
I haven't tried to follow all of your logic but it seems over complicated and could definitely be simplified.
The massive use of global variables is also not a great way to write a program. You should move variables inside the functions where possible, otherwise either pass them to the function as arguments or use a class to hold the state.