From 6c24ad8244a943a5d5f93e58cf2fc776e85259fb Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Sat, 1 Feb 2025 15:08:25 -0800 Subject: [PATCH] Visualization: Tweak systems a bit This should improve performance slightly. It's still recommended to switch off SceneKit to save CPU usage, or switch of vis entirely. Signed-off-by: Christopher Snowhill --- .../VisualizationController.swift | 76 +++++++++---------- Visualization/SpectrumViewCG.m | 38 ++++++++-- Visualization/SpectrumViewSK.m | 56 ++++++++++---- 3 files changed, 107 insertions(+), 63 deletions(-) diff --git a/Audio/Visualization/VisualizationController.swift b/Audio/Visualization/VisualizationController.swift index b5133ddf7..18d46064f 100644 --- a/Audio/Visualization/VisualizationController.swift +++ b/Audio/Visualization/VisualizationController.swift @@ -12,7 +12,7 @@ class VisualizationController : NSObject { var serialQueue = DispatchQueue(label: "Visualization Queue") var sampleRate = 0.0 var latency = 0.0 - var visAudio: [Float] = Array(repeating: 0.0, count: 44100 * 45) + var visAudio: [Float] = Array(repeating: 0.0, count: 44100 * 45) var visAudioCursor = 0 var visAudioSize = 0 var visSamplesPosted: UInt64 = 0 @@ -30,12 +30,10 @@ class VisualizationController : NSObject { @objc func reset() { serialQueue.sync { - self.latency = 0; - let amount = self.visAudioSize - for i in 0..(repeating: 0.0, count: visAudioSize) + self.visSamplesPosted = 0 } } @@ -54,9 +52,9 @@ class VisualizationController : NSObject { serialQueue.sync { if(self.sampleRate != sampleRate) { self.sampleRate = sampleRate - visAudioSize = (Int)(sampleRate * 45.0) - visAudio = Array(repeating: 0.0, count: visAudioSize) - visAudioCursor = 0 + self.visAudioSize = (Int)(sampleRate * 45.0) + self.visAudio = Array(repeating: 0.0, count: visAudioSize) + self.visAudioCursor = 0 } } } @@ -64,17 +62,23 @@ class VisualizationController : NSObject { @objc func postVisPCM(_ inPCM: UnsafePointer?, amount: Int) { serialQueue.sync { - let bufferPointer = UnsafeBufferPointer(start: inPCM, count: amount) - var j = self.visAudioCursor - let k = self.visAudioSize - for i in 0..= k { j = 0 } + if(self.visAudioSize == 0) { + return + } + let bufferPointer = UnsafeRawPointer(inPCM) + if let bptr = bufferPointer { + let dataArray = bptr.assumingMemoryBound(to: Float.self) + var j = self.visAudioCursor + let k = self.visAudioSize + for i in 0..= k { j = 0 } + } + self.visAudioCursor = j + self.latency += Double(amount) / self.sampleRate + self.visSamplesPosted += UInt64(amount) } - self.visAudioCursor = j - self.latency += Double(amount) / self.sampleRate - self.visSamplesPosted += UInt64(amount); } } @@ -87,19 +91,10 @@ class VisualizationController : NSObject { @objc func copyVisPCM(_ outPCM: UnsafeMutablePointer?, visFFT: UnsafeMutablePointer?, latencyOffset: Double) { + outPCM?.update(repeating: 0.0, count: 4096) + visFFT?.update(repeating: 0.0, count: 2048) + if(self.visAudioSize == 0) { - if(outPCM != nil) { - let pcmPointer = UnsafeMutableBufferPointer(start: outPCM, count: 4096) - for i in 0...4095 { - pcmPointer[i] = 0.0 - } - } - if(visFFT != nil) { - let fftPointer = UnsafeMutableBufferPointer(start: visFFT, count: 2048) - for i in 0...2047 { - fftPointer[i] = 0.0 - } - } return } @@ -108,13 +103,16 @@ class VisualizationController : NSObject { serialQueue.sync { // Offset latency so the target sample is in the center of the window let latencySamples = (Int)((self.latency + latencyOffset) * self.sampleRate) + 2048 - var samplesToDo = 4096; + var samplesToDo = 4096 if(latencySamples < 0) { - return; + return + } + if(latencySamples + 4096 > visAudioSize) { + return } if(latencySamples < 4096) { // Latency can sometimes dip below this threshold - samplesToDo = latencySamples; + samplesToDo = latencySamples } var j = self.visAudioCursor - latencySamples let k = self.visAudioSize @@ -131,13 +129,7 @@ class VisualizationController : NSObject { } } - if(outPCM != nil) { - let pcmPointer = UnsafeMutableBufferPointer(start: outPCM, count: 4096) - for i in 0...4095 { - let x = outPCMCopy[i] - pcmPointer[i] = x - } - } + outPCM?.update(from: outPCMCopy, count: 4096) if(visFFT != nil) { serialQueue.sync { diff --git a/Visualization/SpectrumViewCG.m b/Visualization/SpectrumViewCG.m index dc633ff0a..58b41e759 100644 --- a/Visualization/SpectrumViewCG.m +++ b/Visualization/SpectrumViewCG.m @@ -41,6 +41,8 @@ extern NSString *CogPlaybackDidStopNotificiation; NSColor *borderColor; ddb_analyzer_t _analyzer; ddb_analyzer_draw_data_t _draw_data; + BOOL invalidateSpectrum; + float analyzerLastWidth; float visAudio[4096], visFFT[2048]; @@ -96,6 +98,12 @@ extern NSString *CogPlaybackDidStopNotificiation; paused = NO; isListening = NO; + visSamplesLastPosted = [self->visController samplesPosted]; + + invalidateSpectrum = YES; + + analyzerLastWidth = -1.0f; + saLowerBound = LOWER_BOUND; [self colorsDidChange:nil]; @@ -122,6 +130,7 @@ extern NSString *CogPlaybackDidStopNotificiation; - (void)enableFullView { isFullView = YES; _analyzer.freq_is_log = 1; + invalidateSpectrum = YES; [self repaint]; } @@ -239,8 +248,10 @@ extern NSString *CogPlaybackDidStopNotificiation; } - (void)timerRun:(NSTimer *)timer { + invalidateSpectrum = YES; [self repaint]; - visLatencyOffset -= 1.0 / 60.0; + if(visSamplesLastPosted) + visLatencyOffset -= 1.0f / 60.0f; } - (void)colorsDidChange:(NSNotification *)notification { @@ -413,16 +424,28 @@ extern NSString *CogPlaybackDidStopNotificiation; } UInt64 samplesPosted = [self->visController samplesPosted]; - if (samplesPosted != visSamplesLastPosted) { + if(!samplesPosted) return; + + if(samplesPosted != visSamplesLastPosted) { + float latencyOffset = (float)(((double)((SInt64)samplesPosted - (SInt64)visSamplesLastPosted)) / [self->visController readSampleRate]); + if (latencyOffset < 0) + latencyOffset = -visLatencyOffset; visSamplesLastPosted = samplesPosted; - visLatencyOffset = 0.0; + visLatencyOffset += latencyOffset; } - [self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0] latencyOffset:visLatencyOffset]; + BOOL invalidateWidth = isFullView && (fabs(analyzerLastWidth - self.bounds.size.width) > 1e-5); + if(invalidateWidth) + analyzerLastWidth = self.bounds.size.width; - 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); + if(invalidateWidth || invalidateSpectrum) { + [self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0] latencyOffset:visLatencyOffset]; + + 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); + invalidateSpectrum = NO; + } if(isFullView) { [self drawSaGrid]; @@ -438,6 +461,7 @@ extern NSString *CogPlaybackDidStopNotificiation; _analyzer.mode = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS; _analyzer.mode_did_change = 1; + invalidateSpectrum = YES; [self repaint]; } diff --git a/Visualization/SpectrumViewSK.m b/Visualization/SpectrumViewSK.m index 6eae05045..1d8ae0be3 100644 --- a/Visualization/SpectrumViewSK.m +++ b/Visualization/SpectrumViewSK.m @@ -39,6 +39,7 @@ extern NSString *CogPlaybackDidStopNotificiation; NSColor *backgroundColor; ddb_analyzer_t _analyzer; ddb_analyzer_draw_data_t _draw_data; + BOOL invalidateSpectrum; SCNVector3 cameraPosition2d; SCNVector3 cameraEulerAngles2d; @@ -84,7 +85,7 @@ extern NSString *CogPlaybackDidStopNotificiation; NSDictionary *sceneOptions = @{ SCNPreferredRenderingAPIKey: @(SCNRenderingAPIMetal), SCNPreferredDeviceKey: device, - SCNPreferLowPowerDeviceKey: @(NO) + SCNPreferLowPowerDeviceKey: @(YES) }; self = [super initWithFrame:frame options:sceneOptions]; @@ -191,6 +192,10 @@ extern NSString *CogPlaybackDidStopNotificiation; isListening = NO; cameraControlEnabled = NO; + invalidateSpectrum = YES; + + visSamplesLastPosted = [self->visController samplesPosted]; + [self setBackgroundColor:[NSColor clearColor]]; SCNScene *theScene = [SCNScene sceneNamed:@"Scenes.scnassets/Spectrum.scn"]; @@ -311,21 +316,38 @@ extern NSString *CogPlaybackDidStopNotificiation; [self updateVisListening]; if(stopped) { - [self drawBaseBands]; + if(invalidateSpectrum) { + [self drawBaseBands]; + invalidateSpectrum = NO; + } return; } UInt64 samplesPosted = [self->visController samplesPosted]; - if (samplesPosted != visSamplesLastPosted) { - visSamplesLastPosted = samplesPosted; - visLatencyOffset = 0.0; + if(!samplesPosted) { + if(invalidateSpectrum) { + [self drawBaseBands]; + invalidateSpectrum = NO; + } + return; } - [self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0] latencyOffset:visLatencyOffset]; + if(samplesPosted != visSamplesLastPosted) { + float latencyOffset = (float)(((double)((SInt64)samplesPosted - (SInt64)visSamplesLastPosted)) / [self->visController readSampleRate]); + if (latencyOffset < 0) + latencyOffset = -visLatencyOffset; + visSamplesLastPosted = samplesPosted; + visLatencyOffset += latencyOffset; + } - 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); + if(invalidateSpectrum) { + [self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0] latencyOffset:visLatencyOffset]; + + 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); + invalidateSpectrum = NO; + } [self drawAnalyzer]; } @@ -346,8 +368,10 @@ extern NSString *CogPlaybackDidStopNotificiation; } - (void)timerRun:(NSTimer *)timer { + invalidateSpectrum = YES; [self repaint]; - visLatencyOffset -= 1.0 / 60.0; + if(visSamplesLastPosted) + visLatencyOffset -= 1.0f / 60.0f; } - (void)startPlayback { @@ -376,6 +400,7 @@ extern NSString *CogPlaybackDidStopNotificiation; stopped = YES; paused = NO; bandsReset = NO; + invalidateSpectrum = YES; [self updateVisListening]; [self repaint]; } @@ -420,11 +445,13 @@ extern NSString *CogPlaybackDidStopNotificiation; 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; + const float bar_height = bar[barIndex].bar_height; + const float peak_ypos = bar[barIndex].peak_ypos; + if(bar_height > maxValue) { + maxValue = bar_height; } - if(bar[barIndex].peak_ypos > maxMax) { - maxMax = bar[barIndex].peak_ypos; + if(peak_ypos > maxMax) { + maxMax = peak_ypos; } } } @@ -455,6 +482,7 @@ extern NSString *CogPlaybackDidStopNotificiation; _analyzer.mode = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS; _analyzer.mode_did_change = 1; + invalidateSpectrum = YES; [self repaint]; }