IonicWind Software

Aurora Compiler => Software Projects => Topic started by: sapero on May 01, 2008, 02:20:44 PM

Title: purple noise generator
Post by: sapero on May 01, 2008, 02:20:44 PM
This program generates signal very close to purple noise (http://en.wikipedia.org/wiki/Pink_noise), where the energy falls off at 3 dB per octave.
The sound is produced by 32 bit linear feedback shift register (http://en.wikipedia.org/wiki/Linear_feedback_shift_register) (the line with xor operators).
After the noise is generated you will hear it, and will be saved as a wave file with huge amount of very low bass.

By changing wfx.nChannels to 1 or 2, you can generate mono or stereo sound. If the mode is set to stereo, controlling dwSeedL and dwSeedR you can force it to generate mono sound, or just delay one channel.

#include "windows.inc"
#include "stdio.inc"
#include "limits.inc"

sub main()
{
HWAVEOUT     hWave = 0;
HANDLE       hEvent = CreateEvent(0,0,0,0);
WAVEFORMATEX wfx;
WAVEHDR      hdr;
BOOL         fPrepared = false;
BOOL         fSuccess  = false;

hdr.lpData = NULL;

while (1)
{
wfx.wFormatTag      = WAVE_FORMAT_PCM;
wfx.nChannels       = 2;
wfx.wBitsPerSample  = 16;
wfx.nSamplesPerSec  = 44100;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * (wfx.wBitsPerSample>>3) * wfx.nChannels;
wfx.nBlockAlign     = (wfx.wBitsPerSample>>3) * wfx.nChannels;
wfx.cbSize          = 0;

hdr.dwBufferLength  = wfx.nAvgBytesPerSec * 5; /*5 seconds*/
hdr.lpData          = new(byte, hdr.dwBufferLength);
hdr.dwBytesRecorded = 0;
hdr.dwUser          = 0;
hdr.dwFlags         = WHDR_BEGINLOOP | WHDR_ENDLOOP;
hdr.dwLoops         = 0;
hdr.lpNext          = NULL;
hdr.reserved        = 0;

if (!hdr.lpData) {
printf("failed to allocate %d bytes for sound samples\n", hdr.dwBufferLength);
break;
}
MMRESULT res = waveOutOpen(&hWave, WAVE_MAPPER, &wfx, &waveOutProc, hEvent, CALLBACK_FUNCTION);
if (res) {
printf("waveOutOpen failed with code %d\n", res);
break;
}
res = waveOutPrepareHeader(hWave, &hdr, sizeof(hdr));
if (res) {
printf("waveOutPrepareHeader failed with code %d\n", res);
break;
}
fPrepared = true;

// generate nonzero random number
ULONG dwSeedL  = rand(1,65534); // any nonzero value
SHORT wSampleL = 0; // initial amplitude
ULONG dwSeedR  = rand(1,65534); // any nonzero value
SHORT wSampleR = 0; // initial amplitude

if (wfx.nChannels == 1) {
CreateNoiseMono16(hdr.lpData, hdr.dwBufferLength, &wSampleL, &dwSeedL);
}
else if (wfx.nChannels == 2) {
CreateNoiseStereo16(hdr.lpData, hdr.dwBufferLength, &wSampleL, &dwSeedL, &wSampleR, &dwSeedR);
}
else {
printf("unsupported number of channels: %d\n", wfx.nChannels);
break;
}
SaveWaveFile("noises.wav", &wfx, &hdr);

// write sound samples and play
res = waveOutWrite(hWave, &hdr, sizeof(hdr));
if (res) {
printf("waveOutWrite failed with code %d\n", res);
break;
}
if (WAIT_TIMEOUT == WaitForSingleObject(hEvent, 6000)) // wait max 6 seconds
printf("timeout while waiting for buffer\n")
else
printf("done\n");
waveOutPause(hWave);
fSuccess = true;
break;
}
if (fPrepared)
waveOutUnprepareHeader(hWave, &hdr, sizeof(hdr));

if (hWave)
waveOutClose(hWave);

if (hdr.lpData)
delete hdr.lpData;

CloseHandle(hEvent);

if (!fSuccess)
system("pause");

return 0;
}



sub waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance, DWORD dwParam1, DWORD dwParam2)
{
switch (uMsg)
{
case WOM_DONE:
SetEvent(dwInstance);
}
}


sub CreateNoiseMono16(SHORT *pBuffer, int sampleslen, SHORT *pSample, ULONG *pSeed)
{
SHORT max = *pSample;
SHORT min = *pSample;
SHORT *pBufferOrigin = pBuffer;

while (sampleslen > 1)
{
// feedback bits: 31 30 29 9
BOOL feedback = ((*pSeed>>31) xor (*pSeed>>30) xor (*pSeed>>29) xor (*pSeed>>9) & 1);
ULONG seedNext = (*pSeed<<1) | feedback;
// 15 14 12 3
// BOOL feedback = ((*pSeed>>15) xor (*pSeed>>14) xor (*pSeed>>12) xor (*pSeed>>3) & 1);
// USHORT seedNext = (*pSeed<<1) | feedback;

if (feedback && (*pSample > -32768))
{
*pSample--;
}
else if (!feedback && (*pSample < 32767))
{
*pSample++;
}
if (min > *pSample) min = *pSample;
if (max < *pSample) max = *pSample;
*pBuffer = *pSample;
pBuffer += 2;
*pSeed = seedNext;
sampleslen -= 2;
}
// todo: fade out

// amplify whole bufer to fit the range -32768 32767
printf("range: %d - %d\n", min, max);
min = -min;

double multiplier = 32767.0 / __max(min, max);
printf("multiplier: %.1f\n", multiplier);
while (pBufferOrigin < pBuffer)
{
*pBufferOrigin *= multiplier;
pBufferOrigin+=2;
}
}


sub CreateNoiseStereo16(SHORT *pBufferL, int sampleslen, SHORT *pSampleL, ULONG *pSeedL, SHORT *pSampleR, ULONG *pSeedR)
{
SHORT maxL = *pSampleL;
SHORT minL = *pSampleL;
SHORT maxR = *pSampleR;
SHORT minR = *pSampleR;
SHORT *pBufferOrigin = pBufferL;
SHORT *pBufferR      = pBufferL + 2;

while (sampleslen > 3)
{
// feedback bits: 31 30 29 9
BOOL feedbackL = ((*pSeedL>>31) xor (*pSeedL>>30) xor (*pSeedL>>29) xor (*pSeedL>>9) & 1);
ULONG seedNextL = (*pSeedL<<1) | feedbackL;
BOOL feedbackR = ((*pSeedR>>31) xor (*pSeedR>>30) xor (*pSeedR>>29) xor (*pSeedR>>9) & 1);
ULONG seedNextR = (*pSeedR<<1) | feedbackR;

if (feedbackL && (*pSampleL > -32768))
{
*pSampleL--;
}
else if (!feedbackL && (*pSampleL < 32767))
{
*pSampleL++;
}

if (feedbackR && (*pSampleR > -32768))
{
*pSampleR--;
}
else if (!feedbackR && (*pSampleR < 32767))
{
*pSampleR++;
}

if (minL > *pSampleL) minL = *pSampleL;
if (maxL < *pSampleL) maxL = *pSampleL;

if (minR > *pSampleR) minR = *pSampleR;
if (maxR < *pSampleR) maxR = *pSampleR;

*pBufferL = *pSampleL;
pBufferL += 4;

*pBufferR = *pSampleR;
pBufferR += 4;

*pSeedL = seedNextL;
*pSeedR = seedNextR;
sampleslen -= 4;
}
// todo: fade out


// amplify whole bufer to fit the range -32768 32767
printf("range L: %d - %d\n", minL, maxL);
printf("range R: %d - %d\n", minR, maxR);
minL = -minL;
minR = -minR;

double multiplierL = 32767.0 / __max(minL, maxL);
double multiplierR = 32767.0 / __max(minR, maxR);
printf("multiplier L: %.1f\n", multiplierL);
printf("multiplier R: %.1f\n", multiplierR);

while (pBufferOrigin < pBufferL)
{
*pBufferOrigin *= multiplierL;
pBufferOrigin+=2;
*pBufferOrigin *= multiplierR;
pBufferOrigin+=2;
}
}



sub SaveWaveFile(string path, WAVEFORMATEX *wfx, WAVEHDR *hdr)
{
HANDLE hFile = OpenFile(path, MODE_CREATE | MODE_WRITE);
string s = "RIFF****WAVEfmt ****||||||||||||||||data****";
*(DWORD)(&s+ 4) = hdr->dwBufferLength + 36; // RIFF size
*(DWORD)(&s+16) = 16;                       // fmt size
*(DWORD)(&s+16+16+8) = hdr->dwBufferLength; // data size
MoveMemory(&s+20, wfx, 16);
WriteFile(hFile, &s, 44, &path, 0);
WriteFile(hFile, hdr->lpData, hdr->dwBufferLength, &path, 0);
CloseHandle(hFile);
}
Title: Re: purple noise generator
Post by: Barney on May 02, 2008, 02:40:36 AM
This is very useful. Thank you very much for sharing, Sapero. Great work! :)

