Spectrum Visualization: Replaced spectrum view
Introduced a brand new spectrum view based on SceneKit, with a scene created by @kddlb and then altered by me to add the peak spheres. This new scene should be lighter on display resources, even though it's fully 3D instead of a vector 2D scene done in Cocoa drawing primitives. Co-authored-by: Kevin Lopez Brante <kevin@kddlb.cl> Signed-off-by: Christopher Snowhill <kode54@gmail.com>swiftingly
parent
bf54c45242
commit
fce0e129a5
|
@ -97,6 +97,7 @@
|
|||
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; };
|
||||
830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; };
|
||||
8314A46F27A28C29000EBE7E /* equalizerTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 8314A46527A28C28000EBE7E /* equalizerTemplate.pdf */; };
|
||||
8316B3932839FFD5004CC392 /* Scenes.scnassets in Resources */ = {isa = PBXBuildFile; fileRef = 8316B3922839FFD5004CC392 /* Scenes.scnassets */; };
|
||||
831B99BF27C23E88005A969B /* Cog.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 831B99BE27C23E88005A969B /* Cog.sdef */; };
|
||||
832923AF279FAC400048201E /* Cog.q1.json in Resources */ = {isa = PBXBuildFile; fileRef = 832923AE279FAC400048201E /* Cog.q1.json */; };
|
||||
83293070277886250010C07E /* OpenMPTOld.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8329306D277885790010C07E /* OpenMPTOld.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -904,6 +905,7 @@
|
|||
830C37F227B9956C00E02BB0 /* analyzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = analyzer.c; sourceTree = "<group>"; };
|
||||
8314A46527A28C28000EBE7E /* equalizerTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = equalizerTemplate.pdf; path = Images/equalizerTemplate.pdf; sourceTree = "<group>"; };
|
||||
8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = sidplay.xcodeproj; path = Plugins/sidplay/sidplay.xcodeproj; sourceTree = "<group>"; };
|
||||
8316B3922839FFD5004CC392 /* Scenes.scnassets */ = {isa = PBXFileReference; lastKnownFileType = wrapper.scnassets; path = Scenes.scnassets; sourceTree = "<group>"; };
|
||||
831B99BE27C23E88005A969B /* Cog.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Cog.sdef; sourceTree = "<group>"; };
|
||||
832923AE279FAC400048201E /* Cog.q1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Cog.q1.json; sourceTree = "<group>"; };
|
||||
83293065277885790010C07E /* OpenMPTOld.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPTOld.xcodeproj; path = Plugins/OpenMPT.old/OpenMPTOld.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -1505,6 +1507,7 @@
|
|||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8316B3922839FFD5004CC392 /* Scenes.scnassets */,
|
||||
832C1252180BD1E2005507C1 /* Cog.help */,
|
||||
8E07AD280AAC9BE600A4B32F /* Preference Panes */,
|
||||
8E75758E09F31D800080F1EE /* Icons */,
|
||||
|
@ -2443,6 +2446,7 @@
|
|||
17E41E070C130DFF00AC744D /* Credits.html in Resources */,
|
||||
836F462B28207FA4005B9B87 /* StopColorful.png in Resources */,
|
||||
8314A46F27A28C29000EBE7E /* equalizerTemplate.pdf in Resources */,
|
||||
8316B3932839FFD5004CC392 /* Scenes.scnassets in Resources */,
|
||||
8384916618083EAB00E7332D /* repeatModeOneTemplate.pdf in Resources */,
|
||||
8E7575DB09F31E930080F1EE /* Localizable.strings in Resources */,
|
||||
83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */,
|
||||
|
|
Binary file not shown.
|
@ -7,11 +7,13 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <SceneKit/SceneKit.h>
|
||||
|
||||
#import "VisualizationController.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SpectrumView : NSView
|
||||
@interface SpectrumView : SCNView
|
||||
@property(nonatomic) BOOL isListening;
|
||||
@end
|
||||
|
||||
|
|
|
@ -22,11 +22,9 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
BOOL paused;
|
||||
BOOL stopped;
|
||||
BOOL isListening;
|
||||
BOOL bandsReset;
|
||||
|
||||
NSColor *baseColor;
|
||||
NSColor *peakColor;
|
||||
NSColor *backgroundColor;
|
||||
NSColor *borderColor;
|
||||
ddb_analyzer_t _analyzer;
|
||||
ddb_analyzer_draw_data_t _draw_data;
|
||||
}
|
||||
|
@ -37,7 +35,12 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
@synthesize isListening;
|
||||
|
||||
- (id)initWithFrame:(NSRect)frame {
|
||||
self = [super initWithFrame:frame];
|
||||
NSDictionary *sceneOptions = @{
|
||||
SCNPreferredRenderingAPIKey: @(SCNRenderingAPIMetal),
|
||||
SCNPreferLowPowerDeviceKey: @(YES)
|
||||
};
|
||||
|
||||
self = [super initWithFrame:frame options:sceneOptions];
|
||||
if(self) {
|
||||
[self setup];
|
||||
}
|
||||
|
@ -61,7 +64,15 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
paused = NO;
|
||||
isListening = NO;
|
||||
|
||||
[self colorsDidChange:nil];
|
||||
[self setBackgroundColor:[NSColor clearColor]];
|
||||
|
||||
SCNScene *theScene = [SCNScene sceneNamed:@"Scenes.scnassets/Spectrum.scn"];
|
||||
[self setScene:theScene];
|
||||
|
||||
bandsReset = NO;
|
||||
[self drawBaseBands];
|
||||
|
||||
//[self colorsDidChange:nil];
|
||||
|
||||
BOOL freqMode = [[NSUserDefaults standardUserDefaults] boolForKey:@"spectrumFreqMode"];
|
||||
|
||||
|
@ -70,17 +81,13 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
_analyzer.min_freq = 10;
|
||||
_analyzer.max_freq = 22000;
|
||||
_analyzer.peak_hold = 10;
|
||||
_analyzer.view_width = 64;
|
||||
_analyzer.view_width = 11;
|
||||
_analyzer.fractional_bars = 1;
|
||||
_analyzer.octave_bars_step = 2;
|
||||
_analyzer.max_of_stereo_data = 1;
|
||||
_analyzer.freq_is_log = 0;
|
||||
_analyzer.mode = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(colorsDidChange:)
|
||||
name:NSSystemColorsDidChangeNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(playbackDidBegin:)
|
||||
name:CogPlaybackDidBeginNotficiation
|
||||
|
@ -103,9 +110,6 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
ddb_analyzer_dealloc(&_analyzer);
|
||||
ddb_analyzer_draw_data_dealloc(&_draw_data);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSSystemColorsDidChangeNotification
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:CogPlaybackDidBeginNotficiation
|
||||
object:nil];
|
||||
|
@ -121,7 +125,22 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
}
|
||||
|
||||
- (void)repaint {
|
||||
self.needsDisplay = YES;
|
||||
[self updateVisListening];
|
||||
|
||||
if(stopped) {
|
||||
[self drawBaseBands];
|
||||
return;
|
||||
}
|
||||
|
||||
float visAudio[4096], visFFT[2048];
|
||||
|
||||
[self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0]];
|
||||
|
||||
ddb_analyzer_process(&_analyzer, [self->visController readSampleRate] / 2.0, 1, visFFT, 2048);
|
||||
ddb_analyzer_tick(&_analyzer);
|
||||
ddb_analyzer_get_draw_data(&_analyzer, 11.0, 1.0, &_draw_data);
|
||||
|
||||
[self drawAnalyzer];
|
||||
}
|
||||
|
||||
- (void)startTimer {
|
||||
|
@ -143,23 +162,6 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
[self repaint];
|
||||
}
|
||||
|
||||
- (void)colorsDidChange:(NSNotification *)notification {
|
||||
backgroundColor = [NSColor textBackgroundColor];
|
||||
backgroundColor = [backgroundColor colorWithAlphaComponent:0.0];
|
||||
borderColor = [NSColor systemGrayColor];
|
||||
|
||||
if(@available(macOS 10.14, *)) {
|
||||
baseColor = [NSColor textColor];
|
||||
peakColor = [NSColor controlAccentColor];
|
||||
peakColor = [peakColor colorWithAlphaComponent:0.7];
|
||||
} else {
|
||||
peakColor = [NSColor textColor];
|
||||
baseColor = [peakColor colorWithAlphaComponent:0.6];
|
||||
}
|
||||
|
||||
[self repaint];
|
||||
}
|
||||
|
||||
- (void)playbackDidBegin:(NSNotification *)notification {
|
||||
stopped = NO;
|
||||
paused = NO;
|
||||
|
@ -185,78 +187,71 @@ extern NSString *CogPlaybackDidStopNotficiation;
|
|||
[self repaint];
|
||||
}
|
||||
|
||||
- (void)drawAnalyzerDescreteFrequencies {
|
||||
CGContextRef context = NSGraphicsContext.currentContext.CGContext;
|
||||
ddb_analyzer_draw_bar_t *bar = _draw_data.bars;
|
||||
for(int i = 0; i < _draw_data.bar_count; i++, bar++) {
|
||||
CGContextMoveToPoint(context, bar->xpos, 0);
|
||||
CGContextAddLineToPoint(context, bar->xpos, bar->bar_height);
|
||||
}
|
||||
CGContextSetStrokeColorWithColor(context, baseColor.CGColor);
|
||||
CGContextStrokePath(context);
|
||||
- (void)drawBaseBands {
|
||||
if(bandsReset) return;
|
||||
|
||||
bar = _draw_data.bars;
|
||||
for(int i = 0; i < _draw_data.bar_count; i++, bar++) {
|
||||
CGContextMoveToPoint(context, bar->xpos - 0.5, bar->peak_ypos);
|
||||
CGContextAddLineToPoint(context, bar->xpos + 0.5, bar->peak_ypos);
|
||||
SCNScene *scene = [self scene];
|
||||
SCNNode *rootNode = [scene rootNode];
|
||||
NSArray<SCNNode *> *nodes = [rootNode childNodes];
|
||||
|
||||
for(int i = 0; i < 11; ++i) {
|
||||
SCNNode *node = nodes[i + 1];
|
||||
SCNNode *dotNode = nodes[i + 1 + 11];
|
||||
SCNVector3 position = node.position;
|
||||
position.y = 0.0;
|
||||
node.scale = SCNVector3Make(1.0, 0.0, 1.0);
|
||||
node.position = position;
|
||||
|
||||
position = dotNode.position;
|
||||
position.y = 0;
|
||||
dotNode.position = position;
|
||||
}
|
||||
CGContextSetStrokeColorWithColor(context, peakColor.CGColor);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
bandsReset = YES;
|
||||
}
|
||||
|
||||
- (void)drawAnalyzerOctaveBands {
|
||||
CGContextRef context = NSGraphicsContext.currentContext.CGContext;
|
||||
ddb_analyzer_draw_bar_t *bar = _draw_data.bars;
|
||||
for(int i = 0; i < _draw_data.bar_count; i++, bar++) {
|
||||
CGContextAddRect(context, CGRectMake(bar->xpos, 0, _draw_data.bar_width, bar->bar_height));
|
||||
}
|
||||
CGContextSetFillColorWithColor(context, baseColor.CGColor);
|
||||
CGContextFillPath(context);
|
||||
const int maxBars = (int)(ceilf((float)(_draw_data.bar_count) / 11.0));
|
||||
const int barStep = (int)(floorf((float)(_draw_data.bar_count) / 11.0));
|
||||
|
||||
bar = _draw_data.bars;
|
||||
for(int i = 0; i < _draw_data.bar_count; i++, bar++) {
|
||||
CGContextAddRect(context, CGRectMake(bar->xpos, bar->peak_ypos, _draw_data.bar_width, 1.0));
|
||||
ddb_analyzer_draw_bar_t *bar = _draw_data.bars;
|
||||
|
||||
SCNScene *scene = [self scene];
|
||||
SCNNode *rootNode = [scene rootNode];
|
||||
NSArray<SCNNode *> *nodes = [rootNode childNodes];
|
||||
|
||||
for(int i = 0; i < 11; ++i) {
|
||||
float maxValue = 0.0;
|
||||
float maxMax = 0.0;
|
||||
for(int j = 0; j < maxBars; ++j) {
|
||||
const int barBase = i * barStep;
|
||||
const int barIndex = barBase + j;
|
||||
if(barIndex < _draw_data.bar_count) {
|
||||
if(bar[barIndex].bar_height > maxValue) {
|
||||
maxValue = bar[barIndex].bar_height;
|
||||
}
|
||||
if(bar[barIndex].peak_ypos > maxMax) {
|
||||
maxMax = bar[barIndex].peak_ypos;
|
||||
}
|
||||
}
|
||||
}
|
||||
SCNNode *node = nodes[i + 1];
|
||||
SCNNode *dotNode = nodes[i + 1 + 11];
|
||||
SCNVector3 position = node.position;
|
||||
position.y = maxValue * 0.5;
|
||||
node.scale = SCNVector3Make(1.0, maxValue, 1.0);
|
||||
node.position = position;
|
||||
|
||||
position = dotNode.position;
|
||||
position.y = maxMax;
|
||||
dotNode.position = position;
|
||||
}
|
||||
CGContextSetFillColorWithColor(context, peakColor.CGColor);
|
||||
CGContextFillPath(context);
|
||||
|
||||
bandsReset = NO;
|
||||
}
|
||||
|
||||
- (void)drawAnalyzer {
|
||||
if(_analyzer.mode == DDB_ANALYZER_MODE_FREQUENCIES) {
|
||||
[self drawAnalyzerDescreteFrequencies];
|
||||
} else {
|
||||
[self drawAnalyzerOctaveBands];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)drawRect:(NSRect)dirtyRect {
|
||||
[super drawRect:dirtyRect];
|
||||
|
||||
[self updateVisListening];
|
||||
|
||||
[backgroundColor setFill];
|
||||
NSRectFill(dirtyRect);
|
||||
|
||||
CGContextRef context = NSGraphicsContext.currentContext.CGContext;
|
||||
CGContextMoveToPoint(context, 0.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 64.0, 0.0);
|
||||
CGContextAddLineToPoint(context, 64.0, 26.0);
|
||||
CGContextAddLineToPoint(context, 0.0, 26.0);
|
||||
CGContextAddLineToPoint(context, 0.0, 0.0);
|
||||
CGContextSetStrokeColorWithColor(context, borderColor.CGColor);
|
||||
CGContextStrokePath(context);
|
||||
|
||||
if(stopped) return;
|
||||
|
||||
float visAudio[4096], visFFT[2048];
|
||||
|
||||
[self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0]];
|
||||
|
||||
ddb_analyzer_process(&_analyzer, [self->visController readSampleRate] / 2.0, 1, visFFT, 2048);
|
||||
ddb_analyzer_tick(&_analyzer);
|
||||
ddb_analyzer_get_draw_data(&_analyzer, self.bounds.size.width, self.bounds.size.height, &_draw_data);
|
||||
|
||||
[self drawAnalyzer];
|
||||
[self drawAnalyzerOctaveBands];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)event {
|
||||
|
|
Loading…
Reference in New Issue