假设我有一个系统将8820个值分配到96个值中,使用Banker's Round进行四舍五入(称之为pulse
)。公式是:
pulse = BankerRound(8820 * i/96), with i[0,96[
因此,这是脉冲列表:
0
92
184
276
368
459
551
643
735
827
919
1011
1102
1194
1286
1378
1470
1562
1654
1746
1838
1929
2021
2113
2205
2297
2389
2481
2572
2664
2756
2848
2940
3032
3124
3216
3308
3399
3491
3583
3675
3767
3859
3951
4042
4134
4226
4318
4410
4502
4594
4686
4778
4869
4961
5053
5145
5237
5329
5421
5512
5604
5696
5788
5880
5972
6064
6156
6248
6339
6431
6523
6615
6707
6799
6891
6982
7074
7166
7258
7350
7442
7534
7626
7718
7809
7901
7993
8085
8177
8269
8361
8452
8544
8636
8728
现在,假设系统没有直接向我发送这些脉冲。相反,它在8820th发送这些脉冲(称之为tick
):
tick = value * 1/8820
我得到的刻度列表:
0
0.010430839
0.020861678
0.031292517
0.041723356
0.052040816
0.062471655
0.072902494
0.083333333
0.093764172
0.104195011
0.11462585
0.124943311
0.13537415
0.145804989
0.156235828
0.166666667
0.177097506
0.187528345
0.197959184
0.208390023
0.218707483
0.229138322
0.239569161
0.25
0.260430839
0.270861678
0.281292517
0.291609977
0.302040816
0.312471655
0.322902494
0.333333333
0.343764172
0.354195011
0.36462585
0.375056689
0.38537415
0.395804989
0.406235828
0.416666667
0.427097506
0.437528345
0.447959184
0.458276644
0.468707483
0.479138322
0.489569161
0.5
0.510430839
0.520861678
0.531292517
0.541723356
0.552040816
0.562471655
0.572902494
0.583333333
0.593764172
0.604195011
0.61462585
0.624943311
0.63537415
0.645804989
0.656235828
0.666666667
0.677097506
0.687528345
0.697959184
0.708390023
0.718707483
0.729138322
0.739569161
0.75
0.760430839
0.770861678
0.781292517
0.791609977
0.802040816
0.812471655
0.822902494
0.833333333
0.843764172
0.854195011
0.86462585
0.875056689
0.88537415
0.895804989
0.906235828
0.916666667
0.927097506
0.937528345
0.947959184
0.958276644
0.968707483
0.979138322
0.989569161
不幸的是,在这些刻度之间,它还向我发送fake ticks
,它不是原始pulses
的倍数。例如0,029024943
,它是256
的倍数,它不在脉冲列表中。
如何从此列表中找到哪些刻度为valid
且哪些为fake
?
我没有在此过程中与脉冲列表进行比较,因为8820
将在此期间发生变化,因此我没有一个列表可以逐步进行比较。我需要在每次迭代时从滴答中推断出它。
对此最好的数学方法是什么?也许只能用嘀嗒而不是脉冲来推理。
我认为在最近的整数脉冲和上一个/下一个刻度之间找到更接近的误差。 C ++中的Here:
double pulse = tick * 96.;
double prevpulse = (tick - 1/8820.) * 96.;
double nextpulse = (tick + 1/8820.) * 96.;
int pulseRounded=round(pulse);
int buffer=lrint(tick * 8820.);
double pulseABS = abs(pulse - pulseRounded);
double prevpulseABS = abs(prevpulse - pulseRounded);
double nextpulseABS = abs(nextpulse - pulseRounded);
if (nextpulseABS > pulseABS && prevpulseABS > pulseABS) {
// is pulse
}
但是例如tick 0.0417234
(pulse 368
)失败,因为prev tick错误似乎比它更接近:prevpulseABS
错误(0.00543795
)小于{ {1}}错误(pulseABS
)。
那是因为这个比较我不关心舍入。
答案 0 :(得分:1)
新帖子:
好的。根据我现在的理解,这是我修改后的答案。
您拥有构建良好值列表所需的信息。每次切换到新曲目时:
vector<double> good_list;
good_list.reserve(96);
for(int i = 0; i < 96; i++)
good_list.push_back(BankerRound(8820.0 * i / 96.0) / 8820.0);
然后,每次要验证输入时:
auto iter = find(good_list.begin(), good_list.end(), input);
if(iter != good_list.end()) //It's a match!
cout << "Happy days! It's a match!" << endl;
else
cout << "Oh bother. It's not a match." << endl;
数学上确定正确脉冲的问题是BankerRound()函数,它会在您输入的值越高时引入越来越多的错误。然后你需要一个配方公式,然后离开我的驾驶室。或者,您可以跟踪连续值之间的差异。他们中的大多数都是一样的。您只需检查两个可能的错误。但如果你能在一条赛道上跳跃或跳跃,那就会崩溃。
旧帖子:
如果我理解正确的问题,那么您获得的唯一信息应该以(p / v = y)的形式出现,您知道&#39; y&#39; (这是您从设备获得的刻度列表中的每个元素),并且您知道&#39; p&#39;是脉冲和&#39; v&#39;是每个节拍的价值,但你不知道它们中的任何一个。因此,从帖子中提取一个数据点,您可能会得到如下公式:
p / v = 0.010430839
在目前为止您使用的所有示例中,&#39; v&#39;是8820,但据我所知,该值不是保证常量。接下来的问题是:你有办法确定什么&#39; v&#39;在你开始获得所有这些小数值之前?如果你这样做,你可以用数学方法计算出最小的误差(1 / v),然后取你的小数信息,乘以&#39; v&#39;,将它四舍五入到最接近的整数并检查是否圆形形状与非圆形形状之间的差异落在计算误差的范围内,如下所示:
double input; //let input be elements in your list of doubles, such as 0.010430839
double allowed_error = 1.0 / values_per_beat;
double proposed = input * values_per_beat;
double rounded = std::round(proposed);
if(abs(rounded - proposed) < allowed_error){cout << "It's good!" << endl;}
但是,如果您不能够提前确定values_per_beat,那么这将成为一个统计问题。您必须积累足够的数据样本,删除异常值(少数与标准不同)并使用该数据。但是这种方法不是实时的,考虑到你一直在使用的术语(每拍的值,bpm,值44100),听起来实时可能就是你所追求的。
答案 1 :(得分:0)
我假设您使用int i
作为循环变量在for循环中初始化脉冲;然后问题是这一行:
BankerRound(8820 * i/96);
8820 * i / 96
是一个全整数运算,结果再次为整数,切断余数(所以实际上,总是向零舍入),而BankerRound实际上没有任何东西可以循环。试试这个:
BankerRound(8820 * i / 96.0);
如果您尝试计算上一个和下一个脉冲,同样的问题也适用,因为您实际上减去并添加0(同样,1/8820
全部为整数,结果为0)。
修改强>
从我从通讯中读到的,&#39;系统&#39;不是 - 正如我之前所假设的 - 可以修改。实际上,它以
n / 96.0, n ∊ [0, 96) in ℕ的形式计算滴答 然而,包括某种内部舍入似乎独立于采样频率,因此与n / 96.0的真实值存在一些差异,并且蜱数乘以96并不能完全提供[0,96]中的积分值(感谢KarstenKoop) 。一些交付的样本只是无效......
所以任务是检测,如果tick * 96足够接近一个整数值而被接受为有效。
所以我们需要检查:
double value = tick * 96.0;
bool isValid
= value - floor(value) < threshold
|| ceil(value) - value < threshold;
具有一些适当定义的阈值。假设值确实计算为
double tick = round(8820*i/96.0)/8820.0;
那么最大偏差将略大于0.00544(更准确的值见下文),大小为0.006,0.0055,0.00545的某处的阈值可能是一种选择。
舍入可能是内部使用的传感器值位数的问题(如果我们有13位可用,则滴答可能实际计算为floor(8192 * i / 96.0) / 8192.0
,其中8192为1 << 13
&amp; ndash和floor会计到整数除法;只是猜测......)。
最大偏差的确切值,使用8820作为因子,与双重表示的精确值相同,为:
0.00544217687075132516838493756949901580810546875
实际上没有必要乘以96,你可以直接与阈值除以96进行比较,这将是:
0.0000566893424036596371706764330156147480010986328125
答案 2 :(得分:0)
使用Excel,我认为你想要乘以(应该是)整数而不是寻找最接近的脉冲。
Tick Pulse i Error OK
Tick*8820 Pulse*96/8820 ABS( i - INT( i+0.05 ) ) Error < 0.01
------------ ------------ ------------- ------------------------ ------------
0.029024943 255.9999973 2.786394528 0.786394528 FALSE
0.0417234 368.000388 4.0054464 0.0054464 TRUE
0 0 0 0 TRUE
0.010430839 91.99999998 1.001360544 0.001360544 TRUE
0.020861678 184 2.002721088 0.002721088 TRUE
0.031292517 275.9999999 3.004081632 0.004081632 TRUE
0.041723356 367.9999999 4.005442176 0.005442176 TRUE
0.052040816 458.9999971 4.995918336 0.004081664 TRUE
0.062471655 550.9999971 5.99727888 0.00272112 TRUE
0.072902494 642.9999971 6.998639424 0.001360576 TRUE
0.083333333 734.9999971 7.999999968 3.2E-08 TRUE
该表显示了您的两个“问题”案例(真正的错误值,256,您的代码出错了,368),然后是前几个“好”值。
如果两个8820
同时发生变化,那么很明显它们会取消,而i
只会Tick*96
。
Error
项是计算出的i
与最接近的整数之间的差值;如果这小于0.01
,那么这是一个“好”的值。
注意:0.05
和0.01
值的选择有点随意(也就是基于数字的第一次猜测):如果需要调整。虽然我只显示了前几行,但您给出的所有96个“好”值都显示为TRUE。
代码(完全未经测试)类似于:
double pulse = tick * 8820.0 ;
double i = pulse * 96.0 / 8820.0 ;
double error = abs( i - floor( i + 0.05 ) ) ;
if( error < 0.05 ) {
// is pulse
}