TIL How To Make Brown Noise in Python

My daughter has been a terrible sleeper since we brought her home from the hospital and the only thing that makes a difference is white noise. We learned this while I was riding the Copenhagen Metro late at night with her so that my wife could get some sleep. I realized she was almost immediately falling asleep when we got on the subway.

After that we experimented with a lot of "white noise" machines, which worked but ultimately all died. The machines themselves are expensive and only last about 6-8 months of daily use. I decided to rig up a simple Raspberry Pi MP3 player with a speaker and a battery which worked great. Once it's not a rats nest of cables I'll post the instructions on how I did that, but honestly there isn't much to it.

It took some experimentation to get the "layered brown noise" effect I wanted. There are obviously simpler ways to do it that are less computationally expensive but I like how this sounds.

import numpy as np
from scipy.io.wavfile import write
from scipy import signal

# Parameters for the brown noise generation
sample_rate = 44100  # Sample rate in Hz
duration_hours = 1  # Duration of the audio in hours
noise_length = duration_hours * sample_rate * 3600  # Total number of samples

# Generate white noise
white_noise = np.random.randn(noise_length)

# Define frequency bands and corresponding low-pass filter parameters
freq_bands = [5, 10, 20, 40, 80, 160, 320]  # Frequency bands in Hz
filter_order = 4
low_pass_filters = []

for freq in freq_bands:
    b, a = signal.butter(filter_order, freq / (sample_rate / 2), btype='low')
    low_pass_filters.append((b, a))

# Generate multiple layers of brown noise with different frequencies
brown_noise_layers = []
for b, a in low_pass_filters:
    filtered_noise = np.convolve(white_noise, np.ones(filter_order)/filter_order, mode='same')
    filtered_noise = signal.lfilter(b, a, filtered_noise)
    brown_noise_layers.append(filtered_noise)

# Mix all layers together
brown_noise_mixed = np.sum(np.vstack(brown_noise_layers), axis=0)

# Normalize the noise to be within the range [-1, 1]
brown_noise_mixed /= max(abs(brown_noise_mixed))

# Convert to int16 as required by .wav file format
audio_data = (brown_noise_mixed * 32768).astype(np.int16)

# Write the audio data to a .wav file
write('brown_noise.wav', sample_rate, audio_data)

Then to convert it from .wav to mp3 I just ran this: ffmpeg -i brown_noise.wav -ab 320k brown_noise.mp3

So in case you love brown noise and wanted to make a 12 hour or whatever long mp3, this should get you a nice premium multilayer sounding version.