Help with Nyquist filters

Hello there, I am trying to understand filters and have some questions. I’m using window 7, audacity 2.1.2 and used the .exe installer. I’ll preface this by saying I have little to no knowledge of either computer programming or signal processing.

So, as I understand it, this

 (lp (hzosc 300) 50)

creates a barrier to any frequency over 50hz. When applied to the 300hz frequency, it lowers but does not silence the tone. Obviously, it is more of a band fade than a strict cut off. Is there any way to adjust how strict the cutoff is? Or how wide that band is? Some examples would be where the filter silences everything from 51hz and above? Or I set the range, so that a range setting of, say, 400hz would progressively quiet everything between 50hz and 450hz then produce silence on any tone over 450hz?

I experimented with higher order filters, and managed to get some response with the Q but nothing I understand. Like this

(highpass2 (hzosc 300) 500 600)

seems to add something, maybe resonance(?), then filter it away on a curve whose length is affected by the Q setting. Now I understand resonance and especially coefficients could be beyond my current comprehension levels, so I was happy that the even higher order filters didn’t have a Q setting.

It seemed to me that the higher the order, the more strict the cutoff setting became. With

 (lowpass8 (hzosc 300) 200)

filtering much more than lowpass6 and even more than lowpass4. Now this alone would maybe be enough for me, however, I couldn’t get pwl to work with these.

 (lp (hzosc 300) (pwl 0 50 .5 50 .55 1000 1 1000))

works fine but

(lowpass4 (hzosc 300) (pwl 0 50 .5 50 .55 1000 1 1000))

produces no sound. Is there a way to control the higher order filters with piece-wise approximations?

Last question. I can get this to work if I put the same filter on each individual osc but is there a way to apply one filter to all of these at once? I couldn’t figure out the syntax.

(sim
  (scale 0.2 (hzosc 220))
  (scale 0.2 (hzosc 330))
  (scale 0.2 (hzosc 440)))

I’d greatly appreciate any help or knowledge in understanding how these things work, but the three basic questions were:

1: Can you adjust specific cutoff settings?
2: Can you use piece-wise approximations on higher order filters?
3: Can you apply one filter on a group of simultaneous oscillators?

Thank you!

That’s the “Q” or quality value of the filter. Effectively, the steepness or efficiency of the sides of bandpass or characteristic. So if there’s a Q value in the programming instructions, that’s what it does.

You can get a much better idea faster for what’s happening by generating white noise (all frequencies in a basket), apply your filter and then Analyze > Plot Spectrum to see what you did. Attached is what white noise looks like after Steve’s 100Hz voice rumble filter is applied (scroll or click the graphic).

You have some interesting surprises waiting for you the first time you apply filters to sound. Yes, natural sound contains many different frequencies and sizes (the oboe “A” at the top of the orchestra contains 440Hz, but also a festival of other tones), but also phases.

They don’t all add. You may find after carefully removing specific tones with surgical precision that the overall performance volume went up. You removed the bucking frequencies.

Koz
Screen Shot 2016-04-09 at 12.07.27.png

Thank you for your quick response! You blew my mind a little bit there explaining about the phases and bucking waves. That was really interesting and I’d never considered it before, but makes total sense.

Ok, so you answered my first question, Q is the only cutoff/bandwidth control. I’ll continue to play around with that and see if I can get the precision I’m looking for, but the varying cutoff intensities for the higher order filters may work well enough for me that I don’t have to master the Q, if only I could get the piece-wise approximations to work.

I’m trying to do an additive synthesis thing by stacking various harmonics, and inharmonics, on a base tone, while controlling those harmonics with various pitch and amp envelopes, to produce interesting, evolving timbres. I plan on using the filters on combinations of the timbres and noise.

You’ve definitely expanded my understanding of filters and I thank you for that.

Any ideas on my other two questions?

Sorry if this is a double post, I thought I posted this a few hours ago but now think I hit the wrong button or something. (I frequently do dumb things like that)

[Duplication Removed]

You are in the moderation period where we make sure you’re not trying to sell us insurance or male enhancing products. If you hang long enough, it goes away. The posting response messages should tell you what’s happening.

Koz

