My interpretation was always that an fft based soltution would be too slow for general use, but I have no numbers on this. I was trying to imagine schemes to repeat samples to get 96kHz from a 44.1 input but it's hard to get anything to be too precise. I wonder now how hard it would be to have something that runs kind of in real time (or some analogue of it) and feeds the same sample over an over before the 44.1kHz input feeds in a new one, which should average out on something really precise after a few rounds, rather than having to design a repeating scheme before-hand, and the benefit is it would work with almost any input then.
I kind of both agree and disagree
If my input size was assured to be multipication of the output size, I could interpolate lineary either by averaging samples ( the quickest version would be adding both and shifting right by 1 to simulate division by 2)
However our issue is that the interpolation is non linear
The ratio for 98 to 44.1 is around 2.17, it means that for each sample, I'd want to copy it once more and add "0.17" sample and write both the input and the copy sample and "fake" sample to the output buffer
Since its 2d data, its interleaving mode does not matter more than transposing the data...
So if we could add "0.17" pixel it would be a simple upsample, but there is no such thing as 0.17 sample
However image processing suffer the same issue when we resize images to different aspect ratio, linear interpolation is used to approxmiate us closer to the real size, and than we upsample by a factor between 1 and 2 (1.17 for example) with anti aliasing algorithms
Timothy Lottes from nvidia created a very fast anti aliasing algorithm (which will fix the wave corners to fit between edges, which will make sound playback seems less bumpy and more directional), namely FXAA, which marked 6.7 ms on radeon 240, which makes me confident that the pyra is probably capable of about 3ms FXAA on 44.1 to 98 per second (which is equal to 44.1K 16bits) which is roughly 88K bytes, image processing on 50K bytes on ARM omap was already benchmarked for about 2 ms, and considering how fast FXAA is, I assume that even if we cant apply FXAA we can use the idea of scoring samples and averaging tham to create sample each N samples that will result in 98K samples if done ( if i % N == 0 create extra 1 sample based on moving average of previous M samples)
I read the article about FXAA, I actually am confident it is applicable to sound waves but I might be wrong, probably next week I will discover it
the upsample twice code is something similar to this
#include <stdint.h>
#ifdef __ARM__
#include <arm_neon.h> // for neon instrincts
// assumes input sample is preallocated with sizeof(int16_t)*size
// assumes output sample is preallocated with sozeof(int16_t)*size*2
void dualsample_int16(const int16_t* input_sample, size_t size, int16_t* output_sample)
{
const int16_t* end = input_sample + size;
int16_t* y = output_sample;
const int16_t* x = input_sample;
const int16_t* end_minus_8 = end - 8;
const int16_t* end_minus_4 = end - 4;
// one increment is done already inside the loop to save time for the already computed y += 8 required to write the sample twice and to increment
for(; x <= end_minus_8; x += 8, y += 8)
{
// Load vector of 8 int16s
int16x8_t sample_data = vld1q_s16(x);
// write to y sample once
vst1q_f16(y, sample_data);
// increment y to point to the next sample
y += 8;
// write to y sample twice, now the last 32 bits of y is a repeat of those 16 bits on x
vst1q_f16(y, sample_data);
}
// same algorithm, now for vector of 4's for the rest of the data that is not divisible by 8
for(; x <= end_minus_4; x += 4, y + =4)
{
int16x4_t sample_data = vld1_s16(x);
vst1_f16(y, sample_data);
y += 4;
vst1_f16(y, sample_data);
}
// naive, for the rest of the data that is not divisible by either 8 or 4
for(; x < end; x++, y++)
{
(y++)[0] = x[0];
y[0] = x[0];
}
}
#else
void dualsample_int16(const int16_t* input_sample, size_t size, int16_t* output_sample)
{
const int16_t* end = input_sample + size;
int16_t* y = output_sample;
for(const int16_t* x = input_sample; x < end; x++, y++)
{
(y++)[0] = x[0];
y[0] = x[0];
}
}
#endif