Volume control: Make preamp optional, defaulting to a limit of 100% volume

CQTexperiment
Christopher Snowhill 2022-01-17 22:41:26 -08:00
parent 6684a8280f
commit 7a0c1d230e
7 changed files with 97 additions and 38 deletions

View File

@ -63,6 +63,7 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
[NSNumber numberWithBool:NO], @"GraphicEQenable",
[NSNumber numberWithInt:-1], @"GraphicEQpreset",
[NSNumber numberWithBool:NO], @"GraphicEQtrackgenre",
[NSNumber numberWithBool:YES], @"volumeLimit",
nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
@ -72,9 +73,12 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
- (void)awakeFromNib
{
double volume = [[NSUserDefaults standardUserDefaults] doubleForKey:@"volume"];
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
[volumeSlider setDoubleValue:logarithmicToLinear(volume)];
double volume = [[NSUserDefaults standardUserDefaults] doubleForKey:@"volume"];
[volumeSlider setDoubleValue:logarithmicToLinear(volume, MAX_VOLUME)];
[audioPlayer setVolume:volume];
[self setSeekable:NO];
@ -350,9 +354,12 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
*/
- (IBAction)changeVolume:(id)sender
{
DLog(@"VOLUME: %lf, %lf", [sender doubleValue], linearToLogarithmic([sender doubleValue]));
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
[audioPlayer setVolume:linearToLogarithmic([sender doubleValue])];
DLog(@"VOLUME: %lf, %lf", [sender doubleValue], linearToLogarithmic([sender doubleValue], MAX_VOLUME));
[audioPlayer setVolume:linearToLogarithmic([sender doubleValue], MAX_VOLUME)];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
}
@ -374,9 +381,12 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
}
else // volume is at 0 or below, we are ready to release the timer and move on
{
[audioPlayer pause];
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
[audioPlayer pause];
[audioPlayer setVolume:originalVolume];
[volumeSlider setDoubleValue: logarithmicToLinear(originalVolume)];
[volumeSlider setDoubleValue: logarithmicToLinear(originalVolume, MAX_VOLUME)];
[audioTimer invalidate];
fading = NO;
@ -401,8 +411,11 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
}
else // volume is at or near original level, we are ready to release the timer and move on
{
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
[audioPlayer setVolume:originalVolume];
[volumeSlider setDoubleValue: logarithmicToLinear(originalVolume)];
[volumeSlider setDoubleValue: logarithmicToLinear(originalVolume, MAX_VOLUME)];
[audioTimer invalidate];
fading = NO;
@ -539,8 +552,11 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
- (IBAction)volumeDown:(id)sender
{
double newVolume = [audioPlayer volumeDown:DEFAULT_VOLUME_DOWN];
[volumeSlider setDoubleValue:logarithmicToLinear(newVolume)];
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
double newVolume = [audioPlayer volumeDown:DEFAULT_VOLUME_DOWN];
[volumeSlider setDoubleValue:logarithmicToLinear(newVolume, MAX_VOLUME)];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
@ -548,9 +564,12 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
- (IBAction)volumeUp:(id)sender
{
double newVolume;
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
double newVolume;
newVolume = [audioPlayer volumeUp:DEFAULT_VOLUME_UP];
[volumeSlider setDoubleValue:logarithmicToLinear(newVolume)];
[volumeSlider setDoubleValue:logarithmicToLinear(newVolume, MAX_VOLUME)];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
}

View File

@ -566,7 +566,10 @@
- (double)volumeUp:(double)amount
{
double newVolume = linearToLogarithmic(logarithmicToLinear(volume + amount));
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
double newVolume = linearToLogarithmic(logarithmicToLinear(volume + amount, MAX_VOLUME), MAX_VOLUME);
if (newVolume > MAX_VOLUME)
newVolume = MAX_VOLUME;
@ -579,11 +582,14 @@
- (double)volumeDown:(double)amount
{
double newVolume;
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
double newVolume;
if (amount > volume)
newVolume = 0.0;
else
newVolume = linearToLogarithmic(logarithmicToLinear(volume - amount));
newVolume = linearToLogarithmic(logarithmicToLinear(volume - amount, MAX_VOLUME), MAX_VOLUME);
[self setVolume:newVolume];
return newVolume;

View File

@ -7,7 +7,5 @@
*
*/
#define MAX_VOLUME 800.0
double logarithmicToLinear(double logarithmic);
double linearToLogarithmic(double linear);
double logarithmicToLinear(double logarithmic, double MAX_VOLUME);
double linearToLogarithmic(double linear, double MAX_VOLUME);

View File

@ -15,13 +15,13 @@
//Here's why: http://www.dr-lex.34sp.com/info-stuff/volumecontrols.html
//We are using the approximation of X^4.
//Input/Output values are in percents.
double logarithmicToLinear(double logarithmic)
double logarithmicToLinear(double logarithmic, double MAX_VOLUME)
{
return pow((logarithmic/MAX_VOLUME), 0.25) * 100.0;
return pow((logarithmic/MAX_VOLUME), 0.25) * 100.0;
}
double linearToLogarithmic(double linear)
double linearToLogarithmic(double linear, double MAX_VOLUME)
{
return (linear/100.0) * (linear/100.0) * (linear/100.0) * (linear/100.0) * MAX_VOLUME;
return (linear/100.0) * (linear/100.0) * (linear/100.0) * (linear/100.0) * MAX_VOLUME;
}
//End helper volume function thingies. ONWARDS TO GLORY!

View File

@ -245,18 +245,9 @@
</connections>
</customObject>
<customView id="58" userLabel="OutputView">
<rect key="frame" x="0.0" y="0.0" width="530" height="110"/>
<rect key="frame" x="0.0" y="0.0" width="530" height="150"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="65">
<rect key="frame" x="17" y="78" width="123" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Output Device: " id="211">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8GG-SU-ZrX">
<rect key="frame" x="5" y="16" width="135" height="17"/>
<autoresizingMask key="autoresizingMask"/>
@ -267,7 +258,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="60">
<rect key="frame" x="144" y="72" width="251" height="26"/>
<rect key="frame" x="144" y="80" width="251" height="26"/>
<autoresizingMask key="autoresizingMask"/>
<popUpButtonCell key="cell" type="push" title="Item1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" inset="2" arrowPosition="arrowAtCenter" preferredEdge="maxY" selectedItem="62" id="210">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -288,7 +279,7 @@
</connections>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zkP-2E-1Kc">
<rect key="frame" x="17" y="47" width="123" height="17"/>
<rect key="frame" x="17" y="51" width="123" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Volume Level:" id="wK4-EF-8Wa">
<font key="font" metaFont="system"/>
@ -297,7 +288,7 @@
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2v7-Ef-ekr">
<rect key="frame" x="144" y="41" width="251" height="26"/>
<rect key="frame" x="144" y="45" width="251" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" id="vmS-eb-zen">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
@ -338,8 +329,28 @@
<binding destination="52" name="selectedObject" keyPath="values.outputResampling" previousBinding="lIz-gH-9kJ" id="wcT-TT-yKh"/>
</connections>
</popUpButton>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" allowsCharacterPickerTouchBarItem="YES" translatesAutoresizingMaskIntoConstraints="NO" id="65">
<rect key="frame" x="17" y="86" width="123" height="17"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Output Device: " id="211">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DhK-tx-xFv">
<rect key="frame" x="18" y="121" width="227" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Limit volume control to 100%" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="ds2-aw-ebU">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<binding destination="52" name="value" keyPath="values.volumeLimit" id="7Sl-LJ-ljd"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="-151" y="337.5"/>
<point key="canvasLocation" x="-151" y="318"/>
</customView>
<customObject id="i5B-ga-Atm" userLabel="MIDIPane" customClass="MIDIPane">
<connections>

View File

@ -11,6 +11,7 @@
@interface VolumeSlider : NSSlider {
NSPopover *popover;
NSText *textView;
double MAX_VOLUME;
}
- (void)showToolTip;

View File

@ -28,6 +28,9 @@
}
- (void) awakeFromNib {
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
wasInsideSnapRange = NO;
textView = [[NSText alloc] init];
[textView setFrame:NSMakeRect(0, 0, 50, 20)];
@ -44,12 +47,19 @@
popover.behavior = NSPopoverBehaviorTransient;
popover.animates = NO;
[popover setContentSize:textView.bounds.size];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeLimit" options:0 context:nil];
}
- (void)dealloc
{
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeLimit"];
}
- (void)updateToolTip
{
double value = [self doubleValue];
double volume = linearToLogarithmic(value);
double volume = linearToLogarithmic(value, MAX_VOLUME);
NSString *text = [NSString stringWithFormat:@"%0.lf%%", volume];
@ -114,10 +124,24 @@
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([keyPath isEqualToString:@"values.volumeLimit"]) {
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double new_MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
if (MAX_VOLUME != new_MAX_VOLUME) {
double currentLevel = linearToLogarithmic([self doubleValue], MAX_VOLUME);
[self setDoubleValue:logarithmicToLinear(currentLevel, new_MAX_VOLUME)];
}
MAX_VOLUME = new_MAX_VOLUME;
}
}
- (BOOL)sendAction:(SEL)theAction to:(id)theTarget
{
// Snap to 100% if value is close
double snapTarget = logarithmicToLinear(100.0);
double snapTarget = logarithmicToLinear(100.0, MAX_VOLUME);
double snapProgress = ([self doubleValue] - snapTarget) / (self.maxValue - self.minValue);
if (fabs(snapProgress) < 0.005)