I’m trying to do an additive synthesis thing by stacking various harmonics, and inharmonics, on a base tone, while controlling those harmonics with various pitch and amp envelopes, to produce interesting, evolving timbres.

Oh, cool. Like a Moog Synthesizer.

There were two key features in Moog’s new system: he analyzed and systematized the production of electronically generated sounds, breaking down the process into a number of basic functional blocks, which could be carried out by standardized modules. He proposed the use of a standardized scale of voltages for the electrical signals that controlled the various functions of these modules—the Moog oscillators and keyboard, for example, used a standard progression of 1 volt per octave for pitch control. This specific definition means that adding or subtracting control voltage simply transposes pitch, a very valuable feature.

While you could do that in Audacity, it could get very painful in a hurry. For one thing, tone generation always starts with zero, so pushing two tones sooner and later with respect to each other involves selecting one of them and invoking the Time Shift Tool, which only has ratty control. And that’s only two tones.

Let’s see. For modulation we have Effect > Vocoder. That uses one sound channel to control the character of another. Also known as the Peter Frampton effect.

And etc. Each tool and filter is a skillset. Bobby Moog made his instrument popular by limiting the effects, tools and control to the ones most likely to be useful. For example, the Audacity Time Shift Tool will cheerfully push one tone so late you have to come back in ten minutes to hear it. That’s impressive, but probably not useful to create a tonal effect.

Koz

I’ve actually already developed an interface in Autoit Script to easily construct these weird drawn out droning noises with the process I described. It was originally connected to Jsynth for the tone generation but it looks like I can hook it up to Audacity instead for easier and faster results with a good possibility of expanded capabilities. I’m focused on the Nyquist code because my script will be writing all the necessary Nyquist code for me, all I’ll have to do is copy and paste it into the prompt.

I only discovered Audacity a few days ago and have been pretty ecstatic about its abilities. That Audacity includes functions that are “probably not useful” makes me love it even more. Plus, I’ve been pretty salty since Adobe took over Cool Edit, renamed it Audition and proceeded to remove all the features that made it cool.

