Convert frequency of sine tone to vibration (tremolo)

[Topic split from:]

Hi steve and thank you for the response!

I am researching human perception and wish to investigate if and how pitch can be mapped to amplitude modulation (tremolo) when transferring audio messages through vibration.

I am using audio signals and I have equipment that allows me to transfer these signals through vibration to the skin. So I wish to modulate their amplitude depending on their pitch to achieve this effect.

Attaching a file as an example.

So I basically want to modulate the base pitch differently (use different frequency for tremolo), depending on what band of frequencies it belongs to.

The bands I gave in my previous post were an example but this is the idea.

Thank you.

It sounds like an interesting project.
I’ve split this from the original topic as it will require a somewhat different approach.

Will all of the tones be sine tones like your example? If they are then that simplifies the task considerably.

Tell me a bit about this equipment. What sort of signal does it require? Does it require just the modulation wave or does it also require the carrier? If it requires the carrier, what frequency range does the carrier need to be?

Hi Steve, yes I am simplifying the case for now and only focus on pure sine tones, so a sine that will go up and down between around 50 and 350 Hz.

It is basically a speaker tweaked to deliver vibrations ( )
It’s nominal frequency is 250 Hz meaning that the optimal vibration is achieved when playing a sine of 250 Hz.

It can go up or down a bit, that’s what I plan to do, but the equipment just requires a sound file, in this case a .wav.
So I guess the modulation and carrier waves will both be in that file.


Sounds analogous to a bat-detector : where the ultrasonic bat-chirp frequencies are reduced so they are audible. [ i.e. reduce speech frequencies so they are palpable* ].

There are bat-detector circuits on t’internet , IIRC some use cheap digital divider chips, but they alone wouldn’t convey variations in amplitude, they just reduce the frequency.

BTW if a 5-10millsecond attack&decay is added to your “pitch” it sounds better (but whether it feels better is another matter)…

[ * what is the tactile equivalent of flicker fusion threshold, i.e. the frequency beyond which one cannot detect individual pulses by touch ]

So do you want the frequency of the vibrations to be the frequency of your sine wave samples, and then modulate the amplitude according to the frequency?
If so, do you want higher amplitude for higher frequency and lower amplitude for lower frequency?
do you want to output a constant 250 Hz frequency, and modulate the amplitude of that frequency according to the frequency of the input?
If so, do you want higher amplitude for higher frequency and lower amplitude for lower frequency?
do you mean something different?
If so, please describe.

yes, exactly

Ideally, I want an amplitude that I will be able to modify myself depending on the frequency of the sine wave.

For example:

if sine wave frequency is 50-100 Hz then amplitude of tremolo is 30Hz
if sine wave frequency is 100-150 Hz then amplitude of tremolo is 60Hz
if sine wave frequency is 150-200 Hz then amplitude of tremolo is 90Hz

The reason I only want three bands and not the full range, is that available research says that people cannot really discriminate more than 3 different types of tremolo in the form of vibration to the skin.

If this is too hard to do, then I could try higher amplitude for higher frequency and lower amplitude for lower frequency as you mention, but I think this would not be the best in terms of how it would feel.

telephone quality speech has a bandwidth of 200Hz to 4000Hz , only converting frequencies from 50 to 200Hz into three classes of vibration isn’t going to convey enough information for comprehensible speech.

There is an effect called a tracker which will follow (track) the frequency changes which can be transposed much lower …

For Tracker plugin for Audacity see … "Tracker - Pitch tracking oscillator, or pitch tracking EQ".

I’m having trouble understanding what you mean because “amplitude” and “Hz” are different things.

“Amplitude” is how “big” the vibration is.
If it is vibrating just a teeny weeny fraction of a millimetre then that is low amplitude.
If it is vibrating like a pneumatic drill, then that is high amplitude.

“Hz” is the measurement unit for frequency.
A “low note” has low frequency.
An Inn sign that is swinging in the wind is even lower frequency.
A clock pendulum that swings forward and back once per second has a frequency of 1 Hz (one cycle per second)
A high pitched note has a high frequency.
A dog whistle has even higher frequency.
A bee’s wing vibrates at perhaps around 140 Hz (Bzzzzz… 140 cycles per second)
A mosquito’s wing vibrates at perhaps 700 Hz (zeeee… 700 cycles per second).

Now if you want to use a carrier frequency in the range 50 to 200 Hz and you want the modulation frequency (tremolo frequency) to be in the range 30 to 90 Hz, then you have a problem because the frequencies are too close together.

