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; }