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 <kode54@gmail.com>
This commit is contained in:
parent
170fb06a62
commit
6c24ad8244
3 changed files with 107 additions and 63 deletions
|
@ -12,7 +12,7 @@ class VisualizationController : NSObject {
|
||||||
var serialQueue = DispatchQueue(label: "Visualization Queue")
|
var serialQueue = DispatchQueue(label: "Visualization Queue")
|
||||||
var sampleRate = 0.0
|
var sampleRate = 0.0
|
||||||
var latency = 0.0
|
var latency = 0.0
|
||||||
var visAudio: [Float] = Array(repeating: 0.0, count: 44100 * 45)
|
var visAudio: [Float] = Array<Float>(repeating: 0.0, count: 44100 * 45)
|
||||||
var visAudioCursor = 0
|
var visAudioCursor = 0
|
||||||
var visAudioSize = 0
|
var visAudioSize = 0
|
||||||
var visSamplesPosted: UInt64 = 0
|
var visSamplesPosted: UInt64 = 0
|
||||||
|
@ -30,12 +30,10 @@ class VisualizationController : NSObject {
|
||||||
@objc
|
@objc
|
||||||
func reset() {
|
func reset() {
|
||||||
serialQueue.sync {
|
serialQueue.sync {
|
||||||
self.latency = 0;
|
self.latency = 0
|
||||||
let amount = self.visAudioSize
|
self.visAudioSize = 44100 * 45
|
||||||
for i in 0..<amount {
|
self.visAudio = Array<Float>(repeating: 0.0, count: visAudioSize)
|
||||||
self.visAudio[i] = 0
|
self.visSamplesPosted = 0
|
||||||
}
|
|
||||||
self.visSamplesPosted = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,9 +52,9 @@ class VisualizationController : NSObject {
|
||||||
serialQueue.sync {
|
serialQueue.sync {
|
||||||
if(self.sampleRate != sampleRate) {
|
if(self.sampleRate != sampleRate) {
|
||||||
self.sampleRate = sampleRate
|
self.sampleRate = sampleRate
|
||||||
visAudioSize = (Int)(sampleRate * 45.0)
|
self.visAudioSize = (Int)(sampleRate * 45.0)
|
||||||
visAudio = Array(repeating: 0.0, count: visAudioSize)
|
self.visAudio = Array<Float>(repeating: 0.0, count: visAudioSize)
|
||||||
visAudioCursor = 0
|
self.visAudioCursor = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -64,17 +62,23 @@ class VisualizationController : NSObject {
|
||||||
@objc
|
@objc
|
||||||
func postVisPCM(_ inPCM: UnsafePointer<Float>?, amount: Int) {
|
func postVisPCM(_ inPCM: UnsafePointer<Float>?, amount: Int) {
|
||||||
serialQueue.sync {
|
serialQueue.sync {
|
||||||
let bufferPointer = UnsafeBufferPointer<Float>(start: inPCM, count: amount)
|
if(self.visAudioSize == 0) {
|
||||||
var j = self.visAudioCursor
|
return
|
||||||
let k = self.visAudioSize
|
}
|
||||||
for i in 0..<amount {
|
let bufferPointer = UnsafeRawPointer(inPCM)
|
||||||
let x = bufferPointer[i]
|
if let bptr = bufferPointer {
|
||||||
self.visAudio[j] = x
|
let dataArray = bptr.assumingMemoryBound(to: Float.self)
|
||||||
j += 1; if j >= k { j = 0 }
|
var j = self.visAudioCursor
|
||||||
|
let k = self.visAudioSize
|
||||||
|
for i in 0..<amount {
|
||||||
|
let x = dataArray[i]
|
||||||
|
self.visAudio[j] = x
|
||||||
|
j += 1; if j >= 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
|
@objc
|
||||||
func copyVisPCM(_ outPCM: UnsafeMutablePointer<Float>?, visFFT: UnsafeMutablePointer<Float>?, latencyOffset: Double) {
|
func copyVisPCM(_ outPCM: UnsafeMutablePointer<Float>?, visFFT: UnsafeMutablePointer<Float>?, latencyOffset: Double) {
|
||||||
|
outPCM?.update(repeating: 0.0, count: 4096)
|
||||||
|
visFFT?.update(repeating: 0.0, count: 2048)
|
||||||
|
|
||||||
if(self.visAudioSize == 0) {
|
if(self.visAudioSize == 0) {
|
||||||
if(outPCM != nil) {
|
|
||||||
let pcmPointer = UnsafeMutableBufferPointer<Float>(start: outPCM, count: 4096)
|
|
||||||
for i in 0...4095 {
|
|
||||||
pcmPointer[i] = 0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(visFFT != nil) {
|
|
||||||
let fftPointer = UnsafeMutableBufferPointer<Float>(start: visFFT, count: 2048)
|
|
||||||
for i in 0...2047 {
|
|
||||||
fftPointer[i] = 0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,13 +103,16 @@ class VisualizationController : NSObject {
|
||||||
serialQueue.sync {
|
serialQueue.sync {
|
||||||
// Offset latency so the target sample is in the center of the window
|
// Offset latency so the target sample is in the center of the window
|
||||||
let latencySamples = (Int)((self.latency + latencyOffset) * self.sampleRate) + 2048
|
let latencySamples = (Int)((self.latency + latencyOffset) * self.sampleRate) + 2048
|
||||||
var samplesToDo = 4096;
|
var samplesToDo = 4096
|
||||||
if(latencySamples < 0) {
|
if(latencySamples < 0) {
|
||||||
return;
|
return
|
||||||
|
}
|
||||||
|
if(latencySamples + 4096 > visAudioSize) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
if(latencySamples < 4096) {
|
if(latencySamples < 4096) {
|
||||||
// Latency can sometimes dip below this threshold
|
// Latency can sometimes dip below this threshold
|
||||||
samplesToDo = latencySamples;
|
samplesToDo = latencySamples
|
||||||
}
|
}
|
||||||
var j = self.visAudioCursor - latencySamples
|
var j = self.visAudioCursor - latencySamples
|
||||||
let k = self.visAudioSize
|
let k = self.visAudioSize
|
||||||
|
@ -131,13 +129,7 @@ class VisualizationController : NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(outPCM != nil) {
|
outPCM?.update(from: outPCMCopy, count: 4096)
|
||||||
let pcmPointer = UnsafeMutableBufferPointer<Float>(start: outPCM, count: 4096)
|
|
||||||
for i in 0...4095 {
|
|
||||||
let x = outPCMCopy[i]
|
|
||||||
pcmPointer[i] = x
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(visFFT != nil) {
|
if(visFFT != nil) {
|
||||||
serialQueue.sync {
|
serialQueue.sync {
|
||||||
|
|
|
@ -41,6 +41,8 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
NSColor *borderColor;
|
NSColor *borderColor;
|
||||||
ddb_analyzer_t _analyzer;
|
ddb_analyzer_t _analyzer;
|
||||||
ddb_analyzer_draw_data_t _draw_data;
|
ddb_analyzer_draw_data_t _draw_data;
|
||||||
|
BOOL invalidateSpectrum;
|
||||||
|
float analyzerLastWidth;
|
||||||
|
|
||||||
float visAudio[4096], visFFT[2048];
|
float visAudio[4096], visFFT[2048];
|
||||||
|
|
||||||
|
@ -96,6 +98,12 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
paused = NO;
|
paused = NO;
|
||||||
isListening = NO;
|
isListening = NO;
|
||||||
|
|
||||||
|
visSamplesLastPosted = [self->visController samplesPosted];
|
||||||
|
|
||||||
|
invalidateSpectrum = YES;
|
||||||
|
|
||||||
|
analyzerLastWidth = -1.0f;
|
||||||
|
|
||||||
saLowerBound = LOWER_BOUND;
|
saLowerBound = LOWER_BOUND;
|
||||||
|
|
||||||
[self colorsDidChange:nil];
|
[self colorsDidChange:nil];
|
||||||
|
@ -122,6 +130,7 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
- (void)enableFullView {
|
- (void)enableFullView {
|
||||||
isFullView = YES;
|
isFullView = YES;
|
||||||
_analyzer.freq_is_log = 1;
|
_analyzer.freq_is_log = 1;
|
||||||
|
invalidateSpectrum = YES;
|
||||||
[self repaint];
|
[self repaint];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,8 +248,10 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)timerRun:(NSTimer *)timer {
|
- (void)timerRun:(NSTimer *)timer {
|
||||||
|
invalidateSpectrum = YES;
|
||||||
[self repaint];
|
[self repaint];
|
||||||
visLatencyOffset -= 1.0 / 60.0;
|
if(visSamplesLastPosted)
|
||||||
|
visLatencyOffset -= 1.0f / 60.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)colorsDidChange:(NSNotification *)notification {
|
- (void)colorsDidChange:(NSNotification *)notification {
|
||||||
|
@ -413,16 +424,28 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt64 samplesPosted = [self->visController samplesPosted];
|
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;
|
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);
|
if(invalidateWidth || invalidateSpectrum) {
|
||||||
ddb_analyzer_tick(&_analyzer);
|
[self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0] latencyOffset:visLatencyOffset];
|
||||||
ddb_analyzer_get_draw_data(&_analyzer, self.bounds.size.width, self.bounds.size.height, &_draw_data);
|
|
||||||
|
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) {
|
if(isFullView) {
|
||||||
[self drawSaGrid];
|
[self drawSaGrid];
|
||||||
|
@ -438,6 +461,7 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
|
|
||||||
_analyzer.mode = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS;
|
_analyzer.mode = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS;
|
||||||
_analyzer.mode_did_change = 1;
|
_analyzer.mode_did_change = 1;
|
||||||
|
invalidateSpectrum = YES;
|
||||||
|
|
||||||
[self repaint];
|
[self repaint];
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
NSColor *backgroundColor;
|
NSColor *backgroundColor;
|
||||||
ddb_analyzer_t _analyzer;
|
ddb_analyzer_t _analyzer;
|
||||||
ddb_analyzer_draw_data_t _draw_data;
|
ddb_analyzer_draw_data_t _draw_data;
|
||||||
|
BOOL invalidateSpectrum;
|
||||||
|
|
||||||
SCNVector3 cameraPosition2d;
|
SCNVector3 cameraPosition2d;
|
||||||
SCNVector3 cameraEulerAngles2d;
|
SCNVector3 cameraEulerAngles2d;
|
||||||
|
@ -84,7 +85,7 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
NSDictionary *sceneOptions = @{
|
NSDictionary *sceneOptions = @{
|
||||||
SCNPreferredRenderingAPIKey: @(SCNRenderingAPIMetal),
|
SCNPreferredRenderingAPIKey: @(SCNRenderingAPIMetal),
|
||||||
SCNPreferredDeviceKey: device,
|
SCNPreferredDeviceKey: device,
|
||||||
SCNPreferLowPowerDeviceKey: @(NO)
|
SCNPreferLowPowerDeviceKey: @(YES)
|
||||||
};
|
};
|
||||||
|
|
||||||
self = [super initWithFrame:frame options:sceneOptions];
|
self = [super initWithFrame:frame options:sceneOptions];
|
||||||
|
@ -191,6 +192,10 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
isListening = NO;
|
isListening = NO;
|
||||||
cameraControlEnabled = NO;
|
cameraControlEnabled = NO;
|
||||||
|
|
||||||
|
invalidateSpectrum = YES;
|
||||||
|
|
||||||
|
visSamplesLastPosted = [self->visController samplesPosted];
|
||||||
|
|
||||||
[self setBackgroundColor:[NSColor clearColor]];
|
[self setBackgroundColor:[NSColor clearColor]];
|
||||||
|
|
||||||
SCNScene *theScene = [SCNScene sceneNamed:@"Scenes.scnassets/Spectrum.scn"];
|
SCNScene *theScene = [SCNScene sceneNamed:@"Scenes.scnassets/Spectrum.scn"];
|
||||||
|
@ -311,21 +316,38 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
[self updateVisListening];
|
[self updateVisListening];
|
||||||
|
|
||||||
if(stopped) {
|
if(stopped) {
|
||||||
[self drawBaseBands];
|
if(invalidateSpectrum) {
|
||||||
|
[self drawBaseBands];
|
||||||
|
invalidateSpectrum = NO;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UInt64 samplesPosted = [self->visController samplesPosted];
|
UInt64 samplesPosted = [self->visController samplesPosted];
|
||||||
if (samplesPosted != visSamplesLastPosted) {
|
if(!samplesPosted) {
|
||||||
visSamplesLastPosted = samplesPosted;
|
if(invalidateSpectrum) {
|
||||||
visLatencyOffset = 0.0;
|
[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);
|
if(invalidateSpectrum) {
|
||||||
ddb_analyzer_tick(&_analyzer);
|
[self->visController copyVisPCM:&visAudio[0] visFFT:&visFFT[0] latencyOffset:visLatencyOffset];
|
||||||
ddb_analyzer_get_draw_data(&_analyzer, 11.0, 1.0, &_draw_data);
|
|
||||||
|
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];
|
[self drawAnalyzer];
|
||||||
}
|
}
|
||||||
|
@ -346,8 +368,10 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)timerRun:(NSTimer *)timer {
|
- (void)timerRun:(NSTimer *)timer {
|
||||||
|
invalidateSpectrum = YES;
|
||||||
[self repaint];
|
[self repaint];
|
||||||
visLatencyOffset -= 1.0 / 60.0;
|
if(visSamplesLastPosted)
|
||||||
|
visLatencyOffset -= 1.0f / 60.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)startPlayback {
|
- (void)startPlayback {
|
||||||
|
@ -376,6 +400,7 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
stopped = YES;
|
stopped = YES;
|
||||||
paused = NO;
|
paused = NO;
|
||||||
bandsReset = NO;
|
bandsReset = NO;
|
||||||
|
invalidateSpectrum = YES;
|
||||||
[self updateVisListening];
|
[self updateVisListening];
|
||||||
[self repaint];
|
[self repaint];
|
||||||
}
|
}
|
||||||
|
@ -420,11 +445,13 @@ extern NSString *CogPlaybackDidStopNotificiation;
|
||||||
const int barBase = i * barStep;
|
const int barBase = i * barStep;
|
||||||
const int barIndex = barBase + j;
|
const int barIndex = barBase + j;
|
||||||
if(barIndex < _draw_data.bar_count) {
|
if(barIndex < _draw_data.bar_count) {
|
||||||
if(bar[barIndex].bar_height > maxValue) {
|
const float bar_height = bar[barIndex].bar_height;
|
||||||
maxValue = 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) {
|
if(peak_ypos > maxMax) {
|
||||||
maxMax = bar[barIndex].peak_ypos;
|
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 = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS;
|
||||||
_analyzer.mode_did_change = 1;
|
_analyzer.mode_did_change = 1;
|
||||||
|
invalidateSpectrum = YES;
|
||||||
|
|
||||||
[self repaint];
|
[self repaint];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue