Rubber Band: Implement configuration dialog

Now there's a configuration dialog for tweaking the settings
in semi-real time. Everything that can be changed without
restarting is changed without restarting, otherwise the audio
pipeline is reset, which happens quickly enough anyway.

Awaiting translation to Spanish, other languages have been
removed pending their maintainers fixing most of their
problems, which includes me being lazy and AI translating
bits so I could rush updates.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-02-10 14:37:07 -08:00
parent 37f1be354f
commit 0498eb5f81
13 changed files with 870 additions and 14 deletions

View file

@ -55,9 +55,11 @@ using std::atomic_long;
double lastClippedSampleRate;
void *ts;
int tslastoptions, tsnewoptions;
size_t blockSize, toDrop, samplesBuffered;
double ssRenderedIn, ssLastRenderedIn;
double ssRenderedOut;
BOOL tsapplynewoptions;
void *rsvis;
double lastVisRate;

View file

@ -34,6 +34,7 @@ static NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotific
static NSString *CogPlaybackDidResetHeadTracking = @"CogPlaybackDigResetHeadTracking";
#define tts ((RubberBandState)ts)
#define ttslastoptions ((RubberBandOptions)tslastoptions)
simd_float4x4 convertMatrix(CMRotationMatrix r) {
simd_float4x4 matrix = {
@ -53,6 +54,19 @@ OutputCoreAudio *registeredMotionListener = nil;
+ (void)initialize {
motionManagerLock = [[NSLock alloc] init];
{
NSDictionary *defaults = @{@"rubberbandEngine": @"faster",
@"rubberbandTransients": @"crisp",
@"rubberbandDetector": @"compound",
@"rubberbandPhase": @"laminar",
@"rubberbandWindow": @"standard",
@"rubberbandSmoothing": @"off",
@"rubberbandFormant": @"shifted",
@"rubberbandPitch": @"highspeed",
@"rubberbandChannels": @"apart"};
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] registerDefaults:defaults];
}
if(@available(macOS 14, *)) {
CMAuthorizationStatus status = [CMHeadphoneMotionManager authorizationStatus];
if(status == CMAuthorizationStatusDenied) {
@ -406,6 +420,20 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
enableFSurround = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"enableFSurround"];
if(streamFormatStarted)
resetStreamFormat = YES;
} else if([[keyPath substringToIndex:17] isEqualToString:@"values.rubberband"]) {
RubberBandOptions options = [self getRubberbandOptions];
RubberBandOptions changed = options ^ ttslastoptions;
if(changed) {
BOOL engineR3 = !!(options & RubberBandOptionEngineFiner);
// Options which require a restart of the engine
const RubberBandOptions mustRestart = RubberBandOptionEngineFaster | RubberBandOptionEngineFiner | RubberBandOptionWindowStandard | RubberBandOptionWindowShort | RubberBandOptionWindowLong | RubberBandOptionSmoothingOff | RubberBandOptionSmoothingOn | (engineR3 ? RubberBandOptionPitchHighSpeed | RubberBandOptionPitchHighQuality | RubberBandOptionPitchHighConsistency : 0) | RubberBandOptionChannelsApart | RubberBandOptionChannelsTogether;
if(changed & mustRestart) {
resetStreamFormat = YES;
} else {
tsnewoptions = options;
tsapplynewoptions = YES;
}
}
}
}
@ -771,6 +799,94 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
return YES;
}
- (RubberBandOptions)getRubberbandOptions {
RubberBandOptions options = RubberBandOptionProcessRealTime;
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
NSString *value = [defaults stringForKey:@"rubberbandEngine"];
BOOL engineR3 = NO;
if([value isEqualToString:@"faster"]) {
options |= RubberBandOptionEngineFaster;
} else if([value isEqualToString:@"finer"]) {
options |= RubberBandOptionEngineFiner;
engineR3 = YES;
}
if(!engineR3) {
value = [defaults stringForKey:@"rubberbandTransients"];
if([value isEqualToString:@"crisp"]) {
options |= RubberBandOptionTransientsCrisp;
} else if([value isEqualToString:@"mixed"]) {
options |= RubberBandOptionTransientsMixed;
} else if([value isEqualToString:@"smooth"]) {
options |= RubberBandOptionTransientsSmooth;
}
value = [defaults stringForKey:@"rubberbandDetector"];
if([value isEqualToString:@"compound"]) {
options |= RubberBandOptionDetectorCompound;
} else if([value isEqualToString:@"percussive"]) {
options |= RubberBandOptionDetectorPercussive;
} else if([value isEqualToString:@"soft"]) {
options |= RubberBandOptionDetectorSoft;
}
value = [defaults stringForKey:@"rubberbandPhase"];
if([value isEqualToString:@"laminar"]) {
options |= RubberBandOptionPhaseLaminar;
} else if([value isEqualToString:@"independent"]) {
options |= RubberBandOptionPhaseIndependent;
}
}
value = [defaults stringForKey:@"rubberbandWindow"];
if([value isEqualToString:@"standard"]) {
options |= RubberBandOptionWindowStandard;
} else if([value isEqualToString:@"short"]) {
options |= RubberBandOptionWindowShort;
} else if([value isEqualToString:@"long"]) {
if(engineR3) {
options |= RubberBandOptionWindowStandard;
} else {
options |= RubberBandOptionWindowLong;
}
}
if(!engineR3) {
value = [defaults stringForKey:@"rubberbandSmoothing"];
if([value isEqualToString:@"off"]) {
options |= RubberBandOptionSmoothingOff;
} else if([value isEqualToString:@"on"]) {
options |= RubberBandOptionSmoothingOn;
}
}
value = [defaults stringForKey:@"rubberbandFormant"];
if([value isEqualToString:@"shifted"]) {
options |= RubberBandOptionFormantShifted;
} else if([value isEqualToString:@"preserved"]) {
options |= RubberBandOptionFormantPreserved;
}
value = [defaults stringForKey:@"rubberbandPitch"];
if([value isEqualToString:@"highspeed"]) {
options |= RubberBandOptionPitchHighSpeed;
} else if([value isEqualToString:@"highquality"]) {
options |= RubberBandOptionPitchHighQuality;
} else if([value isEqualToString:@"highconsistency"]) {
options |= RubberBandOptionPitchHighConsistency;
}
value = [defaults stringForKey:@"rubberbandChannels"];
if([value isEqualToString:@"apart"]) {
options |= RubberBandOptionChannelsApart;
} else if([value isEqualToString:@"together"]) {
options |= RubberBandOptionChannelsTogether;
}
return options;
}
- (void)updateStreamFormat {
/* Set the channel layout for the audio queue */
resetStreamFormat = NO;
@ -843,7 +959,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
ts = NULL;
}
RubberBandOptions options = RubberBandOptionProcessRealTime;
RubberBandOptions options = [self getRubberbandOptions];
tslastoptions = (int)(options);
ts = rubberband_new(realStreamFormat.mSampleRate, realStreamFormat.mChannelsPerFrame, options, 1.0 / tempo, pitch);
blockSize = 1024;
@ -1000,6 +1117,32 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
int channels = realStreamFormat.mChannelsPerFrame;
//size_t max_block_len = blockSize;
if(tsapplynewoptions) {
tsapplynewoptions = NO;
RubberBandOptions changed = tslastoptions ^ tsnewoptions;
tslastoptions = tsnewoptions;
BOOL engineR3 = !!(tsnewoptions & RubberBandOptionEngineFiner);
const RubberBandOptions transientsmask = RubberBandOptionTransientsCrisp | RubberBandOptionTransientsMixed | RubberBandOptionTransientsSmooth;
const RubberBandOptions detectormask = RubberBandOptionDetectorCompound | RubberBandOptionDetectorPercussive | RubberBandOptionDetectorSoft;
const RubberBandOptions phasemask = RubberBandOptionPhaseLaminar | RubberBandOptionPhaseIndependent;
const RubberBandOptions formantmask = RubberBandOptionFormantShifted | RubberBandOptionFormantPreserved;
const RubberBandOptions pitchmask = RubberBandOptionPitchHighSpeed | RubberBandOptionPitchHighQuality | RubberBandOptionPitchHighConsistency;
if(changed & transientsmask)
rubberband_set_transients_option(tts, tsnewoptions & transientsmask);
if(!engineR3) {
if(changed & detectormask)
rubberband_set_detector_option(tts, tsnewoptions & detectormask);
if(changed & phasemask)
rubberband_set_phase_option(tts, tsnewoptions & phasemask);
}
if(changed & formantmask)
rubberband_set_formant_option(tts, tsnewoptions & formantmask);
if(!engineR3 && (changed & pitchmask))
rubberband_set_pitch_option(tts, tsnewoptions & pitchmask);
}
if(fabs(pitch - lastPitch) > 1e-5 ||
fabs(tempo - lastTempo) > 1e-5) {
lastPitch = pitch;
@ -1333,6 +1476,16 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHrtf" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableHeadTracking" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.enableFSurround" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandEngine" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandTransients" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandDetector" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandPhase" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandWindow" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandSmoothing" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandFormant" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandPitch" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.rubberbandChannels" options:(NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew) context:kOutputCoreAudioContext];
observersapplied = YES;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(resetReferencePosition:) name:CogPlaybackDidResetHeadTracking object:nil];
@ -1422,6 +1575,15 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHrtf" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableHeadTracking" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.enableFSurround" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandEngine" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandTransients" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandDetector" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandPhase" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandWindow" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandSmoothing" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandFormant" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandPitch" context:kOutputCoreAudioContext];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.rubberbandChannels" context:kOutputCoreAudioContext];
observersapplied = NO;
}
stopping = YES;

