0
$\begingroup$

I'm working on a sampler software (that plays .WAV files, when notes are played on a MIDI keyboard).

In order to implement pitch-bend feature (i.e. you have a "pitch bend wheel" on the synth and you can turn this wheel to retune the sound in realtime, classical effect used in funk music),

enter image description here

we have to:

  1. Retune: this is possible by resampling the array of the .WAV. With Python, there is scipy.signal.resample or probably the same in numpy. But I heard there are algorithms optimized for audio repitching. What are the audio-optimized resampling algorithms? [Secret Rabbit Code : http://www.mega-nerd.com/SRC/. Why is this important? How to implement it in Python, with numpy for example?]

  2. Retune in realtime: indeed, we can play with the pitchbend-wheel very fast, thus, hundreds of resampling factor could be needed per second! How to do that? How to resample an array with different resampling factors, in realtime?

$\endgroup$
1
$\begingroup$

There's a lot of theory around about how to resample properly and efficiently while preserving the signal content. However, these methods are mostly not practical for things like pitch bending. Also, the fact that pitch bends (and other realtime rate changing requirements, like note transposition) are limited to -12 to +12 semitones, i.e. a resampling factor of 1/2 or 2, makes it easier to design an efficient algorithm.

Historically, audio samplers have been using linear interpolation for on-the-fly resampling and pitch bending. From today's perspective this almost the worst thing you can do (nearest neighbour tops it), but it got the job done. Sound quality was ok, but in times of 12 bits and 16kHz sampling rate, who would complain. (There have also been samplers that had the ability to change the DAC conversion rate by giving each voice an individual output, but that only as a historical side note)

Later models would use higher order polynomial or windowed sinc interpolation together with a mip-map, and the results became good enough for even modern requirements.

So I would suggest you try in this order:

1) Linear interpolation. Just calculate the sample value at the desired time index from its two nearest neighbours by linear interpolation.

2) Use offline processing to oversample your sample table with a factor of 2, doubling the memory requirements obviously. Use the same linear interpolation on those oversampled samples.

3) Also double the audio sampling rate of the resampler process, using a halfband lowpass filter and a decimation stage at the very end to go back to the audio playback rate

4) Like before, but instead of linear interpolation use a short windowed sinc interpolator.

Each step improves the sound quality but also requires more resources. My guess is that you will find it hard to hear a difference between 3) and 4) if you implement them correctly. Go with 3) if it's good enough, and I think it should be.

$\endgroup$
  • $\begingroup$ Thanks a lot! So linear interpolation from 2 neighbours is still better than "just take the nearest neightbour", right? My audio files are Python numpy arrays. Do you have an idea on implementation the simplest version (nearest neighbour) with Python, without having to "copy" the array to a new one? (it's called a "view" of an array in Numpy afaik) $\endgroup$ – Basj Mar 12 '15 at 12:05
  • $\begingroup$ Another thing: with pitch bend, should I use a constant "resampling factor" for each call of the audio callback? Example : 1st 1024-samples-buffer: resampling x1.3, 2nd 1024-samples-buffer: resampling x1.4, 3rd 1024-samples-buffer: resampling x 1.5 (because pitch bend wheel turning)?... Or should the resampling factor evolve even inside a 1024-samples-buffer ? (that sounds really difficult!) $\endgroup$ – Basj Mar 12 '15 at 12:11
  • $\begingroup$ My python-fu is not very good, so I better not advise you. And yes, linear interpolation is already a lot better than nearest neighbour picking. If you want to resample each buffer with a constant factor you should reduce your buffer size or you will experience severe stepping artefacts. It's probably safe to use a constant pitch factor for around 64 samples, but you should experiment with this. Continuously tracking the shift will give better results though, especially if you want smooth glide effects. $\endgroup$ – Jazzmaniac Mar 12 '15 at 12:57
  • $\begingroup$ Thanks! Yes I also think constant pitch factor on each 1024-sample-buffer will produce stepping artefact... How to continuously track the shift? I really don't see how we can resample a buffer array with many different resampling factors, what do you mean? $\endgroup$ – Basj Mar 13 '15 at 1:58
  • $\begingroup$ It's as simple as updating the buffer read pointer incrementally with the current speed, I.e. you calculate the fractional read positions by advancing the read pointer with a step size that's proportional to the resampling factor. The resulting fractional buffer positions are then used by the interpolator to calculate the new sample values. $\endgroup$ – Jazzmaniac Mar 13 '15 at 20:33
1
$\begingroup$

Audio-optimized resampling is done by using anti-alias filtering and interpolation kernels with good stop-band attenuation and low pass-band ripple/distortion. On current PC and mobile processors (the kind that can easily compute hundreds of transcendental functions per sample time), these filter and interpolation kernels can not only be precomputed and cached in poly-phase lookup tables, but for certain window types, (re)computed in real-time per sample.

Thus, resampling can be done in real-time by computing the needed interpolation kernels and interpolating each sample as needed at the required sample delay from the last for the current sample rate.

Note that changing the sample rate too quickly may result in FM modulation skirts that may need additional filtering to reduce aliasing artifacts.

$\endgroup$
  • $\begingroup$ Can you give an example of such algorithm (even in pseudo-code)? $\endgroup$ – Basj Mar 11 '15 at 18:49
  • $\begingroup$ one trick regarding audio resampling is to put nulls in the frequency response of the interpolation kernel at integer multiples of the original sample rate (except for the integer 0). this way all of the images take a big hit (except for the original image around 0) because most of the energy in audio is in the bottom 5 or 6 octaves. the top 4 or 5 octaves is 15/16 of the whole spectrum in terms of linear frequency. Miller Puckette had a paper about this once in the 90s (where he was asking why sometimes linear interpolation sounds better than Parks-McClellan). $\endgroup$ – robert bristow-johnson Mar 11 '15 at 19:04
  • $\begingroup$ Here's a link to a near pseudo-code example (in old-fashioned BASIC) that I wrote a few years ago: nicholson.com/rhn/dsp.html#3 It uses a Von Hann windowed Sinc. $\endgroup$ – hotpaw2 Mar 11 '15 at 20:27
  • $\begingroup$ @rbj remez/Parks-McClellan generated filters can leave a weird repetitive pattern of ripples in the pass band. I think you've posted in comp.dsp that some people find this audible in the time domain. One reason to prefer using windowed Sincs instead. $\endgroup$ – hotpaw2 Mar 12 '15 at 13:54
  • $\begingroup$ @hotpaw2, didn't see your comment until now (maybe i should get the rbj account here, ya know, that's not a bad idea, hot). anyway, yes, P-McC can have nearly sinusoidal ripples in the passband that causes a pre-echo if the sinc-like impulse response as well as a post-echo. but designing optimally with some other criterion (like least squares) might not have that problem. but the reason that Puckette pointed out why linear-interpolation sometimes sounded better was because of nulls in the frequency response in the center of all of those images. $\endgroup$ – robert bristow-johnson Mar 26 '15 at 4:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.