When a high frequency is modulated by a low frequency, essentially what you get is the high frequency going up and down in amplitude at the speed of the low frequency.
This code can be run in the Nyquist Prompt effect to illustrate this happening (select a few seconds of a mono audio track and apply this code):

(setq modulation 3)
(setq carrier 750)

(setf mod-wave (mult 0.5 (sum 1 (hzosc modulation))))
(setf car-wave (hzosc carrier))
(mult mod-wave car-wave)

However, if the frequencies are close together, then it becomes indistinct as to which is the carrier and which is the modulation frequency. In fact what happens is that the two frequencies interact with each other and produce three distinct frequencies, as you can hear in this example:

(setq modulation 300)
(setq carrier 750)

(setf mod-wave (mult 0.5 (sum 1 (hzosc modulation))))
(setf car-wave (hzosc carrier))
(mult mod-wave car-wave)

To prove that there are three tones (frequencies) produced, this code applies a sweep filter to the sound generated in the previous example:

(setq modulation 300)
(setq carrier 750)

(setf mod-wave (mult 0.5 (sum 1 (hzosc modulation))))
(setf car-wave (hzosc carrier))
(setf result (mult mod-wave car-wave))

(dotimes (i 40 result)
  (setf result
      (hp (mult 1.75 result) (pwlv 300 1 1000))
      (pwlv 400 1 1500))))

What I’m wondering is why you don’t simply feed your sine tones into the equipment. You say that the sine tones are already vibrating in the range 50 to 200 Hz.

Thank you very much for all the responses, really appreciated.

No problem there, I am not trying to convey speech but pure tones.

Looks interesting, seems to be doing good pitch tracking. Not sure however how it handles the result of this tracking. Will look closer, thanks.

Steve, you’re right, I am aware of the difference between the two. I should have said frequency of tremolo instead of amplitude of tremolo. The changes in amplitude of the carrier frequency are a result of the modulation of the modulating frequency. I am not too experienced in sound design so I still have to think to get the terms right :slight_smile:

This is an interesting point, didn’t know that. So what frequencies are good candidates and aren’t close together? Would a carrier of 300Hz and a modulation of 30Hz work? I still see an effect though when modulating a 250 Hz by a 30 Hz, see end of post. I hope my poor use of terminology has not prevented you guys from understanding what I’m trying to say, sorry if it has :frowning:

I can do that (I am doing that), and it works. It’s just that adding modulation on top of the sine wave adds a “roughness” to the vibration and is a good way to make it more prominent.

I am attaching a 10 sec file of a 250 Hz carrier, which after 5sec is modulated by a 30 Hz frequency (Tremolo effect, Starting phase 0, Wet level 100%, Frequency 30Hz). Do you hear how the tone gets “rougher” after 5sec? This is really noticeable on the skin and this exact signal has been effectively used in the past.

To give you more “real” values of what I want to do:

50 - 100 Hz will not be modulated
100 - 150 Hz will be modulated with 10 Hz
150 - 200 Hz will be modulated with 20 Hz
200 - 250 Hz will be modulated with 30 Hz

Of course this may still change, depending on how it will feel.


Your modulation example produces in fact 2 new frequencies: 220 and 280 Hz.
Here’s a similar example that has additionally the carrier in it (code for the Nyquist prompt):

(amosc (hz-to-step 250) (sum 1 (mult 0.5 (lfo 30))))

Since I’m blind, I’m naturally curious about the project and its tactile aspect. I am confused about the final goal, though. It’s interesting that you say that small vibration changes can’t be resolved accurately, experience tells me otherwise. Moreover, I would expect that the vibration bands would increase in a non-linear fashion, similar to Blauert’s frequency bands.
However, I am anxious to know how the project develops.

I’m wondering if we can break this down into an “engineering” problem.
What are the drawbacks/limitations of doing this way (the way that you are currently doing it)?

How are you generating the tones for your test?

I’m nit picking here, but shouldn’t it be “sum 0.5” rather than “sum 1”, otherwise there is clipping

(amosc (hz-to-step 250) (sum 0.5 (mult 0.5 (lfo 30))))

You’re not? :wink:

