Cleaned up project settings to current defaults, except for the macOS deployment version, which is still 10.13. Cleaned up a lot of headers and such to include with angle braces instead of double quotes. Enabled build sandbox in a lot of places. Disabled subproject signing in several places, for libraries and frameworks which will be stripped and signed when they are copied into place in the final build. Also, while trying to solve compilation issues, the visualization controller was reverted to the Objective C implementation, which is probably faster anyway. Stupid Swift/Objective-C language mixing issues. Signed-off-by: Christopher Snowhill <kode54@gmail.com>
171 lines
4 KiB
Objective-C
171 lines
4 KiB
Objective-C
//
|
|
// VisualizationController.m
|
|
// CogAudio Framework
|
|
//
|
|
// Created by Christopher Snowhill on 2/12/22.
|
|
//
|
|
|
|
#import "VisualizationController.h"
|
|
#import <Accelerate/Accelerate.h>
|
|
|
|
#import "fft.h"
|
|
|
|
@implementation VisualizationController {
|
|
double sampleRate;
|
|
double latency;
|
|
float *visAudio;
|
|
int visAudioCursor, visAudioSize;
|
|
uint64_t visSamplesPosted;
|
|
}
|
|
|
|
static VisualizationController *_sharedController = nil;
|
|
|
|
+ (VisualizationController *)sharedController {
|
|
@synchronized(self) {
|
|
if(!_sharedController) {
|
|
_sharedController = [[VisualizationController alloc] init];
|
|
}
|
|
}
|
|
return _sharedController;
|
|
}
|
|
|
|
- (id)init {
|
|
self = [super init];
|
|
if(self) {
|
|
visAudio = NULL;
|
|
visAudioSize = 0;
|
|
latency = 0;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
fft_free();
|
|
}
|
|
|
|
- (void)reset {
|
|
@synchronized (self) {
|
|
latency = 0;
|
|
visAudioCursor = 0;
|
|
visSamplesPosted = 0;
|
|
if(visAudio && visAudioSize) {
|
|
bzero(visAudio, sizeof(float) * visAudioSize);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)postSampleRate:(double)sampleRate {
|
|
@synchronized(self) {
|
|
if(self->sampleRate != sampleRate) {
|
|
self->sampleRate = sampleRate;
|
|
int visAudioSize = (int)(sampleRate * 45.0);
|
|
void *visAudio = realloc(self->visAudio, visAudioSize * sizeof(float));
|
|
if(visAudio && visAudioSize) {
|
|
if(visAudioSize > self->visAudioSize) {
|
|
bzero(((float *)visAudio) + self->visAudioSize, sizeof(float) * (visAudioSize - self->visAudioSize));
|
|
}
|
|
self->visAudio = visAudio;
|
|
self->visAudioSize = visAudioSize;
|
|
visAudioCursor %= visAudioSize;
|
|
} else {
|
|
if(self->visAudio) {
|
|
free(self->visAudio);
|
|
self->visAudio = NULL;
|
|
}
|
|
self->visAudioSize = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)postVisPCM:(const float *)inPCM amount:(int)amount {
|
|
@synchronized(self) {
|
|
if(!visAudioSize) {
|
|
return;
|
|
}
|
|
int samplesRead = 0;
|
|
while(amount > 0) {
|
|
int amountToCopy = (int)(visAudioSize - visAudioCursor);
|
|
if(amountToCopy > amount) amountToCopy = amount;
|
|
cblas_scopy(amountToCopy, inPCM + samplesRead, 1, visAudio + visAudioCursor, 1);
|
|
visAudioCursor = visAudioCursor + amountToCopy;
|
|
if(visAudioCursor >= visAudioSize) visAudioCursor -= visAudioSize;
|
|
amount -= amountToCopy;
|
|
samplesRead += amountToCopy;
|
|
visSamplesPosted += amountToCopy;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)postLatency:(double)latency {
|
|
self->latency = latency;
|
|
assert(latency < 45.0);
|
|
}
|
|
|
|
- (double)readSampleRate {
|
|
@synchronized(self) {
|
|
return sampleRate;
|
|
}
|
|
}
|
|
|
|
- (UInt64)samplesPosted {
|
|
return visSamplesPosted;
|
|
}
|
|
|
|
- (void)copyVisPCM:(float *)outPCM visFFT:(float *)outFFT latencyOffset:(double)latency {
|
|
if(!outPCM && !outFFT) return;
|
|
|
|
if(!visAudio || !visAudioSize) {
|
|
if(outPCM) bzero(outPCM, sizeof(float) * 4096);
|
|
if(outFFT) bzero(outFFT, sizeof(float) * 2048);
|
|
return;
|
|
}
|
|
|
|
void *visAudioTemp = calloc(sizeof(float), 4096);
|
|
if(!visAudioTemp) {
|
|
if(outPCM) bzero(outPCM, sizeof(float) * 4096);
|
|
if(outFFT) bzero(outFFT, sizeof(float) * 2048);
|
|
return;
|
|
}
|
|
|
|
@synchronized(self) {
|
|
if(!sampleRate) {
|
|
bzero(outPCM, 4096 * sizeof(float));
|
|
if(outFFT) {
|
|
bzero(outFFT, 2048 * sizeof(float));
|
|
}
|
|
return;
|
|
}
|
|
int latencySamples = (int)(sampleRate * (self->latency + latency)) + 2048;
|
|
if(latencySamples < 4096) latencySamples = 4096;
|
|
int readCursor = visAudioCursor - latencySamples;
|
|
int samples = 4096;
|
|
int samplesRead = 0;
|
|
if(latencySamples + samples > visAudioSize) {
|
|
samples = (int)(visAudioSize - latencySamples);
|
|
}
|
|
while(readCursor < 0)
|
|
readCursor += visAudioSize;
|
|
while(readCursor >= visAudioSize)
|
|
readCursor -= visAudioSize;
|
|
while(samples > 0) {
|
|
int samplesToRead = (int)(visAudioSize - readCursor);
|
|
if(samplesToRead > samples) samplesToRead = samples;
|
|
cblas_scopy(samplesToRead, visAudio + readCursor, 1, visAudioTemp + samplesRead, 1);
|
|
samplesRead += samplesToRead;
|
|
readCursor += samplesToRead;
|
|
samples -= samplesToRead;
|
|
if(readCursor >= visAudioSize) readCursor -= visAudioSize;
|
|
}
|
|
}
|
|
if(outPCM) {
|
|
cblas_scopy(4096, visAudioTemp, 1, outPCM, 1);
|
|
}
|
|
if(outFFT) {
|
|
fft_calculate(visAudioTemp, outFFT, 2048);
|
|
}
|
|
|
|
free(visAudioTemp);
|
|
}
|
|
|
|
@end
|