View file

@ -17,6 +17,7 @@
<outlet property="outputPane" destination="57" id="75"/>
<outlet property="playlistView" destination="231" id="244"/>
<outlet property="updatesView" destination="50" id="99"/>
<outlet property="rubberbandPane" destination="l4Y-NE-ezS" id="XTA-io-tov"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
@ -336,7 +337,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="60">
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="60" userLabel="Output Device">
<rect key="frame" x="144" y="47" width="480" 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">
@ -366,7 +367,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2v7-Ef-ekr">
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2v7-Ef-ekr" userLabel="Volume Level">
<rect key="frame" x="144" y="16" width="480" 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">
@ -382,9 +383,9 @@
</popUpButtonCell>
<connections>
<binding destination="SuD-jI-ifw" name="content" keyPath="arrangedObjects" id="sCS-i7-Vgf"/>
<binding destination="SuD-jI-ifw" name="contentValues" keyPath="arrangedObjects.name" previousBinding="cEA-28-qQF" id="6uG-yb-1C2"/>
<binding destination="SuD-jI-ifw" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="sCS-i7-Vgf" id="cEA-28-qQF"/>
<binding destination="52" name="selectedObject" keyPath="values.volumeScaling" previousBinding="6uG-yb-1C2" id="mQV-hu-vZC"/>
<binding destination="SuD-jI-ifw" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="sCS-i7-Vgf" id="Gmm-bC-A04"/>
<binding destination="SuD-jI-ifw" name="contentValues" keyPath="arrangedObjects.name" previousBinding="Gmm-bC-A04" id="8qt-aA-iuT"/>
<binding destination="52" name="selectedObject" keyPath="values.volumeScaling" previousBinding="8qt-aA-iuT" id="9QJ-Xb-iOP"/>
</connections>
</popUpButton>
</subviews>
@ -408,9 +409,25 @@
<outlet property="sandboxPathBehaviorController" destination="9mC-3k-IQG" id="1Jd-9v-4rm"/>
</connections>
</customObject>
<customObject id="l4Y-NE-ezS" customClass="RubberbandPane">
<connections>
<outlet property="detectorButton" destination="0YE-uu-Wcf" id="6Zi-9v-8xG"/>
<outlet property="detectorLabel" destination="a8m-fk-rd6" id="0Ba-14-DH0"/>
<outlet property="phaseButton" destination="AGi-va-Euk" id="ePJ-19-Eta"/>
<outlet property="phaseLabel" destination="ORM-SO-bUh" id="VZb-mx-Ov3"/>
<outlet property="smoothingButton" destination="Nd4-eR-Fuc" id="egf-oG-HA9"/>
<outlet property="smoothingLabel" destination="Cgd-Gz-9BJ" id="FuP-9E-5TH"/>
<outlet property="transientsButton" destination="DXC-dj-SqU" id="qBv-lU-s4I"/>
<outlet property="transientsLabel" destination="I7l-Pq-shw" id="FRi-lP-zVy"/>
<outlet property="view" destination="aAk-E4-RKg" id="sNN-AB-eF6"/>
<outlet property="windowBehavior" destination="qKv-dH-ulp" id="573-jZ-bx1"/>
</connections>
</customObject>
<arrayController id="59" userLabel="OutputDevices" customClass="OutputsArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" selectsInsertedObjects="NO" id="246" userLabel="PlaylistBehavior" customClass="PlaylistBehaviorArrayController">
@ -814,7 +831,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Nj-G4-5ag">
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8Nj-G4-5ag" userLabel="MIDI Plugin">
<rect key="frame" x="295" y="176" width="329" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="vaQ-pZ-jXy" id="xcv-1b-kTI">
@ -844,7 +861,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="E1D-Bo-ZVf">
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="E1D-Bo-ZVf" userLabel="Resampling Quality">
<rect key="frame" x="295" y="145" width="329" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="3Gx-cs-3B0" id="5q7-83-7V6">
@ -874,7 +891,7 @@
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Yw-25-Gbs">
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Yw-25-Gbs" userLabel="MIDI Flavor">
<rect key="frame" x="295" y="114" width="329" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" autoenablesItems="NO" altersStateOfSelectedItem="NO" selectedItem="XzK-h2-vIT" id="qzt-Ox-taI">
@ -1058,6 +1075,77 @@
<string>valid</string>
</declaredKeys>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="Ewm-6m-QFa" userLabel="RubberbandEngineBehavior" customClass="RubberbandEngineArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="rD7-3H-4FM" userLabel="RubberbandTransientsBehavior" customClass="RubberbandTransientsArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="0rf-6O-MPh" userLabel="RubberbandDetectorBehavior" customClass="RubberbandDetectorArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="XkG-oY-ZCw" userLabel="RubberbandPhaseBehavior" customClass="RubberbandPhaseArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController id="qKv-dH-ulp" userLabel="RubberbandWindowBehavior" customClass="RubberbandWindowArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="7qK-HD-YRC" userLabel="RubberbandSmoothingBehavior" customClass="RubberbandSmoothingArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="5Cw-Q7-Y6w" userLabel="RubberbandFormantBehavior" customClass="RubberbandFormantArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="mFW-7h-5rd" userLabel="RubberbandPitchBehavior" customClass="RubberbandPitchArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<arrayController objectClassName="NSDictionary" editable="NO" id="Dfm-y4-7ki" userLabel="RubberbandChannelsBehavior" customClass="RubberbandChannelsArrayController">
<declaredKeys>
<string>name</string>
<string>slug</string>
<string>preference</string>
</declaredKeys>
<classReference key="objectClass" className="NSDictionary"/>
</arrayController>
<menu id="ape-AV-kEr" userLabel="SandboxContextMenu">
<items>
<menuItem title="Add Path" id="JxP-0t-mTB">
@ -1088,5 +1176,283 @@
</items>
<point key="canvasLocation" x="533" y="-395"/>
</menu>
<customView id="aAk-E4-RKg" userLabel="RubberbandView">
<rect key="frame" x="0.0" y="0.0" width="640" height="250"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ck9-15-ag4">
<rect key="frame" x="18" y="214" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Engine:" id="ULd-G9-gDZ">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="209-Hk-EX0" userLabel="Rubberband Engine">
<rect key="frame" x="186" y="208" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="1lM-3b-70I" id="GC1-VY-f1a">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="dms-r8-XjB">
<items>
<menuItem title="Item 1" state="on" id="1lM-3b-70I"/>
<menuItem title="Item 2" id="Y6o-zp-dJ6"/>
<menuItem title="Item 3" id="opY-8K-dac"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<action selector="changeState:" target="l4Y-NE-ezS" id="76u-y7-qMk"/>
<binding destination="Ewm-6m-QFa" name="content" keyPath="arrangedObjects" id="Isc-4V-5Jn"/>
<binding destination="Ewm-6m-QFa" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="Isc-4V-5Jn" id="ThL-sW-Drv"/>
<binding destination="Ewm-6m-QFa" name="contentValues" keyPath="arrangedObjects.name" previousBinding="ThL-sW-Drv" id="MZq-Y3-D6P"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandEngine" previousBinding="MZq-Y3-D6P" id="D4h-nt-d3Q"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="I7l-Pq-shw">
<rect key="frame" x="18" y="190" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Transients:" id="eI3-LU-JTj">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="DXC-dj-SqU" userLabel="Rubberband Transients">
<rect key="frame" x="186" y="184" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="VST-yJ-RYa" id="kQi-sh-T5t">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="gh1-qA-abN">
<items>
<menuItem title="Item 1" state="on" id="VST-yJ-RYa"/>
<menuItem title="Item 2" id="Rf1-UA-RIc"/>
<menuItem title="Item 3" id="Ki8-F0-0EB"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="rD7-3H-4FM" name="content" keyPath="arrangedObjects" id="vf7-6E-9v5"/>
<binding destination="rD7-3H-4FM" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="vf7-6E-9v5" id="M9I-iY-Wej"/>
<binding destination="rD7-3H-4FM" name="contentValues" keyPath="arrangedObjects.name" previousBinding="M9I-iY-Wej" id="O0z-2y-UPl"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandTransients" previousBinding="O0z-2y-UPl" id="KLB-xJ-rb9"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="a8m-fk-rd6">
<rect key="frame" x="18" y="166" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Detector:" id="ZUS-Cr-zSa">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0YE-uu-Wcf" userLabel="Rubberband Detector">
<rect key="frame" x="186" y="160" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Gi6-qx-PH1" id="WDV-DN-iwU">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="m0Q-cK-by1">
<items>
<menuItem title="Item 1" state="on" id="Gi6-qx-PH1"/>
<menuItem title="Item 2" id="eyg-lG-IIm"/>
<menuItem title="Item 3" id="zNG-Bz-MLw"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="0rf-6O-MPh" name="content" keyPath="arrangedObjects" id="1Lg-gE-DdG"/>
<binding destination="0rf-6O-MPh" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="1Lg-gE-DdG" id="37N-3K-rq6"/>
<binding destination="0rf-6O-MPh" name="contentValues" keyPath="arrangedObjects.name" previousBinding="37N-3K-rq6" id="kLx-xv-a24"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandDetector" previousBinding="kLx-xv-a24" id="MQw-g6-ogU"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ORM-SO-bUh">
<rect key="frame" x="18" y="142" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Phase:" id="xwT-8b-IsJ">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AGi-va-Euk" userLabel="Rubberband Phase">
<rect key="frame" x="186" y="136" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="iEL-9R-pTH" id="Vcd-rh-VAc">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="FTa-ln-M8f">
<items>
<menuItem title="Item 1" state="on" id="iEL-9R-pTH"/>
<menuItem title="Item 2" id="kZs-SP-i2d"/>
<menuItem title="Item 3" id="TUT-PI-q15"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="XkG-oY-ZCw" name="content" keyPath="arrangedObjects" id="15P-mE-WZ7"/>
<binding destination="XkG-oY-ZCw" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="15P-mE-WZ7" id="iRN-4y-psG"/>
<binding destination="XkG-oY-ZCw" name="contentValues" keyPath="arrangedObjects.name" previousBinding="iRN-4y-psG" id="VjJ-5K-hbu"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandPhase" previousBinding="VjJ-5K-hbu" id="e13-ei-jMs"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zUO-EQ-why">
<rect key="frame" x="18" y="118" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Window Size:" id="qc8-jP-K6o">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9g3-eD-m8o" userLabel="Rubberband Window Size">
<rect key="frame" x="186" y="112" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="tqQ-AN-ZlT" id="avs-IS-hfH">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="SA2-M5-pvX">
<items>
<menuItem title="Item 1" state="on" id="tqQ-AN-ZlT"/>
<menuItem title="Item 2" id="q6G-pe-8hq"/>
<menuItem title="Item 3" id="pmK-Yr-lk6"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="qKv-dH-ulp" name="content" keyPath="arrangedObjects" id="Wph-cL-g56"/>
<binding destination="qKv-dH-ulp" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="Wph-cL-g56" id="RHw-Gv-iI0"/>
<binding destination="qKv-dH-ulp" name="contentValues" keyPath="arrangedObjects.name" previousBinding="RHw-Gv-iI0" id="NOc-zw-E5B"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandWindow" previousBinding="NOc-zw-E5B" id="jO4-fL-0ka"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Cgd-Gz-9BJ">
<rect key="frame" x="18" y="94" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Smoothing:" id="FLf-R7-Kew">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Nd4-eR-Fuc" userLabel="Rubberband Smoothing">
<rect key="frame" x="186" y="88" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="PiI-1l-qbj" id="VKp-9U-hvc">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="uMg-iX-U2g">
<items>
<menuItem title="Item 1" state="on" id="PiI-1l-qbj"/>
<menuItem title="Item 2" id="Dt7-79-aNY"/>
<menuItem title="Item 3" id="EOn-eN-MEA"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="7qK-HD-YRC" name="content" keyPath="arrangedObjects" id="glT-yT-Xy1"/>
<binding destination="7qK-HD-YRC" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="glT-yT-Xy1" id="R8g-75-OWT"/>
<binding destination="7qK-HD-YRC" name="contentValues" keyPath="arrangedObjects.name" previousBinding="R8g-75-OWT" id="OtE-O8-meo"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandSmoothing" previousBinding="OtE-O8-meo" id="jqW-KQ-IiR"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="j5C-An-ylm">
<rect key="frame" x="18" y="70" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Formant:" id="4zC-Y9-iRt">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2Y7-n5-g43" userLabel="Rubberband Formant">
<rect key="frame" x="186" y="64" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="aza-qe-fwC" id="5wR-Iv-QLl">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="4WS-kE-k5Y">
<items>
<menuItem title="Item 1" state="on" id="aza-qe-fwC"/>
<menuItem title="Item 2" id="MZ0-ng-xm6"/>
<menuItem title="Item 3" id="4fv-X4-XK6"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="5Cw-Q7-Y6w" name="content" keyPath="arrangedObjects" id="8PO-vs-kd4"/>
<binding destination="5Cw-Q7-Y6w" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="8PO-vs-kd4" id="Tuw-7B-rA3"/>
<binding destination="5Cw-Q7-Y6w" name="contentValues" keyPath="arrangedObjects.name" previousBinding="Tuw-7B-rA3" id="d3u-gj-AVL"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandFormant" previousBinding="d3u-gj-AVL" id="jQQ-qt-LAC"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6pP-D9-LBr">
<rect key="frame" x="18" y="46" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Pitch:" id="FBC-tN-fwm">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FIB-Jj-YWd" userLabel="Rubberband Pitch">
<rect key="frame" x="186" y="40" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Dlb-p9-5sC" id="xjb-bC-UbD">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="UIS-a0-dyq">
<items>
<menuItem title="Item 1" state="on" id="Dlb-p9-5sC"/>
<menuItem title="Item 2" id="E9b-yj-br7"/>
<menuItem title="Item 3" id="7pe-37-TM4"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="mFW-7h-5rd" name="content" keyPath="arrangedObjects" id="yqp-aH-3Vn"/>
<binding destination="mFW-7h-5rd" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="yqp-aH-3Vn" id="X49-3Z-EWj"/>
<binding destination="mFW-7h-5rd" name="contentValues" keyPath="arrangedObjects.name" previousBinding="X49-3Z-EWj" id="ISf-ZT-bQj"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandPitch" previousBinding="ISf-ZT-bQj" id="O1U-TC-XPY"/>
</connections>
</popUpButton>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tWf-Mw-g4f">
<rect key="frame" x="18" y="22" width="165" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Channels:" id="LpA-ak-Aj5">
<font key="font" usesAppearanceFont="YES"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="f50-KA-ekP" userLabel="Rubberband Channels">
<rect key="frame" x="186" y="16" width="164" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="5jm-EX-ibI" id="OwG-Xq-Gsy">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="Gd8-Cx-gIi">
<items>
<menuItem title="Item 1" state="on" id="5jm-EX-ibI"/>
<menuItem title="Item 2" id="bng-e5-LaN"/>
<menuItem title="Item 3" id="GaX-2L-sxf"/>
</items>
</menu>
</popUpButtonCell>
<connections>
<binding destination="Dfm-y4-7ki" name="content" keyPath="arrangedObjects" id="bas-JW-4u4"/>
<binding destination="Dfm-y4-7ki" name="contentObjects" keyPath="arrangedObjects.preference" previousBinding="bas-JW-4u4" id="K1C-oq-2vZ"/>
<binding destination="Dfm-y4-7ki" name="contentValues" keyPath="arrangedObjects.name" previousBinding="K1C-oq-2vZ" id="8Ja-Tu-lo2"/>
<binding destination="52" name="selectedObject" keyPath="values.rubberbandChannels" previousBinding="8Ja-Tu-lo2" id="Xyj-me-7r1"/>
</connections>
</popUpButton>
</subviews>
<point key="canvasLocation" x="-419" y="624"/>
</customView>
</objects>
</document>

