Speed Control: Implement simple speed control
Implements a simple speed control using a resampler designed for real time changes. A rubberband speed control will be implemented at a later date. Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
2e5140a321
commit
6a309a3075
18 changed files with 770 additions and 41 deletions
|
@ -19,6 +19,9 @@
|
|||
#define DEFAULT_VOLUME_DOWN 5
|
||||
#define DEFAULT_VOLUME_UP DEFAULT_VOLUME_DOWN
|
||||
|
||||
#define DEFAULT_SPEED_DOWN 0.2
|
||||
#define DEFAULT_SPEED_UP DEFAULT_SPEED_DOWN
|
||||
|
||||
extern NSString *CogPlaybackDidBeginNotificiation;
|
||||
extern NSString *CogPlaybackDidPauseNotificiation;
|
||||
extern NSString *CogPlaybackDidResumeNotificiation;
|
||||
|
@ -40,6 +43,7 @@ extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
|
|||
IBOutlet EqualizerWindowController *equalizerWindowController;
|
||||
|
||||
IBOutlet NSSlider *volumeSlider;
|
||||
IBOutlet NSSlider *speedSlider;
|
||||
|
||||
IBOutlet NSArrayController *outputDevices;
|
||||
|
||||
|
@ -69,6 +73,10 @@ extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
|
|||
- (IBAction)volumeDown:(id)sender;
|
||||
- (IBAction)volumeUp:(id)sender;
|
||||
|
||||
- (IBAction)changeSpeed:(id)sender;
|
||||
- (IBAction)speedDown:(id)sender;
|
||||
- (IBAction)speedUp:(id)sender;
|
||||
|
||||
- (IBAction)playPauseResume:(id)sender;
|
||||
- (IBAction)pauseResume:(id)sender;
|
||||
- (IBAction)skipToNextAlbum:(id)sender;
|
||||
|
|
|
@ -91,6 +91,7 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
|
|||
|
||||
- (void)initDefaults {
|
||||
NSDictionary *defaultsDictionary = @{ @"volume": @(75.0),
|
||||
@"speed": @(1.0),
|
||||
@"GraphicEQenable": @(NO),
|
||||
@"GraphicEQpreset": @(-1),
|
||||
@"GraphicEQtrackgenre": @(NO),
|
||||
|
@ -109,6 +110,9 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
|
|||
[volumeSlider setDoubleValue:logarithmicToLinear(volume, MAX_VOLUME)];
|
||||
[audioPlayer setVolume:volume];
|
||||
|
||||
double speed = [[NSUserDefaults standardUserDefaults] doubleForKey:@"speed"];
|
||||
[audioPlayer setSpeed:speed];
|
||||
|
||||
[self setSeekable:NO];
|
||||
}
|
||||
|
||||
|
@ -478,6 +482,14 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
}
|
||||
}
|
||||
|
||||
- (IBAction)changeSpeed:(id)sender {
|
||||
DLog(@"SPEED: %lf", [sender doubleValue]);
|
||||
|
||||
[audioPlayer setSpeed:[sender doubleValue]];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer speed] forKey:@"speed"];
|
||||
}
|
||||
|
||||
- (IBAction)skipToNextAlbum:(id)sender {
|
||||
BOOL found = NO;
|
||||
|
||||
|
@ -579,6 +591,20 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
|||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
|
||||
}
|
||||
|
||||
- (IBAction)speedDown:(id)sender {
|
||||
double newSpeed = [audioPlayer speedDown:DEFAULT_SPEED_DOWN];
|
||||
[speedSlider setDoubleValue:[audioPlayer speed]];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer speed] forKey:@"speed"];
|
||||
}
|
||||
|
||||
- (IBAction)speedUp:(id)sender {
|
||||
double newSpeed = [audioPlayer speedUp:DEFAULT_SPEED_UP];
|
||||
[speedSlider setDoubleValue:[audioPlayer speed]];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer speed] forKey:@"speed"];
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq {
|
||||
|
||||
if(_eq && _eq != eq) {
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
OutputNode *output;
|
||||
|
||||
double volume;
|
||||
double speed;
|
||||
|
||||
NSMutableArray *chainQueue;
|
||||
|
||||
|
@ -74,6 +75,11 @@
|
|||
- (double)volumeUp:(double)amount;
|
||||
- (double)volumeDown:(double)amount;
|
||||
|
||||
- (void)setSpeed:(double)s;
|
||||
- (double)speed;
|
||||
- (double)speedUp:(double)amount;
|
||||
- (double)speedDown:(double)amount;
|
||||
|
||||
- (double)amountPlayed;
|
||||
- (double)amountPlayedInterval;
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@
|
|||
}
|
||||
[output setup];
|
||||
[output setVolume:volume];
|
||||
[output setSpeed:speed];
|
||||
@synchronized(chainQueue) {
|
||||
for(id anObject in chainQueue) {
|
||||
[anObject setShouldContinue:NO];
|
||||
|
@ -210,6 +211,16 @@
|
|||
return volume;
|
||||
}
|
||||
|
||||
- (void)setSpeed:(double)s {
|
||||
speed = s;
|
||||
|
||||
[output setSpeed:s];
|
||||
}
|
||||
|
||||
- (double)speed {
|
||||
return speed;
|
||||
}
|
||||
|
||||
// This is called by the delegate DURING a requestNextStream request.
|
||||
- (void)setNextStream:(NSURL *)url {
|
||||
[self setNextStream:url withUserInfo:nil withRGInfo:nil];
|
||||
|
@ -648,6 +659,32 @@
|
|||
return newVolume;
|
||||
}
|
||||
|
||||
- (double)speedUp:(double)amount {
|
||||
const double MAX_SPEED = 5.0;
|
||||
|
||||
double newSpeed;
|
||||
if((speed + amount) > MAX_SPEED)
|
||||
newSpeed = MAX_SPEED;
|
||||
else
|
||||
newSpeed = speed + amount;
|
||||
|
||||
[self setSpeed:newSpeed];
|
||||
return newSpeed;
|
||||
}
|
||||
|
||||
- (double)speedDown:(double)amount {
|
||||
const double MIN_SPEED = 0.2;
|
||||
|
||||
double newSpeed;
|
||||
if((speed - amount) < MIN_SPEED)
|
||||
newSpeed = MIN_SPEED;
|
||||
else
|
||||
newSpeed = speed - amount;
|
||||
|
||||
[self setSpeed:newSpeed];
|
||||
return newSpeed;
|
||||
}
|
||||
|
||||
- (void)waitUntilCallbacksExit {
|
||||
// This sucks! And since the thread that's inside the function can be calling
|
||||
// event dispatches, we have to pump the message queue if we're on the main
|
||||
|
|
|
@ -61,6 +61,8 @@
|
|||
|
||||
- (void)setVolume:(double)v;
|
||||
|
||||
- (void)setSpeed:(double)s;
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s;
|
||||
|
||||
- (void)setShouldPlayOutBuffer:(BOOL)s;
|
||||
|
|
|
@ -164,6 +164,10 @@
|
|||
[output setVolume:v];
|
||||
}
|
||||
|
||||
- (void)setSpeed:(double)s {
|
||||
[output setSpeed:s];
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
[super setShouldContinue:s];
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ using std::atomic_long;
|
|||
#import <stdio.h>
|
||||
#endif
|
||||
|
||||
#import <soxr.h>
|
||||
|
||||
@class OutputNode;
|
||||
|
||||
@class FSurroundFilter;
|
||||
|
@ -54,6 +56,10 @@ using std::atomic_long;
|
|||
|
||||
double lastClippedSampleRate;
|
||||
|
||||
soxr_t rssimplespeed;
|
||||
double ssRenderedIn, ssLastRenderedIn;
|
||||
double ssRenderedOut;
|
||||
|
||||
void *rsvis;
|
||||
double lastVisRate;
|
||||
|
||||
|
@ -84,6 +90,9 @@ using std::atomic_long;
|
|||
float volume;
|
||||
float eqPreamp;
|
||||
|
||||
double speed;
|
||||
double lastSpeed;
|
||||
|
||||
AVAudioFormat *_deviceFormat;
|
||||
|
||||
AudioDeviceID outputDeviceID;
|
||||
|
@ -174,4 +183,6 @@ using std::atomic_long;
|
|||
|
||||
- (void)reportMotion:(simd_float4x4)matrix;
|
||||
|
||||
- (void)setSpeed:(double)s;
|
||||
|
||||
@end
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
|
||||
#import "FSurroundFilter.h"
|
||||
|
||||
#define OCTAVES 5
|
||||
|
||||
extern void scale_by_volume(float *buffer, size_t count, float volume);
|
||||
|
||||
static NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotificiation";
|
||||
|
@ -336,6 +338,9 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA
|
|||
|
||||
outputLock = [[NSLock alloc] init];
|
||||
|
||||
speed = 1.0;
|
||||
lastSpeed = 1.0;
|
||||
|
||||
#ifdef OUTPUT_LOG
|
||||
NSString *logName = [NSTemporaryDirectory() stringByAppendingPathComponent:@"CogAudioLog.raw"];
|
||||
_logFile = fopen([logName UTF8String], "wb");
|
||||
|
@ -807,6 +812,19 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
[outputLock unlock];
|
||||
}
|
||||
|
||||
if(rssimplespeed) {
|
||||
soxr_delete(rssimplespeed);
|
||||
}
|
||||
|
||||
soxr_error_t error;
|
||||
soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, SOXR_VR);
|
||||
rssimplespeed = soxr_create(1 << OCTAVES, 1, channels, &error, NULL, &q_spec, NULL);
|
||||
soxr_set_io_ratio(rssimplespeed, speed, 0);
|
||||
|
||||
ssRenderedIn = 0.0;
|
||||
ssLastRenderedIn = 0.0;
|
||||
ssRenderedOut = 0.0;
|
||||
|
||||
streamFormat = realStreamFormat;
|
||||
streamFormat.mChannelsPerFrame = channels;
|
||||
streamFormat.mBytesPerFrame = sizeof(float) * channels;
|
||||
|
@ -929,6 +947,42 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
samplePtr = &inputBuffer[0];
|
||||
|
||||
if(samplesRendered || fsurround) {
|
||||
{
|
||||
int simpleSpeedInput = samplesRendered;
|
||||
int simpleSpeedRendered = 0;
|
||||
int channels = realStreamFormat.mChannelsPerFrame;
|
||||
int max_block_len = 8192;
|
||||
|
||||
if (fabs(speed - lastSpeed) > 1e-5) {
|
||||
lastSpeed = speed;
|
||||
soxr_set_io_ratio(rssimplespeed, speed, max_block_len);
|
||||
}
|
||||
|
||||
const double inputRatio = 1.0 / realStreamFormat.mSampleRate;
|
||||
const double outputRatio = inputRatio * speed;
|
||||
|
||||
while (simpleSpeedInput > 0) {
|
||||
int block_len = max_block_len - simpleSpeedRendered;
|
||||
|
||||
if (!block_len)
|
||||
break;
|
||||
|
||||
float *ibuf = samplePtr;
|
||||
int len = simpleSpeedInput;
|
||||
float *obuf = &rsInBuffer[simpleSpeedRendered * channels];
|
||||
size_t idone = 0;
|
||||
size_t odone = 0;
|
||||
int error = soxr_process(rssimplespeed, ibuf, len, &idone, obuf, block_len, &odone);
|
||||
simpleSpeedInput -= idone;
|
||||
ibuf += channels * idone;
|
||||
simpleSpeedRendered += odone;
|
||||
ssRenderedIn += idone * inputRatio;
|
||||
ssRenderedOut += odone * outputRatio;
|
||||
samplePtr = ibuf;
|
||||
}
|
||||
samplePtr = &rsInBuffer[0];
|
||||
samplesRendered = simpleSpeedRendered;
|
||||
}
|
||||
[outputLock lock];
|
||||
if(fsurround) {
|
||||
int countToProcess = samplesRendered;
|
||||
|
@ -1214,9 +1268,13 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
|
||||
- (void)updateLatency:(double)secondsPlayed {
|
||||
if(secondsPlayed > 0) {
|
||||
[outputController incrementAmountPlayed:secondsPlayed];
|
||||
double rendered = ssRenderedIn - ssLastRenderedIn;
|
||||
secondsPlayed = rendered;
|
||||
ssLastRenderedIn = ssRenderedIn;
|
||||
[outputController incrementAmountPlayed:rendered];
|
||||
}
|
||||
double visLatency = visPushed;
|
||||
double simpleSpeedLatency = ssRenderedIn - ssRenderedOut;
|
||||
double visLatency = visPushed + simpleSpeedLatency;
|
||||
visPushed -= secondsPlayed;
|
||||
if(visLatency < secondsPlayed || visLatency > 30.0) {
|
||||
visLatency = secondsPlayed;
|
||||
|
@ -1230,6 +1288,10 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
volume = v * 0.01f;
|
||||
}
|
||||
|
||||
- (void)setSpeed:(double)s {
|
||||
speed = s;
|
||||
}
|
||||
|
||||
- (void)setEqualizerEnabled:(BOOL)enabled {
|
||||
if(enabled && !eqEnabled) {
|
||||
if(_eq) {
|
||||
|
@ -1344,6 +1406,10 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
rsstate_delete(rsvis);
|
||||
rsvis = NULL;
|
||||
}
|
||||
if(rssimplespeed) {
|
||||
soxr_delete(rssimplespeed);
|
||||
rssimplespeed = NULL;
|
||||
}
|
||||
stopCompleted = YES;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22154" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22154"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -25,23 +25,23 @@
|
|||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123">
|
||||
<rect key="frame" x="0.0" y="362" width="1171" height="38"/>
|
||||
<rect key="frame" x="0.0" y="334" width="1186" height="66"/>
|
||||
<subviews>
|
||||
<scrollView fixedFrame="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="24" horizontalPageScroll="0.0" verticalLineScroll="24" verticalPageScroll="0.0" hasHorizontalScroller="NO" usesPredominantAxisScrolling="NO" id="206" userLabel="Scroll View - Playlist View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1171" height="38"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1186" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="KWC-Ti-8KY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1171" height="38"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1186" height="66"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" alternatingRowBackgroundColors="YES" autosaveName="Playlist" rowHeight="18" headerView="1517" viewBased="YES" id="207" customClass="PlaylistView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1171" height="21"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1186" height="49"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="6"/>
|
||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="index" editable="NO" width="63" minWidth="28" maxWidth="64" id="209">
|
||||
<tableColumn identifier="index" editable="NO" width="64" minWidth="28" maxWidth="64" id="209">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="#">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
@ -54,11 +54,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="5N3-SP-Y8z">
|
||||
<rect key="frame" x="11" y="3" width="68" height="18"/>
|
||||
<rect key="frame" x="11" y="3" width="69" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="w5u-JQ-3Hf">
|
||||
<rect key="frame" x="0.0" y="1" width="68" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="69" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="right" title="Table View Cell" id="FMU-QZ-NdQ">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -95,7 +95,7 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="Vw5-xt-0vG">
|
||||
<rect key="frame" x="82" y="3" width="20" height="17"/>
|
||||
<rect key="frame" x="83" y="3" width="20" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ERj-i9-caa">
|
||||
|
@ -127,7 +127,7 @@
|
|||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="rating" editable="NO" width="106.5" minWidth="48" maxWidth="128" id="208" userLabel="Rating">
|
||||
<tableColumn identifier="rating" editable="NO" width="109" minWidth="48" maxWidth="128" id="208" userLabel="Rating">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Rating">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
@ -141,11 +141,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="ZCP-Dx-UBV">
|
||||
<rect key="frame" x="105" y="3" width="107" height="18"/>
|
||||
<rect key="frame" x="106" y="3" width="109" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="exY-Bg-Mjm">
|
||||
<rect key="frame" x="0.0" y="1" width="107" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="109" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="sdo-Sm-KPH">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -171,7 +171,7 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="title" editable="NO" width="168.5" minWidth="96" maxWidth="1024" id="XBr-ec-D81">
|
||||
<tableColumn identifier="title" editable="NO" width="171" minWidth="96" maxWidth="1024" id="XBr-ec-D81">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Title">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.33333299" alpha="1" colorSpace="calibratedWhite"/>
|
||||
|
@ -185,11 +185,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="ZHl-H1-IIC">
|
||||
<rect key="frame" x="215" y="3" width="168" height="18"/>
|
||||
<rect key="frame" x="218" y="3" width="171" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="dQP-wC-mba">
|
||||
<rect key="frame" x="0.0" y="1" width="168" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="171" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="VVx-99-roJ">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -259,7 +259,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="dJs-UO-m5r"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="artist" editable="NO" width="193" minWidth="96" maxWidth="1024" id="391">
|
||||
<tableColumn identifier="artist" editable="NO" width="195" minWidth="96" maxWidth="1024" id="391">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -273,11 +273,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="gpC-Oe-Rog">
|
||||
<rect key="frame" x="386" y="3" width="193" height="18"/>
|
||||
<rect key="frame" x="392" y="3" width="195" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="1WK-qN-Mgj">
|
||||
<rect key="frame" x="0.0" y="1" width="193" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="195" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="71l-3L-S3g">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -347,7 +347,7 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="album" editable="NO" width="193.5" minWidth="96" maxWidth="1024" id="806">
|
||||
<tableColumn identifier="album" editable="NO" width="195.5" minWidth="96" maxWidth="1024" id="806">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Album">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -361,11 +361,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="1ed-gX-bct">
|
||||
<rect key="frame" x="582" y="3" width="194" height="18"/>
|
||||
<rect key="frame" x="590" y="3" width="196" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nEt-s5-vRX">
|
||||
<rect key="frame" x="0.0" y="1" width="194" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="196" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="moV-3G-GpB">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -391,7 +391,7 @@
|
|||
</binding>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="length" editable="NO" width="95" minWidth="43.62012" maxWidth="96" id="807">
|
||||
<tableColumn identifier="length" editable="NO" width="96" minWidth="43.62012" maxWidth="96" id="807">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="Length">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -404,11 +404,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="hhB-nv-e78">
|
||||
<rect key="frame" x="779" y="3" width="95" height="18"/>
|
||||
<rect key="frame" x="788.5" y="3" width="96" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tHy-sM-HDB">
|
||||
<rect key="frame" x="0.0" y="1" width="95" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="96" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Igo-5f-yim">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -435,7 +435,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1919"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="year" editable="NO" width="95" minWidth="42" maxWidth="96" id="848">
|
||||
<tableColumn identifier="year" editable="NO" width="96" minWidth="42" maxWidth="96" id="848">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="Year">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -448,11 +448,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="q93-oh-i5T">
|
||||
<rect key="frame" x="877" y="3" width="95" height="18"/>
|
||||
<rect key="frame" x="887.5" y="3" width="96" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="bOi-LI-TDx">
|
||||
<rect key="frame" x="0.0" y="1" width="95" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="96" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="C2Q-qG-dwX">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -475,7 +475,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1921"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="genre" editable="NO" width="106.5" minWidth="32" maxWidth="512" id="849">
|
||||
<tableColumn identifier="genre" editable="NO" width="108.5" minWidth="32" maxWidth="512" id="849">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Genre">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -489,11 +489,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="rRl-p9-Awr">
|
||||
<rect key="frame" x="975" y="3" width="106" height="18"/>
|
||||
<rect key="frame" x="986.5" y="3" width="108" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yW6-2w-6mN">
|
||||
<rect key="frame" x="0.0" y="1" width="106" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="108" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="js2-sT-U4M">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -516,7 +516,7 @@
|
|||
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1922"/>
|
||||
</connections>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="track" editable="NO" width="71" minWidth="24" maxWidth="72" id="850">
|
||||
<tableColumn identifier="track" editable="NO" width="72" minWidth="24" maxWidth="72" id="850">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="right" title="№">
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -529,11 +529,11 @@
|
|||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView id="hgh-VE-5kl">
|
||||
<rect key="frame" x="1084" y="3" width="75" height="18"/>
|
||||
<rect key="frame" x="1098" y="3" width="76" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yEY-MI-d3o">
|
||||
<rect key="frame" x="0.0" y="1" width="75" height="16"/>
|
||||
<rect key="frame" x="0.0" y="1" width="76" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="tus-lr-RhS">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -848,7 +848,7 @@
|
|||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<tableHeaderView key="headerView" wantsLayer="YES" id="1517">
|
||||
<rect key="frame" x="0.0" y="0.0" width="1171" height="17"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="1186" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableHeaderView>
|
||||
</scrollView>
|
||||
|
@ -861,7 +861,7 @@
|
|||
</connections>
|
||||
</splitView>
|
||||
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="778">
|
||||
<rect key="frame" x="455" y="4" width="261" height="14"/>
|
||||
<rect key="frame" x="463" y="4" width="261" height="14"/>
|
||||
<textFieldCell key="cell" controlSize="small" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="center" title="Total Duration: 00 hours 00 minutes 00 seconds" bezelStyle="round" id="1473">
|
||||
<font key="font" metaFont="controlContent" size="11"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
|
@ -896,7 +896,7 @@
|
|||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
<toolbar key="toolbar" implicitIdentifier="B4998081-90DD-45DD-8243-0F7039C7DEA2" displayMode="iconOnly" sizeMode="regular" id="1523">
|
||||
<toolbar key="toolbar" implicitIdentifier="B4998081-90DD-45DD-8243-0F7039C7DEA2" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="1523">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="1552"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="1529"/>
|
||||
|
@ -998,7 +998,7 @@
|
|||
</textFieldCell>
|
||||
</textField>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="3B680DEB-106E-4549-A478-FFB8A6738053" label="Volume" paletteLabel="Volume" image="volume3Template" visibilityPriority="10" sizingBehavior="auto" id="1610">
|
||||
<toolbarItem implicitItemIdentifier="3B680DEB-106E-4549-A478-FFB8A6738053" label="Volume" paletteLabel="Volume" image="volume3Template" bordered="YES" visibilityPriority="10" sizingBehavior="auto" id="1610">
|
||||
<nil key="toolTip"/>
|
||||
<button key="view" verticalHuggingPriority="750" id="1608" customClass="VolumeButton">
|
||||
<rect key="frame" x="9" y="14" width="29" height="23"/>
|
||||
|
@ -1012,6 +1012,20 @@
|
|||
</connections>
|
||||
</button>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="7BC995AD-3F93-40E1-AE76-94574D34BCB0" label="Speed" paletteLabel="Speed" image="deskclock" catalog="system" bordered="YES" visibilityPriority="10" sizingBehavior="auto" id="ufn-od-xJF" userLabel="Speed">
|
||||
<nil key="toolTip"/>
|
||||
<button key="view" verticalHuggingPriority="750" id="Ta5-Ik-jh9" userLabel="Speed Button" customClass="SpeedButton">
|
||||
<rect key="frame" x="7" y="14" width="27" height="23"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="deskclock" catalog="system" imagePosition="only" alignment="center" borderStyle="border" inset="2" id="2Al-Cl-n36">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<outlet property="_popView" destination="6P4-yi-9TK" id="3d2-Sw-J9J"/>
|
||||
</connections>
|
||||
</button>
|
||||
</toolbarItem>
|
||||
<toolbarItem implicitItemIdentifier="2F487D99-16E9-4BF8-9A98-637FABEB2716" label="Info Inspector" paletteLabel="Info Inspector" image="infoTemplate" navigational="YES" id="1629">
|
||||
<nil key="toolTip"/>
|
||||
<size key="minSize" width="28" height="23"/>
|
||||
|
@ -1175,6 +1189,7 @@
|
|||
<toolbarItem reference="ZH9-ZU-skw"/>
|
||||
<toolbarItem reference="1539"/>
|
||||
<toolbarItem reference="1610"/>
|
||||
<toolbarItem reference="ufn-od-xJF"/>
|
||||
<toolbarItem reference="1552"/>
|
||||
<toolbarItem reference="1568"/>
|
||||
<toolbarItem reference="1551"/>
|
||||
|
@ -1214,7 +1229,7 @@
|
|||
<rect key="frame" x="0.0" y="0.0" width="581" height="0.0"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
</view>
|
||||
<toolbar key="toolbar" implicitIdentifier="35998ECE-5AD8-429E-8479-657249B22C9C" displayMode="iconOnly" sizeMode="regular" id="2222" userLabel="Mini Toolbar">
|
||||
<toolbar key="toolbar" implicitIdentifier="35998ECE-5AD8-429E-8479-657249B22C9C" showsBaselineSeparator="NO" displayMode="iconOnly" sizeMode="regular" id="2222" userLabel="Mini Toolbar">
|
||||
<allowedToolbarItems>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="2227"/>
|
||||
<toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="2228"/>
|
||||
|
@ -2343,6 +2358,7 @@ Gw
|
|||
<outlet property="playlistController" destination="218" id="706"/>
|
||||
<outlet property="playlistLoader" destination="1319" id="ghZ-65-60L"/>
|
||||
<outlet property="playlistView" destination="207" id="717"/>
|
||||
<outlet property="speedSlider" destination="6P4-yi-9TK" id="Xvq-Vj-A88"/>
|
||||
<outlet property="volumeSlider" destination="1612" id="1615"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
|
@ -2522,6 +2538,21 @@ Gw
|
|||
</subviews>
|
||||
<point key="canvasLocation" x="615" y="-25"/>
|
||||
</customView>
|
||||
<customView id="90w-7t-RYP" userLabel="Speed View">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="168"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<slider horizontalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6P4-yi-9TK" userLabel="Speed Slider" customClass="SpeedSlider">
|
||||
<rect key="frame" x="6" y="2" width="20" height="164"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" minValue="0.20000000000000001" maxValue="5" doubleValue="1" tickMarkPosition="left" sliderType="linear" id="vTw-tV-W5R"/>
|
||||
<connections>
|
||||
<action selector="changeSpeed:" target="705" id="FcA-37-2J5"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</subviews>
|
||||
<point key="canvasLocation" x="694" y="-25"/>
|
||||
</customView>
|
||||
<customObject id="1675" customClass="SpotlightWindowController">
|
||||
<connections>
|
||||
<outlet property="playlistLoader" destination="1319" id="1694"/>
|
||||
|
@ -2587,6 +2618,7 @@ Gw
|
|||
</objects>
|
||||
<resources>
|
||||
<image name="deadItemsTemplate" width="20" height="20"/>
|
||||
<image name="deskclock" catalog="system" width="15" height="16"/>
|
||||
<image name="duplicateItemsTemplate" width="20" height="20"/>
|
||||
<image name="equalizerTemplate" width="20" height="20"/>
|
||||
<image name="hdcdLogoTemplate" width="656" height="225"/>
|
||||
|
|
|
@ -154,6 +154,8 @@
|
|||
83988F0E27BE0A5900A0E89A /* RedundantPlaylistDataStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 83988F0D27BE0A5900A0E89A /* RedundantPlaylistDataStore.m */; };
|
||||
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; };
|
||||
839B837F286D7F8D00F529EE /* NumberHertzToStringTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839B837E286D7F8D00F529EE /* NumberHertzToStringTransformer.swift */; };
|
||||
839D48AA2C9E73AA00D03298 /* SpeedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D48A72C9E73AA00D03298 /* SpeedButton.m */; };
|
||||
839D48AB2C9E73AA00D03298 /* SpeedSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 839D48A92C9E73AA00D03298 /* SpeedSlider.m */; };
|
||||
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */; };
|
||||
839E56F52879625100DFB5F4 /* SADIE_D02-96000.mhr in Resources */ = {isa = PBXBuildFile; fileRef = 839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */; };
|
||||
83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -1013,6 +1015,10 @@
|
|||
8399D4E01805A55000B503B1 /* XmlContainer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XmlContainer.m; sourceTree = "<group>"; };
|
||||
8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; sourceTree = "<group>"; };
|
||||
839B837E286D7F8D00F529EE /* NumberHertzToStringTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NumberHertzToStringTransformer.swift; path = Transformers/NumberHertzToStringTransformer.swift; sourceTree = "<group>"; };
|
||||
839D48A62C9E73AA00D03298 /* SpeedButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpeedButton.h; sourceTree = "<group>"; };
|
||||
839D48A72C9E73AA00D03298 /* SpeedButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpeedButton.m; sourceTree = "<group>"; };
|
||||
839D48A82C9E73AA00D03298 /* SpeedSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpeedSlider.h; sourceTree = "<group>"; };
|
||||
839D48A92C9E73AA00D03298 /* SpeedSlider.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpeedSlider.m; sourceTree = "<group>"; };
|
||||
839DA7CB274A2D4C001B18E5 /* NSDictionary+Merge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Merge.h"; sourceTree = "<group>"; };
|
||||
839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Merge.m"; sourceTree = "<group>"; };
|
||||
839E3B53286595D700880EA2 /* GeneralPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GeneralPane.h; path = Preferences/Preferences/GeneralPane.h; sourceTree = "<group>"; };
|
||||
|
@ -1413,6 +1419,10 @@
|
|||
172A12320F5911D20078EF0C /* RepeatTransformers.m */,
|
||||
172A123A0F5912AE0078EF0C /* ShuffleTransformers.h */,
|
||||
172A123B0F5912AE0078EF0C /* ShuffleTransformers.m */,
|
||||
839D48A62C9E73AA00D03298 /* SpeedButton.h */,
|
||||
839D48A72C9E73AA00D03298 /* SpeedButton.m */,
|
||||
839D48A82C9E73AA00D03298 /* SpeedSlider.h */,
|
||||
839D48A92C9E73AA00D03298 /* SpeedSlider.m */,
|
||||
17E0D5E70F520F02005B6FED /* TimeField.h */,
|
||||
17E0D5E80F520F02005B6FED /* TimeField.m */,
|
||||
17E0D6180F520F9F005B6FED /* VolumeButton.h */,
|
||||
|
@ -2551,6 +2561,8 @@
|
|||
83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */,
|
||||
8D11072D0486CEB800E47090 /* main.m in Sources */,
|
||||
8E75757109F31D5A0080F1EE /* DNDArrayController.m in Sources */,
|
||||
839D48AA2C9E73AA00D03298 /* SpeedButton.m in Sources */,
|
||||
839D48AB2C9E73AA00D03298 /* SpeedSlider.m in Sources */,
|
||||
8E75757209F31D5A0080F1EE /* PlaylistController.m in Sources */,
|
||||
8E75757309F31D5A0080F1EE /* PlaylistEntry.m in Sources */,
|
||||
8E75757409F31D5A0080F1EE /* PlaylistView.m in Sources */,
|
||||
|
|
16
SpeedButton.h
Normal file
16
SpeedButton.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// SpeedButton.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 9/20/24.
|
||||
// Copyright 2024 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SpeedSlider.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SpeedButton : NSButton {
|
||||
IBOutlet SpeedSlider *_popView;
|
||||
}
|
||||
|
||||
@end
|
51
SpeedButton.m
Normal file
51
SpeedButton.m
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// SpeedButton.m
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 9/20/24.
|
||||
// Copyright 2024 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SpeedButton.h"
|
||||
#import "PlaybackController.h"
|
||||
|
||||
@implementation SpeedButton {
|
||||
NSPopover *popover;
|
||||
NSViewController *viewController;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
popover = [[NSPopover alloc] init];
|
||||
popover.behavior = NSPopoverBehaviorTransient;
|
||||
[popover setContentSize:_popView.bounds.size];
|
||||
}
|
||||
|
||||
- (void)scrollWheel:(NSEvent *)theEvent {
|
||||
if([popover isShown]) {
|
||||
[_popView scrollWheel:theEvent];
|
||||
return;
|
||||
}
|
||||
|
||||
double change = [theEvent deltaY];
|
||||
|
||||
[_popView setDoubleValue:[_popView doubleValue] + change];
|
||||
|
||||
[[_popView target] changeSpeed:_popView];
|
||||
|
||||
[_popView showToolTipForView:self closeAfter:1.0];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)theEvent {
|
||||
[popover close];
|
||||
|
||||
popover.contentViewController = nil;
|
||||
viewController = [[NSViewController alloc] init];
|
||||
viewController.view = _popView;
|
||||
popover.contentViewController = viewController;
|
||||
|
||||
[popover showRelativeToRect:self.bounds ofView:self preferredEdge:NSRectEdgeMaxY];
|
||||
|
||||
[super mouseDown:theEvent];
|
||||
}
|
||||
|
||||
@end
|
21
SpeedSlider.h
Normal file
21
SpeedSlider.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// SpeedSlider.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 9/20/24.
|
||||
// Copyright 2024 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SpeedSlider : NSSlider {
|
||||
NSPopover *popover;
|
||||
NSText *textView;
|
||||
}
|
||||
|
||||
- (void)showToolTip;
|
||||
- (void)showToolTipForDuration:(NSTimeInterval)duration;
|
||||
- (void)showToolTipForView:(NSView *)view closeAfter:(NSTimeInterval)duration;
|
||||
- (void)hideToolTip;
|
||||
|
||||
@end
|
167
SpeedSlider.m
Normal file
167
SpeedSlider.m
Normal file
|
@ -0,0 +1,167 @@
|
|||
//
|
||||
// SpeedSlider.m
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 9/20/24.
|
||||
// Copyright 2024 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SpeedSlider.h"
|
||||
#import "CogAudio/Helper.h"
|
||||
#import "PlaybackController.h"
|
||||
|
||||
static void *kSpeedSliderContext = &kSpeedSliderContext;
|
||||
|
||||
@implementation SpeedSlider {
|
||||
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 {
|
||||
const double value = [self doubleValue];
|
||||
NSString *text;
|
||||
|
||||
double speed;
|
||||
if(value < 0.2) {
|
||||
speed = 0.2;
|
||||
} else if(value > 5.0) {
|
||||
speed = 5.0;
|
||||
} else {
|
||||
speed = value;
|
||||
}
|
||||
|
||||
if(speed < 1)
|
||||
text = [NSString stringWithFormat:@"%0.2lf×", speed];
|
||||
else
|
||||
text = [NSString stringWithFormat:@"%0.1lf×", speed];
|
||||
|
||||
[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 {
|
||||
if(context != kSpeedSliderContext) {
|
||||
[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;
|
||||
double snapProgress = ([self doubleValue] - snapTarget) / (self.maxValue - self.minValue);
|
||||
|
||||
if(fabs(snapProgress) < 0.005) {
|
||||
[self setDoubleValue:snapTarget];
|
||||
if(!wasInsideSnapRange) {
|
||||
[[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric performanceTime:NSHapticFeedbackPerformanceTimeDefault];
|
||||
}
|
||||
wasInsideSnapRange = YES;
|
||||
} else {
|
||||
wasInsideSnapRange = NO;
|
||||
}
|
||||
|
||||
[self showToolTip];
|
||||
|
||||
return [super sendAction:theAction to:theTarget];
|
||||
}
|
||||
|
||||
- (void)scrollWheel:(NSEvent *)theEvent {
|
||||
double change = [theEvent deltaY];
|
||||
|
||||
[self setDoubleValue:[self doubleValue] + change];
|
||||
|
||||
[[self target] changeSpeed:self];
|
||||
|
||||
[self showToolTipForDuration:1.0];
|
||||
}
|
||||
|
||||
@end
|
16
Window/SpeedButton.h
Normal file
16
Window/SpeedButton.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// VolumeButton.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Vincent Spader on 2/8/09.
|
||||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VolumeSlider.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface VolumeButton : NSButton {
|
||||
IBOutlet VolumeSlider *_popView;
|
||||
}
|
||||
|
||||
@end
|
51
Window/SpeedButton.m
Normal file
51
Window/SpeedButton.m
Normal file
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// VolumeButton.m
|
||||
// Cog
|
||||
//
|
||||
// Created by Vincent Spader on 2/8/09.
|
||||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VolumeButton.h"
|
||||
#import "PlaybackController.h"
|
||||
|
||||
@implementation VolumeButton {
|
||||
NSPopover *popover;
|
||||
NSViewController *viewController;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
popover = [[NSPopover alloc] init];
|
||||
popover.behavior = NSPopoverBehaviorTransient;
|
||||
[popover setContentSize:_popView.bounds.size];
|
||||
}
|
||||
|
||||
- (void)scrollWheel:(NSEvent *)theEvent {
|
||||
if([popover isShown]) {
|
||||
[_popView scrollWheel:theEvent];
|
||||
return;
|
||||
}
|
||||
|
||||
double change = [theEvent deltaY];
|
||||
|
||||
[_popView setDoubleValue:[_popView doubleValue] + change];
|
||||
|
||||
[[_popView target] changeVolume:_popView];
|
||||
|
||||
[_popView showToolTipForView:self closeAfter:1.0];
|
||||
}
|
||||
|
||||
- (void)mouseDown:(NSEvent *)theEvent {
|
||||
[popover close];
|
||||
|
||||
popover.contentViewController = nil;
|
||||
viewController = [[NSViewController alloc] init];
|
||||
viewController.view = _popView;
|
||||
popover.contentViewController = viewController;
|
||||
|
||||
[popover showRelativeToRect:self.bounds ofView:self preferredEdge:NSRectEdgeMaxY];
|
||||
|
||||
[super mouseDown:theEvent];
|
||||
}
|
||||
|
||||
@end
|
22
Window/SpeedSlider.h
Normal file
22
Window/SpeedSlider.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
//
|
||||
// VolumeSlider.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Vincent Spader on 2/8/09.
|
||||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface VolumeSlider : NSSlider {
|
||||
NSPopover *popover;
|
||||
NSText *textView;
|
||||
double MAX_VOLUME;
|
||||
}
|
||||
|
||||
- (void)showToolTip;
|
||||
- (void)showToolTipForDuration:(NSTimeInterval)duration;
|
||||
- (void)showToolTipForView:(NSView *)view closeAfter:(NSTimeInterval)duration;
|
||||
- (void)hideToolTip;
|
||||
|
||||
@end
|
181
Window/SpeedSlider.m
Normal file
181
Window/SpeedSlider.m
Normal file
|
@ -0,0 +1,181 @@
|
|||
//
|
||||
// VolumeSlider.m
|
||||
// Cog
|
||||
//
|
||||
// Created by Vincent Spader on 2/8/09.
|
||||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "VolumeSlider.h"
|
||||
#import "CogAudio/Helper.h"
|
||||
#import "PlaybackController.h"
|
||||
|
||||
static void *kVolumeSliderContext = &kVolumeSliderContext;
|
||||
|
||||
@implementation VolumeSlider {
|
||||
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 {
|
||||
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)];
|
||||
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];
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeLimit" options:0 context:kVolumeSliderContext];
|
||||
observersadded = YES;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if(observersadded) {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeLimit" context:kVolumeSliderContext];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateToolTip {
|
||||
const double value = [self doubleValue];
|
||||
// Sets volume to be the slider value if limit is set to 100% or the actual volume otherwise.
|
||||
const double volume = (MAX_VOLUME == 100) ? value : linearToLogarithmic(value, MAX_VOLUME);
|
||||
NSString *text;
|
||||
|
||||
// If volume becomes less than 1%, display two decimal digits of precision (e.g. 0.34%).
|
||||
if(volume < 1)
|
||||
text = [NSString stringWithFormat:@"%0.2lf%%", volume];
|
||||
// Else if volume becomes less than 10%, display one decimal digit of precision (e.g. 3.4%).
|
||||
else if(volume < 10)
|
||||
text = [NSString stringWithFormat:@"%0.1lf%%", volume];
|
||||
// Else display no decimal digits.
|
||||
else
|
||||
text = [NSString stringWithFormat:@"%0.lf%%", volume];
|
||||
|
||||
[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 {
|
||||
if(context != kVolumeSliderContext) {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
return;
|
||||
}
|
||||
|
||||
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, MAX_VOLUME);
|
||||
double snapProgress = ([self doubleValue] - snapTarget) / (self.maxValue - self.minValue);
|
||||
|
||||
if(fabs(snapProgress) < 0.005) {
|
||||
[self setDoubleValue:snapTarget];
|
||||
if(!wasInsideSnapRange) {
|
||||
[[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric performanceTime:NSHapticFeedbackPerformanceTimeDefault];
|
||||
}
|
||||
wasInsideSnapRange = YES;
|
||||
} else {
|
||||
wasInsideSnapRange = NO;
|
||||
}
|
||||
|
||||
[self showToolTip];
|
||||
|
||||
return [super sendAction:theAction to:theTarget];
|
||||
}
|
||||
|
||||
- (void)scrollWheel:(NSEvent *)theEvent {
|
||||
double change = [theEvent deltaY];
|
||||
|
||||
[self setDoubleValue:[self doubleValue] + change];
|
||||
|
||||
[[self target] changeVolume:self];
|
||||
|
||||
[self showToolTipForDuration:1.0];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in a new issue