Nearest Neighbor Upsampling

The result is “brighter” sounding than other methods because of the distortion that it creates.

Anyway, I thought it was an interesting exercise, so I’ve written some code for you.

One thing that Nyquist cannot do is to change the track sample rate, so you will need to do that manually.
To change the track sample rate, use the track drop down menu. See here: Audacity Manual The Sample Rate settings are described at the bottom of the page.

So let’s say that you want to up-sample using the “nearest neighbour” method by a factor of 3:
If the track sample rate it, say, 8000 Hz, then you will need to change it to 3 x 8000 = 24000 Hz. (the track will now sound high pitched and speeded up).

Next we will do the resampling.
From the Effect menu, select the Nyquist Prompt effect.
Copy and paste this code into the Nyquist Prompt text box:

(setq multiplier 3) ; an integer

(defun bad-resample (sig num)
  (setq size 1000)
  (setf s-array (make-array (* num size)))
  (setf end-array
    (if (/= (rem (truncate len) size) 0)
      (make-array (* num (rem (truncate len) size)))
  (setf output (s-rest 0))
  (dotimes (count (truncate (/ len size)))
    (fill-array s-array sig num)
    (setf output
        (at-abs (/ (* count num size) *sound-srate*)
          (cue (snd-from-array 0 *sound-srate* s-array))))))
  (if end-array
      (fill-array end-array sig num)
        (at-abs (/ (* (truncate (/ len size)) (* num size)) *sound-srate*)
          (cue (snd-from-array 0 *sound-srate* end-array)))))

(defun fill-array (s-a sig times)
  (dotimes (count (/ (length s-a) times))
    (let ((next (snd-fetch sig)))
      (dotimes (i times)
        (setf (aref s-a (+ i (* count times))) next)))))

(multichan-expand #'bad-resample s (truncate multiplier))

Apply the effect.

Note that the first line sets the “upsampling” ratio. If you want to upsample 4 times, change:

(setq multiplier 3) ; an integer


(setq multiplier 4) ; an integer

Wow! I tested it!
Even though you are right about the distortion… it doesn’t ‘add’ it in my understanding… It’s just like an enlarged pixelated image. While it was not meant to be all blocky, it’s a limit of it’s original resolution.

Thanks a lot, really!

Which one is “blockier”?

Before “up-sampling”:
After up-sampling:

Well we can do that with images too:

There are many upscaling algorythms! Depends on the case, really, If I had to upsample a song with voices I’d use polynomial interpolation instead as It has less noise.

The difference between your graphic example and the audio processing is that the graphic up-sampling is reducing the appearance of “steps”, whereas the “Nearest Neighbour up-sampling” is increasing the appearance of steps.

Back to the audio sample:
Where the original sample rate is reasonably high (44.1 kHz or above), the “Nearest Neighbour up-sampling” does not do much harm as the distortion frequencies are all too high to hear.

If the original audio has a low sample rate, then “Nearest Neighbour up-sampling” will create audible distortion.

If audio up-sampling is done perfectly, the result should sound identical to the original.

Here are three audio samples.
The first is the low sample rate original:

The second has been (almost perfectly) up-sampled (using “Very High Quality Sync Interpolation”). You will notice that it sounds identical to the original.

The third has been processed with a multiplication factor of 4

The “distortion” can be clearly heard as additional high frequencies. It certainly makes the sound “brighter” but with something of a metallic sound to it.

The difference can also be seen it the spectrum.
The upper track shows the spectrum of the original audio - note how the frequencies only go up to 4 kHz.
The lower track shows the spectrum of the processed audio - note how frequencies have been added from 4 kHz right up to 16 kHz.
I’m not saying that there is anything “wrong” with this process as “an effect”. I’m just saying that it should not be confused with “high quality up-sampling” :wink:

Yeah, I know it sounds metallic but depending on the thing you’re gonna upsample you’re upsampling it actually doesn’t sound bad!

I’m my case it was an electric guitar with a phaser! hahah Sounded very ‘fitting’.

But get what you are saying and I agree too, but do you have any suggestion in making LQ sound sounding better? Like, I’m a graphic designer and I can understand images, sound is somewhat similar but has some weird differences ahha.

That upsample is weird because it simply flips the upper part of the spectrum and repeats it… Does any program can create frequencies that were not there before?



But as I think about it, it seems it’s not like that example but more like ‘a part of the image is missing’. And that I know is a totally different situation.

Yes I can imagine that.

As I said, it was an interesting exercise, and the sound of the distortion is quite unique - I may even turn this into a kind of “distortion effect” :wink:

Ah yes, you noticed that too. Weird isn’t it.

You mean like “high frequency selective enhancement”?
A technique that was sometimes used in the old analogue days (particularly for drums) was to slightly overload the recording tape (“tape saturation”). This causes a kind of “soft distortion” (as well as “dynamics compression”).
Most forms of distortion add upper harmonics to the sound - the difficulty is in finding a distortion that creates “pleasant” upper harmonics.

There are two plug-ins here that you could try:


To tell the truth… I think using a highpass filter on what this generates and combining with other interpolation methods may be the best… seriously! Getting useful high frequencies from those plugins you sent me seems a lot harder and most of the time they sound like normal clipping, ahhaha.

If you’re curious about what I needed this for… I ripped the Megarace OST from the 3D0 version, but it’s 11025Hz and as it has lots of phasers, distortions, reverse cymbal-like effects, so it’ll sound ok after a bit of work and eq.

See you :slight_smile:

Ps. I changed bad-resample to awesome-resample in your code, now it’s fitting :stuck_out_tongue:

I didn’t test Steve’s code but I think the approach with snd-fetch within loops may be a little bit slow. Here’s another solution without the use of arrays:

;; resample nearest neighbour
(defun smp-map (factor dur)
   (let* (
   (freq (hz-to-step (/ *sound-srate* factor)))
   (smp (/ *sound-srate*))
   (*nigh-table* (maketable (abs-env (seq 
      (snd-const 1 0 *sound-srate* smp) 
      (s-rest (* smp (1- *sound-srate*))))))))
   (integrate (abs-env (osc freq dur  *nigh-table*)))
)); end smp-map

(setf factor 4)
(snd-compose s (smp-map factor  (get-duration factor)))

factor means of course the stretch factor or how many times a sample is repeated. The nigh-table map offers a lot of interesting variations. at a factor of 4, the resulting oscilator function will produce "10001000…). A value of one means “take the next sample” and 0 means “repeat the last sample”. there are other combinations possible. It’s even possible to produce a sample order like: “2 1 3 2 4 3…”. I have no idea how this will sound like.
Optionally, the sound can immediately be resampled to the original sample rate, thus producing a little amount of the metallic character of this sample method.

Definitely so. Quick and dirty programming :smiley:

And I’ve been trying to circumvent such a blunt comment… :smiley:

Here’s a variation from above which swaps the samples in pairs (021324354…). As expected, the influence is most prominent at frequencies near half of the sample-rate. The higher the factor, the more the effect is audible but the clipping danger increases also.
Besides, my code isn’t very efficient either. The solution to the original problem could be written in a single line (but less flexibel though).

;; Swapping samples
(defun smp-map (factor dur)
   (let* (
   (freq (hz-to-step (/ *sound-srate* factor)))
   (smp (/ *sound-srate*))
   (*nigh-table* (maketable (abs-env (seq 
      (snd-const factor  0 *sound-srate* smp) 
      (snd-const -1 0 *sound-srate* 
      (* smp (1- *sound-srate*))))))))
   (integrate (abs-env (osc freq dur  *nigh-table*)))
)); end smp-map

(setf factor 2)
(mult 0.9 (force-srate (/ *sound-srate* factor)
(snd-compose s (smp-map factor  (get-duration factor)))))
;(snd-display (mult 44100 (smp-map factor   0.001)))

But doesn’t that negate the effect by capping the frequency band below the generated frequencies?

how much of the metalic effect is preserved during the back sampling depends largely on the resampling method used.
It is clear that a harmonic spectrum that results from added new samples can’t fully be expressed by only one remaining sample. Nevertheless, the interpolation will be weighted in a different way. I think that the second code above, where the samples are swaped produces a much stronger effect when the sample rate remains the same. It’s presumably only a kind of resonant filter in the high frequencies.

Here’s the shortest code snippet for the resampling that I was able to develop so far:

(setf l (truncate (* len (psetq sr *sound-srate* factor 2))))
(multichan-expand #'snd-compose s (mult (/ sr) (quantize (snd-pwl 0 sr(list l len l)) 1)))

It isn’t likely that it will win a beauty prize. But I grandiously boasted above that all could be squeezed into one line…:wink:

Wow! Your version is very fast o.o

Programming is funny, squeezing big things into small things.

Now you need to officialize it and make a ‘.ny’ <3 So we can install it instead of having a TXT in our desktops hahahahahahah.

But now I started to search about Sinc interpolation. On images it looks a bit better than bicubic

So why people consider it to be the best for upsampling audio (I’m not talking about brightness of sound this time), all audio editors have it as the ‘best’!

Resampling is certainly an interesting field of research. The SINC-interpolation yields the best results because it introduces the least new frequencies while retaining the original spectrum. However, the ideal anti-aliasing filter doesn’t exist for discreet/finite signals. It is therefore the truncation or windowing of the SINC-function that makes the big difference in the numerous plug-ins and software products.
Our method from above elegantly circumvents the problem of anti-aliasing by simply allowing it…(that’s the back-folding of the high frequencies). Furthermore, the integrety of the frequency-response is discarded in respect to normal procedures, where the samples are either linearly interpolated (seldom) or simply filled with zeros (general case) prior to the filter application.

It would be interesting to unite different resampling (and dithering) methods in a Nyquist-plug-in. It’s a pity that I am no mathematician. Filter design gives me the creeps. :wink:

A nice idea to use SND-COMPOSE with QUANTIZE.
A slightly modified version in one line - I think the modern expression is “fugly” :wink:

(setq factor 4)

(multichan-expand #'snd-compose s (mult (/ *sound-srate*)(quantize (snd-pwl 0 *sound-srate* (list (truncate (* len factor)) len (truncate (* len factor)))) 1)))

Perhaps the most “elegant” solution I’ve found:

(setq factor 4)

(control-srate-abs *sound-srate* 
  (let ((sig2 (mult (/ *sound-srate*)
                (quantize (pwl factor len factor) 1))))
    (multichan-expand #'snd-compose s sig2)))

I’m not sure about “officializing” it :slight_smile: but here’s a plug-in. I’ve called the effect “Repeat Samples…”
RepeatSamples.ny (621 Bytes)

There’s a very nice plug-in here that takes much of the sweat out of it: Chebyshev Type I and Butterworth HP & LP Filters

Works perfectly hahaha!

By officializing I meant creating a real plugin, like this. hahah

Thanks :slight_smile:

Thank you Steve for the plug-in (our first cooperation…).
Due to the shrinking process of the code, there is now a lot of space for an additional low pass filter…
Thanks for the Bequat filter link, I am still admiring the code, though I am no fan of the analog inspired filters. All those coefficients are rather egyptian hieroglyphs for me. Well, that’s not exactly true, since I know a few hundred of the later ones but you certainly grasp the meaning. Anyway, there is a lot of room for experimenting in this area, as mentioned before. Especially unwanted side effects of resampling are most interesting from a musical point of view.

Here is a sample of WAV compression on 1kbps—128kbps:

How I made this (not necessarily in the same order for independent operations): You may be tempted to understand Roman language and say X is 10, but not so. X is one of the items of the set of 8 frequencies from 250Hz to 32000Hz, in exponential increments of 2x.

In OpenMPT, I first upsample the 44100Hz song to a multiple of X with a high quality algorithm. This means 44250Hz, 44500Hz, 45000Hz, 46000Hz, 48000Hz or 64000Hz. Then I downsample with Nearest Neighbor to X. I save the result. The first step of compression is done.

Then the resulting sound is open in Audacity. I save it with IMA ADPCM — but sometimes I have to repeat this, as I fall for the trick of the Project Rate number being different. The second step of compression is done.

I repeat it for all 8 values of X. This will give me a set of WAV sounds from 1kbps to 128kbps.

Post–processing: They are opened in OpenMPT. I upsample each of these to 48000Hz with Nearest Neighbor. In case of X being 32000Hz, this will lead to samples alternating between being once and twice. I’m not a “bouncing frequencies” expert, but I don’t think it (half–integer upsampling) causes major distortions as opposed to integer upsampling with Nearest Neighbor.

Then I cut first 15 seconds from them, and combine them. I add labels. I record a video. I upload it to YouTube.