or (mult 0.5 (sum 1

(amosc (hz-to-step 250) (mult 0.5 (sum 1 (lfo 30))))

Thanks folks, I’ve accidently confused the function order. For FM it wouldn’t even be audible–just half a Hertz off…

Hi Robert, thanks for pointing it out. I’m just using the standard technique of modulating a 250 Hz tone with 30 Hz freq so this is the signal I am looking for. The 2 new frequencies although audible are not very palpable.

Good to hear about your interest in this! If you wish PM me and I can point you to some available results that also explain the methods used.

No real drawbacks, it works fine. But if this intervention makes it work better it’s a good thing. I cannot know this though until I do it.

With Audacity, generate tone function.

In the mean time, I have put something together using Max MSP, there I used a combination of third order crossover filters and I get something reasonable.
I know this is irrelevant with Audacity and I really value the open character of Audacity platform so to me this discussion is still interesting.

The best way to do this will be to generate the modulated signals (rather than generating sine tones and then modulating them.

Try this code.
To use it, select a bit of an audio track, starting at the point where you want the sound to start. It does not matter how long the selection is. Then open the Nyquist Prompt and paste this code into the text box and apply.

The first part of the code (with ;; at the start of each line) are “comments” that explain what the code does. The Nyquist Prompt ignores the comments.

;; 'breakpoints' is a list of frequencies and times.
;; The first number is a frequency, the next number
;; is a time, and so on.
;; The final number is a frequency.
;; In this example, the initial frequency is 50 Hz.
;; Over a period of 0.5 seconds (the 2nd number)
;; the frequency rises to 150 Hz (3rd number)
;; For 1 second (4th number) the frequency
;; remains at 150 Hz (5th number) and then
;; over the next 1.3 seconds (6th number)
;; the frequency rises to 200 Hz (7th number)
;; and so on.
;; The final frequency is 40 Hz.
;; The frequency is modulated by 1/4 of the frequency.
;; For example, while the frequency is 100 Hz
;; the modulation frequency is 25 Hz

(setf breakpoints
  (list 50 0.5 150 1 150 1.3 200 0.25 200 1 100 0.5 25 1 40))

(defun mod-tone (bp)
  (let ((frq bp)
        (mod ())
        (bp (reverse bp)))
    (dotimes (i (length bp))
      (if (evenp i)
          (push (/ (nth i bp) 4.0) mod)
          (push (nth i bp) mod)))
    (mult 0.5 
      (fmlfo (pwlvr-list frq))
      (sum 1 (fmlfo (pwlvr-list mod))))))

  (control-srate-abs *sound-srate*
    (mod-tone breakpoints)))

and here is the code without the comments:

(setf breakpoints
  (list 50 0.5 150 1 150 1.3 200 0.25 200 1 100 0.5 25 1 40))

(defun mod-tone (bp)
  (let ((frq bp)
        (mod ())
        (bp (reverse bp)))
    (dotimes (i (length bp))
      (if (evenp i)
          (push (/ (nth i bp) 4.0) mod)
          (push (nth i bp) mod)))
    (mult 0.5 
      (fmlfo (pwlvr-list frq))
      (sum 1 (fmlfo (pwlvr-list mod))))))

  (control-srate-abs *sound-srate*
    (mod-tone breakpoints)))

steve, thanks so much for the code. it does a neat job, with a minor difference: I don’t know what the pitch is in every instance.
I’m more looking for a filter-like functionality, so do this to a ready-made waveform.

a pseudocode for what I’m looking for would be as follows (in C-like syntax, not very used to LISP):

#define N 88200
//N is the number of samples of the .wav file
//no idea how this would be computed,
//maybe just scan through the file until there are no more samples

float getpitch(int i);
//imaginary function that returns the pitch of sample number i

void modulate(int i, float freq);
//imaginary function that modulates the amplitude of sample i
//with a modulation frequency of freq

//this is where my lack of knowledge in sound design becomes a problem
//the above modulation may be not even be possible
//maybe this modulation needs a DURATION rather than a single POINT in time
//from steve's code I tend to think that this is the case, looking at fmlfo function
//if this is true, then modulate for the least amount of time possible,
//possibly until the equivalent of fmlfo has gone through a full circle of modulation

float pitch_right_now = 0.0;
//the pitch (or f0) of the sample

int i = 0;

for (i=1; i<=N; i++)

      pitch_right_now = getpitch(i);
      // get pitch (f0) of sample 1

      switch (pitch_right_now) {
		case 100 ... 150:
			// if pitch (f0) is between 100 and 150 Hz, do am with 10 Hz
		case 150 ... 200:
			// if pitch (f0) is between 150 and 200 Hz, do am with 20 Hz
		case 250 ... 300:
			// if pitch (f0) is between 250 and 300 Hz, do am with 30 Hz
			//don't modulate the rest of the frequencies


//so the above would run 44100 times every sec, but that's not too bad, since my sounds will be around 2 sec long
//the result will be a modified .wav file