Just in case you need to know exactly what's going on. This isn't perfect, it might have some bugs.
#import "EQEFrequencyResponseCurve.h"
#import "macros.h"
#import <syslog.h>
#import "ipc.h"
#import "EQEMainView.h"
struct plot_t {
CGFloat x;
CGFloat y;
};
void eqe_calculate_curve(struct plot_t *plot, size_t len, bool first, float a0, float a1, float a2, float b1, float b2)
{
for(int i = 0; i < len; i++) {
float w = exp(log(1/0.001) * i/(len-1))*0.001*M_PI;
float phi = pow(sin(w/2), 2);
float y = log(pow(a0+a1+a2, 2) - 4*(a0*a1 + 4*a0*a2 + a1*a2)*phi + 16*a0*a2*phi*phi) - log(pow(1+b1+b2, 2) - 4*(b1 + 4*b2 + b1*b2)*phi + 16*b2*phi*phi);
y = y * 10 / log(10);
if(isnan(y)) {
y = -200;
}
if(first) {
plot[i].x = (CGFloat)i / (len - 1) / 2;
plot[i].y = y;
} else {
plot[i].y = plot[i].y + y;
}
}
}
@interface EQEFrequencyResponseCurve()
{
float _preamp;
struct plot_t *_plots;
size_t _plotCount;
BOOL _isFlat;
}
@end
@implementation EQEFrequencyResponseCurve
-(instancetype)init
{
self = [super init];
self.backgroundColor = UIColor.clearColor;
return self;
}
-(void)update
{
float preamp = [lua_rpc("return eqe.preamp") floatValue];
_preamp = preamp;
NSArray<NSArray<NSNumber *> *> *response = lua_rpc("return getcoefs()");
_plotCount = (int)(self.frame.size.width/4);
_plots = calloc(_plotCount, sizeof(struct plot_t));
_isFlat = true;
for(int i = 0; i < response.count; i++) {
NSArray<NSNumber *> *coefs = response[i];
if(coefs.count >= 5) {
eqe_calculate_curve(_plots, _plotCount, _isFlat, coefs[0].floatValue, coefs[1].floatValue, coefs[2].floatValue, coefs[3].floatValue, coefs[4].floatValue);
_isFlat = false;
}
}
}
-(float)ztransy:(float)y
{
float MAX = 70;
return EQETopOffset - y*self.frame.size.height/MAX;
}
-(struct plot_t)ztransform:(struct plot_t)p
{
struct plot_t r;
r.x = p.x * self.frame.size.width * 2;
r.y = [self ztransy:(p.y + _preamp)];
return r;
}
static void contextSetRGB(float r, float g, float b)
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, r, g, b, 1);
CGContextSetRGBFillColor(context, r, g, b, 1);
}
static void setcolor(float y)
{
float scale = 20;
float scaledY = y/scale + 0.5;
if(scaledY < 0.5) {
// green
contextSetRGB(scaledY*2, 1, 0);
} else if(scaledY > 0.5) {
// red
contextSetRGB(1, (1-scaledY)*2, 0);
} else {
// yellow
contextSetRGB(1, 1, 0);
}
}
static const float dotFrequencies[] = {
// This should be 100Hz, 1kHz and 10kHz. The formula in frequencyToX() is bad
99,
990,
9200,
};
static float frequencyToX(EQEFrequencyResponseCurve *self, float f)
{
static const float MIN_F = 20;
static const float MAX_F = 20000;
// this formula is bad
return self.frame.size.width*log((f - MIN_F)*exp(6.75)/(MAX_F - MIN_F) + 1)/6.75;
}
static float shouldHaveDot(EQEFrequencyResponseCurve *self, float x0, float x1)
{
static const int count = sizeof(dotFrequencies) / sizeof(float);
for(int i = 0; i < count; i++) {
float x = frequencyToX(self, dotFrequencies[i]);
if(x >= x0 && x <= x1) {
return x;
}
}
return -1;
}
-(void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetLineWidth(context, 1);
static const float dotSize = 3;
if(_isFlat) {
float y = [self ztransy:_preamp];
setcolor(_preamp);
CGContextMoveToPoint(context, 0, y);
CGContextAddLineToPoint(context, self.frame.size.width, y);
CGContextStrokePath(context);
static const int count = sizeof(dotFrequencies) / sizeof(float);
for(int i = 0; i < count; i++) {
float x = frequencyToX(self, dotFrequencies[i]);
CGContextFillEllipseInRect(context, CGRectMake(x - dotSize/2, y - dotSize/2, dotSize, dotSize));
}
} else {
float lasty;
for(int i = 0; i < _plotCount; i++) {
struct plot_t t = [self ztransform:_plots[i]];
if(i != 0) {
CGContextAddLineToPoint(context, t.x, t.y);
setcolor((_plots[i].y + lasty)/2 + _preamp);
CGContextStrokePath(context);
struct plot_t lastT = [self ztransform:_plots[i - 1]];
float x = shouldHaveDot(self, lastT.x, t.x);
if(x > 0) {
float y = (lastT.y + t.y) / 2;
CGContextFillEllipseInRect(context, CGRectMake(x - dotSize/2, y - dotSize/2, dotSize, dotSize));
}
}
CGContextMoveToPoint(context, t.x, t.y);
lasty = _plots[i].y;
}
}
}
@end