Nearest Neighbor Upsampling


My name’s Pedro and I’m in need of a better upsampling method for low quality files. I’ve tried many and I prefer that one! I call it nearest neighbor because it apparently repeats each sample, so you can only upsample two times, three times, four times, etc. the original sampling rate!

I have absolutely 0 knowledge on audio programming and only a tiny bit about how audio works.

Anyway, I’m only requesting this because my friend wrote a program on DOS that does this but never made it user friendly, for one to use it, the source must be edited to set the conversion parameters and run it. So everytime I have to ask him to upsample my files XD.

What I envisioned was a window with a text field where we could enter a Natural number meaning the number of times a sample would be repeated.

Here is a 7z file with many upsampling methods (Original File, Polynomial Interpolation, Audacity Resample feature, Audition Resample Feature and Nearest Neighbor Upsample).

“Nearest Neighbour” up-sampling could be done using Nyquist and Audacity, but why would you want to? The sound quality of this method is terrible compared to other methods.

If this is only as an academic exercise, then I can show you a simple but very slow way of doing it.
If this is intended as a practical method for resampling then I would suggest that you use some other method, such as (in Audacity) “Tracks menu > Resample”.


Thanks for the response. But to be frank, I do think it sounds better than the other methods! In the compressed file I posted I made many .WAV files I made using many resampling techniques and I really like the nearest neighbor interpolation!

I’d just like to have it as no program has it as an option :*(.

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: 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: