diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 13be2aa0f..8e99b10a0 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -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 = ""; }; 8314A46527A28C28000EBE7E /* equalizerTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = equalizerTemplate.pdf; path = Images/equalizerTemplate.pdf; sourceTree = ""; }; 8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = sidplay.xcodeproj; path = Plugins/sidplay/sidplay.xcodeproj; sourceTree = ""; }; + 8316B3922839FFD5004CC392 /* Scenes.scnassets */ = {isa = PBXFileReference; lastKnownFileType = wrapper.scnassets; path = Scenes.scnassets; sourceTree = ""; }; 831B99BE27C23E88005A969B /* Cog.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Cog.sdef; sourceTree = ""; }; 832923AE279FAC400048201E /* Cog.q1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Cog.q1.json; sourceTree = ""; }; 83293065277885790010C07E /* OpenMPTOld.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = OpenMPTOld.xcodeproj; path = Plugins/OpenMPT.old/OpenMPTOld.xcodeproj; sourceTree = ""; }; @@ -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 */, diff --git a/Scenes.scnassets/Spectrum.scn b/Scenes.scnassets/Spectrum.scn new file mode 100644 index 000000000..15f7cbfb8 Binary files /dev/null and b/Scenes.scnassets/Spectrum.scn differ diff --git a/Visualization/SpectrumView.h b/Visualization/SpectrumView.h index c788622eb..f32c6e1d4 100644 --- a/Visualization/SpectrumView.h +++ b/Visualization/SpectrumView.h @@ -7,11 +7,13 @@ #import +#import + #import "VisualizationController.h" NS_ASSUME_NONNULL_BEGIN -@interface SpectrumView : NSView +@interface SpectrumView : SCNView @property(nonatomic) BOOL isListening; @end diff --git a/Visualization/SpectrumView.m b/Visualization/SpectrumView.m index 64f4347be..97c413630 100644 --- a/Visualization/SpectrumView.m +++ b/Visualization/SpectrumView.m @@ -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 *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 *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 {