Barney
Title: Re: purple noise generator
Post by: sapero on May 02, 2008, 02:56:48 AM
I have added a high pass filter, the DC-Bias was too big for me.
sub CreateNoiseMono16(SHORT *pBuffer, int sampleslen, SHORT *pSample, ULONG *pSeed)
{
SHORT max = *pSample;
SHORT min = *pSample;
SHORT *pBufferOrigin = pBuffer;
int NumberOfSamples = sampleslen/2;

while (sampleslen > 1)
{
// feedback bits: 31 30 29 9
BOOL feedback = ((*pSeed>>31) xor (*pSeed>>30) xor (*pSeed>>29) xor (*pSeed>>9) & 1);
*pSeed = (*pSeed<<1) | feedback;
// 15 14 12 3

if (feedback && (*pSample > -32768))
{
*pSample--;
}
else if (!feedback && (*pSample < 32767))
{
*pSample++;
}
*pBuffer = *pSample;
pBuffer += 2;
sampleslen -= 2;
}
//{ high pass filter
word *p = pBufferOrigin;
float *pIn  = new(float, NumberOfSamples); // x
float *pOut = new(float, NumberOfSamples); // y
// word->float
for (i=0; i<NumberOfSamples; i++) {
*pIn[i] = *p[i];
}
// http://en.wikipedia.org/wiki/High-pass_filter
// function highpass(real[0..n] x, real dt, real RC)
*pOut[0] = *pIn[0];
float RC = 100000 * 0.00000001; // 100kOhm * 10nF;  1/(2 PI R C) = 159Hz
float dt = 1.0/44100.0; // sample time

float alpha = RC / (RC + dt);
for (i=1; i<NumberOfSamples; i++) {
*pOut[i] = (alpha * *pOut[i-1]) + alpha*(*pIn[i] - *pIn[i-1]);
}
// float->word
for (i=0; i<NumberOfSamples; i++) {
*p[i] = *pOut[i];
if (min > *p[i]) min = *p[i];
if (max < *p[i]) max = *p[i];
}
delete pOut;
delete pIn;
//}
// amplify whole bufer to fit the range -32768 32767
printf("range: %d - %d\n", min, max);
min = -min;

double multiplier = 32767.0 / __max(min, max);
printf("multiplier: %.1f\n", multiplier);
while (pBufferOrigin < pBuffer)
{
*pBufferOrigin *= multiplier;
pBufferOrigin+=2;
}
}



