The Visualization Manager PCM/FFT copy function was already observing these parameters for null input and only returning output to the ones which were not null. This just makes it clear that they are both optional parameters. This is useful for future visualization adventures, allowing PCM copy without invoking the FFT processing, or requesting FFT without also having to keep the PCM. This is mostly only a compile time change, and has no noticeable effect on the current runtime behavior, as the only consumers of the Visualizer data currently request both PCM and FFT. 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 *_Nullable)outPCM visFFT:(float *_Nullable)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
|