View file

@ -15,6 +15,7 @@
#import "MIDIPane.h"
#import "OutputPane.h"
#import "AppearancePane.h"
#import "RubberbandPane.h"
@interface GeneralPreferencesPlugin : NSObject <PreferencePanePlugin> {
IBOutlet HotKeyPane *hotKeyPane;
@ -22,6 +23,7 @@
IBOutlet MIDIPane *midiPane;
IBOutlet GeneralPane *generalPane;
IBOutlet AppearancePane *appearancePane;
IBOutlet RubberbandPane *rubberbandPane;
IBOutlet NSView *playlistView;
IBOutlet NSView *updatesView;
@ -39,5 +41,6 @@
- (GeneralPreferencePane *)playlistPane;
- (GeneralPreferencePane *)notificationsPane;
- (GeneralPreferencePane *)appearancePane;
- (GeneralPreferencePane *)rubberbandPane;
@end

View file

@ -40,7 +40,8 @@
[plugin generalPane],
[plugin notificationsPane],
[plugin appearancePane],
[plugin midiPane]];
[plugin midiPane],
[plugin rubberbandPane]];
}
- (HotKeyPane *)hotKeyPane {
@ -93,4 +94,8 @@
return appearancePane;
}
- (RubberbandPane *)rubberbandPane {
return rubberbandPane;
}
@end

