Implemented real pitch and time shifting using Rubber Band

I will implement the more complex setup of providing options for
most of the configuration that Rubber Band provides, at a later
date, when I feel like creating a complex configuration dialog
for it, and asking for help translating every option and setting.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2024-12-09 00:31:58 -08:00
parent 59f3f416ba
commit 9c6915ecb2
34 changed files with 2326 additions and 359 deletions

1
.gitignore vendored
View file

@ -47,6 +47,7 @@ Xcode-config/DEVELOPMENT_TEAM.xcconfig
/ThirdParty/ogg/lib/libogg.0.dylib
/ThirdParty/opus/lib/libopus.0.dylib
/ThirdParty/opusfile/lib/libopusfile.0.dylib
/ThirdParty/rubberband/lib/librubberband.3.dylib
/ThirdParty/speex/libspeex.a
/ThirdParty/vorbis/lib/libvorbisfile.3.dylib
/ThirdParty/vorbis/lib/libvorbis.0.dylib

View file

@ -19,8 +19,11 @@
#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
#define DEFAULT_PITCH_DOWN 0.2
#define DEFAULT_PITCH_UP DEFAULT_PITCH_DOWN
#define DEFAULT_TEMPO_DOWN 0.2
#define DEFAULT_TEMPO_UP DEFAULT_TEMPO_DOWN
extern NSString *CogPlaybackDidBeginNotificiation;
extern NSString *CogPlaybackDidPauseNotificiation;
@ -43,7 +46,9 @@ extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
IBOutlet EqualizerWindowController *equalizerWindowController;
IBOutlet NSSlider *volumeSlider;
IBOutlet NSSlider *speedSlider;
IBOutlet NSSlider *pitchSlider;
IBOutlet NSSlider *tempoSlider;
IBOutlet NSButton *lockButton;
IBOutlet NSArrayController *outputDevices;
@ -73,9 +78,13 @@ 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)changePitch:(id)sender;
- (IBAction)pitchDown:(id)sender;
- (IBAction)pitchUp:(id)sender;
- (IBAction)changeTempo:(id)sender;
- (IBAction)tempoDown:(id)sender;
- (IBAction)tempoUp:(id)sender;
- (IBAction)playPauseResume:(id)sender;
- (IBAction)pauseResume:(id)sender;

View file

