Как я могу сгладить измерение уровня звука для более реалистичного аналогового VU-метра? - PullRequest
1 голос
/ 23 ноября 2010

Я построил эмулированный аналоговый VU Meter для приложения записи, и все подключено правильно и работает так, как я ожидаю, за исключением одного аспекта.Если вы посмотрите это 13-секундное видео индикатора VU в действии, вы увидите, что стрелка отскакивает повсюду, и это не совсем то, что происходит в реальном измерителе VU.Для примера того, что я ищу, попробуйте приложение «Voice Memos» от 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 Meter от Apple.Чтобы получить needleValue, я использую AudioQueue mAveragePower и запрашиваю его каждый раз, когда вызывается currentDB.Как мне это сгладить?

Ответы [ 2 ]

2 голосов
/ 23 ноября 2010

Одна вещь, которую я бы предложил, это изменить.

#define VU_METER_FREQUENCY    1.0/5.0

Это говорит, что обновление 5x в секунду, проблема в том, что я думаю, что яблоко будет держать 0,2 сэмплов, так что вы действительно получаете среднее значение звуков, следовательно, измеритель на самом деле не следует за максимумами и минимумами звуков, больше более низкого среднего.

Я думаю, этот параметр может доходить до 1,0 / 60 (60 Гц).

Что касается плавности метра, то это немного сложнее.

Вы могли бы сделать что-то вроде этого.

  1. Создать массив, содержащий 7-8 значений.
  2. каждый раз, когда вы получаете чтение, добавляйте его в массив и извлекайте 7-е значение (например, сохраняйте только последние семь значений.
  3. Найти среднее значение массива (сумма массива / деление на количество элементов в массиве.
  4. Показать это среднее.

Так что это немного похоже на заполнение трубки, и как только вы прекратите заполнять ее, потребуется некоторое время, чтобы она опустела, и игла медленно упадет.

ИЛИ вы можете позволить игле только так сильно падать каждый цикл.

Допустим, колебания иглы составляют 0 (самое низкое значение) и 1 (самое высокое значение в правой части). Допустим также, что вы производите выборку с частотой 20 Гц (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;
2 голосов
/ 23 ноября 2010

Согласно Википедии, поведение VUMeter определено в спецификации ANSI C16.5-1942.Предполагается, что время полного подъема и спада иглы составляет 300 мсек, что в среднем составляет громкость за это время.

Я бы попробовал 1-полюсный фильтр нижних частот на угле иглы, чтобы приблизить эту угловую скорость, и анимировать измеритель вручную по кадрам, используя анимацию drawRect на основе CADisplayLink.Анимация при просмотре может не давать такой же отзывчивости.

...