What Does Perlin Noise Sound Like?

In one of the previous posts, we discussed how to use Perlin noise to generate some imagery. This time, I wanted to try something slightly different: I was curious what it would be like to generate an audio signal (as opposed to pictures) with Perlin noise.

The Noise Function

Since audio is a one-dimensional signal, the one-dimensional version of the noise function should fit our purposes nicely. With some very minor changes, it's just a copy of the code from the previous post (we're going to be using C++ here, while the original version was meant to be used in a GLSL shader). Anyway, here's the code we're going to be using to generate noise:


float fade(float t) {
  return t*t*t*(t*(t*6.0f - 15.0f) + 10.0f);
}

float grad(float p) {
  static float rand_noise[44100];
  static bool have_noise = false;
  if (!have_noise) {
    srand(time(NULL));
    for (size_t i = 0; i < sizeof(rand_noise) / sizeof(float); ++i) {
      rand_noise[i] = static_cast(rand())/static_cast(RAND_MAX);
    }
    have_noise = true;
  }
  float v =  rand_noise[static_cast(floor(p)) % (sizeof(rand_noise)/sizeof(float))];
  return v > 0.5f ? 1.0f : -1.0f;
}

float noise(float p) {
  float p0 = floor(p);
  float p1 = p0 + 1.0f;

  float t = p - p0;
  float fade_t = fade(t);

  float g0 = grad(p0);
  float g1 = grad(p1);

  return (1.0f - fade_t)*g0*(p - p0) + fade_t*g1*(p - p1);
}

The main difference is how we generate randomness for the gradients (GLSL version used a noise texture).

Generating Sound

We're going to be writing a stream of raw PCM samples to a file, using 44.1 kHz sampling rate (meaning 1 second worth of sound will contain 44100 PCM samples). We shall be using signed 16-bit samples. The code is actually quite straighforward:


int main() {
  const int duration_seconds = 60;
  const int sampling_rate_hz = 44100;  
  const int freq = 440;

  FILE * output = fopen("output.pcm", "wb");
  for (size_t sample_idx = 0;
       sample_idx < duration_seconds * sampling_rate_hz;
       ++sample_idx) {

    /* The expression below defines how noise function samples are distributed
    across seconds. We want each second to contain `freq' samples of noise at
    integer points. */
    float x1 =
      static_cast(sample_idx) / (static_cast(sampling_rate_hz) / static_cast(freq));
      
    float n = noise(x1);
    float max = std::numeric_limits::max();
    float min = std::numeric_limits::min();
    float range = max - min;
    float s = range * n;
    int16_t pcm_sample = s;
    fwrite(&pcm_sample, sizeof(int16_t), 1, output);
  }
  fclose(output);
  return 0;
}

If you compile and run the code, it will generate a file called "output.pcm", containing raw pcm samples. You can play it in Audacity. To do so, in Audacity, click File -> Import -> Raw Data, select the output file, ensure that the import dialog has encoding set to "Signed 16 bit PCM", number of channels set to 1 and that the sampling rate is set to 44100 Hz.

Alternatively, you can simply listen to the result here:

Of course, it is easy to create fractal noise by simply adding noise sampled at different frequencies together:


int main() {
  const int duration_seconds = 60;
  const int sampling_rate_hz = 44100;
  const int freq = 110;
  const int freq2 = 220;
  const int freq3 = 440;

  FILE * output = fopen("output.pcm", "wb");
  for (size_t sample_idx = 0;
       sample_idx < duration_seconds * sampling_rate_hz;
       ++sample_idx) {
    float x1 = static_cast(sample_idx) / (static_cast(sampling_rate_hz) / static_cast(freq));
    float x2 = static_cast(sample_idx) / (static_cast(sampling_rate_hz) / static_cast(freq2));
    float x3 = static_cast(sample_idx) / (static_cast(sampling_rate_hz) / static_cast(freq3));
    float n = noise(x1);
    float n2 = noise(x2);
    float n3 = noise(x3);
    float max = std::numeric_limits::max();
    float min = std::numeric_limits::min();
    float range = max - min;
    float s = range * (0.5 * n + 0.3 * n2 + 0.2 * n3);
    int16_t pcm_sample = s;
    fwrite(&pcm_sample, sizeof(int16_t), 1, output);
  }
  fclose(output);
  return 0;
}

And here's the result of that:

Compare the above to white noise and note how starkly different it sounds:

Just like its visual representation, white noise sounds synthetic, while Perlin noise sounds more natural. I find it similar to the sound you hear in the cabin of a flying plane, or maybe the muffled sound of traffic you hear from the top floor of a high-rise building.

I actually find this relaxing to listen to. Recommend putting it on to drown out the voices of people talking in the office to help you concentrate :-)


Like this post? Follow this blog on Twitter for more!