MeteringView.mm
/* |
Copyright (C) 2016 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
The Metering View |
*/ |
#import "MeteringView.h" |
#include <math.h> |
double dbamp(double db) { return pow(10., 0.05 * db); } |
double ampdb(double amp) { return 20. * log10(amp); } |
#define kMinBarGap 3 |
#define kBarWidth 11 |
#define kBarInteriorWidth 9 |
#define kClipBoxHeight 6 |
@implementation MeteringView |
- (id)initWithFrame:(NSRect)frame { |
self = [super initWithFrame:frame]; |
if (self) { |
// initialization code here |
mMeterValues = nil; |
mOldMeterValues = nil; |
mNumChannels = 0; // must set with setNumChannels: |
drawsMetersOnly = NO; |
mHasClip = NO; |
mMinValue = 0.; |
mMinDB = ampdb(0.); |
mMaxValue = 1.; |
mMaxDB = ampdb(1.); |
} |
return self; |
} |
- (void) dealloc { |
if (mMeterValues) |
free(mMeterValues); |
if (mOldMeterValues) |
free(mOldMeterValues); |
if (mClipValues) |
free(mClipValues); |
[super dealloc]; |
} |
- (void)drawRect:(NSRect)rect { |
// Drawing code here. |
NSRect bounds = [self bounds]; |
float xOffset = firstTrackOffset + .5; |
float yOffset = 0; |
float topGap = mHasClip ? kClipBoxHeight + 2: 0; |
// draw the frame |
for (int i = 0; i < mNumChannels; i++) { |
NSRect barRect = NSMakeRect(xOffset + .5, .5, kBarWidth-2, bounds.size.height-1.5 - topGap); |
if (!drawsMetersOnly) { |
NSPoint pt1 = NSMakePoint(xOffset, yOffset); |
NSPoint pt2 = NSMakePoint(xOffset, bounds.size.height-.5 - topGap); |
NSPoint pt3 = NSMakePoint(xOffset + kBarWidth-1, pt2.y); |
NSPoint pt4 = NSMakePoint(pt3.x, yOffset); |
[[NSColor colorWithCalibratedWhite: .37 alpha: 1] set]; // light color |
[NSBezierPath strokeLineFromPoint: pt1 toPoint: pt2]; |
[NSBezierPath strokeLineFromPoint: pt2 toPoint: pt3]; |
[[NSColor colorWithCalibratedWhite: .53 alpha: 1] set]; // shadow color |
[NSBezierPath strokeLineFromPoint: pt3 toPoint: pt4]; |
[NSBezierPath strokeLineFromPoint: pt4 toPoint: pt1]; |
if (mHasClip) { |
NSPoint pt5 = NSMakePoint(xOffset, bounds.size.height + 2.5 - topGap); |
NSPoint pt6 = NSMakePoint(xOffset, bounds.size.height-.5); |
NSPoint pt7 = NSMakePoint(xOffset + kBarWidth-1, pt6.y); |
NSPoint pt8 = NSMakePoint(pt7.x, pt5.y); |
[NSBezierPath strokeLineFromPoint: pt7 toPoint: pt8]; |
[NSBezierPath strokeLineFromPoint: pt8 toPoint: pt5]; |
[[NSColor colorWithCalibratedWhite: .37 alpha: 1] set]; // light color |
[NSBezierPath strokeLineFromPoint: pt5 toPoint: pt6]; |
[NSBezierPath strokeLineFromPoint: pt6 toPoint: pt7]; |
} |
// now draw the background above and including the current value |
[[NSColor colorWithCalibratedWhite: .4 alpha: 1] set]; |
float value = roundf(mMeterValues[i * 2]); |
barRect.origin.y = value + 1; |
barRect.size.height = bounds.size.height-2 - value - topGap; |
[NSBezierPath fillRect: barRect]; |
if (mHasClip) { |
NSRect clipRect = NSMakeRect(barRect.origin.x, bounds.size.height + 3 - topGap, barRect.size.width, kClipBoxHeight -1.5); |
int clipVal = mClipValues[i]; |
if (clipVal == 0) |
[[NSColor colorWithCalibratedWhite: .4 alpha: 1] set]; |
else if (clipVal >= 1 && clipVal < 10) { |
[[NSColor redColor] set]; |
clipVal++; |
} else |
[[NSColor colorWithCalibratedRed: 0.75 green: .18 blue: .18 alpha: 1] set]; |
[NSBezierPath fillRect: clipRect]; |
} |
[[NSColor greenColor] set]; |
barRect.size.height = barRect.origin.y; |
barRect.origin.y = 1; |
[NSBezierPath fillRect: barRect]; |
} else { |
// only draw the difference area |
float old = roundf(mOldMeterValues[i * 2]); |
float curr= roundf(mMeterValues[i * 2]); |
// erase previous peak if it is different from the current |
float oldPeak = roundf(mOldMeterValues[(i*2) + 1]) + .5; |
float newPeak = roundf(mMeterValues[(i*2) + 1]) + .5; |
if (oldPeak != newPeak) { |
[[NSColor colorWithCalibratedWhite: .4 alpha: 1] set]; |
[NSBezierPath strokeLineFromPoint: NSMakePoint(barRect.origin.x, oldPeak) toPoint: NSMakePoint(barRect.origin.x+barRect.size.width, oldPeak)]; |
} |
if (curr > old) { // draw only green difference |
[[NSColor greenColor] set]; |
barRect.origin.y = (old < 1)? 1 : old; |
barRect.size.height = curr - old; |
[NSBezierPath fillRect: barRect]; |
} else if (curr < old) { // draw only gray difference |
[[NSColor colorWithCalibratedWhite: .4 alpha: 1] set]; |
barRect.origin.y = curr + 1; |
barRect.size.height = old - curr; |
[NSBezierPath fillRect: barRect]; |
} |
// draw the peak |
if (oldPeak != newPeak || mClipValues[i] == 1) { |
if (mClipValues[i] == 1) |
[[NSColor redColor] set]; |
else |
[[NSColor colorWithCalibratedWhite: .8 alpha: 1] set]; |
[NSBezierPath strokeLineFromPoint: NSMakePoint(barRect.origin.x, newPeak) toPoint: NSMakePoint(barRect.origin.x+barRect.size.width, newPeak)]; |
} |
if (mHasClip) { |
NSRect clipRect = NSMakeRect(barRect.origin.x, bounds.size.height + 3 - topGap, barRect.size.width, kClipBoxHeight -1.5); |
int clipVal = mClipValues[i]; |
if (clipVal == 1) { |
[[NSColor redColor] set]; |
[NSBezierPath fillRect: clipRect]; |
clipVal++; |
} |
else if (clipVal > 1 && clipVal < 10) |
clipVal++; |
else if (clipVal == 10) { |
[[NSColor colorWithCalibratedRed: 0.75 green: .18 blue: .18 alpha: 1] set]; |
[NSBezierPath fillRect: clipRect]; |
clipVal++; |
} |
mClipValues[i] = clipVal; |
} |
} |
xOffset += kMinBarGap + kBarWidth; |
} |
drawsMetersOnly = NO; |
} |
- (void) setNumChannels: (int) num { |
if (0 == num) { NSLog(@"Can't set 0 number of channles!!!!\n"); return; } |
if (mNumChannels != num) { |
mNumChannels = num; |
if (mMeterValues != nil) |
free(mMeterValues); |
if (mOldMeterValues != nil) |
free(mOldMeterValues); |
if (mClipValues != nil) |
free(mClipValues); |
mMeterValues = (float *) calloc (2 * mNumChannels, sizeof(float)); |
mOldMeterValues = (float *) calloc (2 * mNumChannels, sizeof(float)); |
mClipValues = (int *) calloc (num, sizeof(int)); |
drawsMetersOnly = NO; |
firstTrackOffset = floorf(([self bounds].size.width - (mNumChannels * kBarWidth + (mNumChannels-1) * kMinBarGap))/2); |
[self setNeedsDisplay: YES]; |
} |
} |
- (void) setMinValue: (double) num { // lowest value possible slider (should be divisible by 2) |
if (num != mMinValue) { |
mMinValue = num; |
mMinDB = ampdb(num); |
drawsMetersOnly = NO; |
[self setNeedsDisplay: YES]; |
} |
} |
- (void) setMaxValue: (double) num { // highest possible slider value (should be divisible by 2) |
if (num != mMaxValue) { |
mMaxValue = num; |
mMaxDB = ampdb(num); |
drawsMetersOnly = NO; |
[self setNeedsDisplay: YES]; |
} |
} |
- (void) setMinDB: (double) num { |
if (num != mMinDB) { |
mMinDB = num; |
mMinValue = dbamp(num); |
drawsMetersOnly = NO; |
[self setNeedsDisplay: YES]; |
} |
} |
- (void) setMaxDB: (double) num { |
if (num != mMaxDB) { |
mMaxDB = num; |
mMaxValue = dbamp(num); |
drawsMetersOnly = NO; |
[self setNeedsDisplay: YES]; |
} |
} |
- (void) setHasClipIndicator: (BOOL) hasClip { |
if (hasClip != mHasClip) { |
mHasClip = hasClip; |
drawsMetersOnly = NO; |
[self setNeedsDisplay: YES]; |
} |
} |
- (float) pixelForValue: (double) value inSize: (int) size { |
return size * ((value - mMinValue) / (mMaxValue - mMinValue)); // figure out what percentage the value is of the entire range |
} |
- (void) updateMeters: (float *)meterValues { |
if (![self inLiveResize]) { |
int numItems = 2 * mNumChannels; |
for (int i = 0, j = 0; i < numItems; i++) { |
float tempValue = dbamp(meterValues[i]); |
mOldMeterValues[i] = mMeterValues[i]; |
float pixelValue = [self pixelForValue: tempValue inSize: (int) [self bounds].size.height]; |
float top = [self bounds].size.height - (mHasClip ? kClipBoxHeight + 4: 2); |
if (pixelValue < 0) |
pixelValue = 0; |
else if (pixelValue > top) |
pixelValue = top; |
mMeterValues[i] = pixelValue; |
if (mHasClip) { |
if ((i & 1) == 0) { |
if (tempValue > mMaxValue) |
mClipValues[j] = 1; |
++j; |
} |
} |
} |
drawsMetersOnly = YES; |
[self setNeedsDisplay: YES]; |
} |
} |
- (void)mouseDown:(NSEvent *)theEvent |
{ |
if (mHasClip) { |
float xOffset = .5 + firstTrackOffset; |
float topGap = mHasClip ? kClipBoxHeight + 3: 0; |
NSRect clipRect = NSMakeRect(0, [self bounds].size.height - topGap, kBarWidth-2, kClipBoxHeight); |
NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; |
for (int i = 0; i < mNumChannels; i++) { |
clipRect.origin.x = xOffset + .5; |
if ([self mouse:mouseLoc inRect: clipRect]) { |
mClipValues[i] = 0; |
break; |
} |
xOffset += kMinBarGap + kBarWidth; |
} |
} |
drawsMetersOnly = NO; |
[self setNeedsDisplay: YES]; |
} |
- (BOOL) acceptsFirstMouse: (NSEvent *) event { |
return YES; |
} |
- (BOOL) isOpaque { |
return YES; |
} |
@end |
Copyright © 2016 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2016-02-11