sub CreateNoiseStereo16(SHORT *pBufferL, int sampleslen, SHORT *pSampleL, ULONG *pSeedL, SHORT *pSampleR, ULONG *pSeedR)
{
SHORT maxL = *pSampleL;
SHORT minL = *pSampleL;
SHORT maxR = *pSampleR;
SHORT minR = *pSampleR;
SHORT *pBufferOrigin = pBufferL;
SHORT *pBufferR      = pBufferL + 2;
int NumberOfSamples = sampleslen/4;

while (sampleslen > 3)
{
// feedback bits: 31 30 29 9
BOOL  feedbackL = ((*pSeedL>>31) xor (*pSeedL>>30) xor (*pSeedL>>29) xor (*pSeedL>>9) & 1);
*pSeedL = (*pSeedL<<1) | feedbackL;
BOOL  feedbackR = ((*pSeedR>>31) xor (*pSeedR>>30) xor (*pSeedR>>29) xor (*pSeedR>>9) & 1);
*pSeedR = (*pSeedR<<1) | feedbackR;


if (feedbackL && (*pSampleL > -32768))
{
*pSampleL--;
}
else if (!feedbackL && (*pSampleL < 32767))
{
*pSampleL++;
}

if (feedbackR && (*pSampleR > -32768))
{
*pSampleR--;
}
else if (!feedbackR && (*pSampleR < 32767))
{
*pSampleR++;
}

*pBufferL = *pSampleL;
pBufferL += 4;

*pBufferR = *pSampleR;
pBufferR += 4;

sampleslen -= 4;
}
//{ high pass filter
word *pL = pBufferOrigin;
word *pR = pBufferOrigin+2;
float *pInL  = new(float, NumberOfSamples); // x
float *pInR  = new(float, NumberOfSamples); // x
float *pOutL = new(float, NumberOfSamples); // y
float *pOutR = new(float, NumberOfSamples); // y
// word->float
for (i=0; i<NumberOfSamples; i++) {
*pInL[i] = *pL[i*2];
*pInR[i] = *pR[i*2];
}
// http://en.wikipedia.org/wiki/High-pass_filter
// function highpass(real[0..n] x, real dt, real RC)
float RC = 100000 * 0.00000001; // 100kOhm * 10nF;  1/(2 PI R C) = 159Hz
float dt = 1.0/44100.0; // time of sample
float alpha = RC / (RC + dt);
*pOutL[0] = *pInL[0];
*pOutR[0] = *pInR[0];
for (i=1; i<NumberOfSamples; i++) {
*pOutL[i] = (alpha * *pOutL[i-1]) + alpha*(*pInL[i] - *pInL[i-1]);
*pOutR[i] = (alpha * *pOutR[i-1]) + alpha*(*pInR[i] - *pInR[i-1]);
}
/* again ?
MoveMemory(pInL, pOutL, sizeof(float) * NumberOfSamples);
MoveMemory(pInR, pOutR, sizeof(float) * NumberOfSamples);
for (i=1; i<NumberOfSamples; i++) {
*pOutL[i] = (alpha * *pOutL[i-1]) + alpha*(*pInL[i] - *pInL[i-1]);
*pOutR[i] = (alpha * *pOutR[i-1]) + alpha*(*pInR[i] - *pInR[i-1]);
}
*/
// float->word
for (i=0; i<NumberOfSamples; i++) {
*pL[i*2] = *pOutL[i];
*pR[i*2] = *pOutR[i];
if (minL > *pL[i]) minL = *pL[i];
if (maxL < *pL[i]) maxL = *pL[i];
if (minR > *pR[i]) minR = *pR[i];
if (maxR < *pR[i]) maxR = *pR[i];
}
delete pOutL;
delete pOutR;
delete pInL;
delete pInR;
//}


// amplify whole bufer to fit the range -32768 32767
printf("range L: %d - %d\n", minL, maxL);
printf("range R: %d - %d\n", minR, maxR);
minL = -minL;
minR = -minR;

double multiplierL = 32767.0 / __max(minL, maxL);
double multiplierR = 32767.0 / __max(minR, maxR);
printf("multiplier L: %.1f\n", multiplierL);
printf("multiplier R: %.1f\n", multiplierR);

while (pBufferOrigin < pBufferL)
{
*pBufferOrigin *= multiplierL;
pBufferOrigin+=2;
*pBufferOrigin *= multiplierR;
pBufferOrigin+=2;
}
}