Overview
We walk through the process of digitally synthesizing a White Noise sound. To begin we provide the audible outcome, general context and motivation. Then technical information and coding samples follow.
Contents
- What Is White Noise?
- How Does It Sound Like?
- Noise and Pure Waves
- Applications
- Randomness and Independence
- Code Samples
- Audio Stream
- WAV File Header
- WAV File
- Summary
What Is White Noise?
Noise is a special kind of signal. As an audio signal it is neither just a high nor just a low sound but a mixture of a lot of frequencies. White noise contains all audible frequencies from lowest to highest. It is named in analogy with white light which is a mixture of all visible wavelengths of light. The picture of a prism breaking up white light into its colour components illustrates this well.
How Does It Sound Like?
What does white noise sound Like? You already know sounds similar to white noise. It's reminiscent of rain and ocean waves. Sounds of wind and rustling of leaves are also noisy in our sense and can be very enjoyable nonetheless. Here are three examples of noise-like sound.
Rain
Flowing Air
Flowing Water
Noise and Pure Waves
White Noise contains all frequencies at once. It corresponds to the beam of white light that enters the prism in our introductory image. The opposite extreme is a single selected frequency only. It corresponds to one of the coloured light components that leave the prism separately. These two sounds are nothing alike. Have a listen to a single frequency wave in contrast to white noise.
Sine Wave
White Noise
Their waveforms look very different too. To generate white noise, we will use a random stream of bits. For the sound of a pure frequency a sine wave would have to be encoded instead.
Sine Waveform
Random Waveform
Applications
White noise is used as a test signal by audio engineers. The signal to noise ratio is a key measurement in signal processing. Audiophiles care a lot about the signal to noise ratio. Some people swear by noise sounds as a sleeping aid or for relaxation. Others feel it helps with concentrated work and improves focus. It is even used for masking tinnitus. White noise is a fundamental building block of both analogue and digital synthesizers for musical and more general sound design. It can be shaped into snare and hi-hat drum sounds for example.
Randomness and Independence
We generate the white noise sound digitally from scratch. To do so we build a signal in which each data point bears no connection to its previous data points. Each point is independent from rest of data points. This contrasts the case of a pure sine wave in which you can predict the continuation of the periodic wave once you know its general form. A random signal with uniformly distributed amplitude features the characteristics we need.
Code Samples
The code samples are given in C++ as it is widely used in audio applications and digital signal processing. If you would like to see the code in a different language, we encourage you to do the transfer. The general ideas and concepts remain the same.
Audio Stream
The C++ standard library provides us with a number generator that produces a uniformly distributed sequence of values. The default_random_engine
is to be found in <random>
.
1random_chars_engine engine = 2 independent_bits_engine<default_random_engine, CHAR_BIT, unsigned char>;
We wrap the default_random_engine
in a independent_bits_engine
to take control of the type of values that are generated. We would like to obtain byte sized values. A standard engine produces unsigned integer types so choose unsigned char
. CHAR_BIT
is the number of bits that is used to represent values of this type. It specifies that all bits of the generated values shall become random. If we had chosen a smaller number, the remaining bits would always be zero.
1vector<unsigned char> audioStream(bytesPerSecond*lengthOfAudioInSeconds);
The vector data
is our container for the values we generate with the random_chars_engine
. Let us assume that bytesPerSecond
is the number of bytes that are used to encode one second of audible audio and that the size of unsigned char
is one byte. To contain the data for an audio clip of a duration lengthOfAudioInSeconds
we choose the length of the vector data
to be bytesPerSecond*lengthOfAudioInSeconds
.
1generate(begin(audioStream), end(audioStream), ref(engine));
We have prepared a random_chars_engine
as a generator of random values and a suitable vector
as a container to receive the generated values. The helper function generate
now fills the container data
from beginning to end with values from the random_chars_engine
called engine
.
1random_bytes_engine engine =
2independent_bits_engine<default_random_engine, CHAR_BIT, unsigned char>;
3vector<unsigned char> audioStream(bytesPerSecond*lengthOfAudioInSeconds);
4generate(begin(audioStream), end(audioStream), ref(engine));
This results in a vector filled with random values of type 'unsigned char' such that their combined binary representation encodes an audio stream of our desired duration.
We will write this binary representation to a file eventually. This bitstream is to be played back by a media application as the sound it was meant to represent that is as white noise in our case.
WAV Header
Common media players as are not meant to play back a raw bitstream of encoded audio data. To handle it additional information about how to interpret the bitstream are in order. We package the necessary additional metadata together with the raw random bitstream as a WAV (Waveform Audio) file. As such the WAV file is suitable for play back via common media applications. Here we need to take care of the details to satisfy the WAV file format specification. Otherwise upon violation media players may reject the WAV file as invalid and rightfully so. The technical details follow.
We represent the necessary metadata as a struct
in C++ code. It contains the information that make up the metadata.
1typedef struct WAV_HEADER {
2 uint8_t RIFF[4] = {'R', 'I', 'F', 'F'};
3 uint32_t ChunkSize;
4 uint8_t WAVE[4] = {'W', 'A', 'V', 'E'};
5
6 uint8_t fmt[4] = {'f', 'm', 't', ' '};
7 uint32_t Subchunk1Size = 16;
8 uint16_t AudioFormat = 1;
9 uint16_t NumOfChan = 1;
10 uint32_t SamplesPerSec = 44100;
11 uint32_t bytesPerSec = 44100 * 3;
12 uint16_t blockAlign = 2;
13 uint16_t bitsPerSample = 24;
14
15 uint8_t Subchunk2ID[4] = {'d', 'a', 't', 'a'};
16 uint32_t Subchunk2Size;
17} wav_header;
The WAV file format tells us what information is necessary as metadata and how many bytes each piece of information should occupy.
The WAV file header is structured into a main chunk RIFF
that contains two subchunks 'fmt' and 'data'. Each chunk or subchunk has a name and a size. The names of C++'s fixed integer types uint8_t
, uint16_t
and uint32_t
indicate their respective sizes in bits. These types are used to represent integers as well as characters. We use these types to guarantee that their assigned values occupy the correct number of bytes for the WAV file header to be valid.
A WAV file is a special kind of RIFF file. To be a valid RIFF file it has to start with the four bytes that make up the chunk name RIFF
. Next the 'ChunkSize' of the RIFF-chunk is given. It has to contain the size of the remaining header plus the size of the audio bit stream that follows the header in the WAV file. We will fill in this value once we have these sizes at hand. Then the specific kind of the RIFF file is given which is 'WAVE' in case of a WAV file.
Next The first of two subchunks begins. The name of the fmt
subchunk contains a space since it needs to occupy four bytes to be in accordance with the specification. The size of the fmt
subchunk is given as 'Subchunk1Size'. The following six entries are concerned with the details of digital representation of audio data. There are different ways to digitally represent audio data, but we choose the canonical uncompressed format LPCM (Linear Pulse Code Modulation) which is what the value 1
of AudioFormat
stands for. By setting 'NumOfChannels' to one we state that our bit stream will represent a mono audio signal. 'SamplesPerSecond' is the number of data points within a duration of one second that are described by our bit stream and 'bitsPerSample' is the number of bits used for the description of one such data point. The value of 'bytesPerSec' result from the values of 'NumOfChan', 'bitsPerSample' and 'SamplesPerSec'. 'blockAlign' is the number of bytes in a sample across all channels. Its value is derived from 'NumOfChan' and 'bitsPerSample'.
The second and last subchunk 'data' again has a name and a size. Its size is the size of the audio bit stream that follows the header in the WAV file.
To complete the WAV file header, we now set the remaining values that depend on the size of the audio stream and the size of the header itself.
1wav_header meta_data; 2meta_data.ChunkSize = audioStreamSize + sizeof(wav_header) - 8; 3meta_data.Subchunk2Size = audioStreamSize;
WAV File
We have prepared both the audio bitstream and the WAV file header. The two parts combined comprise the WAV file.
The header and audio stream can be written to the WAV file as follows.
1out.write(reinterpret_cast<char *>(&meta_data), sizeof(meta_data)); 2out.write(reinterpret_cast<char *>(&audioStream[0]), audioStream.size());
Modifications
Modifications of bit depths and sample rate lead variations in sound. Feel free to go extremely low for a pronounced effect. A stereo white noise signal will also sound different. For this to work make sure to generate different white noise signals for the right and left channels as our random engine as it stands will produce exactly the same data again when used a second time which is not what you want for the stereo case.
Summary
We got to know the idea and concept of noise in general and more specifically white noise. Then we saw how its sound information can be represented as random data. We added metadata and packaged it together with the audio bitstream as a WAV file for play back.
Dein Job bei codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog-Autor*in
Uwe Kranz
Senior IT Consultant
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.