我已经为录音应用程序构建了一个模拟的模拟VU表,并且除了一个方面外,所有内容都正常连接并按照我的预期方式工作。如果您正在使用VU表的watch this 13-second video,您将看到针头在整个地方反弹,并不是真正的VU表中会发生什么。有关我正在寻找的示例,请尝试Apple“语音备忘录”应用程序并查看。
到目前为止我的逻辑很容易:
#define VU_METER_FREQUENCY 1.0/5.0
- (void)someMethod {
_updateTimer = [NSTimer
scheduledTimerWithTimeInterval:VU_METER_FREQUENCY
target:self
selector:@selector(_refresh)
userInfo:nil
repeats:YES];
}
- (void)_refresh {
// if we have no queue, but still have levels, gradually bring them down
if (_delegate == nil) {
CFAbsoluteTime thisFire = CFAbsoluteTimeGetCurrent();
// calculate how much time passed since the last draw
CFAbsoluteTime timePassed = thisFire - _peakFalloffLastFire;
needleValue = needleValue - timePassed * VU_METER_LEVEL_FALL_OFF_PER_SECOND;
if (needleValue < VU_METER_MIN_DB) {
needleValue = VU_METER_MIN_DB;
TT_INVALIDATE_TIMER(_updateTimer);
}
_peakFalloffLastFire = thisFire;
} else {
prevNeedleValue = needleValue;
needleValue = [_delegate currentDB];
}
[self updateNeedle];
}
- (void)updateNeedle {
[UIView beginAnimations:nil context:NULL]; // arguments are optional
[UIView setAnimationDuration:VU_METER_FREQUENCY];
[UIView setAnimationCurve:(needleValue > prevNeedleValue ? UIViewAnimationCurveEaseOut : UIViewAnimationCurveEaseIn)];
CGFloat radAngle = [self radianAngleForValue:needleValue];
self.needle.transform = CGAffineTransformMakeRotation(radAngle);
[UIView commitAnimations];
}
基本上,我设置一个计时器在VU_METER_FREQUENCY
运行,并使用UIView动画更新针旋转,并使用优先保持针更高的缓动。我正在寻找一种方法来调整它以提供更平滑的针,我的基准测试尽可能接近Apple的模拟VU仪表。要获得needleValue
,我使用AudioQueue
的{{1}}并在每次调用mAveragePower
时查询它。我怎么能顺利度过这个?
答案 0 :(得分:2)
根据维基百科,VUMeter的行为在ANSI规范C16.5-1942中定义。针的完全上升和下降时间应该是300毫秒,平均响度超过该持续时间。
我会尝试在针角上使用1极低通滤波器来近似该角速率,并使用基于CADisplayLink的drawRect动画逐帧手动设置仪表的动画。查看动画可能无法提供相同的响应。
答案 1 :(得分:2)
我建议改变这一点。
#define VU_METER_FREQUENCY 1.0/5.0
那说更新5倍一秒,问题是我认为苹果将保持0.2秒的样本,所以你真的得到了声音的平均值,因此仪表并没有真正跟随声音的高点和低点但是更低的平均值。
我认为此设置可以高达1.0 / 60(60hz)。
至于使仪表平稳,这有点小问题。
你可以这样做。
所以它有点像填充管道,一旦你停止填充,它将需要一些时间来清空,针将慢慢下降。
或者你只能让针在每个周期都掉下来。
让我们说针摆动是0(最低值)和1(最右边的最高值)。 让我们再说你以20hz(每秒20次)的样品。
每次更新位置时,只允许针头上升,最大值为0.1,而值仅为0.05。
你可以做这样的事情,并使用这些值来使它变得美观和流畅。
if newValue>currentMeterValue
currentMeterValue = Min(currentMeterValue + 0.1, newValue);
else
currentMeterValue = Max(currentMeterValue - 0.05, newValue);
OR
您只需按照与每个值之间的距离成比例的速度移动仪表(这应该很好地平滑)并且实际上接近真实的仪表,弹簧推动由电磁铁驱动的针。
currentMeterValue += (newValue - currentMeterValue)/4.0;