diff --git a/PlaybackController.h b/PlaybackController.h
index c94b7cebd..ada95f374 100644
--- a/PlaybackController.h
+++ b/PlaybackController.h
@@ -21,6 +21,8 @@
IBOutlet NSButton *playButton;
+ IBOutlet NSArrayController *outputDevices;
+
NSTimer *positionTimer;
BOOL waitingForPlay; //No sneaky changing on us
diff --git a/PlaybackController.m b/PlaybackController.m
index f567d2550..10f0636c8 100644
--- a/PlaybackController.m
+++ b/PlaybackController.m
@@ -26,7 +26,7 @@
[volumeSlider setDoubleValue:pow(10.0, log10(0.5)/4.0)*[volumeSlider maxValue]];
}
-
+
- (IBAction)playPauseResume:(id)sender
{
DBLog(@"PLAYING");
diff --git a/Preferences/General/English.lproj/Preferences.nib/classes.nib b/Preferences/General/English.lproj/Preferences.nib/classes.nib
index 9636e27fd..0d1b71b3f 100644
--- a/Preferences/General/English.lproj/Preferences.nib/classes.nib
+++ b/Preferences/General/English.lproj/Preferences.nib/classes.nib
@@ -26,12 +26,25 @@
SUPERCLASS = PreferencePane;
},
{CLASS = NDHotKeyControl; LANGUAGE = ObjC; SUPERCLASS = NSTextField; },
+ {
+ ACTIONS = {takeDeviceID = id; };
+ CLASS = OutputPane;
+ LANGUAGE = ObjC;
+ OUTLETS = {outputDevices = OutputsArrayController; };
+ SUPERCLASS = PreferencePane;
+ },
+ {
+ CLASS = OutputsArrayController;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSArrayController;
+ },
{
CLASS = PrefPaneController;
LANGUAGE = ObjC;
OUTLETS = {
fileDrawerPane = FileDrawerPane;
hotKeyPane = HotKeyPane;
+ outputPane = OutputPane;
remotePane = RemotePane;
updatesPane = UpdatesPane;
};
diff --git a/Preferences/General/English.lproj/Preferences.nib/info.nib b/Preferences/General/English.lproj/Preferences.nib/info.nib
index eaa7bdd15..5b8344bba 100644
--- a/Preferences/General/English.lproj/Preferences.nib/info.nib
+++ b/Preferences/General/English.lproj/Preferences.nib/info.nib
@@ -3,17 +3,19 @@
IBDocumentLocation
- 102 216 356 240 0 0 1024 746
+ 218 642 356 240 0 0 1680 1028
IBEditorPositions
10
- 259 468 506 102 0 0 1024 746
+ 587 659 506 102 0 0 1680 1028
11
- 375 444 273 151 0 0 1024 746
+ 703 634 273 151 0 0 1680 1028
43
- 166 564 337 96 0 0 1024 746
+ 671 662 337 96 0 0 1680 1028
50
- 233 146 355 96 0 0 1024 746
+ 662 662 355 96 0 0 1680 1028
+ 58
+ 634 659 411 101 0 0 1680 1028
IBFramework Version
446.1
@@ -21,10 +23,11 @@
11
10
+ 58
50
43
IBSystem Version
- 8L127
+ 8L2127
diff --git a/Preferences/General/English.lproj/Preferences.nib/keyedobjects.nib b/Preferences/General/English.lproj/Preferences.nib/keyedobjects.nib
index 2e84930ca..144cb21c9 100644
Binary files a/Preferences/General/English.lproj/Preferences.nib/keyedobjects.nib and b/Preferences/General/English.lproj/Preferences.nib/keyedobjects.nib differ
diff --git a/Preferences/General/General.xcodeproj/project.pbxproj b/Preferences/General/General.xcodeproj/project.pbxproj
index f73a670c2..42e404d72 100644
--- a/Preferences/General/General.xcodeproj/project.pbxproj
+++ b/Preferences/General/General.xcodeproj/project.pbxproj
@@ -9,6 +9,11 @@
/* Begin PBXBuildFile section */
172D72480B891FEF00D095BB /* RemotePane.m in Sources */ = {isa = PBXBuildFile; fileRef = 172D72470B891FEF00D095BB /* RemotePane.m */; };
172D72AD0B8926CA00D095BB /* apple_remote.png in Resources */ = {isa = PBXBuildFile; fileRef = 172D72AC0B8926CA00D095BB /* apple_remote.png */; };
+ 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C643360B8A77CC00C53518 /* OutputsArrayController.m */; };
+ 17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 17C6433E0B8A783F00C53518 /* OutputPane.m */; };
+ 17C643690B8A788000C53518 /* output.png in Resources */ = {isa = PBXBuildFile; fileRef = 17C643680B8A788000C53518 /* output.png */; };
+ 17C644330B8A791D00C53518 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17C644310B8A791D00C53518 /* CoreAudio.framework */; };
+ 17C644340B8A791D00C53518 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 17C644320B8A791D00C53518 /* CoreAudioKit.framework */; };
8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; };
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; };
8E07AA870AAC8EA200A4B32F /* FileDrawerPane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E07AA7F0AAC8EA200A4B32F /* FileDrawerPane.m */; };
@@ -34,6 +39,13 @@
172D72460B891FEF00D095BB /* RemotePane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RemotePane.h; sourceTree = ""; };
172D72470B891FEF00D095BB /* RemotePane.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = RemotePane.m; sourceTree = ""; };
172D72AC0B8926CA00D095BB /* apple_remote.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = apple_remote.png; path = Icons/apple_remote.png; sourceTree = ""; };
+ 17C643360B8A77CC00C53518 /* OutputsArrayController.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputsArrayController.m; sourceTree = ""; };
+ 17C643370B8A77CC00C53518 /* OutputsArrayController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputsArrayController.h; sourceTree = ""; };
+ 17C6433D0B8A783F00C53518 /* OutputPane.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = OutputPane.h; sourceTree = ""; };
+ 17C6433E0B8A783F00C53518 /* OutputPane.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; path = OutputPane.m; sourceTree = ""; };
+ 17C643680B8A788000C53518 /* output.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = output.png; path = Icons/output.png; sourceTree = ""; };
+ 17C644310B8A791D00C53518 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = ""; };
+ 17C644320B8A791D00C53518 /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = /System/Library/Frameworks/CoreAudioKit.framework; sourceTree = ""; };
32DBCF630370AF2F00C91783 /* General_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = General_Prefix.pch; sourceTree = ""; };
8D5B49B6048680CD000E48DA /* General.preferencePane */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = General.preferencePane; sourceTree = BUILT_PRODUCTS_DIR; };
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Info.plist; sourceTree = ""; };
@@ -69,6 +81,8 @@
files = (
8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */,
8E6C123A0AACAEF200819171 /* Carbon.framework in Frameworks */,
+ 17C644330B8A791D00C53518 /* CoreAudio.framework in Frameworks */,
+ 17C644340B8A791D00C53518 /* CoreAudioKit.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -131,6 +145,8 @@
1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = {
isa = PBXGroup;
children = (
+ 17C644310B8A791D00C53518 /* CoreAudio.framework */,
+ 17C644320B8A791D00C53518 /* CoreAudioKit.framework */,
089C167FFE841241C02AAC07 /* AppKit.framework */,
D2F7E65807B2D6F200F64583 /* CoreData.framework */,
089C1672FE841209C02AAC07 /* Foundation.framework */,
@@ -151,6 +167,8 @@
172D72470B891FEF00D095BB /* RemotePane.m */,
8E15A8340B8944C4006DC802 /* UpdatesPane.h */,
8E15A8350B8944C4006DC802 /* UpdatesPane.m */,
+ 17C6433D0B8A783F00C53518 /* OutputPane.h */,
+ 17C6433E0B8A783F00C53518 /* OutputPane.m */,
);
name = Panes;
sourceTree = "";
@@ -164,6 +182,8 @@
8E6C12150AACAE4100819171 /* NDHotKeyEvent.m */,
8E6C139E0AACBAB500819171 /* HotKeyControl.h */,
8E6C139F0AACBAB500819171 /* HotKeyControl.m */,
+ 17C643370B8A77CC00C53518 /* OutputsArrayController.h */,
+ 17C643360B8A77CC00C53518 /* OutputsArrayController.m */,
);
name = Custom;
sourceTree = "";
@@ -187,6 +207,7 @@
8E07ABD90AAC95AF00A4B32F /* Icons */ = {
isa = PBXGroup;
children = (
+ 17C643680B8A788000C53518 /* output.png */,
8E15A86B0B894768006DC802 /* updates.png */,
172D72AC0B8926CA00D095BB /* apple_remote.png */,
8E07ABDA0AAC95BC00A4B32F /* file_drawer.png */,
@@ -242,6 +263,7 @@
8E07AC050AAC968C00A4B32F /* Preferences.nib in Resources */,
172D72AD0B8926CA00D095BB /* apple_remote.png in Resources */,
8E15A86C0B894768006DC802 /* updates.png in Resources */,
+ 17C643690B8A788000C53518 /* output.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -261,6 +283,8 @@
8E6C13A00AACBAB500819171 /* HotKeyControl.m in Sources */,
172D72480B891FEF00D095BB /* RemotePane.m in Sources */,
8E15A8360B8944C4006DC802 /* UpdatesPane.m in Sources */,
+ 17C643380B8A77CC00C53518 /* OutputsArrayController.m in Sources */,
+ 17C6433F0B8A783F00C53518 /* OutputPane.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
diff --git a/Preferences/General/OutputPane.h b/Preferences/General/OutputPane.h
new file mode 100644
index 000000000..d47b2f50d
--- /dev/null
+++ b/Preferences/General/OutputPane.h
@@ -0,0 +1,19 @@
+//
+// OutputPane.h
+// Preferences
+//
+// Created by Vincent Spader on 9/4/06.
+// Copyright 2006 Vincent Spader. All rights reserved.
+//
+
+#import
+#import "PreferencePane.h"
+#import "OutputsArrayController.h"
+
+@interface OutputPane : PreferencePane {
+ IBOutlet OutputsArrayController *outputDevices;
+}
+
+- (IBAction) takeDeviceID:(id)sender;
+
+@end
diff --git a/Preferences/General/OutputPane.m b/Preferences/General/OutputPane.m
new file mode 100644
index 000000000..f0e8d2834
--- /dev/null
+++ b/Preferences/General/OutputPane.m
@@ -0,0 +1,29 @@
+//
+// OutputPane.m
+// Preferences
+//
+// Created by Vincent Spader on 9/4/06.
+// Copyright 2006 Vincent Spader. All rights reserved.
+//
+
+#import "OutputPane.h"
+
+
+@implementation OutputPane
+
+- (void)awakeFromNib
+{
+ NSLog(@"AWOKEN!");
+ [self setName:@"Output"];
+ [self setIcon:@"output"];
+}
+
+- (IBAction) takeDeviceID:(id)sender
+{
+ NSLog(@"Taking thing: %@", [outputDevices selectedObjects]);
+ NSDictionary *device = [[outputDevices selectedObjects] objectAtIndex:0];
+ [[NSUserDefaults standardUserDefaults] setObject: device forKey:@"outputDevice"];
+}
+
+
+@end
diff --git a/Preferences/General/OutputsArrayController.h b/Preferences/General/OutputsArrayController.h
new file mode 100644
index 000000000..ea581f6ae
--- /dev/null
+++ b/Preferences/General/OutputsArrayController.h
@@ -0,0 +1,10 @@
+/* OutputsArrayController */
+
+#import
+
+#import
+
+@interface OutputsArrayController : NSArrayController
+{
+}
+@end
diff --git a/Preferences/General/OutputsArrayController.m b/Preferences/General/OutputsArrayController.m
new file mode 100644
index 000000000..b9426104c
--- /dev/null
+++ b/Preferences/General/OutputsArrayController.m
@@ -0,0 +1,47 @@
+#import "OutputsArrayController.h"
+
+@implementation OutputsArrayController
+
+- (void)awakeFromNib
+{
+ NSLog(@"initOutputDeviceList");
+ [self removeObjects:[self arrangedObjects]];
+
+ [self setSelectsInsertedObjects:NO];
+
+ NSLog(@"OutputCoreAudio.setup()");
+ UInt32 propsize;
+ verify_noerr(AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propsize, NULL));
+ int nDevices = propsize / sizeof(AudioDeviceID);
+ AudioDeviceID *devids = malloc(propsize);
+ verify_noerr(AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propsize, devids));
+ int i;
+ NSLog(@"Number of devices: %d", nDevices);
+ for (i = 0; i < nDevices; ++i) {
+ char name[64];
+ UInt32 maxlen = 64;
+ verify_noerr(AudioDeviceGetProperty(devids[i], 0, false, kAudioDevicePropertyDeviceName, &maxlen, name));
+ NSLog(@"Device: %d %s", devids[i], name);
+
+ // Ignore devices that have no output channels:
+ // This tells us the size of the buffer required to hold the information about the channels
+ UInt32 propSize;
+ verify_noerr(AudioDeviceGetPropertyInfo(devids[i], 0, false, kAudioDevicePropertyStreamConfiguration, &propSize, NULL));
+ // Knowing the size of the required buffer, we can determine how many channels there are
+ // without actually allocating a buffer and requesting the information.
+ // (we don't care about the exact number of channels, only if there are more than zero or not)
+ if (propSize <= sizeof(UInt32)) continue;
+
+ NSObject *deviceInfo = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSString stringWithCString:name], @"name",
+ [NSNumber numberWithLong:devids[i]], @"deviceID",
+ nil];
+ [self addObject:deviceInfo];
+ [deviceInfo release];
+ }
+ free(devids);
+
+ [self setSelectionIndex:0];
+}
+
+@end
diff --git a/Preferences/General/PrefPaneController.h b/Preferences/General/PrefPaneController.h
index c1219e720..38d3407fa 100644
--- a/Preferences/General/PrefPaneController.h
+++ b/Preferences/General/PrefPaneController.h
@@ -13,17 +13,20 @@
#import "FileDrawerPane.h"
#import "RemotePane.h"
#import "UpdatesPane.h"
+#import "OutputPane.h"
@interface PrefPaneController : NSObject {
IBOutlet HotKeyPane *hotKeyPane;
IBOutlet FileDrawerPane *fileDrawerPane;
IBOutlet RemotePane *remotePane;
IBOutlet UpdatesPane *updatesPane;
+ IBOutlet OutputPane *outputPane;
}
- (HotKeyPane *)hotKeyPane;
- (FileDrawerPane *)fileDrawerPane;
- (RemotePane *)remotePane;
- (UpdatesPane *)updatesPane;
+- (UpdatesPane *)outputPane;
@end
diff --git a/Preferences/General/PrefPaneController.m b/Preferences/General/PrefPaneController.m
index 376cb987f..65a49d3a1 100644
--- a/Preferences/General/PrefPaneController.m
+++ b/Preferences/General/PrefPaneController.m
@@ -18,7 +18,7 @@
PrefPaneController *prefPaneController = [[PrefPaneController alloc] init];
loaded = [NSBundle loadNibNamed:@"Preferences" owner:prefPaneController];
- return [NSArray arrayWithObjects: [prefPaneController hotKeyPane], [prefPaneController fileDrawerPane], [prefPaneController remotePane], [prefPaneController updatesPane], nil];
+ return [NSArray arrayWithObjects: [prefPaneController hotKeyPane], [prefPaneController fileDrawerPane], [prefPaneController remotePane], [prefPaneController updatesPane], [prefPaneController outputPane], nil];
}
- (HotKeyPane *)hotKeyPane
@@ -41,4 +41,9 @@
return updatesPane;
}
+- (OutputPane *)outputPane
+{
+ return outputPane;
+}
+
@end
diff --git a/Sound/OutputCoreAudio.h b/Sound/OutputCoreAudio.h
index 2a56b9b52..708888430 100644
--- a/Sound/OutputCoreAudio.h
+++ b/Sound/OutputCoreAudio.h
@@ -23,7 +23,11 @@
- (id)initWithController:(id)c;
- (BOOL)setup;
+- (BOOL)setOutputDevice:(AudioDeviceID)outputDevice;
- (void)start;
+- (void)pause;
+- (void)resume;
+- (void)stop;
- (void)setVolume:(double) v;
diff --git a/Sound/OutputCoreAudio.m b/Sound/OutputCoreAudio.m
index 2b03fde1f..60b3ee273 100644
--- a/Sound/OutputCoreAudio.m
+++ b/Sound/OutputCoreAudio.m
@@ -18,6 +18,8 @@
{
outputController = c;
outputUnit = NULL;
+
+ [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:NULL];
}
return self;
@@ -54,11 +56,65 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
ioData->mBuffers[0].mDataByteSize = amountRead;
return err;
-}
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+ if ([keyPath isEqualToString:@"values.outputDevice"]) {
+ NSLog(@"CHANGED!");
+ NSDictionary *device = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"outputDevice"];
+
+ NSNumber *deviceID = [device objectForKey:@"deviceID"];
+
+ NSLog(@"Selecting output device %d %@", [deviceID longValue], [device objectForKey:@"name"]);
+ [self setOutputDevice:[deviceID longValue]];
+ }
+}
+
+
+
+- (BOOL)setOutputDevice:(AudioDeviceID)outputDevice
+{
+ // Set the output device
+ AudioDeviceID deviceID = outputDevice; //XXX use default if null
+ NSLog(@"WEEE");
+ NSLog(@"Using output device %d", deviceID);
+ OSStatus err;
+
+ if (outputDevice == -1) {
+ UInt32 size = sizeof(AudioDeviceID);
+ err = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+ &size,
+ &deviceID);
+
+ if (err != noErr) {
+ NSLog(@"THERES NO DEFAULT OUTPUT DEVICE! GARRRGGHHH");
+
+ return NO;
+ }
+
+ NSLog(@"Default output device: %i", deviceID);
+ }
+
+
+ err = AudioUnitSetProperty(outputUnit,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &deviceID,
+ sizeof(AudioDeviceID));
+
+ if (err != noErr) {
+ NSLog(@"THERES NO OUTPUT DEVICE! AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!!! %i", err);
+
+ return NO;
+ }
+
+ return YES;
+}
- (BOOL)setup
{
- DBLog(@"SETUP");
if (outputUnit)
[self stop];
@@ -84,6 +140,7 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
if (err != noErr)
return NO;
+ NSLog(@"SETUP");
UInt32 size = sizeof (AudioStreamBasicDescription);
Boolean outWritable;
@@ -138,6 +195,25 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc
DBLog(@"Audio output successfully initialized");
+ NSDictionary *device = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"outputDevice"];
+
+ if (device) {
+ NSLog(@"THIS ONE");
+ BOOL ok = [self setOutputDevice:[[device objectForKey:@"deviceID"] longValue]];
+ if (!ok) {
+ //Ruh roh.
+ [self setOutputDevice: -1];
+
+ [[[NSUserDefaultsController sharedUserDefaultsController] defaults] removeObjectForKey:@"outputDevice"];
+ }
+ }
+ else {
+ NSLog(@"THAT ONE");
+
+ [self setOutputDevice: -1];
+ }
+
+ NSLog(@"DONE SETTING UP");
return (err == noErr);
}
diff --git a/Sound/SoundController.m b/Sound/SoundController.m
index 82dacff63..fb94d0719 100644
--- a/Sound/SoundController.m
+++ b/Sound/SoundController.m
@@ -14,7 +14,7 @@
- (id)initWithDelegate:(id)d
{
DBLog(@"Initializing\n");
-
+
self = [super init];
if (self)
{
@@ -120,7 +120,6 @@
- (void)setNextEntry:(PlaylistEntry *)pe
{
[pe retain];
- NSLog(@"Releasing: %@", [pe display]);
[nextEntry release];
nextEntry = pe;
}