如何平滑音频电平测量以获得更真实的模拟 VU 表?

发布于 2024-10-04 06:11:11 字数 2043 浏览 0 评论 0原文

我已经为录音应用程序构建了一个模拟模拟 VU 表,并且所有东西都正确连接并按照我期望的方式工作,除了一个方面。如果您观看这个 13 秒的 VU 计视频,您会看到指针在整个屏幕上弹跳。的地方,并不是真正的 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 动画更新针旋转,并优先保持针更高。我正在寻找一种方法来调整它,以提供更平滑的针,我的基准尽可能接近苹果的模拟 VU 表。为了获取 needleValue,我使用 AudioQueuemAveragePower 并在每次调用 currentDB 时查询它。我怎样才能平滑这个?

I have built an emulated Analog VU Meter for a recording app and have everything hooked up properly and working the way I expect except for one aspect. If you watch this 13-second video of the VU meter in action, you will see that the needle bounces all over the place and is not really what would happen in a real VU meter. For an example of what I am looking for, try out the Apple "Voice Memos" app and see.

My logic so far is easy:

#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];
}

Basically, I setup a timer to run at VU_METER_FREQUENCY and update the needle rotation using a UIView animation with easing that is preferential to keep the needle higher. I am looking for a way to adjust this somehow to provide a smoother needle, with my benchmark being as close as possible to Apple's analog VU Meter. To get the needleValue, I am using AudioQueue's mAveragePower and querying it every time currentDB is called. How can I smooth this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

瞎闹 2024-10-11 06:11:11

我建议的一件事是改变这一点。

#define VU_METER_FREQUENCY    1.0/5.0

也就是说每秒更新 5 次,问题是我认为苹果将保存 0.2 秒的样本,所以你确实得到了声音的平均值,因此仪表并没有真正跟踪声音的高点和低点,而是更多地跟踪声音的高点和低点。平均水平较低。

我认为这个设置可以高达 1.0/60 (60hz)。

至于让仪表变得平滑,那就有点棘手了。

你可以做这样的事情。

  1. 创建一个包含 7-8 个值的数组。
  2. 每次获得读数时,将其添加到数组中并弹出第 7 个值(例如,仅保存最后七个值。
  3. 求数组的平均值(数组求和/除以数组中的元素数量。
  4. 显示此内容) 。

所以这有点像填充一根管子,一旦你停止填充,它需要一段时间才能排空,并且针头会慢慢落下,

只能让针头在每个周期落下这么多

或者你 假设针摆幅为 0(最低值)和 1(最右侧的最高值)。
假设您以 20hz(每秒 20 次)采样。

每次更新位置时,只允许指针上升 0.1 个最大值,仅下降 0.05 个值。

你可以做这样的事情并使用这些值来让它变得漂亮和顺利。

if newValue>currentMeterValue
   currentMeterValue = Min(currentMeterValue + 0.1, newValue);
else
   currentMeterValue = Max(currentMeterValue - 0.05, newValue);

或者,

您只需以与每个值之间的距离成比例的速率移动仪表(这应该很好地平滑它),并且实际上接近真实的仪表,用弹簧推动由电磁体供电的指针。

   currentMeterValue += (newValue - currentMeterValue)/4.0;

One thing I would suggest is changing this.

#define VU_METER_FREQUENCY    1.0/5.0

That says update 5x a second, the issue is that I think apple will hold 0.2s of samples, so you really are getting an average of the sounds, hence the meter is not really following the highs and lows of the sounds but more of a lower average.

I think this setting can go as high as 1.0/60 (60hz).

As for making the meter smooth, that is a little tricker.

You could do something like this.

  1. Create an array that holds 7-8 values.
  2. every time you get a reading, add it to the array and pop the 7th value of (eg only hold the last seven values.
  3. Find the average of the array (sum the array / divide by the number of elements in the array.
  4. Display this average.

So its a bit like filling up a pipe, and once you stop filling it will take some time for it to empty and needle will slowly fall down.

OR you could only allow the needle to fall down only so much every cycle.

Lets say the needle swings be 0 (lowest value) and 1 (highest value far right hand side).
Lets also say you sample at 20hz (20x times a second).

Every time you update the position only allow the needle to rise say 0.1 of a value max and fall only 0.05 of value.

you could do something like this and play with the values to get it nice and smooth.

if newValue>currentMeterValue
   currentMeterValue = Min(currentMeterValue + 0.1, newValue);
else
   currentMeterValue = Max(currentMeterValue - 0.05, newValue);

OR

You simply move the meter at a rate proportionally to the distance between each value (this should smooth it nicely) and actually be close to real meter with a spring pushing against the needle which is powered by an electromagnet.

   currentMeterValue += (newValue - currentMeterValue)/4.0;
泅渡 2024-10-11 06:11:11

根据 Wikipedia,VUMeter 的行为在 ANSI 规范 C16.5-1942 中定义。针的完全上升和下降时间应为 300 毫秒,即该持续时间的平均响度。

我会尝试在针角上使用 1 极低通滤波器来近似该角速率,并使用基于 CADisplayLink 的 drawRect 动画逐帧手动设置仪表动画。视图动画可能不会提供相同的响应能力。

According to Wikipedia, the behavior of a VUMeter is defined in in ANSI specification C16.5-1942. The needle full rise and fall time is supposed to be 300 mSec, averaging loudness over that duration.

I would try a 1-pole low-pass filter on the needle angle to approximate that angular rate, and animate the meter manually on a frame-by-frame basis using CADisplayLink based drawRect animation. View animation might not give the same responsiveness.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文