11

What Does Perlin Noise Sound Like?

 3 years ago
source link: https://gpfault.net/posts/perlin-sound.txt.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

What Does Perlin Noise Sound Like?

01.png

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:

Sorry, your browser does not support the audio element :(

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:

Sorry, your browser does not support the audio element :(

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

Sorry, your browser does not support the audio element :(

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!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK