2024-09-21 02:23:59 -03:00
|
|
|
|
//
|
2024-12-09 05:31:58 -03:00
|
|
|
|
// PitchSlider.m
|
2024-09-21 02:23:59 -03:00
|
|
|
|
// Cog
|
|
|
|
|
//
|
|
|
|
|
// Created by Christopher Snowhill on 9/20/24.
|
|
|
|
|
// Copyright 2024 __LoSnoCo__. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
#import "PitchSlider.h"
|
2024-09-21 02:23:59 -03:00
|
|
|
|
#import "CogAudio/Helper.h"
|
|
|
|
|
#import "PlaybackController.h"
|
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
static void *kPitchSliderContext = &kPitchSliderContext;
|
2024-09-21 02:23:59 -03:00
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
@implementation PitchSlider {
|
2024-09-21 02:23:59 -03:00
|
|
|
|
NSTimer *currentTimer;
|
|
|
|
|
BOOL wasInsideSnapRange;
|
|
|
|
|
/*BOOL observersadded;*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)initWithFrame:(NSRect)frame {
|
|
|
|
|
self = [super initWithFrame:frame];
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (id)initWithCoder:(NSCoder *)coder {
|
|
|
|
|
self = [super initWithCoder:coder];
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)awakeFromNib {
|
|
|
|
|
wasInsideSnapRange = NO;
|
|
|
|
|
textView = [[NSText alloc] init];
|
|
|
|
|
[textView setFrame:NSMakeRect(0, 0, 50, 20)];
|
|
|
|
|
textView.drawsBackground = NO;
|
|
|
|
|
textView.editable = NO;
|
|
|
|
|
textView.alignment = NSTextAlignmentCenter;
|
|
|
|
|
|
|
|
|
|
NSViewController *viewController = [[NSViewController alloc] init];
|
|
|
|
|
viewController.view = textView;
|
|
|
|
|
|
|
|
|
|
popover = [[NSPopover alloc] init];
|
|
|
|
|
popover.contentViewController = viewController;
|
|
|
|
|
// Don't hide the popover automatically.
|
|
|
|
|
popover.behavior = NSPopoverBehaviorTransient;
|
|
|
|
|
popover.animates = NO;
|
|
|
|
|
[popover setContentSize:textView.bounds.size];
|
|
|
|
|
|
|
|
|
|
/*observersadded = YES;*/
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*- (void)dealloc {
|
|
|
|
|
if(observersadded) {
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
- (void)updateToolTip {
|
2024-12-09 05:31:58 -03:00
|
|
|
|
const double value = ([self doubleValue] - [self minValue]) * 100.0 / ([self maxValue] - [self minValue]);
|
2024-09-21 02:23:59 -03:00
|
|
|
|
NSString *text;
|
2024-12-09 05:31:58 -03:00
|
|
|
|
|
|
|
|
|
const double adjustedValue = ((value * value) * (5.0 - 0.2) / 10000.0) + 0.2;
|
|
|
|
|
|
|
|
|
|
double pitch;
|
|
|
|
|
if(adjustedValue < 0.2) {
|
|
|
|
|
pitch = 0.2;
|
|
|
|
|
} else if(adjustedValue > 5.0) {
|
|
|
|
|
pitch = 5.0;
|
2024-09-21 02:23:59 -03:00
|
|
|
|
} else {
|
2024-12-09 05:31:58 -03:00
|
|
|
|
pitch = adjustedValue;
|
2024-09-21 02:23:59 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
if(pitch < 1)
|
|
|
|
|
text = [NSString stringWithFormat:@"%0.2lf×", pitch];
|
2024-09-21 02:23:59 -03:00
|
|
|
|
else
|
2024-12-09 05:31:58 -03:00
|
|
|
|
text = [NSString stringWithFormat:@"%0.1lf×", pitch];
|
2024-09-21 02:23:59 -03:00
|
|
|
|
|
|
|
|
|
[textView setString:text];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showToolTip {
|
|
|
|
|
[self updateToolTip];
|
|
|
|
|
|
|
|
|
|
double progress = (self.maxValue - [self doubleValue]) / (self.maxValue - self.minValue);
|
|
|
|
|
CGFloat width = self.knobThickness - 1;
|
|
|
|
|
// Show tooltip to the left of the Slider Knob
|
|
|
|
|
CGFloat height = self.knobThickness / 2.f + (self.bounds.size.height - self.knobThickness) * progress - 1;
|
|
|
|
|
|
|
|
|
|
NSWindow *window = self.window;
|
|
|
|
|
NSPoint screenPoint = [window convertPointToScreen:NSMakePoint(width + 1, height + 1)];
|
|
|
|
|
|
|
|
|
|
if(window.screen.frame.size.width < screenPoint.x + textView.bounds.size.width + 64) // wing it
|
|
|
|
|
[popover showRelativeToRect:NSMakeRect(1, height, 2, 2) ofView:self preferredEdge:NSRectEdgeMinX];
|
|
|
|
|
else
|
|
|
|
|
[popover showRelativeToRect:NSMakeRect(width, height, 2, 2) ofView:self preferredEdge:NSRectEdgeMaxX];
|
|
|
|
|
[self.window.parentWindow makeKeyWindow];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showToolTipForDuration:(NSTimeInterval)duration {
|
|
|
|
|
[self showToolTip];
|
|
|
|
|
|
|
|
|
|
[self hideToolTipAfterDelay:duration];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)showToolTipForView:(NSView *)view closeAfter:(NSTimeInterval)duration {
|
|
|
|
|
[self updateToolTip];
|
|
|
|
|
|
|
|
|
|
[popover showRelativeToRect:view.bounds ofView:view preferredEdge:NSRectEdgeMaxY];
|
|
|
|
|
|
|
|
|
|
[self hideToolTipAfterDelay:duration];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hideToolTip {
|
|
|
|
|
[popover close];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)hideToolTipAfterDelay:(NSTimeInterval)duration {
|
|
|
|
|
if(currentTimer) {
|
|
|
|
|
[currentTimer invalidate];
|
|
|
|
|
currentTimer = nil;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(duration > 0.0) {
|
|
|
|
|
currentTimer = [NSTimer scheduledTimerWithTimeInterval:duration
|
|
|
|
|
target:self
|
|
|
|
|
selector:@selector(hideToolTip)
|
|
|
|
|
userInfo:nil
|
|
|
|
|
repeats:NO];
|
|
|
|
|
[[NSRunLoop mainRunLoop] addTimer:currentTimer forMode:NSRunLoopCommonModes];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
2024-12-09 05:31:58 -03:00
|
|
|
|
if(context != kPitchSliderContext) {
|
2024-09-21 02:23:59 -03:00
|
|
|
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
- (BOOL)sendAction:(SEL)theAction to:(id)theTarget {
|
|
|
|
|
// Snap to 1.0× if value is close
|
|
|
|
|
double snapTarget = 1.0;
|
2024-12-09 05:31:58 -03:00
|
|
|
|
const double value = ([self doubleValue] - [self minValue]) * 100.0 / ([self maxValue] - [self minValue]);
|
|
|
|
|
const double adjustedValue = ((value * value) * (5.0 - 0.2) / 10000.0) + 0.2;
|
|
|
|
|
double snapProgress = (adjustedValue - snapTarget);
|
2024-09-21 02:23:59 -03:00
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
|
|
|
|
|
if(speedLock) {
|
|
|
|
|
[_TempoSlider setDoubleValue:[self doubleValue]];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(fabs(snapProgress) < 0.01) {
|
|
|
|
|
const double inverseValue = sqrtf((snapTarget - 0.2) * 10000.0 / (5.0 - 0.2));
|
|
|
|
|
[self setDoubleValue:inverseValue];
|
|
|
|
|
if(speedLock) {
|
|
|
|
|
[_TempoSlider setDoubleValue:inverseValue];
|
|
|
|
|
}
|
2024-09-21 02:23:59 -03:00
|
|
|
|
if(!wasInsideSnapRange) {
|
|
|
|
|
[[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric performanceTime:NSHapticFeedbackPerformanceTimeDefault];
|
|
|
|
|
}
|
|
|
|
|
wasInsideSnapRange = YES;
|
|
|
|
|
} else {
|
|
|
|
|
wasInsideSnapRange = NO;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
[self showToolTipForDuration:1.0];
|
2024-09-21 02:23:59 -03:00
|
|
|
|
|
|
|
|
|
return [super sendAction:theAction to:theTarget];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)scrollWheel:(NSEvent *)theEvent {
|
|
|
|
|
double change = [theEvent deltaY];
|
|
|
|
|
|
|
|
|
|
[self setDoubleValue:[self doubleValue] + change];
|
|
|
|
|
|
2024-12-09 05:31:58 -03:00
|
|
|
|
[[self target] changePitch:self];
|
|
|
|
|
|
|
|
|
|
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
|
|
|
|
|
if(speedLock) {
|
|
|
|
|
[_TempoSlider setDoubleValue:[self doubleValue]];
|
|
|
|
|
|
|
|
|
|
[[self target] changeTempo:self];
|
|
|
|
|
}
|
2024-09-21 02:23:59 -03:00
|
|
|
|
|
|
|
|
|
[self showToolTipForDuration:1.0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|