Spectrum Visualizer: Add customization options

Add options to the Appearance preferences page to allow changing the
spectrum's projection between a 2D-like one and 3D perspective, and add
options to change the bar and peak dot colors.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
swiftingly
Christopher Snowhill 2022-05-22 15:26:27 -07:00
parent fce0e129a5
commit 4035ca861f
8 changed files with 221 additions and 6 deletions

View File

@ -19,6 +19,8 @@
#import "Logging.h"
#import "MiniModeMenuTitleTransformer.h"
#import "ColorToValueTransformer.h"
#import "Shortcuts.h"
#import <MASShortcut/Shortcut.h>
@ -47,6 +49,10 @@ void *kAppControllerContext = &kAppControllerContext;
NSValueTransformer *miniModeMenuTitleTransformer = [[MiniModeMenuTitleTransformer alloc] init];
[NSValueTransformer setValueTransformer:miniModeMenuTitleTransformer
forName:@"MiniModeMenuTitleTransformer"];
NSValueTransformer *colorToValueTransformer = [[ColorToValueTransformer alloc] init];
[NSValueTransformer setValueTransformer:colorToValueTransformer
forName:@"ColorToValueTransformer"];
}
- (id)init {
@ -460,6 +466,17 @@ void *kAppControllerContext = &kAppControllerContext;
[userDefaultsValuesDict setObject:[NSNumber numberWithBool:NO] forKey:@"quitOnNaturalStop"];
[userDefaultsValuesDict setObject:[NSNumber numberWithBool:NO] forKey:@"spectrumFreqMode"];
[userDefaultsValuesDict setObject:[NSNumber numberWithBool:YES] forKey:@"spectrumProjectionMode"];
NSValueTransformer *colorToValueTransformer = [NSValueTransformer valueTransformerForName:@"ColorToValueTransformer"];
NSData *barColor = [colorToValueTransformer reverseTransformedValue:[NSColor colorWithSRGBRed:1.0 green:0.5 blue:0 alpha:1.0]];
NSData *dotColor = [colorToValueTransformer reverseTransformedValue:[NSColor systemRedColor]];
[userDefaultsValuesDict setObject:barColor forKey:@"spectrumBarColor"];
[userDefaultsValuesDict setObject:dotColor forKey:@"spectrumDotColor"];
// Register and sync defaults
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
[[NSUserDefaults standardUserDefaults] synchronize];

View File

@ -171,6 +171,7 @@
8399D4E21805A55000B503B1 /* XmlContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8399D4E01805A55000B503B1 /* XmlContainer.m */; };
839DA7CF274A2D4C001B18E5 /* NSDictionary+Merge.m in Sources */ = {isa = PBXBuildFile; fileRef = 839DA7CE274A2D4C001B18E5 /* NSDictionary+Merge.m */; };
83A360B220E4E81D00192DAB /* Flac.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8303A30C20E4E3D000951EF8 /* Flac.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */; };
83AA7D04279EBCA900087AA4 /* libavcodec.59.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83AA7D05279EBCAB00087AA4 /* libavformat.59.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D03279EBC8300087AA4 /* libavformat.59.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
83AA7D06279EBCAD00087AA4 /* libavutil.57.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83AA7D02279EBC8200087AA4 /* libavutil.57.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@ -1004,6 +1005,8 @@
8399D4E11805A55000B503B1 /* XmlContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XmlContainer.h; 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>"; };
83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = ColorToValueTransformer.m; path = Preferences/Preferences/ColorToValueTransformer.m; sourceTree = "<group>"; };
83A3B733283AE6AA00CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ColorToValueTransformer.h; path = Preferences/Preferences/ColorToValueTransformer.h; sourceTree = "<group>"; };
83AA7D00279EBC8200087AA4 /* libavcodec.59.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavcodec.59.dylib; path = ThirdParty/ffmpeg/lib/libavcodec.59.dylib; sourceTree = "<group>"; };
83AA7D01279EBC8200087AA4 /* libswresample.4.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libswresample.4.dylib; path = ThirdParty/ffmpeg/lib/libswresample.4.dylib; sourceTree = "<group>"; };
83AA7D02279EBC8200087AA4 /* libavutil.57.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libavutil.57.dylib; path = ThirdParty/ffmpeg/lib/libavutil.57.dylib; sourceTree = "<group>"; };
@ -1438,6 +1441,8 @@
17E0D5F60F520F42005B6FED /* Transformers */ = {
isa = PBXGroup;
children = (
83A3B733283AE6AA00CC6593 /* ColorToValueTransformer.h */,
83A3B72F283AE6AA00CC6593 /* ColorToValueTransformer.m */,
17E0D6120F520F87005B6FED /* FontSizetoLineHeightTransformer.h */,
17E0D6130F520F87005B6FED /* FontSizetoLineHeightTransformer.m */,
17E0D6140F520F87005B6FED /* StringToURLTransformer.h */,
@ -2523,6 +2528,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
83A3B734283AE89000CC6593 /* ColorToValueTransformer.m in Sources */,
8D11072D0486CEB800E47090 /* main.m in Sources */,
8E75757109F31D5A0080F1EE /* DNDArrayController.m in Sources */,
8E75757209F31D5A0080F1EE /* PlaylistController.m in Sources */,

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -470,11 +470,11 @@
<point key="canvasLocation" x="450" y="-52"/>
</customView>
<customView id="CgN-sy-RmM" userLabel="AppearanceView">
<rect key="frame" x="0.0" y="0.0" width="530" height="74"/>
<rect key="frame" x="0.0" y="0.0" width="530" height="162"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="haX-dq-OIe">
<rect key="frame" x="18" y="43" width="329" height="18"/>
<rect key="frame" x="18" y="125" width="329" height="18"/>
<autoresizingMask key="autoresizingMask"/>
<buttonCell key="cell" type="check" title="Colorful dock icons" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="GdX-5e-NeU">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -485,7 +485,7 @@
</connections>
</button>
<button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AA7-fr-NOk">
<rect key="frame" x="18" y="15" width="329" height="18"/>
<rect key="frame" x="18" y="98" width="329" height="18"/>
<autoresizingMask key="autoresizingMask"/>
<buttonCell key="cell" type="check" title="Show stop button" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="vMI-lj-Lrz">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@ -495,8 +495,61 @@
<binding destination="52" name="value" keyPath="values.enableStopButton" id="JH0-bm-IC4"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4xb-C2-wmz">
<rect key="frame" x="18" y="70" width="146" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Spectrum 2D view" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="gXL-j6-df0">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<binding destination="52" name="value" keyPath="values.spectrumProjectionMode" id="UGh-1j-iFC"/>
</connections>
</button>
<colorWell fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="7hw-NI-bF1">
<rect key="frame" x="20" y="40" width="44" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.05813049898" green="0.055541899059999997" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<binding destination="52" name="value" keyPath="values.spectrumBarColor" id="GXa-dK-oTa">
<dictionary key="options">
<string key="NSValueTransformerName">ColorToValueTransformer</string>
</dictionary>
</binding>
</connections>
</colorWell>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="1Xc-Cr-icC">
<rect key="frame" x="70" y="43" width="105" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Spectrum bars" id="Bll-IJ-lje">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<colorWell fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="WjH-M4-Gqf">
<rect key="frame" x="20" y="12" width="44" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="color" red="0.05813049898" green="0.055541899059999997" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<binding destination="52" name="value" keyPath="values.spectrumDotColor" id="43q-fa-aDY">
<dictionary key="options">
<string key="NSValueTransformerName">ColorToValueTransformer</string>
</dictionary>
</binding>
</connections>
</colorWell>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ahQ-bv-j9g">
<rect key="frame" x="70" y="15" width="117" height="16"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Spectrum peaks" id="CQI-YZ-B3V">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="450" y="139"/>
<point key="canvasLocation" x="450" y="183"/>
</customView>
<customView wantsLayer="YES" id="JXu-ar-J3Y" userLabel="MIDIView">
<rect key="frame" x="0.0" y="0.0" width="530" height="140"/>

View File

@ -0,0 +1,16 @@
//
// ColorToValueTransformer.h
// Preferences
//
// Created by Christopher Snowhill on 5/22/22.
//
#import <Cocoa/Cocoa.h>
NS_ASSUME_NONNULL_BEGIN
@interface ColorToValueTransformer : NSValueTransformer
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,60 @@
//
// ColorToValueTransformer.m
// Preferences
//
// Created by Christopher Snowhill on 5/22/22.
//
#import "ColorToValueTransformer.h"
@implementation ColorToValueTransformer
+ (Class)transformedValueClass {
return [NSColor class];
}
+ (BOOL)allowsReverseTransformation {
return YES;
}
// Convert from string to NSURL
- (id)reverseTransformedValue:(id)value {
if(value == nil) return nil;
NSError *error;
NSData *data;
if(@available(macOS 10.13, *)) {
data = [NSKeyedArchiver archivedDataWithRootObject:value
requiringSecureCoding:YES
error:&error];
} else {
data = [NSArchiver archivedDataWithRootObject:value];
}
return data;
}
- (id)transformedValue:(id)value {
if(value == nil) return [NSColor colorWithRed:0 green:0 blue:0 alpha:1.0];
NSError *error;
NSColor *color = nil;
if(@available(macOS 11.0, *)) {
color = (NSColor *)[NSKeyedUnarchiver unarchivedArrayOfObjectsOfClass:[NSColor class]
fromData:value
error:&error];
} else {
if(@available(macOS 10.13, *)) {
NSSet *allowed = [NSSet setWithArray:@[[NSColor class]]];
color = (NSColor *)[NSKeyedUnarchiver unarchivedObjectOfClasses:allowed
fromData:value
error:&error];
} else {
color = (NSColor *)[NSUnarchiver unarchiveObjectWithData:value];
}
}
return color;
}
@end

View File

@ -7,6 +7,7 @@
//
#import "GeneralPreferencesPlugin.h"
#import "ColorToValueTransformer.h"
#import "PathToFileTransformer.h"
@implementation GeneralPreferencesPlugin
@ -15,6 +16,10 @@
NSValueTransformer *pathToFileTransformer = [[PathToFileTransformer alloc] init];
[NSValueTransformer setValueTransformer:pathToFileTransformer
forName:@"PathToFileTransformer"];
NSValueTransformer *colorToValueTransformer = [[ColorToValueTransformer alloc] init];
[NSValueTransformer setValueTransformer:colorToValueTransformer
forName:@"ColorToValueTransformer"];
}
+ (NSArray *)preferencePanes {

View File

@ -22,6 +22,7 @@
837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 837C0D3F1C50954000CAE18F /* MIDIPluginBehaviorArrayController.m */; };
8384917718084D9F00E7332D /* appearance.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917518084D9F00E7332D /* appearance.png */; };
8384917818084D9F00E7332D /* growl.png in Resources */ = {isa = PBXBuildFile; fileRef = 8384917618084D9F00E7332D /* growl.png */; };
83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */; };
83B06729180D85B8008E3612 /* MIDIPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 83B06728180D85B8008E3612 /* MIDIPane.m */; };
83B0672B180D8B39008E3612 /* midi.png in Resources */ = {isa = PBXBuildFile; fileRef = 83B0672A180D8B39008E3612 /* midi.png */; };
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 83EF495E17FBC96A00642E3C /* VolumeBehaviorArrayController.m */; };
@ -105,6 +106,8 @@
8384913618081ECB00E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
8384917518084D9F00E7332D /* appearance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = appearance.png; path = Icons/appearance.png; sourceTree = "<group>"; };
8384917618084D9F00E7332D /* growl.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = growl.png; path = Icons/growl.png; sourceTree = "<group>"; };
83A3B72A283AE04800CC6593 /* ColorToValueTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ColorToValueTransformer.h; sourceTree = "<group>"; };
83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ColorToValueTransformer.m; sourceTree = "<group>"; };
83B06727180D85B8008E3612 /* MIDIPane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MIDIPane.h; sourceTree = "<group>"; };
83B06728180D85B8008E3612 /* MIDIPane.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MIDIPane.m; sourceTree = "<group>"; };
83B0672A180D8B39008E3612 /* midi.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = midi.png; path = Icons/midi.png; sourceTree = "<group>"; };
@ -286,6 +289,8 @@
children = (
83F27E721810E45D00CEF538 /* PathToFileTransformer.h */,
83F27E731810E45D00CEF538 /* PathToFileTransformer.m */,
83A3B72A283AE04800CC6593 /* ColorToValueTransformer.h */,
83A3B72B283AE04800CC6593 /* ColorToValueTransformer.m */,
);
name = Transformers;
sourceTree = "<group>";
@ -454,6 +459,7 @@
8E07AA8A0AAC8EA200A4B32F /* GeneralPreferencesPlugin.m in Sources */,
83EF495F17FBC96A00642E3C /* VolumeBehaviorArrayController.m in Sources */,
17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */,
83A3B72C283AE04800CC6593 /* ColorToValueTransformer.m in Sources */,
837C0D401C50954000CAE18F /* MIDIPluginBehaviorArrayController.m in Sources */,
17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */,
99F1813F0DE01D7A00FD5FFB /* PlaylistBehaviorArrayController.m in Sources */,

