/* |
|
<codex> |
|
*/ |
|
#import "CALevelMeter.h" |
|
#import "LevelMeter.h" |
#import "GLLevelMeter.h" |
|
#import <QuartzCore/QuartzCore.h> |
|
@interface CALevelMeter (CALevelMeter_priv) |
- (void)layoutSubLevelMeters; |
- (void)pauseTimer; |
- (void)resumeTimer; |
- (void)registerForBackgroundNotifications; |
@end |
|
|
@implementation CALevelMeter |
|
@synthesize showsPeaks = _showsPeaks; |
@synthesize vertical = _vertical; |
|
- (id)initWithFrame:(CGRect)frame { |
if (self = [super initWithFrame:frame]) { |
_showsPeaks = YES; |
_channelNumbers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil]; |
_vertical = ([self frame].size.width < [self frame].size.height) ? YES : NO; // NO; |
_useGL = YES; |
_meterTable = new MeterTable(kMinDBvalue); |
[self layoutSubLevelMeters]; |
[self registerForBackgroundNotifications]; |
} |
return self; |
} |
|
|
- (id)initWithCoder:(NSCoder *)coder { |
if (self = [super initWithCoder:coder]) { |
_showsPeaks = YES; |
_channelNumbers = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil]; |
_vertical = ([self frame].size.width < [self frame].size.height) ? YES : NO; // NO; |
_useGL = YES; |
_meterTable = new MeterTable(kMinDBvalue); |
[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]; |
} |
|
- (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]]; |
|
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; |
[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 (_player == NULL) |
{ |
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 { |
[_player updateMeters]; |
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([_player averagePowerForChannel:i]); |
if (_showsPeaks) channelView.peakLevel = _meterTable->ValueAt([_player peakPowerForChannel:i]); |
else |
channelView.peakLevel = 0.; |
[channelView setNeedsDisplay]; |
success = YES; |
} |
} |
|
bail: |
|
if (!success) |
{ |
for (LevelMeter *thisMeter in _subLevelMeters) { thisMeter.level = 0.; [thisMeter setNeedsDisplay]; } |
NSLog(@"ERROR: metering failed\n"); |
} |
} |
|
|
- (void)dealloc |
{ |
[_updateTimer invalidate]; |
[_channelNumbers release]; |
[_subLevelMeters release]; |
delete _meterTable; |
|
[super dealloc]; |
} |
|
|
- (AVAudioPlayer*)player { return _player; } |
- (void)setPlayer:(AVAudioPlayer*)v |
{ |
if ((_player == NULL) && (v != NULL)) |
{ |
if (_updateTimer) [_updateTimer invalidate]; |
_updateTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(_refresh)]; |
[_updateTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; |
|
} else if ((_player != NULL) && (v == NULL)) { |
_peakFalloffLastFire = CFAbsoluteTimeGetCurrent(); |
} |
|
_player = v; |
|
if (_player) |
{ |
_player.meteringEnabled = YES; |
// now check the number of channels in the new queue, we will need to reallocate if this has changed |
if (_player.numberOfChannels != [_channelNumbers count]) |
{ |
NSArray *chan_array; |
if (_player.numberOfChannels < 2) |
chan_array = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil]; |
else |
chan_array = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:0], [NSNumber numberWithInt:1], nil]; |
[self setChannelNumbers:chan_array]; |
[chan_array release]; |
} |
} else { |
for (LevelMeter *thisMeter in _subLevelMeters) { |
[thisMeter setNeedsDisplay]; |
} |
} |
} |
|
|
- (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]; |
} |
|
- (void)pauseTimer |
{ |
[_updateTimer invalidate]; |
_updateTimer = nil; |
} |
|
- (void)resumeTimer |
{ |
if (_player) |
{ |
_updateTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(_refresh)]; |
[_updateTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; |
} |
} |
|
@end |