Binary file not shown.

View file

@ -33,6 +33,8 @@
83AC573A2861A54D009D6F50 /* PathSuggester.m in Sources */ = {isa = PBXBuildFile; fileRef = 83AC57382861A54D009D6F50 /* PathSuggester.m */; };
83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; };
83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; };
83B8FE242D59CDA7005854C1 /* rubberband.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83B8FE232D59CDA7005854C1 /* rubberband.pdf */; };
83B8FE292D59D059005854C1 /* RubberbandPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B8FE282D59D059005854C1 /* RubberbandPane.m */; };
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; };
83F27E6B1810DD3A00CEF538 /* appearance@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E651810DD3A00CEF538 /* appearance@2x.png */; };
83F27E6E1810DD3A00CEF538 /* midi@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 83F27E681810DD3A00CEF538 /* midi@2x.png */; };
@ -133,6 +135,9 @@
83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = "<group>"; };
83B06728180D85B8008E3612 /* MIDIPane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPane.m; sourceTree = "<group>"; };
83B0672A180D8B39008E3612 /* midi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = midi.png; path = Icons/midi.png; sourceTree = "<group>"; };
83B8FE232D59CDA7005854C1 /* rubberband.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = rubberband.pdf; path = Icons/rubberband.pdf; sourceTree = "<group>"; };
83B8FE272D59D03F005854C1 /* RubberbandPane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RubberbandPane.h; sourceTree = "<group>"; };
83B8FE282D59D059005854C1 /* RubberbandPane.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RubberbandPane.m; sourceTree = "<group>"; };
83BC5AB320E4C90F00631CD4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Preferences.xib; sourceTree = "<group>"; };
83DAD9F1286EDBCD000FAA9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/PathSuggester.xib; sourceTree = "<group>"; };
83DAD9F3286EDBD2000FAA9A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/PathSuggester.strings; sourceTree = "<group>"; };
@ -264,6 +269,8 @@
83B06728180D85B8008E3612 /* MIDIPane.m */,
8307D309286057B5000FF8EB /* GeneralPane.h */,
8307D30A286057B5000FF8EB /* GeneralPane.m */,
83B8FE272D59D03F005854C1 /* RubberbandPane.h */,
83B8FE282D59D059005854C1 /* RubberbandPane.m */,
);
name = Panes;
sourceTree = "<group>";
@ -346,6 +353,7 @@
17C643680B8A788000C53518 /* output.png */,
17C7E5AF0DCCC30A003CBCF7 /* playlist.png */,
83F27E691810DD3A00CEF538 /* playlist@2x.png */,
83B8FE232D59CDA7005854C1 /* rubberband.pdf */,
8E15A86B0B894768006DC802 /* updates.png */,
83F27E6A1810DD3A00CEF538 /* updates@2x.png */,
);
@ -473,6 +481,7 @@
83F27E6F1810DD3A00CEF538 /* playlist@2x.png in Resources */,
17E41DB80C130AA500AC744D /* Localizable.strings in Resources */,
8307D31828606EAF000FF8EB /* general.png in Resources */,
83B8FE242D59CDA7005854C1 /* rubberband.pdf in Resources */,
17E78A7E0D68BE3C005C5A59 /* file_tree.png in Resources */,
83F27E701810DD3A00CEF538 /* updates@2x.png in Resources */,
17E78B6A0D68C1E3005C5A59 /* Preferences.xib in Resources */,
@ -490,6 +499,7 @@
buildActionMask = 2147483647;
files = (
83AC573A2861A54D009D6F50 /* PathSuggester.m in Sources */,
83B8FE292D59D059005854C1 /* RubberbandPane.m in Sources */,
83726F022CF41B3200F15FBF /* AppearancePane.m in Sources */,
83651DA527322C8700A2C097 /* MIDIFlavorBehaviorArrayController.m in Sources */,
83B06729180D85B8008E3612 /* MIDIPane.m in Sources */,

View file

@ -0,0 +1,68 @@
//
// Rubberband.h
// Preferences
//
// Created by Christopher Snowhill on 2/9/25.
//
#ifndef Rubberband_h
#define Rubberband_h
#import "GeneralPreferencePane.h"
#import <Cocoa/Cocoa.h>
@interface RubberbandWindowArrayController : NSArrayController
- (void)reinitWithEngine:(BOOL)engineR3;
@end
@interface RubberbandPane : GeneralPreferencePane {
IBOutlet NSTextField *transientsLabel;
IBOutlet NSPopUpButton *transientsButton;
IBOutlet NSTextField *detectorLabel;
IBOutlet NSPopUpButton *detectorButton;
IBOutlet NSTextField *phaseLabel;
IBOutlet NSPopUpButton *phaseButton;
IBOutlet RubberbandWindowArrayController *windowBehavior;
IBOutlet NSTextField *smoothingLabel;
IBOutlet NSPopUpButton *smoothingButton;
}
- (IBAction)changeState:(id)sender;
@end
@interface RubberbandEngineArrayController : NSArrayController
@end
@interface RubberbandTransientsArrayController : NSArrayController
@end
@interface RubberbandDetectorArrayController : NSArrayController
@end
@interface RubberbandPhaseArrayController : NSArrayController
@end
@interface RubberbandSmoothingArrayController : NSArrayController
@end
@interface RubberbandFormantArrayController : NSArrayController
@end
@interface RubberbandPitchArrayController : NSArrayController
@end
@interface RubberbandChannelsArrayController : NSArrayController
@end
#endif /* Rubberband_h */

View file

@ -0,0 +1,163 @@
//
// Rubberband.m
// Preferences
//
// Created by Christopher Snowhill on 2/9/25.
//
#import <Foundation/Foundation.h>
#import "RubberbandPane.h"
@implementation RubberbandPane
- (NSString *)title {
return NSLocalizedPrefString(@"Rubber Band");
}
- (NSImage *)icon {
NSImage *icon = [[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForImageResource:@"rubberband"]];
[icon setTemplate:YES];
return icon;
}
- (void)awakeFromNib {
[self changeState:self];
}
- (IBAction)changeState:(id)sender {
NSUserDefaults *defaults = [[NSUserDefaultsController sharedUserDefaultsController] defaults];
BOOL engineR3 = [[defaults stringForKey:@"rubberbandEngine"] isEqualToString:@"finer"];
[transientsLabel setEnabled:!engineR3];
[transientsButton setEnabled:!engineR3];
[phaseLabel setEnabled:!engineR3];
[phaseButton setEnabled:!engineR3];
[smoothingLabel setEnabled:!engineR3];
[smoothingButton setEnabled:!engineR3];
[windowBehavior reinitWithEngine:engineR3];
if(engineR3) {
NSString *window = [defaults stringForKey:@"rubberbandWindow"];
if([window isEqualToString:@"long"]) {
[defaults setValue:@"standard" forKey:@"rubberbandWindow"];
}
}
}
@end
@implementation RubberbandEngineArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"EngineFaster", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"faster"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"EngineFiner", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"finer"}];
}
@end
@implementation RubberbandTransientsArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"TransientsCrisp", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"crisp"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"TransientsMixed", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"mixed"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"TransientsSmooth", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"smooth"}];
}
@end
@implementation RubberbandDetectorArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"DetectorCompound", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"compound"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"DetectorPercussive", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"percussive"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"DetectorSoft", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"soft"}];
}
@end
@implementation RubberbandPhaseArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"PhaseLaminar", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"laminar"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"PhaseIndependent", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"independent"}];
}
@end
@implementation RubberbandWindowArrayController
- (void)awakeFromNib {
BOOL engineR3 = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"rubberbandEngine"] isEqualToString:@"finer"];
[self reinitWithEngine:engineR3];
}
- (void)reinitWithEngine:(BOOL)engineR3 {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"WindowStandard", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"standard"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"WindowShort", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"short"}];
if(!engineR3) {
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"WindowLong", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"long"}];
}
}
@end
@implementation RubberbandSmoothingArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"SmoothingOff", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"off"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"SmoothingOn", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"on"}];
}
@end
@implementation RubberbandFormantArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"FormantShifted", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"shifted"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"FormantPreserved", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"preserved"}];
}
@end
@implementation RubberbandPitchArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"PitchHighSpeed", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"highspeed"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"PitchHighQuality", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"highquality"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"PitchHighConsistency", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"highconsistency"}];
}
@end
@implementation RubberbandChannelsArrayController
- (void)awakeFromNib {
[self removeObjects:[self arrangedObjects]];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"ChannelsApart", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"apart"}];
[self addObject:@{@"name": NSLocalizedStringFromTableInBundle(@"ChannelsTogether", nil, [NSBundle bundleForClass:[self class]], @""), @"preference": @"together"}];
}
@end