View File

@ -11,6 +11,8 @@
#define LOWER_BOUND -80
void *kSpectrumViewContext = &kSpectrumViewContext;
extern NSString *CogPlaybackDidBeginNotficiation;
extern NSString *CogPlaybackDidPauseNotficiation;
extern NSString *CogPlaybackDidResumeNotficiation;
@ -57,6 +59,46 @@ extern NSString *CogPlaybackDidStopNotficiation;
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
context:(void *)context {
if(context == kSpectrumViewContext) {
[self updateControls];
}
}
- (void)updateControls {
BOOL projectionMode = [[NSUserDefaults standardUserDefaults] boolForKey:@"spectrumProjectionMode"];
SCNNode *rootNode = [[self scene] rootNode];
SCNNode *cameraNode = [rootNode childNodeWithName:@"camera" recursively:NO];
SCNCamera *camera = [cameraNode camera];
[camera setUsesOrthographicProjection:projectionMode];
NSValueTransformer *colorToValueTransformer = [NSValueTransformer valueTransformerForName:@"ColorToValueTransformer"];
NSColor *barColor = [colorToValueTransformer transformedValue:[[NSUserDefaults standardUserDefaults] dataForKey:@"spectrumBarColor"]];
NSColor *dotColor = [colorToValueTransformer transformedValue:[[NSUserDefaults standardUserDefaults] dataForKey:@"spectrumDotColor"]];
{
SCNNode *barNode = [rootNode childNodeWithName:@"cylinder0" recursively:NO];
SCNGeometry *geometry = [barNode geometry];
NSArray<SCNMaterial *> *materials = [geometry materials];
SCNMaterial *material = materials[0];
material.diffuse.contents = barColor;
material.emission.contents = barColor;
}
{
SCNNode *dotNode = [rootNode childNodeWithName:@"sphere0" recursively:NO];
SCNGeometry *geometry = [dotNode geometry];
NSArray<SCNMaterial *> *materials = [geometry materials];
SCNMaterial *material = materials[0];
material.diffuse.contents = dotColor;
material.emission.contents = dotColor;
}
}
- (void)setup {
visController = [NSClassFromString(@"VisualizationController") sharedController];
timer = nil;
@ -69,6 +111,8 @@ extern NSString *CogPlaybackDidStopNotficiation;
SCNScene *theScene = [SCNScene sceneNamed:@"Scenes.scnassets/Spectrum.scn"];
[self setScene:theScene];
[self updateControls];
bandsReset = NO;
[self drawBaseBands];
@ -88,6 +132,10 @@ extern NSString *CogPlaybackDidStopNotficiation;
_analyzer.freq_is_log = 0;
_analyzer.mode = freqMode ? DDB_ANALYZER_MODE_FREQUENCIES : DDB_ANALYZER_MODE_OCTAVE_NOTE_BANDS;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.spectrumProjectionMode" options:0 context:kSpectrumViewContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.spectrumBarColor" options:0 context:kSpectrumViewContext];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.spectrumDotColor" options:0 context:kSpectrumViewContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playbackDidBegin:)
name:CogPlaybackDidBeginNotficiation
@ -110,6 +158,10 @@ extern NSString *CogPlaybackDidStopNotficiation;
ddb_analyzer_dealloc(&_analyzer);
ddb_analyzer_draw_data_dealloc(&_draw_data);
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.spectrumProjectionMode"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.spectrumBarColor"];
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.spectrumDotColor"];
[[NSNotificationCenter defaultCenter] removeObserver:self
name:CogPlaybackDidBeginNotficiation
object:nil];