Colorful noise algorithm

Hey everyone, this is my first time posting on the audacity forums. I’ve been looking at the nyquist language, but I have yet to build a plugin.

Anyway, I have this algorithm which seems to produce different types of noise. Basically, when generating a next random sample, its range of values is limited to a window of values surrounding the previous sample. Here is the code, which i run by pasting into the nyquist prompt for an existing silence track:

;; create array
(setq maxlen 10000000)
(setq samplerate (snd-srate s))
(setf common (make-array (snd-length s maxlen)))

;; set window parameters, windowpos will change
(setq windowsize 0.2) ;; 0.02 -> brown, 2.0 -> white
(setq windowpos 0)

(do
  (
    (n 0 (1+ n))
    (v (snd-fetch s) (setq v (snd-fetch s)))
  )

  ;; code taken from
  ;; https://forum.audacityteam.org/t/help-with-iteration-functions-in-nyquist/7268/4
  ((not v) common)

  ;; sample = windowpos + random * windowsize - windowsize/2
  (setf sample
    (+
      windowpos
      (* (rrandom) windowsize)
      (/ windowsize -2.0)
    )
  )

  ;; clamp sample to [-1, +1] range
  (setf sample (min 1 (max -1 sample)))

  ;; determine probability next sample will go downward
  ;; window for next values is offset accordingly
  (setf downprobability
    (+ 0.5 (* 0.5 sample))
  )

  ;; windowpos = sample + windowsize/2 - downprob * windowsize
  (setf windowpos
    (+
      sample
      (/ windowsize 2.0)
      (* downprobability windowsize -1)
    )
  )

  (setf (aref common n) sample)
) ;; end of do loop

;; turn array into sound
(snd-from-array 0 samplerate common)

It appears to successfully recreate brown noise at a window size of 0.02 (1/100 of possible values) and it degrades into white noise at a window size of 2 (all possible values). It “almost” sounds like blue noise at higher window sizes due to the clamping from -1 to +1, but it’s definitely got problems recreating blue noise and even pink noise: The frequency spectrum it produces is not linear on a logarithmic plot like those seen at http://en.wikipedia.org/wiki/Colors_of_noise.

My question is would some changes to this algorithm allow it to recreate pink noise or would that be a lost cause? My original thought was to use a different “down” probability for the next sample, but have yet to find one that works.

I’ve not yet tested your code.
All I can say for now is that the two snd-fetch commands are not necessary since you do not make use of the original values (from the silent track in the project).
You can of course do some randomization with real audio values (as seed or direction probability) in which case the values are needed.
However, it is always faster to fetch a whole array instead of single values. The only problem is than to take care of the transitions between two such arrays e.g. for longer selections.
You could also tighten up the code by LISP-like setting of the parenthesis’ i.e. without setting them in a new line.

Anyways, congratulations to your first snippet.

Congratulations LimeLaser, it works :smiley:

If you want to turn that code into a plug-in, it should be fairly straightforward to do so. See here for useful information: http://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference

I agree with Robert’s comment about brackets (parentheses). Indentation and parentheses can be a nightmare in longer code unless used concisely.
If your current text editor does not have parentheses matching, I’d highly recommend getting one that does. If you are on Windows, NotePad++ is an excellent (free) text editor. It also has syntax highlighting for LISP (perhaps listed as “Scheme” or “Common Lisp”, don’t remember for sure). While these dialects are a bit different from Nyquist and XLisp, they are close enough to be useful. There is a good guide to indentation (and parentheses) here: http://dept-info.labri.u-bordeaux.fr/~idurand/enseignement/PFS/Common/Strandh-Tutorial/indentation.html

We have a special forum board for Nyquist programming, so I’ll move this topic.

Making “accurate” pink noise is surprisingly difficult. The usual way is to filter white noise. If you are familiar with C++ there is an excellent example of very high quality pink noise in the Audacity code (I’ll post a link if you’re interested).

Robert and I had quite a lengthy discussion about noise a while back. The topic is here: A Study in Pink

Hey all,

I saw your replies; thank you very much for the feedback. I have been working on a plugin, but I’ve been distracted, so what I currently have is very much hacked together. It is currently a “process” type plugin and still requires an existing sound as input:

;nyquist plug-in
;version 3
;type process
;name "Windowed Noise"
;action "Create noise samples restricted to a window"
;info "Try window size exponent -2 for a brown-type noise.nA value of 0 is white noise."

;; control window size by affecting exponent value: 2 * 10^x, 0.001 to 100
;control windowsize-exp "Window Size Exponent" real "2E__" -1 -4 2

;; decide whether to "normalize" sound to [-1, +1] range
;control normalize-select "Normalize?" choice "true,false" "true"

;; create array
(setq maxlen 10000000)
(setq samplerate (snd-srate s))
(setf common (make-array (snd-length s maxlen)))

;; set window parameters, windowpos will change
(setq windowsize (* 2.0 (expt 10.0 windowsize-exp)))
(setq windowpos 0)

(do
  (
    (n 0 (1+ n))
    (v (snd-fetch s) (setq v (snd-fetch s))))

  ;; code taken from
  ;; https://forum.audacityteam.org/t/help-with-iteration-functions-in-nyquist/7268/4
  ((not v) common)

  ;; sample = windowpos + random * windowsize - windowsize/2
  (setf sample
    (+
      windowpos
      (* (rrandom) windowsize)
      (/ windowsize -2.0)))

  ;; clamp sample to [-1, +1] range
  (setf sample (min 1 (max -1 sample)))

  ;; determine probability next sample will go downward
  ;; window for next values is offset accordingly
  (setf downprobability
    (+ 0.5 (* 0.5 sample)))

  ;; windowpos = sample + windowsize/2 - downprob * windowsize
  (setf windowpos
    (+
      sample
      (/ windowsize 2.0)
      (* downprobability windowsize -1)))

  (setf (aref common n) sample))

;; turn array into sound
(setf output (snd-from-array 0 samplerate common))

;; normalize output (if chosen)
(if (string= normalize-select "true")
  (mult output
    (/ 1.0 (peak output ny:all)))
  (mult output 1.0)

I tried to make the lisp-type code look a bit nicer; not sure if this is what y’all had in mind. I have hit a few road blocks though: the string compare to chose whether to normalize the noise does not work and I have no idea how to generate an array of the appropriate length. For the array length, I imagine it would require a duration and a sampling rate, but would I get an existing sample rate or provide that option? Once this issue is addressed, I believe I can ditch the snd-fetch commands since I would be writing produced amplitudes to an array.

Also, I would definitely be interested in seeing the C++ pink noise algorithm! And I will be sure to check out the discussion about noise types; having recently gotten into audio programming I find noise to be a very interesting topic.

Thanks again!

See here: Google Code Archive - Long-term storage for Google Code Project Hosting.
particularly from line 97.
Also: DSP Generation of Pink Noise

Much nicer :slight_smile:

A tip for indenting “IF” statements:

(if (test-expr)
    (then-expr)
    (else-expr))

Note that the test-expression, then-expression and else-expression all line up, which makes it easy to see at a glance the structure of the if-then-else form.
Example from your code with a small change to the indentation:

;; normalize output (if chosen)
(if (string= normalize-select "true")
    (mult output
      (/ 1.0 (peak output ny:all)))
    (mult output 1.0))

The reason that this does not work is because “normalize-select” is not a string value.
It is set here:

;control normalize-select "Normalize?" choice "true,false" "true"

and as you will see in the documentation here (Missing features - Audacity Support) the returned value is an integer, representing the choice index.

I think this will give the desired result:

;; normalize output (if chosen)
(if (= normalize-select 0)
    (mult output
      (/ 1.0 (peak output ny:all)))
    (mult output 1.0))

To create an array that is the same length as the selection duration, there is a convenient “global variable” called “LEN”, the value of which is set by Audacity to the number of samples as a floating point number. For “make-array” you need an integer, so convert to integer with (truncate len)

(setf common (make-array (truncate len)))

If written as a “generate” type plug-in you will probably want a control to set how much noise to generate. The simplest way would be something like:

;control number-of-samples "Number of samples to generate" int "" 1000 0 100000

but probably more convenient for users to set the time in seconds:

;control dur "Duration to generate" real "seconds" 30 0 100
(setf common (make-array (truncate (* dur *sound-srate*))))

where sound-srate is a global variable for the current sound sample rate.