View file

@ -30,3 +30,26 @@
"ValidYes" = "Yes";
"Volume scale tag only" = "Volume scale tag only";
"Zero Order Hold" = "Zero Order Hold";
"Rubber Band" = "Rubber Band";
"EngineFaster" = "Faster";
"EngineFiner" = "Finer";
"TransientsCrisp" = "Crisp";
"TransientsMixed" = "Mixed";
"TransientsSmooth" = "Smooth";
"DetectorCompound" = "Compound";
"DetectorPercussive" = "Percussive";
"DetectorSoft" = "Soft";
"PhaseLaminar" = "Laminar";
"PhaseIndependent" = "Independent";
"WindowStandard" = "Standard";
"WindowShort" = "Short";
"WindowLong" = "Long";
"SmoothingOff" = "Off";
"SmoothingOn" = "On";
"FormantShifted" = "Shifted";
"FormantPreserved" = "Preserved";
"PitchHighSpeed" = "High Speed";
"PitchHighQuality" = "High Quality";
"PitchHighConsistency" = "High Consistency";
"ChannelsApart" = "Apart";
"ChannelsTogether" = "Together";

View file

@ -256,3 +256,30 @@
/* Class = "NSButtonCell"; title = "Render over background plaque"; ObjectID = "KZd-iR-dXL"; */
"KZd-iR-dXL.title" = "Render over background plaque";
/* Class = "NSTextFieldCell"; title = "Engine:"; ObjectID = "ULd-G9-gDZ"; */
"ULd-G9-gDZ.title" = "Engine:";
/* Class = "NSTextFieldCell"; title = "Transients:"; ObjectID = "eI3-LU-JTj"; */
"eI3-LU-JTj.title" = "Transients:";
/* Class = "NSTextFieldCell"; title = "Detector:"; ObjectID = "ZUS-Cr-zSa"; */
"ZUS-Cr-zSa.title" = "Detector:";
/* Class = "NSTextFieldCell"; title = "Phase:"; ObjectID = "xwT-8b-IsJ"; */
"xwT-8b-IsJ.title" = "Phase:";
/* Class = "NSTextFieldCell"; title = "Window Size:"; ObjectID = "qc8-jP-K6o"; */
"qc8-jP-K6o.title" = "Window Size:";
/* Class = "NSTextFieldCell"; title = "Smoothing:"; ObjectID = "FLf-R7-Kew"; */
"FLf-R7-Kew.title" = "Smoothing:";
/* Class = "NSTextFieldCell"; title = "Formant:"; ObjectID = "4zC-Y9-iRt"; */
"4zC-Y9-iRt.title" = "Formant:";
/* Class = "NSTextFieldCell"; title = "Pitch:"; ObjectID = "FBC-tN-fwm"; */
"FBC-tN-fwm.title" = "Pitch:";
/* Class = "NSTextFieldCell"; title = "Channels:"; ObjectID = "LpA-ak-Aj5"; */
"LpA-ak-Aj5.title" = "Channels:";

