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
Christopher Snowhill 2022-05-22 00:03:52 -07:00
parent bf54c45242
commit fce0e129a5
4 changed files with 95 additions and 94 deletions

View File

@ -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.

View File

@ -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

View File

@ -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 {