There is some explanation of high / low pass filters in the manual:
http://manual.audacityteam.org/o/man/high_pass_filter.html
http://manual.audacityteam.org/o/man/low_pass_filter.html
These two effects are written using Nyquist. If you look in the Audacity plug-ins folder you will find “highpass.ny” and “lowpass.ny”. Open those files in a plain text editor to see the code (Notepad++ recommended https://notepad-plus-plus.org/).

No, only the “first order” LP and HP filters, but you can “cascade” low order filters to produce steeper (higher order) filters:

(lp
  (lp
    (lp
      (lp (noise)
          (pwl 0 50 .5 50 .55 1000 1 1000))
      (pwl 0 50 .5 50 .55 1000 1 1000))
    (pwl 0 50 .5 50 .55 1000 1 1000))
  (pwl 0 50 .5 50 .55 1000 1 1000))

or written another way:

(let ((sig (noise))
      (control (pwl 0 50 .5 50 .55 1000 1 1000)))
  (dotimes (i 4 sig)
    (setf sig (lp sig control))))





(lowpass2
  (sim
    (scale 0.2 (hzosc 220))
    (scale 0.2 (hzosc 330))
    (scale 0.2 (hzosc 440)))
  250)

Excellent! This should help me do exactly what I’d like. Thanks so much Koz and Steve! Keep up the good work!

The built-in “Generate > Tone” effect always starts the tone at “zero phase”, but with Nyquist you don’t have that limitation.
For the basic “OSC” oscillator, the full syntax is:

(osc pitch [duration table phase])

where duration, table and phase are optional arguments (but must be supplied in order.
So, for example, to create a mix of two triangle waves, both with frequencies of 440 Hz, but one with its phase shifted by 90 degrees, you could have something like:

(mult 0.4
  (sim
    (osc (hz-to-step 440) 1 *tri-table* 0)
    (osc (hz-to-step 440) 1 *tri-table* 90)))

which produces a waveform like this:
firsttrack001.png

You can also use “eq-band”.
All three arguments frequency, gain boost/cut, and bandwidth (in octaves) can be replaced by sounds, e.g.

(eq-band *track* (pwl 0 200 1 500 1) (pwl 0 -20 1 6 1) (pwl 0 0.1 0.5 2 1 0.3 1))

Just for fun:

(setf hz1 440)
(setf hz2 220)  
(stretch-abs 3.0
  (mult 0.2 (pwlv 0 0.01 1 0.2 0.4 0.8 0.3 1.0 0)
    (lp 
      (sim
        (osc-pulse (pwlv hz1 0.4 hz1 0.5 hz2 1 hz2)
                   (mult 0.4 (sum 1 (hzosc 1))))
        (osc-pulse (pwlv (* 1.01 hz1) 0.4 (* 1.01 hz1) 0.5 (* 1.01 hz2) 1 (* 1.01 hz2))
                    (mult 0.4 (sum 1 (hzosc 2.3))))
        (osc-pulse (pwlv (* 1.5 hz1) 0.4 (* 1.5 hz1) 0.5 (* 1.5 hz2) 1 (* 1.5 hz2))
                   (mult 0.4 (sum 1 (hzosc 7.3)))))
      (pwlv 40 0.3 10000 0.8 400 1 20))))

Making it into a sequence:

(defun tone (hz dur)
  (stretch-abs dur
    (mult 0.4 (pwlv 0 0.01 1 0.2 0.4 0.8 0.3 1.0 0)
      (lp 
        (sim
          (osc-pulse (pwlv hz 0.4 hz 0.5 (/ hz 2.0) 1 (/ hz 2.0))
                     (mult 0.4 (sum 1 (hzosc 1))))
          (osc-pulse (pwlv (* 1.01 hz) 0.4 (* 1.01 hz) 0.5 (/ (* 1.01 hz) 2.0) 1 (/ (* 1.01 hz) 2.0))
                      (mult 0.4 (sum 1 (hzosc 2.3))))
          (osc-pulse (pwlv (* 1.5 hz) 0.4 (* 1.5 hz) 0.5 (/ (* 1.5 hz) 2.0) 1 (/ (* 1.5 hz) 2.0))
                     (mult 0.4 (sum 1 (hzosc 7.3)))))
        (pwlv 40 0.3 10000 0.8 400 1 20)))))

(setf hzlist (list  440     659.25    329.63
                    440     659.25    329.63
                    392     783.99    739.99  659.25  493.88
                    659.25  587.33    880     220))
(setf durlist(list  1       0.5       0.5
                    1       0.5       0.5
                    1       0.25      0.25    0.25    0.25
                    1.0     0.5       0.5     2.0))

(setf riff (seqrep (i (length hzlist)) (tone (nth i hzlist)(nth i durlist))))

(reson (hp riff 300)
       (pwlv 100  0.2 4000 0.3 100 0.5 2000 1 800 )
       (pwlv 100  0.2 400  0.3 500 0.5 100  1 1000)
       1)

Wow, this community delivers, I love it!

So, for this one (which WAS fun), is there a way to make it so that it doesn’t stretch to the current duration?

	   (defun tone (hz dur)
  (stretch-abs dur
    (mult 0.4 (pwlv 0 .01 1 .2 .4 .8 .3 1 0)
      (lp 
        (sim
          (osc-pulse (pwlv hz .4 hz .5 (/ hz 2) 1 (/ hz 2))
                     (mult 0.4 (sum 1 (hzosc 1))))
          (osc-pulse (pwlv (* 1.01 hz) 0.4 (* 1.01 hz) 0.5 (/ (* 1.01 hz) 2) 1 (/ (* 1.01 hz) 2))
                      (mult 0.4 (sum 1 (hzosc 2.3))))
          (osc-pulse (pwlv (* 1.5 hz) 0.4 (* 1.5 hz) 0.5 (/ (* 1.5 hz) 2) 1 (/ (* 1.5 hz) 2))
                     (mult 0.4 (sum 1 (hzosc 7.3)))))
        (pwlv 40 0.3 10000 0.8 400 1 20)))))

		
(setf hzlist (list  440     659.25    329.63
                    440     659.25    329.63
                    392     783.99    739.99  659.25  493.88
                    659.25  587.33    880     220))
(setf durlist(list  1       0.5       0.5
                    1       0.5       0.5
                    1       0.25      0.25    0.25    0.25
                    1.0     0.5       0.5     2.0))

					
(setf riff (seqrep (i (length hzlist)) (tone (nth i hzlist)(nth i durlist))))


(reson (hp riff 300)
       (pwlv 100  0.2 4000 0.3 100 0.5 2000 1 800 )
       (pwlv 100  0.2 400  0.3 500 0.5 100  1 1000)
       1)

So that the amp, pitch and filter pwlv’s are calculated by the longest duration and shorter durations only use some of their breakpoints?

Thanks again for everybodys help.

Normally you would wrap this sort of thing up as a “generate” type plug-in (because it is generating audio). For “generate” type plug-ins, one second in “Nyquist time” is one second in real (“global”) time. For example to produce 2.5 seconds of MIDI note number 60:

(osc 60 2.5)

However, if you try this in the Nyquist Prompt effect, you will notice that rather than producing 2.5 seconds, it produces 2.5 times the selection length. This is because, by default, the Nyquist Prompt is a “process” type effect (an “Effect” that processes the selected audio). When processing audio it makes sense to “warp” time so that a time unit of “one” means “all of the selection”.

To work around the “warp” that occurs with process type effects, I used “stretch-abs” (stretch to absolute time), which causes “transformations” to be relative to “real” (global) time rather than “local” time. However, in the final addition (the resonant filter at the end of the script), I forgot the “stretch-abs”.
I’ve added it here:

(defun tone (hz dur)
  (stretch-abs dur
    (mult 0.4 (pwlv 0 .01 1 .2 .4 .8 .3 1 0)
      (lp
        (sim
          (osc-pulse (pwlv hz .4 hz .5 (/ hz 2) 1 (/ hz 2))
                     (mult 0.4 (sum 1 (hzosc 1))))
          (osc-pulse (pwlv (* 1.01 hz) 0.4 (* 1.01 hz) 0.5 (/ (* 1.01 hz) 2) 1 (/ (* 1.01 hz) 2))
                      (mult 0.4 (sum 1 (hzosc 2.3))))
          (osc-pulse (pwlv (* 1.5 hz) 0.4 (* 1.5 hz) 0.5 (/ (* 1.5 hz) 2) 1 (/ (* 1.5 hz) 2))
                     (mult 0.4 (sum 1 (hzosc 7.3)))))
        (pwlv 40 0.3 10000 0.8 400 1 20)))))

      
(setf hzlist (list  440     659.25    329.63
                    440     659.25    329.63
                    392     783.99    739.99  659.25  493.88
                    659.25  587.33    880     220))
(setf durlist(list  1       0.5       0.5
                    1       0.5       0.5
                    1       0.25      0.25    0.25    0.25
                    1.0     0.5       0.5     2.0))

               
(setf riff (seqrep (i (length hzlist)) (tone (nth i hzlist)(nth i durlist))))

(stretch-abs (/ (snd-length riff ny:all) *sound-srate*)
  (reson (hp riff 300)
         (pwlv 100  0.2 4000 0.3 100 0.5 2000 1 800 )
         (pwlv 100  0.2 400  0.3 500 0.5 100  1 1000)
         1))

Another way to approach the “time warp” issue would be to tell Audacity to treat the Nyquist Prompt as a “generate” type effect. To do this, you can add a special “comment” line:

;type generate

The semicolon tells Nyquist that whatever follows on that line is a “comment” and should be ignored. This allows us to use “comments” to pass instructions to Audacity, telling Audacity how to handle the Nyquist code.
See the “plug-ins headers” section here: Missing features - Audacity Support

If we now try our 2.5 second oscillator and include the “header comment” that tells Audacity to treat it as a generate type effect:

;type generate
(osc 60 2.5)

then you will see that it now produces 2.5 seconds of audio.

Ok, so thanks to everybody’s help and examples, I almost have it doing everything I’d like. The last thing I’m stuck on is making a band pass filter.

I think I was able to use eq-band(thank you Robert J. H.) as a rough band stop filter, and a very imprecise band pass filter, but I’d rather do a band pass as a combo of low and high pass filters.
I had two ideas on how I might achieve that in the format I’m using, but the syntax is all wrong and I can’t seem to right it. I’d use bandpass2, but I need to be able to control it with pwl.

In this format:

(let ((sig(sim
    (scale 0.2 (hzosc 220))
    (scale 0.2 (hzosc 330))
    (scale 0.2 (hzosc 440))
    (scale 0.2 (hzosc 880))
    (scale 0.2 (hzosc 1100))))
      (control (pwl 0 400 1 400)))
	  
  (dotimes (i 4 sig)
    (setf sig (hp sig control))))

Is it possible to add a 2nd filter with another dotimes thingy?

Something like this but I’m sure this is very wrong. Control2 is probably not a thing, but the idea here is it lowers all but 440hz.

	(let ((sig(sim
    (scale 0.2 (hzosc 220))
    (scale 0.2 (hzosc 330))
    (scale 0.2 (hzosc 440))
    (scale 0.2 (hzosc 880))
    (scale 0.2 (hzosc 1100))))
      (control (pwl 0 400 1 400))
	  (control2 (pwl 0 500 1 500)))
	  
  (dotimes (i 4 sig)
    (setf sig (hp sig control)))
	 
  (dotimes (i 4 sig)
    (setf sig (lp sig control2))))

Or maybe assigning the (sim (hzosc)'s) to a variable and performing the filters on that? This syntax is all wrong, though I’m sure I saw something similar to this while pouring over this forum for the past week, but I can’t find it again.

currtones = (sim
    (scale 0.2 (hzosc 220))
    (scale 0.2 (hzosc 330))
    (scale 0.2 (hzosc 440))
    (scale 0.2 (hzosc 880))
    (scale 0.2 (hzosc 1100)))

(let ((sig(currtones))
      (control (pwl 0 400 1 400)))
  (dotimes (i 4 sig)
    (setf sig (hp sig control))))
	
(let ((sig(currtones))
      (control (pwl 0 500 1 500)))	  
  (dotimes (i 4 sig)
    (setf sig (lp sig control))))

Once again, thank you for all your help. I believe if I can get this last thing working, I’ll be able to stay out of your hair. For a little while at least. :slight_smile:

You can nest filters one inside another:

(dotimes (i 4 sig)
    (setf sig (lp (hp sig control) control2))))

Ah, control2 IS a thing. Cool. This works well for me. Thank you for the help Steve, you’ve been amazing.

On another note, if everybody will give me their email, I have a wide variety of Male Enhancement Products to sell… Just Kidding!!!

Thanks again, everybody!

“Control2” is a “thing” if you make it a “thing”.

Almost any word you choose may be a “thing”. More technically, you can use most words as “variables” to represent “data”.
A “variable” is a “symbol” that represents some data. More technically, the symbol is “bound” to the data.
Example:
If we want to say “val = 3”, then in Nyquist we can write that as:

(setq val 3)

“val” is a symbol to which we bind the numeric value “3”.

There are many “types” of “data” (called “data types” :open_mouth: ).
As with most computer languages, “data types” in Nyquist include

  • integers: whole numbers such as “3”)
  • floating point numbers: fractional numbers such as “3.142”
  • characters: such as “C”
  • strings: such as “Hello World”
  • lists: such as '(1 2.0 3 35)
  • arrays: such as #(1 4 6)
  • and others

Because Nyquist is designed to work with audio, it has some other useful data types built in, specifically “sound”. Nyquist treats “sounds” as “types of data” in much the same way as any other “type of data”.

Just as we can use a “variable” to represent a number, we can use a variable to represent as “sound”.
Example:

(setq val 3) ;integer data



(setq val 3.123) ;float data



(setq val (osc 60 1)) ;sound data



(setq val (pwlv 0 1 1)) ;sound data

Note that in the final example, “pwlv” makes a “control signal”. A control signal is a “sound” though usually at a low sample rate (by default the sample rate of a control signal is 1/20th of the sound sample rate.


From your earlier code:

(let ((control2 (pwl 0 500 1 500))))

“control2” is a “variable” to which you have bound the control signal (pwl 0 500 1 500).