MeteringViews/AULevelMeter.mm
/* |
</samplecode> |
*/ |
#import "AULevelMeter.h" |
#import "LevelMeter.h" |
#import "GLLevelMeter.h" |
@interface AULevelMeter (AULevelMeter_priv) |
- (void)layoutSubLevelMeters; |
- (void)registerForBackgroundNotifications; |
- (void)pauseTimer; |
- (void)resumeTimer; |
@end |
@implementation AULevelMeter |
@synthesize showsPeaks = _showsPeaks; |
@synthesize vertical = _vertical; |
@synthesize powerMeters = _powerMeters; |
- (id)initWithFrame:(CGRect)frame { |
if (self = [super initWithFrame:frame]) { |
_refreshHz = 1. / kRefreshRate; |
_showsPeaks = YES; |
_channelNumbers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil]; |
_vertical = NO; |
_useGL = YES; |
sampleRate = kDefaultSampleRate; |
_meterTable = new MeterTable(kMinDBvalue); |
_powerMeters = new PowerMeter; |
color = nil; |
[self layoutSubLevelMeters]; |
[self registerForBackgroundNotifications]; |
} |
return self; |
} |
- (id)initWithCoder:(NSCoder *)coder { |
if (self = [super initWithCoder:coder]) { |
_refreshHz = 1. / kRefreshRate; |
_showsPeaks = YES; |
_channelNumbers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil]; |
_vertical = NO; |
_useGL = YES; |
sampleRate = kDefaultSampleRate; |
_meterTable = new MeterTable(kMinDBvalue); |
_powerMeters = new PowerMeter; |
color = nil; |
[self layoutSubLevelMeters]; |
[self registerForBackgroundNotifications]; |
} |
return self; |
} |
- (void)registerForBackgroundNotifications |
{ |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(pauseTimer) |
name:UIApplicationWillResignActiveNotification |
object:nil]; |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(resumeTimer) |
name:UIApplicationWillEnterForegroundNotification |
object:nil]; |
[[NSNotificationCenter defaultCenter] addObserver:self |
selector:@selector(resumeTimer) |
name:UIApplicationDidBecomeActiveNotification |
object:nil]; |
} |
- (void)layoutSubLevelMeters |
{ |
int i; |
for (i=0; i<[_subLevelMeters count]; i++) |
{ |
UIView *thisMeter = [_subLevelMeters objectAtIndex:i]; |
[thisMeter removeFromSuperview]; |
} |
[_subLevelMeters release]; |
NSMutableArray *meters_build = [[NSMutableArray alloc] initWithCapacity:[_channelNumbers count]]; |
_powerMeters = (PowerMeter*)realloc(_powerMeters, [_channelNumbers count] * sizeof(PowerMeter)); |
for (NSInteger i=0; i < [_channelNumbers count]; i++) { |
_powerMeters[i].SetSampleRate(sampleRate); |
} |
CGRect totalRect; |
if (_vertical) totalRect = CGRectMake(0., 0., [self frame].size.width + 2., [self frame].size.height); |
else totalRect = CGRectMake(0., 0., [self frame].size.width, [self frame].size.height + 2.); |
for (i=0; i<[_channelNumbers count]; i++) |
{ |
CGRect fr; |
if (_vertical) { |
fr = CGRectMake( |
totalRect.origin.x + (((CGFloat)i / (CGFloat)[_channelNumbers count]) * totalRect.size.width), |
totalRect.origin.y, |
(1. / (CGFloat)[_channelNumbers count]) * totalRect.size.width - 2., |
totalRect.size.height |
); |
} else { |
fr = CGRectMake( |
totalRect.origin.x, |
totalRect.origin.y + (((CGFloat)i / (CGFloat)[_channelNumbers count]) * totalRect.size.height), |
totalRect.size.width, |
(1. / (CGFloat)[_channelNumbers count]) * totalRect.size.height - 2. |
); |
} |
LevelMeter *newMeter; |
if (_useGL) newMeter = [[GLLevelMeter alloc] initWithFrame:fr]; |
else newMeter = [[LevelMeter alloc] initWithFrame:fr]; |
newMeter.numLights = 30; |
newMeter.vertical = self.vertical; |
if (color != nil) { |
LevelMeterColorThreshold tmpThreshold = { 0.0f, color }; |
[newMeter setColorThresholds:&tmpThreshold count:1]; |
} |
[meters_build addObject:newMeter]; |
[self addSubview:newMeter]; |
[newMeter release]; |
} |
_subLevelMeters = [[NSArray alloc] initWithArray:meters_build]; |
[meters_build release]; |
} |
- (void)_refresh |
{ |
BOOL success = NO; |
// if we have no queue, but still have levels, gradually bring them down |
if (_running == NO) |
{ |
CGFloat maxLvl = -1.; |
CFAbsoluteTime thisFire = CFAbsoluteTimeGetCurrent(); |
// calculate how much time passed since the last draw |
CFAbsoluteTime timePassed = thisFire - _peakFalloffLastFire; |
for (LevelMeter *thisMeter in _subLevelMeters) |
{ |
CGFloat newPeak, newLevel; |
newLevel = thisMeter.level - timePassed * kLevelFalloffPerSec; |
if (newLevel < 0.) newLevel = 0.; |
thisMeter.level = newLevel; |
if (_showsPeaks) |
{ |
newPeak = thisMeter.peakLevel - timePassed * kPeakFalloffPerSec; |
if (newPeak < 0.) newPeak = 0.; |
thisMeter.peakLevel = newPeak; |
if (newPeak > maxLvl) maxLvl = newPeak; |
} |
else if (newLevel > maxLvl) maxLvl = newLevel; |
[thisMeter setNeedsDisplay]; |
} |
// stop the timer when the last level has hit 0 |
if (maxLvl <= 0.) |
{ |
[_updateTimer invalidate]; |
_updateTimer = nil; |
} |
_peakFalloffLastFire = thisFire; |
success = YES; |
} else { |
for (int i=0; i<[_channelNumbers count]; i++) |
{ |
NSInteger channelIdx = [(NSNumber *)[_channelNumbers objectAtIndex:i] intValue]; |
LevelMeter *channelView = [_subLevelMeters objectAtIndex:channelIdx]; |
if (channelIdx >= [_channelNumbers count]) goto bail; |
if (channelIdx > 127) goto bail; |
channelView.level = _meterTable->ValueAt(self.powerMeters[i].GetAveragePowerDB()); |
if (_showsPeaks) channelView.peakLevel = _meterTable->ValueAt(self.powerMeters[i].GetPeakPowerDB()); |
else |
channelView.peakLevel = 0.; |
[channelView setNeedsDisplay]; |
success = YES; |
} |
} |
bail: |
if (!success) |
{ |
for (LevelMeter *thisMeter in _subLevelMeters) { thisMeter.level = 0.; [thisMeter setNeedsDisplay]; } |
printf("ERROR: metering failed\n"); |
} |
} |
- (void)dealloc { |
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil]; |
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil]; |
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil]; |
[_updateTimer invalidate]; |
[_channelNumbers release]; |
[_subLevelMeters release]; |
delete _meterTable; |
[super dealloc]; |
} |
- (BOOL)running { return _running; } |
- (void)setRunning:(BOOL)v |
{ |
if ((_running == NO) && v) |
{ |
if (_updateTimer) [_updateTimer invalidate]; |
_updateTimer = [NSTimer |
scheduledTimerWithTimeInterval:_refreshHz |
target:self |
selector:@selector(_refresh) |
userInfo:nil |
repeats:YES |
]; |
} else if ((_running == NO) && (v == NO)) { |
_peakFalloffLastFire = CFAbsoluteTimeGetCurrent(); |
} |
_running = v; |
for (LevelMeter *thisMeter in _subLevelMeters) { |
[thisMeter setNeedsDisplay]; |
} |
} |
- (double)sampleRate { return sampleRate; } |
- (void)setSampleRate:(double)v |
{ |
sampleRate = v; |
[self layoutSubLevelMeters]; |
} |
- (CGFloat)refreshHz { return _refreshHz; } |
- (void)setRefreshHz:(CGFloat)v |
{ |
_refreshHz = v; |
if (_updateTimer) |
{ |
[_updateTimer invalidate]; |
_updateTimer = [NSTimer |
scheduledTimerWithTimeInterval:_refreshHz |
target:self |
selector:@selector(_refresh) |
userInfo:nil |
repeats:YES |
]; |
} |
} |
- (NSArray *)channelNumbers { return _channelNumbers; } |
- (void)setChannelNumbers:(NSArray *)v |
{ |
[v retain]; |
[_channelNumbers release]; |
_channelNumbers = v; |
[self layoutSubLevelMeters]; |
} |
- (BOOL)useGL { return _useGL; } |
- (void)setUseGL:(BOOL)v |
{ |
_useGL = v; |
[self layoutSubLevelMeters]; |
} |
- (UIColor*)color { return color; } |
- (void)setColor:(UIColor*)c |
{ |
[c retain]; |
[color release]; |
color = c; |
[self layoutSubLevelMeters]; |
} |
- (void)pauseTimer |
{ |
[self setRunning:NO]; |
} |
- (void)resumeTimer |
{ |
[self setRunning:YES]; |
} |
@end |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-11-29