@ -91,7 +91,9 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
- (void)initDefaults {
NSDictionary *defaultsDictionary = @{ @"volume": @(75.0),
@"speed": @(1.0),
@"pitch": @(1.0),
@"tempo": @(1.0),
@"speedLock": @(YES),
@"GraphicEQenable": @(NO),
@"GraphicEQpreset": @(-1),
@"GraphicEQtrackgenre": @(NO),
@ -101,6 +103,16 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
}
static double speedScale(double input, double min, double max) {
input = (input - min) * 100.0 / (max - min);
return ((input * input) * (5.0 - 0.2) / 10000.0) + 0.2;
}
static double reverseSpeedScale(double input, double min, double max) {
input = sqrtf((input - 0.2) * 10000.0 / (5.0 - 0.2));
return (input * (max - min) / 100.0) + min;
}
- (void)awakeFromNib {
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
@ -110,8 +122,15 @@ NSString *CogPlaybackDidStopNotificiation = @"CogPlaybackDidStopNotificiation";
[volumeSlider setDoubleValue:logarithmicToLinear(volume, MAX_VOLUME)];
[audioPlayer setVolume:volume];
double speed = [[NSUserDefaults standardUserDefaults] doubleForKey:@"speed"];
[audioPlayer setSpeed:speed];
double pitch = [[NSUserDefaults standardUserDefaults] doubleForKey:@"pitch"];
[audioPlayer setPitch:pitch];
[pitchSlider setDoubleValue:reverseSpeedScale(pitch, [pitchSlider minValue], [pitchSlider maxValue])];
double tempo = [[NSUserDefaults standardUserDefaults] doubleForKey:@"tempo"];
[audioPlayer setTempo:tempo];
[tempoSlider setDoubleValue:reverseSpeedScale(tempo, [tempoSlider minValue], [tempoSlider maxValue])];
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
[lockButton setTitle:speedLock ? @"🔒" : @"🔓"];
[self setSeekable:NO];
}
@ -482,12 +501,34 @@ NSDictionary *makeRGInfo(PlaylistEntry *pe) {
}
}
- (IBAction)changeSpeed:(id)sender {
DLog(@"SPEED: %lf", [sender doubleValue]);
- (IBAction)changePitch:(id)sender {
const double pitch = speedScale([sender doubleValue], [pitchSlider minValue], [pitchSlider maxValue]);
DLog(@"PITCH: %lf", pitch);
[audioPlayer setSpeed:[sender doubleValue]];
[audioPlayer setPitch:pitch];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer speed] forKey:@"speed"];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer pitch] forKey:@"pitch"];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
[audioPlayer setTempo:pitch];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer tempo] forKey:@"tempo"];
}
}
- (IBAction)changeTempo:(id)sender {
const double tempo = speedScale([sender doubleValue], [tempoSlider minValue], [tempoSlider maxValue]);
DLog(@"TEMPO: %lf", tempo);
[audioPlayer setTempo:tempo];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer tempo] forKey:@"tempo"];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
[audioPlayer setPitch:tempo];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer pitch] forKey:@"pitch"];
}
}
- (IBAction)skipToNextAlbum:(id)sender {
@ -591,18 +632,64 @@ 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]];
- (IBAction)pitchDown:(id)sender {
/*double newPitch = */[audioPlayer pitchDown:DEFAULT_PITCH_DOWN];
[pitchSlider setDoubleValue:reverseSpeedScale([audioPlayer pitch], [pitchSlider minValue], [pitchSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer speed] forKey:@"speed"];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer pitch] forKey:@"pitch"];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
[audioPlayer setTempo:[audioPlayer pitch]];
[tempoSlider setDoubleValue:reverseSpeedScale([audioPlayer tempo], [tempoSlider minValue], [tempoSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer tempo] forKey:@"tempo"];
}
}
- (IBAction)speedUp:(id)sender {
double newSpeed = [audioPlayer speedUp:DEFAULT_SPEED_UP];
[speedSlider setDoubleValue:[audioPlayer speed]];
- (IBAction)pitchUp:(id)sender {
/*double newPitch = */[audioPlayer tempoUp:DEFAULT_PITCH_UP];
[pitchSlider setDoubleValue:reverseSpeedScale([audioPlayer pitch], [pitchSlider minValue], [pitchSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer speed] forKey:@"speed"];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer pitch] forKey:@"pitch"];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
[audioPlayer setTempo:[audioPlayer pitch]];
[tempoSlider setDoubleValue:reverseSpeedScale([audioPlayer tempo], [tempoSlider minValue], [tempoSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer tempo] forKey:@"tempo"];
}
}
- (IBAction)tempoDown:(id)sender {
/*double newTempo = */[audioPlayer tempoDown:DEFAULT_TEMPO_DOWN];
[tempoSlider setDoubleValue:reverseSpeedScale([audioPlayer tempo], [tempoSlider minValue], [tempoSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer tempo] forKey:@"tempo"];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
[audioPlayer setPitch:[audioPlayer tempo]];
[pitchSlider setDoubleValue:reverseSpeedScale([audioPlayer pitch], [pitchSlider minValue], [pitchSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer pitch] forKey:@"pitch"];
}
}
- (IBAction)tempoUp:(id)sender {
/*double newTempo = */[audioPlayer tempoUp:DEFAULT_PITCH_UP];
[tempoSlider setDoubleValue:reverseSpeedScale([audioPlayer tempo], [tempoSlider minValue], [tempoSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer tempo] forKey:@"tempo"];
if([[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"]) {
[audioPlayer setPitch:[audioPlayer tempo]];
[pitchSlider setDoubleValue:reverseSpeedScale([audioPlayer pitch], [pitchSlider minValue], [pitchSlider maxValue])];
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer pitch] forKey:@"pitch"];
}
}
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq {

View file

@ -26,7 +26,8 @@
OutputNode *output;
double volume;
double speed;
double pitch;
double tempo;
NSMutableArray *chainQueue;
@ -75,10 +76,15 @@
- (double)volumeUp:(double)amount;
- (double)volumeDown:(double)amount;
- (void)setSpeed:(double)s;
- (double)speed;
- (double)speedUp:(double)amount;
- (double)speedDown:(double)amount;
- (void)setPitch:(double)s;
- (double)pitch;
- (double)pitchUp:(double)amount;
- (double)pitchDown:(double)amount;
- (void)setTempo:(double)s;
- (double)tempo;
- (double)tempoUp:(double)amount;
- (double)tempoDown:(double)amount;
- (double)amountPlayed;
- (double)amountPlayedInterval;

View file

@ -25,6 +25,10 @@
outputLaunched = NO;
endOfInputReached = NO;
// Safety
pitch = 1.0;
tempo = 1.0;
chainQueue = [[NSMutableArray alloc] init];
semaphore = [[Semaphore alloc] init];
@ -75,7 +79,8 @@
}
[output setup];
[output setVolume:volume];
[output setSpeed:speed];
[output setPitch:pitch];
[output setTempo:tempo];
@synchronized(chainQueue) {
for(id anObject in chainQueue) {
[anObject setShouldContinue:NO];
@ -211,14 +216,24 @@
return volume;
}
- (void)setSpeed:(double)s {
speed = s;
- (void)setPitch:(double)p {
pitch = p;
[output setSpeed:s];
[output setPitch:p];
}
- (double)speed {
return speed;
- (double)pitch {
return pitch;
}
- (void)setTempo:(double)t {
tempo = t;
[output setTempo:t];
}
- (double)tempo {
return tempo;
}
// This is called by the delegate DURING a requestNextStream request.
@ -659,30 +674,56 @@
return newVolume;
}
- (double)speedUp:(double)amount {
const double MAX_SPEED = 5.0;
- (double)pitchUp:(double)amount {
const double MAX_PITCH = 5.0;
double newSpeed;
if((speed + amount) > MAX_SPEED)
newSpeed = MAX_SPEED;
double newPitch;
if((pitch + amount) > MAX_PITCH)
newPitch = MAX_PITCH;
else
newSpeed = speed + amount;
newPitch = pitch + amount;
[self setSpeed:newSpeed];
return newSpeed;
[self setPitch:newPitch];
return newPitch;
}
- (double)speedDown:(double)amount {
const double MIN_SPEED = 0.2;
- (double)pitchDown:(double)amount {
const double MIN_PITCH = 0.2;
double newSpeed;
if((speed - amount) < MIN_SPEED)
newSpeed = MIN_SPEED;
double newPitch;
if((pitch - amount) < MIN_PITCH)
newPitch = MIN_PITCH;
else
newSpeed = speed - amount;
newPitch = pitch - amount;
[self setSpeed:newSpeed];
return newSpeed;
[self setPitch:newPitch];
return newPitch;
}
- (double)tempoUp:(double)amount {
const double MAX_TEMPO = 5.0;
double newTempo;
if((tempo + amount) > MAX_TEMPO)
newTempo = MAX_TEMPO;
else
newTempo = tempo + amount;
[self setTempo:newTempo];
return newTempo;
}
- (double)tempoDown:(double)amount {
const double MIN_TEMPO = 0.2;
double newTempo;
if((tempo - amount) < MIN_TEMPO)
newTempo = MIN_TEMPO;
else
newTempo = tempo - amount;
[self setTempo:newTempo];
return newTempo;
}
- (void)waitUntilCallbacksExit {

View file

@ -61,7 +61,8 @@
- (void)setVolume:(double)v;
- (void)setSpeed:(double)s;
- (void)setPitch:(double)p;
- (void)setTempo:(double)t;
- (void)setShouldContinue:(BOOL)s;

View file

@ -164,8 +164,12 @@
[output setVolume:v];
}
- (void)setSpeed:(double)s {
[output setSpeed:s];
- (void)setPitch:(double)p {
[output setPitch:p];
}
- (void)setTempo:(double)t {
[output setTempo:t];
}
- (void)setShouldContinue:(BOOL)s {

View file

@ -79,6 +79,7 @@
8377C64C27B8C51500E8BC0F /* fft_accelerate.c in Sources */ = {isa = PBXBuildFile; fileRef = 8377C64B27B8C51500E8BC0F /* fft_accelerate.c */; };
8377C64E27B8C54400E8BC0F /* fft.h in Headers */ = {isa = PBXBuildFile; fileRef = 8377C64D27B8C54400E8BC0F /* fft.h */; };
8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; };
838A33722D06A97D00D0D770 /* librubberband.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 838A33712D06A97D00D0D770 /* librubberband.3.dylib */; };
839065F32853338700636FBB /* dsd2float.h in Headers */ = {isa = PBXBuildFile; fileRef = 839065F22853338700636FBB /* dsd2float.h */; };
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
@ -192,6 +193,7 @@
8377C64B27B8C51500E8BC0F /* fft_accelerate.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fft_accelerate.c; sourceTree = "<group>"; };
8377C64D27B8C54400E8BC0F /* fft.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fft.h; sourceTree = "<group>"; };
8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
838A33712D06A97D00D0D770 /* librubberband.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librubberband.3.dylib; path = ../ThirdParty/rubberband/lib/librubberband.3.dylib; sourceTree = SOURCE_ROOT; };
839065F22853338700636FBB /* dsd2float.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dsd2float.h; sourceTree = "<group>"; };
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
@ -233,6 +235,7 @@
17D21DAE0B8BE76800D1EBDE /* AudioUnit.framework in Frameworks */,
17D21DAF0B8BE76800D1EBDE /* CoreAudio.framework in Frameworks */,
17D21DB00B8BE76800D1EBDE /* CoreAudioKit.framework in Frameworks */,
838A33722D06A97D00D0D770 /* librubberband.3.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -324,6 +327,7 @@
1058C7B2FEA5585E11CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
838A33712D06A97D00D0D770 /* librubberband.3.dylib */,
836DF615298F6E8900CD0580 /* libsoxr.0.dylib */,
83725A7B27AA0D8A0003F694 /* Accelerate.framework */,
17D21DAA0B8BE76800D1EBDE /* AudioUnit.framework */,
@ -693,11 +697,18 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = CogAudio_Prefix.pch;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
HEADER_SEARCH_PATHS = ../ThirdParty/soxr/include;
HEADER_SEARCH_PATHS = (
../ThirdParty/soxr/include,
../ThirdParty/rubberband/include,
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = ../ThirdParty/soxr/lib;
LIBRARY_SEARCH_PATHS = (
../ThirdParty/rubberband/lib,
../ThirdParty/soxr/lib,
"$(PROJECT_DIR)",
);
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cogaudio;
PRODUCT_NAME = CogAudio;
@ -724,11 +735,18 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = CogAudio_Prefix.pch;
GCC_PREPROCESSOR_DEFINITIONS = "";
HEADER_SEARCH_PATHS = ../ThirdParty/soxr/include;
HEADER_SEARCH_PATHS = (
../ThirdParty/soxr/include,
../ThirdParty/rubberband/include,
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@executable_path/../Frameworks";
LD_RUNPATH_SEARCH_PATHS = "@loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = ../ThirdParty/soxr/lib;
LIBRARY_SEARCH_PATHS = (
../ThirdParty/rubberband/lib,
../ThirdParty/soxr/lib,
"$(PROJECT_DIR)",
);
OTHER_LDFLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = org.cogx.cogaudio;
PRODUCT_NAME = CogAudio;

View file

@ -35,8 +35,6 @@ using std::atomic_long;
#import <stdio.h>
#endif
#import <soxr.h>
@class OutputNode;
@class FSurroundFilter;
@ -56,7 +54,8 @@ using std::atomic_long;
double lastClippedSampleRate;
soxr_t rssimplespeed;
void *ts;
size_t blockSize, toDrop, samplesBuffered;
double ssRenderedIn, ssLastRenderedIn;
double ssRenderedOut;
@ -90,8 +89,8 @@ using std::atomic_long;
float volume;
float eqPreamp;
double speed;
double lastSpeed;
double pitch, tempo;
double lastPitch, lastTempo;
AVAudioFormat *_deviceFormat;
@ -139,7 +138,9 @@ using std::atomic_long;
float *samplePtr;
float tempBuffer[512 * 32];
float rsInBuffer[8192 * 32];
float *rsPtrs[32];
float rsInBuffer[1024 * 32];
float rsOutBuffer[65536 * 32];
float inputBuffer[4096 * 32]; // 4096 samples times maximum supported channel count
float fsurroundBuffer[8192 * 6];
float hrtfBuffer[4096 * 2];
@ -182,6 +183,7 @@ using std::atomic_long;
- (void)reportMotion:(simd_float4x4)matrix;
- (void)setSpeed:(double)s;
- (void)setPitch:(double)p;
- (void)setTempo:(double)t;
@end

View file

@ -23,12 +23,16 @@
#import "FSurroundFilter.h"
#import <rubberband/rubberband-c.h>
#define OCTAVES 5
extern void scale_by_volume(float *buffer, size_t count, float volume);
static NSString *CogPlaybackDidBeginNotificiation = @"CogPlaybackDidBeginNotificiation";
#define tts ((RubberBandState)ts)
simd_float4x4 convertMatrix(CMRotationMatrix r) {
simd_float4x4 matrix = {
simd_make_float4(r.m33, -r.m31, r.m32, 0.0f),
@ -110,7 +114,7 @@ static void fillBuffers(AudioBufferList *ioData, const float *inbuffer, size_t c
static void clearBuffers(AudioBufferList *ioData, size_t count, size_t offset) {
for(int i = 0; i < ioData->mNumberBuffers; ++i) {
memset(ioData->mBuffers[i].mData + offset * sizeof(float), 0, count * sizeof(float));
memset((uint8_t *)ioData->mBuffers[i].mData + offset * sizeof(float), 0, count * sizeof(float));
ioData->mBuffers[i].mNumberChannels = 1;
}
}
@ -338,8 +342,8 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA
outputLock = [[NSLock alloc] init];
speed = 1.0;
lastSpeed = 1.0;
pitch = 1.0; tempo = 1.0;
lastPitch = 1.0; lastTempo = 1.0;
#ifdef OUTPUT_LOG
NSString *logName = [NSTemporaryDirectory() stringByAppendingPathComponent:@"CogAudioLog.raw"];
@ -352,21 +356,21 @@ static OSStatus eqRenderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioA
static OSStatus
default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData) {
OutputCoreAudio *this = (__bridge OutputCoreAudio *)inUserData;
return [this setOutputDeviceByID:-1];
OutputCoreAudio *_self = (__bridge OutputCoreAudio *)inUserData;
return [_self setOutputDeviceByID:-1];
}
static OSStatus
current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData) {
OutputCoreAudio *this = (__bridge OutputCoreAudio *)inUserData;
OutputCoreAudio *_self = (__bridge OutputCoreAudio *)inUserData;
for(UInt32 i = 0; i < inNumberAddresses; ++i) {
switch(inAddresses[i].mSelector) {
case kAudioDevicePropertyDeviceIsAlive:
return [this setOutputDeviceByID:-1];
return [_self setOutputDeviceByID:-1];
case kAudioDevicePropertyNominalSampleRate:
case kAudioDevicePropertyStreamFormat:
this->outputdevicechanged = YES;
_self->outputdevicechanged = YES;
return noErr;
}
}
@ -611,7 +615,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
__Verify_noErr(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize));
UInt32 nDevices = propsize / (UInt32)sizeof(AudioDeviceID);
AudioDeviceID *devids = malloc(propsize);
AudioDeviceID *devids = (AudioDeviceID *)malloc(propsize);
__Verify_noErr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids));
theAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
@ -812,14 +816,35 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
[outputLock unlock];
}
if(rssimplespeed) {
soxr_delete(rssimplespeed);
if(ts) {
rubberband_delete(tts);
ts = NULL;
}
soxr_error_t error;
soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, SOXR_VR);
rssimplespeed = soxr_create(1 << OCTAVES, 1, realStreamFormat.mChannelsPerFrame, &error, NULL, &q_spec, NULL);
soxr_set_io_ratio(rssimplespeed, speed, 0);
RubberBandOptions options = RubberBandOptionProcessRealTime;
ts = rubberband_new(realStreamFormat.mSampleRate, realStreamFormat.mChannelsPerFrame, options, 1.0 / tempo, pitch);
blockSize = 1024;
toDrop = rubberband_get_start_delay(tts);
samplesBuffered = 0;
rubberband_set_max_process_size(tts, (int)blockSize);
for(size_t i = 0; i < 32; ++i) {
rsPtrs[i] = &rsInBuffer[1024 * i];
}
size_t toPad = rubberband_get_preferred_start_pad(tts);
if(toPad > 0) {
for(size_t i = 0; i < realStreamFormat.mChannelsPerFrame; ++i) {
memset(rsPtrs[i], 0, 1024 * sizeof(float));
}
while(toPad > 0) {
size_t p = toPad;
if(p > blockSize) p = blockSize;
rubberband_process(tts, (const float * const *)rsPtrs, (int)p, false);
toPad -= p;
}
}
ssRenderedIn = 0.0;
ssLastRenderedIn = 0.0;
@ -951,37 +976,65 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
int simpleSpeedInput = samplesRendered;
int simpleSpeedRendered = 0;
int channels = realStreamFormat.mChannelsPerFrame;
int max_block_len = 8192;
size_t max_block_len = blockSize;
if (fabs(speed - lastSpeed) > 1e-5) {
lastSpeed = speed;
soxr_set_io_ratio(rssimplespeed, speed, max_block_len);
if (fabs(pitch - lastPitch) > 1e-5 ||
fabs(tempo - lastTempo) > 1e-5) {
lastPitch = pitch;
lastTempo = tempo;
rubberband_set_pitch_scale(tts, pitch);
rubberband_set_time_ratio(tts, 1.0 / tempo);
}
const double inputRatio = 1.0 / realStreamFormat.mSampleRate;
const double outputRatio = inputRatio * speed;
const double outputRatio = inputRatio * tempo;
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;
size_t len = simpleSpeedInput;
if(len > blockSize) len = blockSize;
for(size_t i = 0; i < channels; ++i) {
cblas_scopy((int)len, ibuf + i, channels, rsPtrs[i], 1);
}
rubberband_process(tts, (const float * const *)rsPtrs, (int)len, false);
simpleSpeedInput -= len;
ibuf += len * channels;
ssRenderedIn += len * inputRatio;
size_t samplesAvailable;
while ((samplesAvailable = rubberband_available(tts)) > 0) {
if(toDrop > 0) {
size_t blockDrop = toDrop;
if(blockDrop > blockSize) blockDrop = blockSize;
rubberband_retrieve(tts, (float * const *)rsPtrs, (int)blockDrop);
toDrop -= blockDrop;
continue;
}
size_t maxAvailable = 65536 - samplesBuffered;
if(samplesAvailable > maxAvailable) {
samplesAvailable = maxAvailable;
if(!samplesAvailable) {
break;
}
}
size_t samplesOut = samplesAvailable;
if(samplesOut > blockSize) samplesOut = blockSize;
rubberband_retrieve(tts, (float * const *)rsPtrs, (int)samplesOut);
for(size_t i = 0; i < channels; ++i) {
cblas_scopy((int)samplesOut, rsPtrs[i], 1, &rsOutBuffer[samplesBuffered * channels + i], channels);
}
samplesBuffered += samplesOut;
ssRenderedOut += samplesOut * outputRatio;
simpleSpeedRendered += samplesOut;
}
samplePtr = ibuf;
}
samplePtr = &rsInBuffer[0];
samplePtr = &rsOutBuffer[0];
samplesRendered = simpleSpeedRendered;
samplesBuffered = 0;
}
[outputLock lock];
if(fsurround) {
@ -1288,8 +1341,12 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
volume = v * 0.01f;
}
- (void)setSpeed:(double)s {
speed = s;
- (void)setPitch:(double)p {
pitch = p;
}
- (void)setTempo:(double)t {
tempo = t;
}
- (void)setEqualizerEnabled:(BOOL)enabled {
@ -1406,9 +1463,9 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
rsstate_delete(rsvis);
rsvis = NULL;
}
if(rssimplespeed) {
soxr_delete(rssimplespeed);
rssimplespeed = NULL;
if(ts) {
rubberband_delete(tts);
ts = NULL;
}
stopCompleted = YES;
}

View file

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23094" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23094"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -25,17 +25,17 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<splitView dividerStyle="thin" vertical="YES" translatesAutoresizingMaskIntoConstraints="NO" id="2123">
<rect key="frame" x="0.0" y="334" width="1186" height="66"/>
<rect key="frame" x="0.0" y="328" width="1205" height="72"/>
<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="1186" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="1217" height="72"/>
<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="1186" height="66"/>
<rect key="frame" x="0.0" y="0.0" width="1217" height="72"/>
<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="1186" height="49"/>
<rect key="frame" x="0.0" y="0.0" width="1217" height="55"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="6"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -57,7 +57,7 @@
<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">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="w5u-JQ-3Hf">
<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"/>
@ -102,7 +102,7 @@
<rect key="frame" x="0.0" y="3" width="17" height="11"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="8rO-fU-Njw"/>
</imageView>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5cp-JI-ogI">
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" allowsExpansionToolTips="YES" translatesAutoresizingMaskIntoConstraints="NO" id="5cp-JI-ogI">
<rect key="frame" x="23" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="wky-z8-Cj5">
<font key="font" metaFont="system"/>
@ -127,7 +127,7 @@
</tableCellView>
</prototypeCellViews>
</tableColumn>
<tableColumn identifier="rating" editable="NO" width="109" minWidth="48" maxWidth="128" id="208" userLabel="Rating">
<tableColumn identifier="rating" editable="NO" width="116" 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="106" y="3" width="109" height="18"/>
<rect key="frame" x="106" y="3" width="116" 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="109" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="exY-Bg-Mjm">
<rect key="frame" x="0.0" y="1" width="116" 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="171" minWidth="96" maxWidth="1024" id="XBr-ec-D81">
<tableColumn identifier="title" editable="NO" width="177.5" 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="218" y="3" width="171" height="18"/>
<rect key="frame" x="225" y="3" width="178" 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="171" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="dQP-wC-mba">
<rect key="frame" x="0.0" y="1" width="178" 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"/>
@ -231,7 +231,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ZWb-jm-i9i">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="ZWb-jm-i9i">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="3QN-Ok-QPu">
<font key="font" usesAppearanceFont="YES"/>
@ -259,7 +259,7 @@
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="dJs-UO-m5r"/>
</connections>
</tableColumn>
<tableColumn identifier="artist" editable="NO" width="195" minWidth="96" maxWidth="1024" id="391">
<tableColumn identifier="artist" editable="NO" width="200.5" 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="392" y="3" width="195" height="18"/>
<rect key="frame" x="406" y="3" width="200" 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="195" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="1WK-qN-Mgj">
<rect key="frame" x="0.0" y="1" width="200" 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"/>
@ -320,7 +320,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="4cz-aa-d2B">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="4cz-aa-d2B">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="DDa-1D-XYS">
<font key="font" usesAppearanceFont="YES"/>
@ -347,7 +347,7 @@
</binding>
</connections>
</tableColumn>
<tableColumn identifier="album" editable="NO" width="195.5" minWidth="96" maxWidth="1024" id="806">
<tableColumn identifier="album" editable="NO" width="201" 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="590" y="3" width="196" height="18"/>
<rect key="frame" x="609" y="3" width="201" 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="196" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nEt-s5-vRX">
<rect key="frame" x="0.0" y="1" width="201" 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"/>
@ -404,10 +404,10 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="hhB-nv-e78">
<rect key="frame" x="788.5" y="3" width="96" height="18"/>
<rect key="frame" x="813" 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">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tHy-sM-HDB">
<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"/>
@ -448,10 +448,10 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="q93-oh-i5T">
<rect key="frame" x="887.5" y="3" width="96" height="18"/>
<rect key="frame" x="912" 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">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="bOi-LI-TDx">
<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"/>
@ -475,7 +475,7 @@
<binding destination="1689" name="fontSize" keyPath="values.fontSize" id="1921"/>
</connections>
</tableColumn>
<tableColumn identifier="genre" editable="NO" width="108.5" minWidth="32" maxWidth="512" id="849">
<tableColumn identifier="genre" editable="NO" width="115" 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="986.5" y="3" width="108" height="18"/>
<rect key="frame" x="1011" y="3" width="115" 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="108" height="16"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yW6-2w-6mN">
<rect key="frame" x="0.0" y="1" width="115" 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"/>
@ -529,10 +529,10 @@
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
<prototypeCellViews>
<tableCellView id="hgh-VE-5kl">
<rect key="frame" x="1098" y="3" width="76" height="18"/>
<rect key="frame" x="1129" 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">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="yEY-MI-d3o">
<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"/>
@ -572,7 +572,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="QFJ-4l-2O6">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="QFJ-4l-2O6">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="gKK-cS-RP5">
<font key="font" usesAppearanceFont="YES"/>
@ -616,7 +616,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Qvd-sk-vRc">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Qvd-sk-vRc">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="YwT-z9-2d2">
<font key="font" usesAppearanceFont="YES"/>
@ -660,7 +660,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="gXW-DX-EsQ">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="gXW-DX-EsQ">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="vaJ-Bc-ebE">
<font key="font" usesAppearanceFont="YES"/>
@ -704,7 +704,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="3V9-dD-ecy">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="3V9-dD-ecy">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="2ha-ys-sRi">
<font key="font" usesAppearanceFont="YES"/>
@ -748,7 +748,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="1rT-Fe-Pho">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="1rT-Fe-Pho">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="DdU-ro-7Yi">
<font key="font" usesAppearanceFont="YES"/>
@ -792,7 +792,7 @@
<rect key="frame" x="1" y="3" width="0.0" height="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="L7E-1S-m3b">
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="L7E-1S-m3b">
<rect key="frame" x="0.0" y="1" width="4" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="cAf-1y-D7M">
<font key="font" usesAppearanceFont="YES"/>
@ -844,11 +844,11 @@
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="1515">
<rect key="frame" x="85" y="17" width="15" height="68"/>
<rect key="frame" x="1202" y="17" width="15" height="11"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="1517">
<rect key="frame" x="0.0" y="0.0" width="1186" height="17"/>
<rect key="frame" x="0.0" y="0.0" width="1217" height="17"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
@ -860,8 +860,8 @@
<outlet property="delegate" destination="2172" id="2182"/>
</connections>
</splitView>
<textField focusRingType="none" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="778">
<rect key="frame" x="463" y="4" width="261" height="14"/>
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="778">
<rect key="frame" x="472" 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"/>
@ -923,7 +923,7 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="0D05748D-3258-44F5-9D1C-CBF211C15E2D" label="Search" paletteLabel="Search" sizingBehavior="auto" id="1533" customClass="NSSearchToolbarItem">
<nil key="toolTip"/>
<searchField key="view" wantsLayer="YES" focusRingType="none" verticalHuggingPriority="750" id="1531">
<searchField key="view" wantsLayer="YES" verticalHuggingPriority="750" id="1531">
<rect key="frame" x="0.0" y="14" width="79" height="22"/>
<autoresizingMask key="autoresizingMask"/>
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" placeholderString="All" bezelStyle="round" recentsAutosaveName="CogFilter" id="1532">
@ -988,7 +988,7 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="B042D8A5-AFF4-43B2-9DFB-E87A09B7F861" label="Current Time" paletteLabel="Current Time" visibilityPriority="5" sizingBehavior="auto" id="1568">
<nil key="toolTip"/>
<textField key="view" focusRingType="none" verticalHuggingPriority="750" id="1566" customClass="TimeField">
<textField key="view" verticalHuggingPriority="750" id="1566" customClass="TimeField">
<rect key="frame" x="15" y="14" width="47" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="center" title="0:00" usesSingleLineMode="YES" bezelStyle="round" id="1567">
@ -1022,7 +1022,11 @@
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<outlet property="_popView" destination="6P4-yi-9TK" id="3d2-Sw-J9J"/>
<outlet property="_LockButton" destination="OQS-2p-1yP" id="F1I-fb-SDQ"/>
<outlet property="_PitchSlider" destination="6P4-yi-9TK" id="Ni0-G0-USM"/>
<outlet property="_ResetButton" destination="3Zc-Xv-g24" id="C7x-EU-QpA"/>
<outlet property="_TempoSlider" destination="stI-oD-51s" id="Mt0-7i-R4f"/>
<outlet property="_popView" destination="90w-7t-RYP" id="qfi-e0-UZc"/>
</connections>
</button>
</toolbarItem>
@ -1273,7 +1277,7 @@
</toolbarItem>
<toolbarItem implicitItemIdentifier="C0FF70A3-EE67-43F6-9956-95B89425CF0E" label="Current Time" paletteLabel="Current Time" visibilityPriority="5" sizingBehavior="auto" id="2274">
<nil key="toolTip"/>
<textField key="view" focusRingType="none" verticalHuggingPriority="750" id="2291" customClass="TimeField">
<textField key="view" verticalHuggingPriority="750" id="2291" customClass="TimeField">
<rect key="frame" x="15" y="14" width="47" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" controlSize="small" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="center" title="0:00" usesSingleLineMode="YES" bezelStyle="round" id="2292">
@ -2355,10 +2359,12 @@ Gw
<connections>
<outlet property="appController" destination="226" id="TnP-DA-nJl"/>
<outlet property="equalizerWindowController" destination="dJ9-b3-BFu" id="gB5-Bu-vqC"/>
<outlet property="lockButton" destination="OQS-2p-1yP" id="RfR-WH-ZPY"/>
<outlet property="pitchSlider" destination="6P4-yi-9TK" id="Lnu-kw-iYW"/>
<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="tempoSlider" destination="stI-oD-51s" id="dDc-lb-crL"/>
<outlet property="volumeSlider" destination="1612" id="1615"/>
</connections>
</customObject>
@ -2539,19 +2545,69 @@ Gw
<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"/>
<rect key="frame" x="0.0" y="0.0" width="69" height="216"/>
<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"/>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="uVG-0w-rW4">
<rect key="frame" x="4" y="199" width="24" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="🎵" id="bQV-ZD-SDv">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VUX-JL-rjr">
<rect key="frame" x="41" y="199" width="24" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" controlSize="small" lineBreakMode="clipping" alignment="center" title="⏰" id="4R4-ba-Ndx">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<slider horizontalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="6P4-yi-9TK" userLabel="Pitch Slider" customClass="PitchSlider">
<rect key="frame" x="6" y="34" 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"/>
<sliderCell key="cell" controlSize="small" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="40.824829049999998" tickMarkPosition="left" sliderType="linear" id="vTw-tV-W5R"/>
<connections>
<action selector="changeSpeed:" target="705" id="FcA-37-2J5"/>
<action selector="changePitch:" target="705" id="bZt-zY-tjg"/>
<outlet property="_TempoSlider" destination="stI-oD-51s" id="HMq-pE-Ssc"/>
</connections>
</slider>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OQS-2p-1yP" userLabel="Lock Button">
<rect key="frame" x="17" y="101" width="36" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="🔒" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="M0v-A9-meu">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
<action selector="pressLock:" target="Ta5-Ik-jh9" id="HnF-AC-arz"/>
</connections>
</button>
<slider horizontalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="stI-oD-51s" userLabel="Tempo Slider" customClass="TempoSlider">
<rect key="frame" x="42" y="34" width="20" height="164"/>
<autoresizingMask key="autoresizingMask"/>
<sliderCell key="cell" controlSize="small" continuous="YES" alignment="left" maxValue="100" doubleValue="40.824829049999998" tickMarkPosition="left" sliderType="linear" id="94j-7B-a8j"/>
<connections>
<action selector="changeTempo:" target="705" id="8xu-Dm-ceG"/>
<outlet property="_PitchSlider" destination="6P4-yi-9TK" id="QaV-cx-2wf"/>
</connections>
</slider>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="3Zc-Xv-g24" userLabel="Reset Button">
<rect key="frame" x="2" y="2" width="64" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="1.0×" bezelStyle="rounded" alignment="center" controlSize="small" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ijs-k0-EIP">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="smallSystem"/>
</buttonCell>
<connections>
<action selector="pressReset:" target="Ta5-Ik-jh9" id="zoi-N3-teA"/>
</connections>
</button>
</subviews>
<point key="canvasLocation" x="694" y="-25"/>
<point key="canvasLocation" x="866.5" y="-9"/>
</customView>
<customObject id="1675" customClass="SpotlightWindowController">
<connections>

View file

@ -98,13 +98,11 @@
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 830C37A427B95EB300E02BB0 /* EqualizerWindowController.m */; };
830C37FC27B9956C00E02BB0 /* analyzer.c in Sources */ = {isa = PBXBuildFile; fileRef = 830C37F227B9956C00E02BB0 /* analyzer.c */; };
831B99BF27C23E88005A969B /* Cog.sdef in Resources */ = {isa = PBXBuildFile; fileRef = 831B99BE27C23E88005A969B /* Cog.sdef */; };
83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83229C9D283B0095004626A8 /* SpectrumWindowController.m */; };
83256B68286661FC0036D9C0 /* libmpg123.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; };
83256B69286661FC0036D9C0 /* libmpg123.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83256B672866617F0036D9C0 /* libmpg123.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
8327DBA9293CAD2400CD0580 /* Organya.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8327DB94293C923500CD0580 /* Organya.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
832923AF279FAC400048201E /* Cog.q1.json in Resources */ = {isa = PBXBuildFile; fileRef = 832923AE279FAC400048201E /* Cog.q1.json */; };
832CFC4F2851AA1A002AC26F /* NSView+Visibility.m in Sources */ = {isa = PBXBuildFile; fileRef = 832CFC4E2851AA1A002AC26F /* NSView+Visibility.m */; };
832CFC562851AA8B002AC26F /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 832CFC552851AA8B002AC26F /* SpectrumViewCG.m */; };
833D0C2527C4ABB80060E16A /* ScriptAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 833D0C2427C4ABB80060E16A /* ScriptAdditions.m */; };
83489C6B2782F78700BDCEA2 /* libvgmPlayer.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83489C542782F2DF00BDCEA2 /* libvgmPlayer.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
834B05EA2859C006000B7DC0 /* TotalTimeTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 834B05E92859C006000B7DC0 /* TotalTimeTransformer.m */; };
@ -145,6 +143,13 @@
837DC931285B3F790005C58A /* DataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */; };
8381A09227C5F72F00A1C530 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8381A09127C5F72F00A1C530 /* SHA256Digest.m */; };
8384914018083E4E00E7332D /* filetype.icns in Resources */ = {isa = PBXBuildFile; fileRef = 8384913D18083E4E00E7332D /* filetype.icns */; };
838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 838A33732D06A9B100D0D770 /* librubberband.3.dylib */; };
838A33752D06A9CE00D0D770 /* librubberband.3.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 838A33732D06A9B100D0D770 /* librubberband.3.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
838A337D2D06C14200D0D770 /* TempoSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A337C2D06C14200D0D770 /* TempoSlider.m */; };
838A337E2D06C14200D0D770 /* PitchSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A337A2D06C14200D0D770 /* PitchSlider.m */; };
838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33802D06CF4100D0D770 /* SpectrumViewCG.m */; };
838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33822D06CF4100D0D770 /* SpectrumWindowController.m */; };
838A33872D06CFCA00D0D770 /* SpeedButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 838A33862D06CFCA00D0D770 /* SpeedButton.m */; };
83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83922FB6286B1AA900A0B039 /* WebKit.framework */; };
839614A2286ED97200D3EEDB /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614A0286ED97200D3EEDB /* AboutWindowController.xib */; };
839614AD286EDA5C00D3EEDB /* SpectrumWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 839614AB286EDA5C00D3EEDB /* SpectrumWindow.xib */; };
@ -154,8 +159,6 @@
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, ); }; };
@ -721,6 +724,7 @@
836EF0C827BB91E600BF35B2 /* libogg.0.dylib in CopyFiles */,
83AA7D07279EBCAF00087AA4 /* libswresample.5.dylib in CopyFiles */,
83AA7D06279EBCAD00087AA4 /* libavutil.59.dylib in CopyFiles */,
838A33752D06A9CE00D0D770 /* librubberband.3.dylib in CopyFiles */,
83AA7D05279EBCAB00087AA4 /* libavformat.61.dylib in CopyFiles */,
83AA7D04279EBCA900087AA4 /* libavcodec.61.dylib in CopyFiles */,
83B72E3B279045B7006007A3 /* libfdk-aac.2.dylib in CopyFiles */,
@ -770,7 +774,7 @@
177042970B8BC53600B86321 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = "<group>"; };
177042980B8BC53600B86321 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = "<group>"; };
177042990B8BC53600B86321 /* PlaybackController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = PlaybackController.h; sourceTree = "<group>"; };
1770429A0B8BC53600B86321 /* PlaybackController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = PlaybackController.m; sourceTree = "<group>"; };
1770429A0B8BC53600B86321 /* PlaybackController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlaybackController.m; sourceTree = "<group>"; };
1778D3C80F645BF00037E7A0 /* MissingAlbumArtTransformer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MissingAlbumArtTransformer.h; path = InfoInspector/MissingAlbumArtTransformer.h; sourceTree = "<group>"; };
1778D3C90F645BF00037E7A0 /* MissingAlbumArtTransformer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MissingAlbumArtTransformer.m; path = InfoInspector/MissingAlbumArtTransformer.m; sourceTree = "<group>"; };
177EBF860B8BC2A70000BC8C /* ImageTextCell.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ImageTextCell.h; sourceTree = "<group>"; };
@ -907,15 +911,11 @@
830C37F227B9956C00E02BB0 /* analyzer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = analyzer.c; sourceTree = "<group>"; };
8314D63B1A354DFE00EEE8E6 /* sidplay.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = sidplay.xcodeproj; path = Plugins/sidplay/sidplay.xcodeproj; sourceTree = "<group>"; };
831B99BE27C23E88005A969B /* Cog.sdef */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Cog.sdef; sourceTree = "<group>"; };
83229C9C283B0095004626A8 /* SpectrumWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SpectrumWindowController.h; sourceTree = "<group>"; };
83229C9D283B0095004626A8 /* SpectrumWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpectrumWindowController.m; sourceTree = "<group>"; };
83256B672866617F0036D9C0 /* libmpg123.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpg123.0.dylib; path = ThirdParty/mpg123/lib/libmpg123.0.dylib; sourceTree = "<group>"; };
8327DB8F293C923500CD0580 /* Organya.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Organya.xcodeproj; path = Plugins/Organya/Organya.xcodeproj; sourceTree = "<group>"; };
832923AE279FAC400048201E /* Cog.q1.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Cog.q1.json; sourceTree = "<group>"; };
832CFC4E2851AA1A002AC26F /* NSView+Visibility.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSView+Visibility.m"; sourceTree = "<group>"; };
832CFC532851AA37002AC26F /* NSView+Visibility.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSView+Visibility.h"; sourceTree = "<group>"; };
832CFC542851AA8B002AC26F /* SpectrumViewCG.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpectrumViewCG.h; sourceTree = SOURCE_ROOT; };
832CFC552851AA8B002AC26F /* SpectrumViewCG.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SpectrumViewCG.m; sourceTree = SOURCE_ROOT; };
833D0C2027C4ABA00060E16A /* ScriptAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScriptAdditions.h; sourceTree = "<group>"; };
833D0C2427C4ABB80060E16A /* ScriptAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScriptAdditions.m; sourceTree = "<group>"; };
833F681E1CDBCAA700AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
@ -981,6 +981,17 @@
8384912518080F2D00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Logging.h; sourceTree = "<group>"; };
8384913D18083E4E00E7332D /* filetype.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = filetype.icns; sourceTree = "<group>"; };
83859520234FEB35004E9946 /* Cog.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Cog.entitlements; sourceTree = "<group>"; };
838A33732D06A9B100D0D770 /* librubberband.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = librubberband.3.dylib; path = ThirdParty/rubberband/lib/librubberband.3.dylib; sourceTree = "<group>"; };
838A33792D06C14200D0D770 /* PitchSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = PitchSlider.h; path = Window/PitchSlider.h; sourceTree = "<group>"; };
838A337A2D06C14200D0D770 /* PitchSlider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = PitchSlider.m; path = Window/PitchSlider.m; sourceTree = "<group>"; };
838A337B2D06C14200D0D770 /* TempoSlider.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TempoSlider.h; path = Window/TempoSlider.h; sourceTree = "<group>"; };
838A337C2D06C14200D0D770 /* TempoSlider.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TempoSlider.m; path = Window/TempoSlider.m; sourceTree = "<group>"; };
838A337F2D06CF4100D0D770 /* SpectrumViewCG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpectrumViewCG.h; path = Visualization/SpectrumViewCG.h; sourceTree = "<group>"; };
838A33802D06CF4100D0D770 /* SpectrumViewCG.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SpectrumViewCG.m; path = Visualization/SpectrumViewCG.m; sourceTree = "<group>"; };
838A33812D06CF4100D0D770 /* SpectrumWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpectrumWindowController.h; path = Visualization/SpectrumWindowController.h; sourceTree = "<group>"; };
838A33822D06CF4100D0D770 /* SpectrumWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SpectrumWindowController.m; path = Visualization/SpectrumWindowController.m; sourceTree = "<group>"; };
838A33852D06CFCA00D0D770 /* SpeedButton.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SpeedButton.h; path = Window/SpeedButton.h; sourceTree = "<group>"; };
838A33862D06CFCA00D0D770 /* SpeedButton.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SpeedButton.m; path = Window/SpeedButton.m; sourceTree = "<group>"; };
838EE79E29A8556000CD0580 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
838EE7A029A8556500CD0580 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
838EE7A229A8557000CD0580 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/LyricsWindow.strings; sourceTree = "<group>"; };
@ -1015,10 +1026,6 @@
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>"; };
@ -1112,6 +1119,7 @@
83922FBA286B1AA900A0B039 /* WebKit.framework in Frameworks */,
835FAC7E27BCDF5B00BA8562 /* libaom.a in Frameworks */,
837DC92B285B05710005C58A /* CoreData.framework in Frameworks */,
838A33742D06A9B100D0D770 /* librubberband.3.dylib in Frameworks */,
83978E26285C596F0076ED21 /* FirebaseAnalytics in Frameworks */,
17BB5CF90B8A86350009ACB1 /* AudioUnit.framework in Frameworks */,
17BB5CFA0B8A86350009ACB1 /* CoreAudio.framework in Frameworks */,
@ -1168,6 +1176,7 @@
1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
838A33732D06A9B100D0D770 /* librubberband.3.dylib */,
836DF616298F6EC400CD0580 /* libsoxr.0.dylib */,
83256B672866617F0036D9C0 /* libmpg123.0.dylib */,
835FAC7C27BCDF5B00BA8562 /* libaom.a */,
@ -1412,6 +1421,8 @@
836D28A718086386005B7299 /* MiniModeMenuTitleTransformer.m */,
17E0D5E30F520F02005B6FED /* MiniWindow.h */,
17E0D5E40F520F02005B6FED /* MiniWindow.m */,
838A33792D06C14200D0D770 /* PitchSlider.h */,
838A337A2D06C14200D0D770 /* PitchSlider.m */,
1752C36A0F59E00100F85F28 /* PlaybackButtons.h */,
1752C36B0F59E00100F85F28 /* PlaybackButtons.m */,
17E0D5E50F520F02005B6FED /* PositionSlider.h */,
@ -1421,10 +1432,10 @@
172A12320F5911D20078EF0C /* RepeatTransformers.m */,
172A123A0F5912AE0078EF0C /* ShuffleTransformers.h */,
172A123B0F5912AE0078EF0C /* ShuffleTransformers.m */,
839D48A62C9E73AA00D03298 /* SpeedButton.h */,
839D48A72C9E73AA00D03298 /* SpeedButton.m */,
839D48A82C9E73AA00D03298 /* SpeedSlider.h */,
839D48A92C9E73AA00D03298 /* SpeedSlider.m */,
838A33852D06CFCA00D0D770 /* SpeedButton.h */,
838A33862D06CFCA00D0D770 /* SpeedButton.m */,
838A337B2D06C14200D0D770 /* TempoSlider.h */,
838A337C2D06C14200D0D770 /* TempoSlider.m */,
17E0D5E70F520F02005B6FED /* TimeField.h */,
17E0D5E80F520F02005B6FED /* TimeField.m */,
17E0D6180F520F9F005B6FED /* VolumeButton.h */,
@ -1811,15 +1822,15 @@
isa = PBXGroup;
children = (
830C37EF27B9956C00E02BB0 /* ThirdParty */,
8377C66427B8CF7A00E8BC0F /* VisualizationController.h */,
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */,
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */,
832CFC542851AA8B002AC26F /* SpectrumViewCG.h */,
832CFC552851AA8B002AC26F /* SpectrumViewCG.m */,
8377C6B727B900F000E8BC0F /* SpectrumItem.h */,
8377C6B827B900F000E8BC0F /* SpectrumItem.m */,
83229C9C283B0095004626A8 /* SpectrumWindowController.h */,
83229C9D283B0095004626A8 /* SpectrumWindowController.m */,
838A337F2D06CF4100D0D770 /* SpectrumViewCG.h */,
838A33802D06CF4100D0D770 /* SpectrumViewCG.m */,
8377C66227B8CF6300E8BC0F /* SpectrumViewSK.h */,
8377C66127B8CF6300E8BC0F /* SpectrumViewSK.m */,
838A33812D06CF4100D0D770 /* SpectrumWindowController.h */,
838A33822D06CF4100D0D770 /* SpectrumWindowController.m */,
8377C66427B8CF7A00E8BC0F /* VisualizationController.h */,
);
name = Visualization;
sourceTree = "<group>";
@ -2561,8 +2572,6 @@
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 */,
@ -2572,6 +2581,7 @@
177EBFA70B8BC2A70000BC8C /* ImageTextCell.m in Sources */,
177EC0270B8BC2CF0000BC8C /* TrackingCell.m in Sources */,
177EC0290B8BC2CF0000BC8C /* TrackingSlider.m in Sources */,
838A33872D06CFCA00D0D770 /* SpeedButton.m in Sources */,
1770429C0B8BC53600B86321 /* AppController.m in Sources */,
1770429E0B8BC53600B86321 /* PlaybackController.m in Sources */,
8355D6B6180612F300D05687 /* NSData+MD5.m in Sources */,
@ -2588,7 +2598,10 @@
5604D4F60D60726E004F5C5D /* SpotlightPlaylistEntry.m in Sources */,
56462EAF0D6341F6000AB68C /* SpotlightTransformers.m in Sources */,
830C37A527B95EB300E02BB0 /* EqualizerWindowController.m in Sources */,
832CFC562851AA8B002AC26F /* SpectrumViewCG.m in Sources */,
838A337D2D06C14200D0D770 /* TempoSlider.m in Sources */,
838A337E2D06C14200D0D770 /* PitchSlider.m in Sources */,
838A33832D06CF4100D0D770 /* SpectrumViewCG.m in Sources */,
838A33842D06CF4100D0D770 /* SpectrumWindowController.m in Sources */,
83B61E2829A82A0200CD0580 /* LyricsWindowController.m in Sources */,
56462EB20D634206000AB68C /* SpotlightPlaylistController.m in Sources */,
07E18DF30D62B38400BB0E11 /* NSArray+ShuffleUtils.m in Sources */,
@ -2635,7 +2648,6 @@
17F6C8070F603701000D9DA9 /* PlaybackEventController.m in Sources */,
83BC5AB220E4C87100631CD4 /* DualWindow.m in Sources */,
8307D30E28606148000FF8EB /* SandboxBroker.m in Sources */,
83229C9F283B0095004626A8 /* SpectrumWindowController.m in Sources */,
835F00BB279BD1CD00055FCF /* SecondsFormatter.m in Sources */,
1784560F0F631E24007E8021 /* FileTreeViewController.m in Sources */,
178456120F631E31007E8021 /* SideViewController.m in Sources */,
@ -3025,6 +3037,8 @@
"$(PROJECT_DIR)/ThirdParty/avif/lib",
"$(PROJECT_DIR)/ThirdParty/mpg123/lib",
"$(PROJECT_DIR)/ThirdParty/soxr/lib",
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/ThirdParty/rubberband/lib",
);
OTHER_CFLAGS = (
"-D__MACOSX__",
@ -3082,6 +3096,8 @@
"$(PROJECT_DIR)/ThirdParty/avif/lib",
"$(PROJECT_DIR)/ThirdParty/mpg123/lib",
"$(PROJECT_DIR)/ThirdParty/soxr/lib",
"$(PROJECT_DIR)",
"$(PROJECT_DIR)/ThirdParty/rubberband/lib",
);
OTHER_CFLAGS = (
"-D__MACOSX__",

View file

@ -1,16 +0,0 @@
//
// 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

View file

@ -1,51 +0,0 @@
//
// 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

Binary file not shown.

7
ThirdParty/rubberband/README.md vendored Normal file
View file

@ -0,0 +1,7 @@
Build the stock release of librubberband with meson and ninja, using the stock
cross build option files to build each architecture, then combine them with lipo.
No changes were made to the upstream library, these binaries are only packaged to
simplify building and packaging the project.
Version 4.0 was used.

View file

@ -0,0 +1,353 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2024 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Live Pitch Shifter obtained by agreement with the
copyright holders, you may redistribute and/or modify it under the
terms described in that licence.
If you wish to distribute code using Rubber Band Live under terms
other than those of the GNU General Public License, you must
obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_LIVE_SHIFTER_H
#define RUBBERBAND_LIVE_SHIFTER_H
#define RUBBERBAND_VERSION "4.0.0"
#define RUBBERBAND_API_MAJOR_VERSION 3
#define RUBBERBAND_API_MINOR_VERSION 0
#undef RUBBERBAND_LIVE_DLLEXPORT
#ifdef _MSC_VER
#define RUBBERBAND_LIVE_DLLEXPORT __declspec(dllexport)
#else
#define RUBBERBAND_LIVE_DLLEXPORT
#endif
#include <vector>
#include <map>
#include <string>
#include <memory>
#include <cstddef>
namespace RubberBand
{
/**
* ### Summary
*
* RubberBand::RubberBandLiveShifter is an interface to the Rubber
* Band Library designed for applications that need to perform
* pitch-shifting only, without time-stretching, and to do so in a
* straightforward block-by-block process with the shortest available
* processing delay. For the more general interface, see
* RubberBand::RubberBandStretcher.
*
* RubberBandLiveShifter has a much simpler API than
* RubberBandStretcher. Its process function, called
* RubberBandLiveShifter::shift(), accepts a fixed number of sample
* frames on each call and always returns exactly the same number of
* sample frames. This is in contrast to the
* process/available/retrieve call sequence that RubberBandStretcher
* requires as a result of its variable output rate.
*
* The number of frames accepted by and returned from
* RubberBandLiveShifter::shift() are not under the caller's control:
* they must always be exactly the number given by
* RubberBandLiveShifter::getBlockSize(). But this number is fixed for
* the lifetime of the shifter, so it only needs to be queried once
* after construction and then fixed-size buffers may be used.
*
* Using RubberBandLiveShifter also gives a shorter processing delay
* than a typical buffering setup using RubberBandStretcher, making it
* a useful choice for some streamed or live situations. However, it
* is still not a low-latency effect, with a delay of 50ms or more
* between input and output signals depending on configuration. (The
* actual value may be queried via
* RubberBandLiveShifter::getStartDelay().) The shifter is real-time
* safe in the sense of avoiding allocation, locking, or blocking
* operations in the processing path.
*
* ### Thread safety
*
* Multiple instances of RubberBandLiveShifter may be created and used
* in separate threads concurrently. However, for any single instance
* of RubberBandLiveShifter, you may not call
* RubberBandLiveShifter::shift() more than once concurrently, and you
* may not change the pitch scaling ratio using
* RubberBandLiveShifter::setPitchScale() while a
* RubberBandLiveShifter::shift() call is being executed. Changing the
* ratio is real-time safe, so when the pitch ratio is time-varying,
* it is normal to update the ratio before each shift call.
*/
class RUBBERBAND_LIVE_DLLEXPORT
RubberBandLiveShifter
{
public:
enum Option {
OptionWindowShort = 0x00000000,
OptionWindowMedium = 0x00100000,
OptionFormantShifted = 0x00000000,
OptionFormantPreserved = 0x01000000,
OptionChannelsApart = 0x00000000,
OptionChannelsTogether = 0x10000000
// n.b. Options is int, so we must stop before 0x80000000
};
/**
* A bitwise OR of values from the RubberBandLiveShifter::Option
* enum.
*/
typedef int Options;
enum PresetOption {
DefaultOptions = 0x00000000
};
/**
* Interface for log callbacks that may optionally be provided to
* the shifter on construction.
*
* If a Logger is provided, the shifter will call one of these
* functions instead of sending output to \c cerr when there is
* something to report. This allows debug output to be diverted to
* an application's logging facilities, and/or handled in an
* RT-safe way. See setDebugLevel() for details about how and when
* RubberBandLiveShifter reports something in this way.
*
* The message text passed to each of these log functions is a
* C-style string with no particular guaranteed lifespan. If you
* need to retain it, copy it before returning. Do not free it.
*
* @see setDebugLevel
* @see setDefaultDebugLevel
*/
struct Logger {
/// Receive a log message with no numeric values.
virtual void log(const char *) = 0;
/// Receive a log message and one accompanying numeric value.
virtual void log(const char *, double) = 0;
/// Receive a log message and two accompanying numeric values.
virtual void log(const char *, double, double) = 0;
virtual ~Logger() { }
};
/**
* Construct a pitch shifter object to run at the given sample
* rate, with the given number of channels.
*/
RubberBandLiveShifter(size_t sampleRate, size_t channels,
Options options);
/**
* Construct a pitch shifter object with a custom debug
* logger. This may be useful for debugging if the default logger
* output (which simply goes to \c cerr) is not visible in the
* runtime environment, or if the application has a standard or
* more realtime-appropriate logging mechanism.
*
* See the documentation for the other constructor above for
* details of the arguments other than the logger.
*
* Note that although the supplied logger gets to decide what to
* do with log messages, the separately-set debug level (see
* setDebugLevel() and setDefaultDebugLevel()) still determines
* whether any given debug message is sent to the logger in the
* first place.
*/
RubberBandLiveShifter(size_t sampleRate, size_t channels,
std::shared_ptr<Logger> logger,
Options options);
~RubberBandLiveShifter();
/**
* Reset the shifter's internal buffers. The shifter should
* subsequently behave as if it had just been constructed
* (although retaining the current pitch ratio).
*/
void reset();
/**
* Set the pitch scaling ratio for the shifter. This is the ratio
* of target frequency to source frequency. For example, a ratio
* of 2.0 would shift up by one octave; 0.5 down by one octave; or
* 1.0 leave the pitch unaffected.
*
* To put this in musical terms, a pitch scaling ratio
* corresponding to a shift of S equal-tempered semitones (where S
* is positive for an upwards shift and negative for downwards) is
* pow(2.0, S / 12.0).
*
* This function may be called at any time, so long as it is not
* called concurrently with shift(). You should either call this
* function from the same thread as shift(), or provide your own
* mutex or similar mechanism to ensure that setPitchScale and
* shift() cannot be run at once (there is no internal mutex for
* this purpose).
*/
void setPitchScale(double scale);
/**
* Set a pitch scale for the vocal formant envelope separately
* from the overall pitch scale. This is a ratio of target
* frequency to source frequency. For example, a ratio of 2.0
* would shift the formant envelope up by one octave; 0.5 down by
* one octave; or 1.0 leave the formant unaffected.
*
* By default this is set to the special value of 0.0, which
* causes the scale to be calculated automatically. It will be
* treated as 1.0 / the pitch scale if OptionFormantPreserved is
* specified, or 1.0 for OptionFormantShifted.
*
* Conversely, if this is set to a value other than the default
* 0.0, formant shifting will happen regardless of the state of
* the OptionFormantPreserved/OptionFormantShifted option.
*
* This function is provided for special effects only. You do not
* need to call it for ordinary pitch shifting, with or without
* formant preservation - just specify or omit the
* OptionFormantPreserved option as appropriate. Use this function
* only if you want to shift formants by a distance other than
* that of the overall pitch shift.
*/
void setFormantScale(double scale);
/**
* Return the last pitch scaling ratio value that was set (either
* on construction or with setPitchScale()).
*/
double getPitchScale() const;
/**
* Return the last formant scaling ratio that was set with
* setFormantScale, or 0.0 if the default automatic scaling is in
* effect.
*/
double getFormantScale() const;
/**
* Return the output delay of the shifter. This is the number of
* audio samples that one should discard at the start of the
* output, in order to ensure that the resulting audio has the
* expected time alignment with the input.
*
* Ensure you have set the pitch scale to its proper starting
* value before calling getStartDelay().
*/
size_t getStartDelay() const;
/**
* Return the number of channels this shifter was constructed
* with.
*/
size_t getChannelCount() const;
/**
* Change an OptionFormant configuration setting. This may be
* called at any time in any mode.
*
* Note that if running multi-threaded in Offline mode, the change
* may not take effect immediately if processing is already under
* way when this function is called.
*/
void setFormantOption(Options options);
/**
* Query the number of sample frames that must be passed to, and
* will be returned by, each shift() call. This value is fixed for
* the lifetime of the shifter.
*
* Note that the blocksize refers to the number of audio sample
* frames, which may be multi-channel, not the number of
* individual samples.
*/
size_t getBlockSize() const;
/**
* Pitch-shift a single block of sample frames. The number of
* sample frames (samples per channel) processed per call is
* constant.
*
* "input" should point to de-interleaved audio data with one
* float array per channel, with each array containing n samples
* where n is the value returned by getBlockSize().
*
* "output" should point to a float array per channel, with each
* array having enough room to store n samples where n is the value
* returned by getBlockSize().
*
* The input and output must be separate arrays; they cannot alias
* one another or overlap.
*
* Sample values are conventionally expected to be in the range
* -1.0f to +1.0f.
*/
void shift(const float *const *input, float *const *output);
/**
* Set the level of debug output. The supported values are:
*
* 0. Report errors only.
*
* 1. Report some information on construction and ratio
* change. Nothing is reported during normal processing unless
* something changes.
*
* 2. Report a significant amount of information about ongoing
* calculations during normal processing.
*
* The default is whatever has been set using
* setDefaultDebugLevel(), or 0 if that function has not been
* called.
*
* All output goes to \c cerr unless a custom
* RubberBandLiveShifter::Logger has been provided on
* construction. Because writing to \c cerr is not RT-safe, only
* debug level 0 is RT-safe in normal use by default. Debug levels
* 0 and 1 use only C-string constants as debug messages, so they
* are RT-safe if your custom logger is RT-safe. Levels 2 and 3
* are not guaranteed to be RT-safe in any conditions as they may
* construct messages by allocation.
*
* @see Logger
* @see setDefaultDebugLevel
*/
void setDebugLevel(int level);
/**
* Set the default level of debug output for subsequently
* constructed shifters.
*
* @see setDebugLevel
*/
static void setDefaultDebugLevel(int level);
protected:
class Impl;
Impl *m_d;
RubberBandLiveShifter(const RubberBandLiveShifter &) =delete;
RubberBandLiveShifter &operator=(const RubberBandLiveShifter &) =delete;
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,216 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Rubber Band Library
An audio time-stretching and pitch-shifting library.
Copyright 2007-2024 Particular Programs Ltd.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. See the file
COPYING included with this distribution for more information.
Alternatively, if you have a valid commercial licence for the
Rubber Band Library obtained by agreement with the copyright
holders, you may redistribute and/or modify it under the terms
described in that licence.
If you wish to distribute code using the Rubber Band Library
under terms other than those of the GNU General Public License,
you must obtain a valid commercial licence before doing so.
*/
#ifndef RUBBERBAND_C_API_H
#define RUBBERBAND_C_API_H
#ifdef __cplusplus
extern "C" {
#endif
#define RUBBERBAND_VERSION "4.0.0"
#define RUBBERBAND_API_MAJOR_VERSION 3
#define RUBBERBAND_API_MINOR_VERSION 0
#undef RB_EXTERN
#ifdef _MSC_VER
#ifndef RUBBERBAND_STATIC
#define RB_EXTERN extern __declspec(dllexport)
#else
#define RB_EXTERN extern
#endif
#else
#define RB_EXTERN extern
#endif
/**
* This is a C-linkage interface to the Rubber Band time stretcher.
*
* This is a wrapper interface: the primary interface is in C++ and is
* defined and documented in RubberBandStretcher.h and
* RubberBandLiveShifter.h. The library itself is implemented in C++,
* and requires C++ standard library support even when using the
* C-linkage API.
*
* Please see RubberBandStretcher.h and RubberBandLiveShifter.h for
* documentation.
*
* If you are writing to the C++ API, do not include this header.
*/
enum RubberBandOption {
RubberBandOptionProcessOffline = 0x00000000,
RubberBandOptionProcessRealTime = 0x00000001,
RubberBandOptionStretchElastic = 0x00000000, // obsolete
RubberBandOptionStretchPrecise = 0x00000010, // obsolete
RubberBandOptionTransientsCrisp = 0x00000000,
RubberBandOptionTransientsMixed = 0x00000100,
RubberBandOptionTransientsSmooth = 0x00000200,
RubberBandOptionDetectorCompound = 0x00000000,
RubberBandOptionDetectorPercussive = 0x00000400,
RubberBandOptionDetectorSoft = 0x00000800,
RubberBandOptionPhaseLaminar = 0x00000000,
RubberBandOptionPhaseIndependent = 0x00002000,
RubberBandOptionThreadingAuto = 0x00000000,
RubberBandOptionThreadingNever = 0x00010000,
RubberBandOptionThreadingAlways = 0x00020000,
RubberBandOptionWindowStandard = 0x00000000,
RubberBandOptionWindowShort = 0x00100000,
RubberBandOptionWindowLong = 0x00200000,
RubberBandOptionSmoothingOff = 0x00000000,
RubberBandOptionSmoothingOn = 0x00800000,
RubberBandOptionFormantShifted = 0x00000000,
RubberBandOptionFormantPreserved = 0x01000000,
RubberBandOptionPitchHighSpeed = 0x00000000,
RubberBandOptionPitchHighQuality = 0x02000000,
RubberBandOptionPitchHighConsistency = 0x04000000,
RubberBandOptionChannelsApart = 0x00000000,
RubberBandOptionChannelsTogether = 0x10000000,
RubberBandOptionEngineFaster = 0x00000000,
RubberBandOptionEngineFiner = 0x20000000
};
typedef int RubberBandOptions;
struct RubberBandState_;
typedef struct RubberBandState_ *RubberBandState;
RB_EXTERN RubberBandState rubberband_new(unsigned int sampleRate,
unsigned int channels,
RubberBandOptions options,
double initialTimeRatio,
double initialPitchScale);
RB_EXTERN void rubberband_delete(RubberBandState);
RB_EXTERN void rubberband_reset(RubberBandState);
RB_EXTERN int rubberband_get_engine_version(RubberBandState);
RB_EXTERN void rubberband_set_time_ratio(RubberBandState, double ratio);
RB_EXTERN void rubberband_set_pitch_scale(RubberBandState, double scale);
RB_EXTERN double rubberband_get_time_ratio(const RubberBandState);
RB_EXTERN double rubberband_get_pitch_scale(const RubberBandState);
RB_EXTERN void rubberband_set_formant_scale(RubberBandState, double scale);
RB_EXTERN double rubberband_get_formant_scale(const RubberBandState);
RB_EXTERN unsigned int rubberband_get_preferred_start_pad(const RubberBandState);
RB_EXTERN unsigned int rubberband_get_start_delay(const RubberBandState);
RB_EXTERN unsigned int rubberband_get_latency(const RubberBandState);
RB_EXTERN void rubberband_set_transients_option(RubberBandState, RubberBandOptions options);
RB_EXTERN void rubberband_set_detector_option(RubberBandState, RubberBandOptions options);
RB_EXTERN void rubberband_set_phase_option(RubberBandState, RubberBandOptions options);
RB_EXTERN void rubberband_set_formant_option(RubberBandState, RubberBandOptions options);
RB_EXTERN void rubberband_set_pitch_option(RubberBandState, RubberBandOptions options);
RB_EXTERN void rubberband_set_expected_input_duration(RubberBandState, unsigned int samples);
RB_EXTERN unsigned int rubberband_get_samples_required(const RubberBandState);
RB_EXTERN void rubberband_set_max_process_size(RubberBandState, unsigned int samples);
RB_EXTERN unsigned int rubberband_get_process_size_limit(RubberBandState);
RB_EXTERN void rubberband_set_key_frame_map(RubberBandState, unsigned int keyframecount, unsigned int *from, unsigned int *to);
RB_EXTERN void rubberband_study(RubberBandState, const float *const *input, unsigned int samples, int final);
RB_EXTERN void rubberband_process(RubberBandState, const float *const *input, unsigned int samples, int final);
RB_EXTERN int rubberband_available(const RubberBandState);
RB_EXTERN unsigned int rubberband_retrieve(const RubberBandState, float *const *output, unsigned int samples);
RB_EXTERN unsigned int rubberband_get_channel_count(const RubberBandState);
RB_EXTERN void rubberband_calculate_stretch(RubberBandState);
RB_EXTERN void rubberband_set_debug_level(RubberBandState, int level);
RB_EXTERN void rubberband_set_default_debug_level(int level);
enum RubberBandLiveOption {
RubberBandLiveOptionWindowShort = 0x00000000,
RubberBandLiveOptionWindowMedium = 0x00100000,
RubberBandLiveOptionFormantShifted = 0x00000000,
RubberBandLiveOptionFormantPreserved = 0x01000000,
RubberBandLiveOptionChannelsApart = 0x00000000,
RubberBandLiveOptionChannelsTogether = 0x10000000
};
typedef int RubberBandLiveOptions;
struct RubberBandLiveState_;
typedef struct RubberBandLiveState_ *RubberBandLiveState;
RB_EXTERN RubberBandLiveState rubberband_live_new(unsigned int sampleRate,
unsigned int channels,
RubberBandOptions options);
RB_EXTERN void rubberband_live_delete(RubberBandLiveState);
RB_EXTERN void rubberband_live_reset(RubberBandLiveState);
RB_EXTERN void rubberband_live_set_pitch_scale(RubberBandLiveState, double scale);
RB_EXTERN double rubberband_live_get_pitch_scale(const RubberBandLiveState);
RB_EXTERN void rubberband_live_set_formant_scale(RubberBandLiveState, double scale);
RB_EXTERN double rubberband_live_get_formant_scale(const RubberBandLiveState);
RB_EXTERN unsigned int rubberband_live_get_start_delay(const RubberBandLiveState);
RB_EXTERN void rubberband_live_set_formant_option(RubberBandLiveState, RubberBandOptions options);
RB_EXTERN unsigned int rubberband_live_get_block_size(RubberBandLiveState);
RB_EXTERN void rubberband_live_shift(RubberBandLiveState, const float *const *input, float *const *output);
RB_EXTERN unsigned int rubberband_live_get_channel_count(const RubberBandLiveState);
RB_EXTERN void rubberband_live_set_debug_level(RubberBandLiveState, int level);
RB_EXTERN void rubberband_live_set_default_debug_level(int level);
#ifdef __cplusplus
}
#endif
#undef RB_EXTERN
#endif

View file

@ -1,16 +1,19 @@
//
// SpeedSlider.h
// PitchSlider.h
// Cog
//
// Created by Christopher Snowhill on 9/20/24.
// Copyright 2024 __LoSnoCo__. All rights reserved.
//
#import "TempoSlider.h"
#import <Cocoa/Cocoa.h>
@interface SpeedSlider : NSSlider {
@interface PitchSlider : NSSlider {
NSPopover *popover;
NSText *textView;
IBOutlet NSSlider *_TempoSlider;
}
- (void)showToolTip;

View file

@ -1,18 +1,18 @@
//
// SpeedSlider.m
// PitchSlider.m
// Cog
//
// Created by Christopher Snowhill on 9/20/24.
// Copyright 2024 __LoSnoCo__. All rights reserved.
//
#import "SpeedSlider.h"
#import "PitchSlider.h"
#import "CogAudio/Helper.h"
#import "PlaybackController.h"
static void *kSpeedSliderContext = &kSpeedSliderContext;
static void *kPitchSliderContext = &kPitchSliderContext;
@implementation SpeedSlider {
@implementation PitchSlider {
NSTimer *currentTimer;
BOOL wasInsideSnapRange;
/*BOOL observersadded;*/
@ -55,22 +55,24 @@ static void *kSpeedSliderContext = &kSpeedSliderContext;
}*/
- (void)updateToolTip {
const double value = [self doubleValue];
const double value = ([self doubleValue] - [self minValue]) * 100.0 / ([self maxValue] - [self minValue]);
NSString *text;
double speed;
if(value < 0.2) {
speed = 0.2;
} else if(value > 5.0) {
speed = 5.0;
const double adjustedValue = ((value * value) * (5.0 - 0.2) / 10000.0) + 0.2;
double pitch;
if(adjustedValue < 0.2) {
pitch = 0.2;
} else if(adjustedValue > 5.0) {
pitch = 5.0;
} else {
speed = value;
pitch = adjustedValue;
}
if(speed < 1)
text = [NSString stringWithFormat:@"%0.2lf×", speed];
if(pitch < 1)
text = [NSString stringWithFormat:@"%0.2lf×", pitch];
else
text = [NSString stringWithFormat:@"%0.1lf×", speed];
text = [NSString stringWithFormat:@"%0.1lf×", pitch];
[textView setString:text];
}
@ -128,7 +130,7 @@ static void *kSpeedSliderContext = &kSpeedSliderContext;
}
/*- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context != kSpeedSliderContext) {
if(context != kPitchSliderContext) {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
return;
}
@ -137,10 +139,21 @@ static void *kSpeedSliderContext = &kSpeedSliderContext;
- (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);
const double value = ([self doubleValue] - [self minValue]) * 100.0 / ([self maxValue] - [self minValue]);
const double adjustedValue = ((value * value) * (5.0 - 0.2) / 10000.0) + 0.2;
double snapProgress = (adjustedValue - snapTarget);
if(fabs(snapProgress) < 0.005) {
[self setDoubleValue:snapTarget];
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
if(speedLock) {
[_TempoSlider setDoubleValue:[self doubleValue]];
}
if(fabs(snapProgress) < 0.01) {
const double inverseValue = sqrtf((snapTarget - 0.2) * 10000.0 / (5.0 - 0.2));
[self setDoubleValue:inverseValue];
if(speedLock) {
[_TempoSlider setDoubleValue:inverseValue];
}
if(!wasInsideSnapRange) {
[[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric performanceTime:NSHapticFeedbackPerformanceTimeDefault];
}
@ -149,7 +162,7 @@ static void *kSpeedSliderContext = &kSpeedSliderContext;
wasInsideSnapRange = NO;
}
[self showToolTip];
[self showToolTipForDuration:1.0];
return [super sendAction:theAction to:theTarget];
}
@ -159,7 +172,14 @@ static void *kSpeedSliderContext = &kSpeedSliderContext;
[self setDoubleValue:[self doubleValue] + change];
[[self target] changeSpeed:self];
[[self target] changePitch:self];
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
if(speedLock) {
[_TempoSlider setDoubleValue:[self doubleValue]];
[[self target] changeTempo:self];
}
[self showToolTipForDuration:1.0];
}

View file

@ -1,16 +1,24 @@
//
// VolumeButton.h
// SpeedButton.h
// Cog
//
// Created by Vincent Spader on 2/8/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
// Created by Christopher Snowhill on 9/20/24.
// Copyright 2024 __LoSnoCo__. All rights reserved.
//
#import "VolumeSlider.h"
#import "PitchSlider.h"
#import "TempoSlider.h"
#import <Cocoa/Cocoa.h>
@interface VolumeButton : NSButton {
IBOutlet VolumeSlider *_popView;
@interface SpeedButton : NSButton {
IBOutlet NSView *_popView;
IBOutlet PitchSlider *_PitchSlider;
IBOutlet TempoSlider *_TempoSlider;
IBOutlet NSButton *_LockButton;
IBOutlet NSButton *_ResetButton;
}
- (IBAction)pressLock:(id)sender;
- (IBAction)pressReset:(id)sender;
@end

View file

@ -1,15 +1,20 @@
//
// VolumeButton.m
// SpeedButton.m
// Cog
//
// Created by Vincent Spader on 2/8/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
// Created by Christopher Snowhill on 9/20/24.
// Copyright 2024 __LoSnoCo__. All rights reserved.
//
#import "VolumeButton.h"
#import "SpeedButton.h"
#import "PlaybackController.h"
@implementation VolumeButton {
static double reverseSpeedScale(double input, double min, double max) {
input = sqrtf((input - 0.2) * 10000.0 / (5.0 - 0.2));
return (input * (max - min) / 100.0) + min;
}
@implementation SpeedButton {
NSPopover *popover;
NSViewController *viewController;
}
@ -20,21 +25,6 @@
[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];
@ -48,4 +38,30 @@
[super mouseDown:theEvent];
}
- (IBAction)pressLock:(id)sender {
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
speedLock = !speedLock;
[_LockButton setTitle:speedLock ? @"🔒" : @"🔓"];
[[NSUserDefaults standardUserDefaults] setBool:speedLock forKey:@"speedLock"];
if(speedLock) {
const double pitchValue = ([_PitchSlider doubleValue] - [_PitchSlider minValue]) / ([_PitchSlider maxValue] - [_PitchSlider minValue]);
const double tempoValue = ([_TempoSlider doubleValue] - [_TempoSlider minValue]) / ([_TempoSlider maxValue] - [_TempoSlider minValue]);
const double averageValue = (pitchValue + tempoValue) * 0.5;
[_PitchSlider setDoubleValue:(averageValue * ([_PitchSlider maxValue] - [_PitchSlider minValue])) + [_PitchSlider minValue]];
[_TempoSlider setDoubleValue:(averageValue * ([_TempoSlider maxValue] - [_TempoSlider minValue])) + [_TempoSlider minValue]];
[[_PitchSlider target] changePitch:_PitchSlider];
[[_TempoSlider target] changeTempo:_TempoSlider];
}
}
- (IBAction)pressReset:(id)sender {
[_PitchSlider setDoubleValue:reverseSpeedScale(1.0, [_PitchSlider minValue], [_PitchSlider maxValue])];
[_TempoSlider setDoubleValue:reverseSpeedScale(1.0, [_TempoSlider minValue], [_TempoSlider maxValue])];
[[_PitchSlider target] changePitch:_PitchSlider];
[[_TempoSlider target] changeTempo:_TempoSlider];
}
@end

View file

@ -1,17 +1,19 @@
//
// VolumeSlider.h
// TempoSlider.h
// Cog
//
// Created by Vincent Spader on 2/8/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
// Created by Christopher Snowhill on 9/20/24.
// Copyright 2024 __LoSnoCo__. All rights reserved.
//
#import "TempoSlider.h"
#import <Cocoa/Cocoa.h>
@interface VolumeSlider : NSSlider {
@interface TempoSlider : NSSlider {
NSPopover *popover;
NSText *textView;
double MAX_VOLUME;
IBOutlet NSSlider *_PitchSlider;
}
- (void)showToolTip;

View file

@ -1,21 +1,21 @@
//
// VolumeSlider.m
// TempoSlider.m
// Cog
//
// Created by Vincent Spader on 2/8/09.
// Copyright 2009 __MyCompanyName__. All rights reserved.
// Created by Christopher Snowhill on 9/20/24.
// Copyright 2024 __LoSnoCo__. All rights reserved.
//
#import "VolumeSlider.h"
#import "TempoSlider.h"
#import "CogAudio/Helper.h"
#import "PlaybackController.h"
static void *kVolumeSliderContext = &kVolumeSliderContext;
static void *kTempoSliderContext = &kTempoSliderContext;
@implementation VolumeSlider {
@implementation TempoSlider {
NSTimer *currentTimer;
BOOL wasInsideSnapRange;
BOOL observersadded;
/*BOOL observersadded;*/
}
- (id)initWithFrame:(NSRect)frame {
@ -29,9 +29,6 @@ static void *kVolumeSliderContext = &kVolumeSliderContext;
}
- (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)];
@ -49,31 +46,33 @@ static void *kVolumeSliderContext = &kVolumeSliderContext;
popover.animates = NO;
[popover setContentSize:textView.bounds.size];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeLimit" options:0 context:kVolumeSliderContext];
observersadded = YES;
/*observersadded = YES;*/
}
- (void)dealloc {
/*- (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);
const double value = ([self doubleValue] - [self minValue]) * 100.0 / ([self maxValue] - [self minValue]);
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.
const double adjustedValue = ((value * value) * (5.0 - 0.2) / 10000.0) + 0.2;
double tempo;
if(adjustedValue < 0.2) {
tempo = 0.2;
} else if(adjustedValue > 5.0) {
tempo = 5.0;
} else {
tempo = adjustedValue;
}
if(tempo < 1)
text = [NSString stringWithFormat:@"%0.2lf×", tempo];
else
text = [NSString stringWithFormat:@"%0.lf%%", volume];
text = [NSString stringWithFormat:@"%0.1lf×", tempo];
[textView setString:text];
}
@ -130,31 +129,31 @@ static void *kVolumeSliderContext = &kVolumeSliderContext;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context != kVolumeSliderContext) {
/*- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if(context != kTempoSliderContext) {
[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);
// Snap to 1.0× if value is close
double snapTarget = 1.0;
const double value = ([self doubleValue] - [self minValue]) * 100.0 / ([self maxValue] - [self minValue]);
const double adjustedValue = ((value * value) * (5.0 - 0.2) / 10000.0) + 0.2;
double snapProgress = (adjustedValue - snapTarget);
if(fabs(snapProgress) < 0.005) {
[self setDoubleValue:snapTarget];
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
if(speedLock) {
[_PitchSlider setDoubleValue:[self doubleValue]];
}
if(fabs(snapProgress) < 0.01) {
const double inverseValue = sqrtf((snapTarget - 0.2) * 10000.0 / (5.0 - 0.2));
[self setDoubleValue:inverseValue];
if(speedLock) {
[_PitchSlider setDoubleValue:inverseValue];
}
if(!wasInsideSnapRange) {
[[NSHapticFeedbackManager defaultPerformer] performFeedbackPattern:NSHapticFeedbackPatternGeneric performanceTime:NSHapticFeedbackPerformanceTimeDefault];
}
@ -163,7 +162,7 @@ static void *kVolumeSliderContext = &kVolumeSliderContext;
wasInsideSnapRange = NO;
}
[self showToolTip];
[self showToolTipForDuration:1.0];
return [super sendAction:theAction to:theTarget];
}
@ -173,7 +172,14 @@ static void *kVolumeSliderContext = &kVolumeSliderContext;
[self setDoubleValue:[self doubleValue] + change];
[[self target] changeVolume:self];
[[self target] changeTempo:self];
BOOL speedLock = [[NSUserDefaults standardUserDefaults] boolForKey:@"speedLock"];
if(speedLock) {
[_PitchSlider setDoubleValue:[self doubleValue]];
[[self target] changePitch:self];
}
[self showToolTipForDuration:1.0];
}

View file

@ -35,6 +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.
</div>
</body>

View file

@ -36,6 +36,7 @@
<br><br>
<em>Este programa ha sido posible gracias a las contribuciones de usuarios y usuarias como tú.</em>
<br><br> Todo el código de Cog es de mi propiedad, y está licenciado bajo la <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a>. Cog contiene código de terceros que están bajo sus propias licencias.
<br><br> La biblioteca <a href="https://breakfastquay.com/rubberband/">Rubber Band</a> se usa bajo licencia <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a> para transponer tono y/o cambiar velocidad.
</div>
</body>

View file

@ -35,6 +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> Biblioteka <a href="https://breakfastquay.com/rubberband/">Rubber Band Library</a> była używana zgodnie z zasadami licencji <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a>, aby dostarczyć funkcję zmiany tonu i/lub przerwy w czasie.
</div>
</body>

View file

@ -35,6 +35,8 @@
<br><br>
<em>Создние этой программы стало возможным благодаря вкладу таких пользователей, как вы.</em>
<br><br> Весь код Cog защищен моим авторским правом и распространяется под лицензией <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a>. Cog содержит фрагменты другого кода от третьих сторон, который находятся под их собственными лицензиями.
<br><br> Библиотека <a href="https://breakfastquay.com/rubberband/">Rubber Band Library</a> использовалась под лицензией <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a> для предоставления функции изменения тона и/или сдвига во времени.
</div>
</body>

View file

@ -35,6 +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 Kütüphanesi</a>, ton ve/veya zaman kayması sağlamak için <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt">GPL</a> altında kullanıldı.
</div>
</body>