我有这个代码迭代一些样本并在点之间建立一个简单的线性插值:
foreach sample:
base = floor(index_pointer)
frac = index_pointer - base
out = in[base] * (1 - frac) + in[base + 1] * frac
index_pointer += speed
// restart
if(index_pointer >= sample_length)
{
index_pointer = 0
}
使用等于1的“速度”,游戏完成。但是如果index_pointer
不同于1(即得到小数部分),我需要包装last / first元素以保持翻译的一致性。
你会怎么做?双重索引?
这是我拥有的价值观的一个例子。让in
数组包含4个值:[8,12,16,20]。
它将是:
1.0*in[0] + 0.0*in[1]=8
0.28*in[0] + 0.72*in[1]=10.88
0.56*in[1] + 0.44*in[2]=13.76
0.84*in[2] + 0.14*in[3]=16.64
0.12*in[2] + 0.88*in[3]=19.52
0.4*in[3] + 0.6*in[4]=8 // wrong; here I need to wrapper
最后一点是错误的。 [4]将为0,因为我没有[4],但第一部分需要注意0.4和第一个样本的重量(我认为?)。
答案 0 :(得分:1)
只需环绕索引:
out = in[base] * (1 - frac) + in[(base + 1) % N] * frac
,其中%
是模运算符,N
是输入样本的数量。
此过程为您的样本数据生成以下行(虚线是插值的采样点,圆圈是输入值):
答案 1 :(得分:1)
我想我现在明白了这个问题(答案只适用于我真的......):
您以标称速度sn采样值。但实际上你的采样器以真实的速度进行采样,其中s!= sn。现在,您想要创建一个函数,该函数对以速度s采样的系列进行重新采样,因此它产生一个系列,就像通过2个相邻样本之间的线性插值以速度sn采样一样。或者,您的采样器抖动(实际采样时的时间差异为sn + Noise(sn)
)。
这是我的方法 - 一个名为“重新样本”的函数。它采用样本数据和所需的重新采样点列表。 对于将在原始数据之外索引的任何重新采样点,它将返回相应的边界值。
let resample (data : float array) times =
let N = Array.length data
let maxIndex = N-1
let weight (t : float) =
t - (floor t)
let interpolate x1 x2 w = x1 * (1.0 - w) + x2 * w
let interp t1 t2 w =
//printfn "t1 = %d t2 = %d w = %f" t1 t2 w
interpolate (data.[t1]) (data.[t2]) w
let inter t =
let t1 = int (floor t)
match t1 with
| x when x >= 0 && x < maxIndex ->
let t2 = t1 + 1
interp t1 t2 (weight t)
| x when x >= maxIndex -> data.[maxIndex]
| _ -> data.[0]
times
|> List.map (fun t -> t, inter t)
|> Array.ofList
let raw_data = [8; 12; 16; 20] |> List.map float |> Array.ofList
let resampled = resample raw_data [0.0..0.2..4.0]
收益率:
val resample:data:float array - &gt; times:浮动列表 - &gt; (float * float)[]
val raw_data:float [] = [| 8.0; 12.0; 16.0; 20.0 |]
val resampled:(float * float)[] =
[|(0.0,8.0); (0.2,8.8); (0.4,9.6); (0.6,10.4); (0.8,11.2); (1.0,12.0);
(1.2,12.8); (1.4,13.6); (1.6,14.4); (1.8,15.2); (2.0,16.0);
(2.2,16.8); (2.4,17.6); (2.6,18.4); (2.8,19.2); (3.0,20.0);
(3.2,20.0); (3.4,20.0); (3.6,20.0); (3.8,20.0); (4.0,20.0)|]
现在,我仍然无法理解你问题的“环绕”部分。最后,插值 - 与外推相反,仅针对[0..N-1]中的值定义。因此,您需要决定该函数是否应该产生运行时错误,或者只是使用边值(或0)来表示原始数据数组之外的时间值。
<强> 修改 强>
事实证明,它也是关于如何使用循环(环)缓冲区的。
这里是resample
函数的一个版本,使用循环缓冲区。以及一些操作。
update
将新的样本值添加到环形缓冲区read
读取内容一个环形缓冲区元素,就好像它是一个普通数组,索引为[0..N-1]。initXXX
以各种形式创建环形缓冲区的函数。length
,返回环形缓冲区的长度或容量。将环形缓冲区逻辑纳入模块中以使其全部清洁。
module Cyclic =
let wrap n x = x % n // % is modulo operator, just like in C/C++
type Series = { A : float array; WritePosition : int }
let init (n : int) =
{ A = Array.init n (fun i -> 0.);
WritePosition = 0
}
let initFromArray a =
let n = Array.length a
{ A = Array.copy a;
WritePosition = 0
}
let initUseArray a =
let n = Array.length a
{ A = a;
WritePosition = 0
}
let update (sample : float ) (series : Series) =
let wrapper = wrap (Array.length series.A)
series.A.[series.WritePosition] <- sample
{ series with
WritePosition = wrapper (series.WritePosition + 1) }
let read i series =
let n = Array.length series.A
let wrapper = wrap (Array.length series.A)
series.A.[wrapper (series.WritePosition + i)]
let length (series : Series) = Array.length (series.A)
let resampleSeries (data : Cyclic.Series) times =
let N = Cyclic.length data
let maxIndex = N-1
let weight (t : float) =
t - (floor t)
let interpolate x1 x2 w = x1 * (1.0 - w) + x2 * w
let interp t1 t2 w =
interpolate (Cyclic.read t1 data) (Cyclic.read t2 data) w
let inter t =
let t1 = int (floor t)
match t1 with
| x when x >= 0 && x < maxIndex ->
let t2 = t1 + 1
interp t1 t2 (weight t)
| x when x >= maxIndex -> Cyclic.read maxIndex data
| _ -> Cyclic.read 0 data
times
|> List.map (fun t -> t, inter t)
|> Array.ofList
let input = raw_data
let rawSeries0 = Cyclic.initFromArray input
(resampleSeries rawSeries0 [0.0..0.2..4.0]) = resampled