View file

@ -255,3 +255,30 @@
/* Class = "NSButtonCell"; title = "Render over background plaque"; ObjectID = "KZd-iR-dXL"; */
"KZd-iR-dXL.title" = "Mostrar fondo del icono";
/* Class = "NSTextFieldCell"; title = "Engine:"; ObjectID = "ULd-G9-gDZ"; */
"ULd-G9-gDZ.title" = "Engine:";
/* Class = "NSTextFieldCell"; title = "Transients:"; ObjectID = "eI3-LU-JTj"; */
"eI3-LU-JTj.title" = "Transients:";
/* Class = "NSTextFieldCell"; title = "Detector:"; ObjectID = "ZUS-Cr-zSa"; */
"ZUS-Cr-zSa.title" = "Detector:";
/* Class = "NSTextFieldCell"; title = "Phase:"; ObjectID = "xwT-8b-IsJ"; */
"xwT-8b-IsJ.title" = "Phase:";
/* Class = "NSTextFieldCell"; title = "Window Size:"; ObjectID = "qc8-jP-K6o"; */
"qc8-jP-K6o.title" = "Window Size:";
/* Class = "NSTextFieldCell"; title = "Smoothing:"; ObjectID = "FLf-R7-Kew"; */
"FLf-R7-Kew.title" = "Smoothing:";
/* Class = "NSTextFieldCell"; title = "Formant:"; ObjectID = "4zC-Y9-iRt"; */
"4zC-Y9-iRt.title" = "Formant:";
/* Class = "NSTextFieldCell"; title = "Pitch:"; ObjectID = "FBC-tN-fwm"; */
"FBC-tN-fwm.title" = "Pitch:";
/* Class = "NSTextFieldCell"; title = "Channels:"; ObjectID = "LpA-ak-Aj5"; */
"LpA-ak-Aj5.title" = "Channels:";

View file

@ -35,7 +35,7 @@
<br><br>
<em>This program has been made possible through contributions from users like you.</em>
<br><br> All Cog code is copyrighted by me, and is licensed under the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a>. Cog contains bits of other code from third parties that are under their own licenses.
<br><br> <a href="https://breakfastquay.com/rubberband/">Rubber Band Library</a> was used under the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a> to provide pitch and/or time shifting.
<br><br> <a href="https://breakfastquay.com/rubberband/">Rubber Band Library</a> was used under the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a> to provide pitch and/or time shifting. <a href="https://thenounproject.com/icon/rubber-band-4214821/">Rubber Band style icon</a> by <a href="https://thenounproject.com/creator/lucashelle/">Lucas Helle</a>.
</div>
</body>