Reformat my own source code with clang-format
Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
361f61618a
commit
85c7073649
397 changed files with 2238946 additions and 210159 deletions
176
.clang-format
Normal file
176
.clang-format
Normal file
|
@ -0,0 +1,176 @@
|
|||
# The style used for all options not specifically set in the configuration.
|
||||
BasedOnStyle: LLVM
|
||||
|
||||
# The extra indent or outdent of access modifiers, e.g. public:.
|
||||
AccessModifierOffset: 0
|
||||
|
||||
# If true, aligns escaped newlines as far left as possible. Otherwise puts them into the right-most column.
|
||||
AlignEscapedNewlinesLeft: false
|
||||
|
||||
# If true, aligns trailing comments.
|
||||
AlignTrailingComments: false
|
||||
|
||||
# Allow putting all parameters of a function declaration onto the next line even if BinPackParameters is false.
|
||||
AllowAllParametersOfDeclarationOnNextLine: false
|
||||
|
||||
# Allows contracting simple braced statements to a single line.
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
|
||||
# If true, short case labels will be contracted to a single line.
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
|
||||
# Dependent on the value, int f() { return 0; } can be put on a single line. Possible values: None, Inline, All.
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
|
||||
# If true, if (a) return; can be put on a single line.
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
|
||||
# If true, while (true) continue; can be put on a single line.
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
|
||||
# If true, always break after function definition return types. More truthfully called ‘break before the identifier following the type in a function definition’.
|
||||
AlwaysBreakAfterDefinitionReturnType: false
|
||||
|
||||
# If true, always break before multiline string literals.
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
|
||||
# If true, always break after the template<...> of a template declaration.
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
|
||||
# If false, a function call’s arguments will either be all on the same line or will have one line each.
|
||||
#BinPackArguments: true
|
||||
|
||||
# If false, a function declaration’s or function definition’s parameters will either all be on the same line or will have one line each.
|
||||
BinPackParameters: true
|
||||
|
||||
# The way to wrap binary operators. Possible values: None, NonAssignment, All.
|
||||
BreakBeforeBinaryOperators: None
|
||||
|
||||
# The brace breaking style to use. Possible values: Attach, Linux, Stroustrup, Allman, GNU.
|
||||
BreakBeforeBraces: Attach
|
||||
|
||||
# If true, ternary operators will be placed after line breaks.
|
||||
BreakBeforeTernaryOperators: false
|
||||
|
||||
# Always break constructor initializers before commas and align the commas with the colon.
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
|
||||
# The column limit. A column limit of 0 means that there is no column limit.
|
||||
ColumnLimit: 0
|
||||
|
||||
# A regular expression that describes comments with special meaning, which should not be split into lines or otherwise changed.
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
|
||||
# If the constructor initializers don’t fit on a line, put each initializer on its own line.
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
|
||||
# The number of characters to use for indentation of constructor initializer lists.
|
||||
ConstructorInitializerIndentWidth: 0
|
||||
|
||||
# Indent width for line continuations.
|
||||
ContinuationIndentWidth: 0
|
||||
|
||||
# If true, format braced lists as best suited for C++11 braced lists.
|
||||
Cpp11BracedListStyle: false
|
||||
|
||||
# If true, analyze the formatted file for the most common alignment of & and *. PointerAlignment is then used only as fallback.
|
||||
DerivePointerAlignment: true
|
||||
|
||||
# Disables formatting at all.
|
||||
DisableFormat: false
|
||||
|
||||
# If true, clang-format detects whether function calls and definitions are formatted with one parameter per line.
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
|
||||
# A vector of macros that should be interpreted as foreach loops instead of as function calls.
|
||||
#ForEachMacros: ''
|
||||
|
||||
# Indent case labels one level from the switch statement. When false, use the same indentation level as for the switch statement. Switch statement body is always indented one level more than case labels.
|
||||
IndentCaseLabels: true
|
||||
|
||||
# The number of columns to use for indentation.
|
||||
IndentWidth: 4
|
||||
|
||||
# Indent if a function definition or declaration is wrapped after the type.
|
||||
IndentWrappedFunctionNames: false
|
||||
|
||||
# If true, empty lines at the start of blocks are kept.
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
|
||||
# Language, this format style is targeted at. Possible values: None, Cpp, Java, JavaScript, Proto.
|
||||
# Language: None
|
||||
|
||||
# The maximum number of consecutive empty lines to keep.
|
||||
MaxEmptyLinesToKeep: 1
|
||||
|
||||
# The indentation used for namespaces. Possible values: None, Inner, All.
|
||||
NamespaceIndentation: All
|
||||
|
||||
# The number of characters to use for indentation of ObjC blocks.
|
||||
ObjCBlockIndentWidth: 4
|
||||
|
||||
# Add a space after @property in Objective-C, i.e. use \@property (readonly) instead of \@property(readonly).
|
||||
ObjCSpaceAfterProperty: false
|
||||
|
||||
# Add a space in front of an Objective-C protocol list, i.e. use Foo <Protocol> instead of Foo<Protocol>.
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
# The penalty for breaking a function call after “call(”.
|
||||
PenaltyBreakBeforeFirstCallParameter: 1000
|
||||
|
||||
# The penalty for each line break introduced inside a comment.
|
||||
PenaltyBreakComment: 1000
|
||||
|
||||
# The penalty for breaking before the first <<.
|
||||
PenaltyBreakFirstLessLess: 1000
|
||||
|
||||
# The penalty for each line break introduced inside a string literal.
|
||||
PenaltyBreakString: 1000
|
||||
|
||||
# The penalty for each character outside of the column limit.
|
||||
PenaltyExcessCharacter: 1000
|
||||
|
||||
# Penalty for putting the return type of a function onto its own line.
|
||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||
|
||||
# Pointer and reference alignment style. Possible values: Left, Right, Middle.
|
||||
PointerAlignment: Left
|
||||
|
||||
# If true, a space may be inserted after C style casts.
|
||||
SpaceAfterCStyleCast: false
|
||||
|
||||
# If false, spaces will be removed before assignment operators.
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
|
||||
# Defines in which cases to put a space before opening parentheses. Possible values: Never, ControlStatements, Always.
|
||||
SpaceBeforeParens: Never
|
||||
|
||||
# If true, spaces may be inserted into ‘()’.
|
||||
SpaceInEmptyParentheses: false
|
||||
|
||||
# The number of spaces before trailing line comments (// - comments).
|
||||
SpacesBeforeTrailingComments: 1
|
||||
|
||||
# If true, spaces will be inserted after ‘<’ and before ‘>’ in template argument lists.
|
||||
SpacesInAngles: false
|
||||
|
||||
# If true, spaces may be inserted into C style casts.
|
||||
SpacesInCStyleCastParentheses: false
|
||||
|
||||
# If true, spaces are inserted inside container literals (e.g. ObjC and Javascript array and dict literals).
|
||||
SpacesInContainerLiterals: false
|
||||
|
||||
# If true, spaces will be inserted after ‘(‘ and before ‘)’.
|
||||
SpacesInParentheses: false
|
||||
|
||||
# If true, spaces will be inserted after ‘[‘ and before ‘]’.
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
# Format compatible with this standard, e.g. use A<A<int> > instead of A<A<int>> for LS_Cpp03. Possible values: Cpp03, Cpp11, Auto.
|
||||
Standard: Auto
|
||||
|
||||
# The number of columns used for tab stops.
|
||||
TabWidth: 4
|
||||
|
||||
# The way to use tab characters in the resulting file. Possible values: Never, ForIndentation, Always.
|
||||
UseTab: ForIndentation
|
|
@ -9,8 +9,7 @@
|
|||
@class PlaylistLoader;
|
||||
@class SUUpdater;
|
||||
|
||||
@interface AppController : NSObject
|
||||
{
|
||||
@interface AppController : NSObject {
|
||||
IBOutlet NSObjectController *currentEntryController;
|
||||
|
||||
IBOutlet PlaybackController *playbackController;
|
||||
|
@ -53,15 +52,13 @@
|
|||
|
||||
NSOperationQueue *queue; // Since we are the app delegate, we take care of the op queue
|
||||
|
||||
NSMutableSet* expandedNodes;
|
||||
NSMutableSet *expandedNodes;
|
||||
|
||||
BOOL miniMode;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@property (strong) IBOutlet NSButton *infoButton;
|
||||
@property (strong) IBOutlet NSButton *infoButtonMini;
|
||||
@property(strong) IBOutlet NSButton *infoButton;
|
||||
@property(strong) IBOutlet NSButton *infoButtonMini;
|
||||
|
||||
- (IBAction)openURL:(id)sender;
|
||||
|
||||
|
@ -78,7 +75,7 @@
|
|||
|
||||
- (void)initDefaults;
|
||||
|
||||
//Fun stuff
|
||||
// Fun stuff
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag;
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename;
|
||||
- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames;
|
||||
|
@ -91,23 +88,23 @@
|
|||
- (void)clickPrev;
|
||||
- (void)clickNext;
|
||||
- (void)clickSpam;
|
||||
- (void)clickSeek: (NSTimeInterval)position;
|
||||
- (void)clickSeek:(NSTimeInterval)position;
|
||||
|
||||
- (IBAction)increaseFontSize:(id)sender;
|
||||
- (IBAction)decreaseFontSize:(id)sender;
|
||||
- (void)changeFontSize:(float)size;
|
||||
|
||||
- (void)nodeExpanded:(NSNotification*)notification;
|
||||
- (void)nodeCollapsed:(NSNotification*)notification;
|
||||
- (void)nodeExpanded:(NSNotification *)notification;
|
||||
- (void)nodeCollapsed:(NSNotification *)notification;
|
||||
|
||||
- (IBAction)toggleMiniMode:(id)sender;
|
||||
- (IBAction)toggleToolbarStyle:(id)sender;
|
||||
|
||||
@property NSWindow * mainWindow;
|
||||
@property NSWindow * miniWindow;
|
||||
@property NSWindow *mainWindow;
|
||||
@property NSWindow *miniWindow;
|
||||
|
||||
@property BOOL miniMode;
|
||||
|
||||
@property (nonatomic) BOOL floatingMiniWindow;
|
||||
@property(nonatomic) BOOL floatingMiniWindow;
|
||||
|
||||
@end
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
#import "AppController.h"
|
||||
#import "Cog-Swift.h"
|
||||
#import "FileTreeController.h"
|
||||
#import "FileTreeViewController.h"
|
||||
#import "FileTreeOutlineView.h"
|
||||
#import "FileTreeViewController.h"
|
||||
#import "FontSizetoLineHeightTransformer.h"
|
||||
#import "OpenURLPanel.h"
|
||||
#import "PathNode.h"
|
||||
#import "PlaybackController.h"
|
||||
#import "PlaylistController.h"
|
||||
#import "PlaylistView.h"
|
||||
#import "PlaylistEntry.h"
|
||||
#import "PlaylistLoader.h"
|
||||
#import "OpenURLPanel.h"
|
||||
#import "PlaylistView.h"
|
||||
#import "SpotlightWindowController.h"
|
||||
#import "StringToURLTransformer.h"
|
||||
#import "FontSizetoLineHeightTransformer.h"
|
||||
#import "Cog-Swift.h"
|
||||
#import "PathNode.h"
|
||||
#import <CogAudio/Status.h>
|
||||
|
||||
#import "DualWindow.h"
|
||||
#import "Logging.h"
|
||||
#import "MiniModeMenuTitleTransformer.h"
|
||||
#import "DualWindow.h"
|
||||
|
||||
#import <MASShortcut/Shortcut.h>
|
||||
#import "Shortcuts.h"
|
||||
#import <MASShortcut/Shortcut.h>
|
||||
|
||||
#import <Sparkle/Sparkle.h>
|
||||
|
||||
void* kAppControllerContext = &kAppControllerContext;
|
||||
|
||||
void *kAppControllerContext = &kAppControllerContext;
|
||||
|
||||
@implementation AppController {
|
||||
BOOL _isFullToolbarStyle;
|
||||
|
@ -34,8 +33,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
@synthesize mainWindow;
|
||||
@synthesize miniWindow;
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
+ (void)initialize {
|
||||
// Register transformers
|
||||
NSValueTransformer *stringToURLTransformer = [[StringToURLTransformer alloc] init];
|
||||
[NSValueTransformer setValueTransformer:stringToURLTransformer
|
||||
|
@ -51,22 +49,18 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
forName:@"MiniModeMenuTitleTransformer"];
|
||||
}
|
||||
|
||||
|
||||
- (id)init
|
||||
{
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
[self initDefaults];
|
||||
|
||||
queue = [[NSOperationQueue alloc]init];
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)openFiles:(id)sender
|
||||
{
|
||||
- (IBAction)openFiles:(id)sender {
|
||||
NSOpenPanel *p;
|
||||
|
||||
p = [NSOpenPanel openPanel];
|
||||
|
@ -76,8 +70,9 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[p setAllowsMultipleSelection:YES];
|
||||
[p setResolvesAliases:YES];
|
||||
|
||||
[p beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) {
|
||||
if ( result == NSModalResponseOK ) {
|
||||
[p beginSheetModalForWindow:mainWindow
|
||||
completionHandler:^(NSInteger result) {
|
||||
if(result == NSModalResponseOK) {
|
||||
[self->playlistLoader willInsertURLs:[p URLs] origin:URLOriginInternal];
|
||||
[self->playlistLoader didInsertURLs:[self->playlistLoader addURLs:[p URLs] sort:YES] origin:URLOriginInternal];
|
||||
} else {
|
||||
|
@ -86,8 +81,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
}];
|
||||
}
|
||||
|
||||
- (IBAction)savePlaylist:(id)sender
|
||||
{
|
||||
- (IBAction)savePlaylist:(id)sender {
|
||||
NSSavePanel *p;
|
||||
|
||||
p = [NSSavePanel savePanel];
|
||||
|
@ -97,8 +91,9 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
* extension if the user does not supply one. */
|
||||
[p setAllowedFileTypes:@[@"m3u", @"pls"]];
|
||||
|
||||
[p beginSheetModalForWindow:mainWindow completionHandler:^(NSInteger result) {
|
||||
if ( result == NSModalResponseOK ) {
|
||||
[p beginSheetModalForWindow:mainWindow
|
||||
completionHandler:^(NSInteger result) {
|
||||
if(result == NSModalResponseOK) {
|
||||
[self->playlistLoader save:[[p URL] path]];
|
||||
} else {
|
||||
[p close];
|
||||
|
@ -106,8 +101,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
}];
|
||||
}
|
||||
|
||||
- (IBAction)openURL:(id)sender
|
||||
{
|
||||
- (IBAction)openURL:(id)sender {
|
||||
OpenURLPanel *p;
|
||||
|
||||
p = [OpenURLPanel openURLPanel];
|
||||
|
@ -115,32 +109,26 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[p beginSheetWithWindow:mainWindow delegate:self didEndSelector:@selector(openURLPanelDidEnd:returnCode:contextInfo:) contextInfo:nil];
|
||||
}
|
||||
|
||||
- (void)openURLPanelDidEnd:(OpenURLPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
if (returnCode == NSModalResponseOK)
|
||||
{
|
||||
- (void)openURLPanelDidEnd:(OpenURLPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
||||
if(returnCode == NSModalResponseOK) {
|
||||
[playlistLoader willInsertURLs:@[[panel url]] origin:URLOriginInternal];
|
||||
[playlistLoader didInsertURLs:[playlistLoader addURLs:@[[panel url]] sort:NO] origin:URLOriginInternal];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)delEntries:(id)sender
|
||||
{
|
||||
- (IBAction)delEntries:(id)sender {
|
||||
[playlistController remove:self];
|
||||
}
|
||||
|
||||
- (PlaylistEntry *)currentEntry
|
||||
{
|
||||
- (PlaylistEntry *)currentEntry {
|
||||
return [playlistController currentEntry];
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key
|
||||
{
|
||||
- (BOOL)application:(NSApplication *)sender delegateHandlesKey:(NSString *)key {
|
||||
return [key isEqualToString:@"currentEntry"] || [key isEqualToString:@"play"];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
- (void)awakeFromNib {
|
||||
#ifdef DEBUG
|
||||
// Prevent updates automatically in debug builds
|
||||
[updater setAutomaticallyChecksForUpdates:NO];
|
||||
|
@ -157,7 +145,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
[self registerHotKeys];
|
||||
|
||||
(void) [spotlightWindowController init];
|
||||
(void)[spotlightWindowController init];
|
||||
|
||||
[[playlistController undoManager] disableUndoRegistration];
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
|
@ -168,28 +156,21 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
NSString *oldFilename = @"Default.m3u";
|
||||
NSString *newFilename = @"Default.xml";
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:dbFilename]])
|
||||
{
|
||||
if([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:dbFilename]]) {
|
||||
[playlistLoader addDatabase];
|
||||
}
|
||||
else if ([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]])
|
||||
{
|
||||
} else if([[NSFileManager defaultManager] fileExistsAtPath:[basePath stringByAppendingPathComponent:newFilename]]) {
|
||||
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:newFilename]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[playlistLoader addURL:[NSURL fileURLWithPath:[basePath stringByAppendingPathComponent:oldFilename]]];
|
||||
}
|
||||
|
||||
[[playlistController undoManager] enableUndoRegistration];
|
||||
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"resumePlaybackOnStartup"])
|
||||
{
|
||||
int lastStatus = (int) [[NSUserDefaults standardUserDefaults] integerForKey:@"lastPlaybackStatus"];
|
||||
int lastIndex = (int) [[NSUserDefaults standardUserDefaults] integerForKey:@"lastTrackPlaying"];
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"resumePlaybackOnStartup"]) {
|
||||
int lastStatus = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"lastPlaybackStatus"];
|
||||
int lastIndex = (int)[[NSUserDefaults standardUserDefaults] integerForKey:@"lastTrackPlaying"];
|
||||
|
||||
if (lastStatus != CogStatusStopped && lastIndex >= 0)
|
||||
{
|
||||
if(lastStatus != CogStatusStopped && lastIndex >= 0) {
|
||||
[playbackController playEntryAtIndex:lastIndex startPaused:(lastStatus == CogStatusPaused) andSeekTo:[NSNumber numberWithDouble:[[NSUserDefaults standardUserDefaults] floatForKey:@"lastTrackPosition"]]];
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +191,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
// but i'm too stupid/tired to figure it out now
|
||||
[fileTreeViewController view];
|
||||
|
||||
FileTreeOutlineView* outlineView = [fileTreeViewController outlineView];
|
||||
FileTreeOutlineView *outlineView = [fileTreeViewController outlineView];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeExpanded:) name:NSOutlineViewItemDidExpandNotification object:outlineView];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nodeCollapsed:) name:NSOutlineViewItemDidCollapseNotification object:outlineView];
|
||||
|
||||
|
@ -221,12 +202,9 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
NSArray *expandedNodesArray = [[NSUserDefaults standardUserDefaults] valueForKey:@"fileTreeViewExpandedNodes"];
|
||||
|
||||
if (expandedNodesArray)
|
||||
{
|
||||
if(expandedNodesArray) {
|
||||
expandedNodes = [[NSMutableSet alloc] initWithArray:expandedNodesArray];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
expandedNodes = [[NSMutableSet alloc] init];
|
||||
}
|
||||
|
||||
|
@ -234,20 +212,17 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
DLog(@"Num of rows: %ld", [outlineView numberOfRows]);
|
||||
|
||||
if (!outlineView)
|
||||
{
|
||||
if(!outlineView) {
|
||||
DLog(@"outlineView is NULL!");
|
||||
}
|
||||
|
||||
[outlineView reloadData];
|
||||
|
||||
for (NSInteger i=0; i<[outlineView numberOfRows]; i++)
|
||||
{
|
||||
for(NSInteger i = 0; i < [outlineView numberOfRows]; i++) {
|
||||
PathNode *pn = [outlineView itemAtRow:i];
|
||||
NSString *str = [[pn URL] absoluteString];
|
||||
|
||||
if ([expandedNodes containsObject:str])
|
||||
{
|
||||
if([expandedNodes containsObject:str]) {
|
||||
[outlineView expandItem:pn];
|
||||
}
|
||||
}
|
||||
|
@ -260,18 +235,18 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary<NSKeyValueChangeKey,id> *)change
|
||||
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
|
||||
context:(void *)context {
|
||||
if (context != kAppControllerContext) {
|
||||
if(context != kAppControllerContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([keyPath isEqualToString:@"playlistController.currentEntry"]) {
|
||||
if([keyPath isEqualToString:@"playlistController.currentEntry"]) {
|
||||
PlaylistEntry *entry = playlistController.currentEntry;
|
||||
if (!entry) {
|
||||
if(!entry) {
|
||||
miniWindow.title = @"Cog";
|
||||
mainWindow.title = @"Cog";
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if(@available(macOS 11.0, *)) {
|
||||
miniWindow.subtitle = @"";
|
||||
mainWindow.subtitle = @"";
|
||||
}
|
||||
|
@ -282,7 +257,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
self.infoButtonMini.image = [NSImage imageNamed:@"infoTemplate"];
|
||||
}
|
||||
|
||||
if (entry.albumArt) {
|
||||
if(entry.albumArt) {
|
||||
self.infoButton.imageScaling = NSImageScaleProportionallyUpOrDown;
|
||||
self.infoButton.image = playlistController.currentEntry.albumArt;
|
||||
self.infoButtonMini.imageScaling = NSImageScaleProportionallyUpOrDown;
|
||||
|
@ -294,9 +269,9 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
self.infoButtonMini.image = [NSImage imageNamed:@"infoTemplate"];
|
||||
}
|
||||
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if(@available(macOS 11.0, *)) {
|
||||
NSString *title = @"Cog";
|
||||
if (entry.title) {
|
||||
if(entry.title) {
|
||||
title = entry.title;
|
||||
}
|
||||
miniWindow.title = title;
|
||||
|
@ -304,14 +279,14 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
NSString *subtitle = @"";
|
||||
NSMutableArray<NSString *> *subtitleItems = [NSMutableArray array];
|
||||
if (entry.album && ![entry.album isEqualToString:@""]) {
|
||||
if(entry.album && ![entry.album isEqualToString:@""]) {
|
||||
[subtitleItems addObject:entry.album];
|
||||
}
|
||||
if (entry.artist && ![entry.artist isEqualToString:@""]) {
|
||||
if(entry.artist && ![entry.artist isEqualToString:@""]) {
|
||||
[subtitleItems addObject:entry.artist];
|
||||
}
|
||||
|
||||
if ([subtitleItems count]) {
|
||||
if([subtitleItems count]) {
|
||||
subtitle = [subtitleItems componentsJoinedByString:@" - "];
|
||||
}
|
||||
|
||||
|
@ -319,7 +294,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
mainWindow.subtitle = subtitle;
|
||||
} else {
|
||||
NSString *title = @"Cog";
|
||||
if (entry.display) {
|
||||
if(entry.display) {
|
||||
title = entry.display;
|
||||
}
|
||||
miniWindow.title = title;
|
||||
|
@ -328,34 +303,30 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)nodeExpanded:(NSNotification*)notification
|
||||
{
|
||||
PathNode* node = [[notification userInfo] objectForKey:@"NSObject"];
|
||||
NSString* url = [[node URL] absoluteString];
|
||||
- (void)nodeExpanded:(NSNotification *)notification {
|
||||
PathNode *node = [[notification userInfo] objectForKey:@"NSObject"];
|
||||
NSString *url = [[node URL] absoluteString];
|
||||
|
||||
[expandedNodes addObject:url];
|
||||
}
|
||||
|
||||
- (void)nodeCollapsed:(NSNotification*)notification
|
||||
{
|
||||
PathNode* node = [[notification userInfo] objectForKey:@"NSObject"];
|
||||
NSString* url = [[node URL] absoluteString];
|
||||
- (void)nodeCollapsed:(NSNotification *)notification {
|
||||
PathNode *node = [[notification userInfo] objectForKey:@"NSObject"];
|
||||
NSString *url = [[node URL] absoluteString];
|
||||
|
||||
[expandedNodes removeObject:url];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification {
|
||||
CogStatus currentStatus = [playbackController playbackStatus];
|
||||
NSInteger lastTrackPlaying = -1;
|
||||
double lastTrackPosition = 0;
|
||||
|
||||
if (currentStatus == CogStatusStopping)
|
||||
if(currentStatus == CogStatusStopping)
|
||||
currentStatus = CogStatusStopped;
|
||||
|
||||
if (currentStatus != CogStatusStopped)
|
||||
{
|
||||
PlaylistEntry * pe = [playlistController currentEntry];
|
||||
if(currentStatus != CogStatusStopped) {
|
||||
PlaylistEntry *pe = [playlistController currentEntry];
|
||||
lastTrackPlaying = [pe index];
|
||||
lastTrackPosition = [pe currentPosition];
|
||||
}
|
||||
|
@ -371,14 +342,13 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *folder = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
||||
|
||||
if ([fileManager fileExistsAtPath: folder] == NO)
|
||||
{
|
||||
[fileManager createDirectoryAtPath: folder withIntermediateDirectories:NO attributes:nil error:nil];
|
||||
if([fileManager fileExistsAtPath:folder] == NO) {
|
||||
[fileManager createDirectoryAtPath:folder withIntermediateDirectories:NO attributes:nil error:nil];
|
||||
}
|
||||
|
||||
[playlistController clearFilterPredicate:self];
|
||||
|
||||
NSString * fileName = @"Default.xml";
|
||||
NSString *fileName = @"Default.xml";
|
||||
|
||||
NSError *error;
|
||||
[[NSFileManager defaultManager] removeItemAtPath:[folder stringByAppendingPathComponent:fileName] error:&error];
|
||||
|
@ -395,33 +365,29 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[miniWindow saveFrameUsingName:@"Mini Window"];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
|
||||
{
|
||||
if (flag == NO)
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag {
|
||||
if(flag == NO)
|
||||
[mainWindow makeKeyAndOrderFront:self]; // TODO: do we really need this? We never close the main window.
|
||||
|
||||
for(NSWindow* win in [NSApp windows]) // Maximizing all windows
|
||||
for(NSWindow *win in [NSApp windows]) // Maximizing all windows
|
||||
if([win isMiniaturized])
|
||||
[win deminiaturize:self];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
|
||||
{
|
||||
NSArray* urls = @[[NSURL fileURLWithPath:filename]];
|
||||
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
|
||||
NSArray *urls = @[[NSURL fileURLWithPath:filename]];
|
||||
[playlistLoader willInsertURLs:urls origin:URLOriginExternal];
|
||||
[playlistLoader didInsertURLs:[playlistLoader addURLs:urls sort:NO] origin:URLOriginExternal];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames
|
||||
{
|
||||
//Need to convert to urls
|
||||
- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)filenames {
|
||||
// Need to convert to urls
|
||||
NSMutableArray *urls = [NSMutableArray array];
|
||||
|
||||
for (NSString *filename in filenames)
|
||||
{
|
||||
for(NSString *filename in filenames) {
|
||||
[urls addObject:[NSURL fileURLWithPath:filename]];
|
||||
}
|
||||
[playlistLoader willInsertURLs:urls origin:URLOriginExternal];
|
||||
|
@ -429,34 +395,30 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[theApplication replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
|
||||
}
|
||||
|
||||
- (IBAction)openLiberapayPage:(id)sender
|
||||
{
|
||||
- (IBAction)openLiberapayPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://liberapay.com/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)openPaypalPage:(id)sender
|
||||
{
|
||||
- (IBAction)openPaypalPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.paypal.com/paypalme/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)openKofiPage:(id)sender
|
||||
{
|
||||
- (IBAction)openKofiPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://ko-fi.com/kode54"]];
|
||||
}
|
||||
|
||||
- (IBAction)openPatreonPage:(id)sender
|
||||
{
|
||||
- (IBAction)openPatreonPage:(id)sender {
|
||||
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.patreon.com/kode54"]];
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)feedback:(id)sender
|
||||
{
|
||||
- (IBAction)feedback:(id)sender {
|
||||
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
|
||||
|
||||
NSArray<NSURLQueryItem *> *query = @[
|
||||
[NSURLQueryItem queryItemWithName:@"labels" value:@"bug"],
|
||||
[NSURLQueryItem queryItemWithName:@"template" value:@"bug_report.md"],
|
||||
[NSURLQueryItem queryItemWithName:@"labels"
|
||||
value:@"bug"],
|
||||
[NSURLQueryItem queryItemWithName:@"template"
|
||||
value:@"bug_report.md"],
|
||||
[NSURLQueryItem queryItemWithName:@"title"
|
||||
value:[NSString stringWithFormat:@"[Cog %@] ", version]]
|
||||
];
|
||||
|
@ -468,8 +430,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[[NSWorkspace sharedWorkspace] openURL:components.URL];
|
||||
}
|
||||
|
||||
- (void)initDefaults
|
||||
{
|
||||
- (void)initDefaults {
|
||||
NSMutableDictionary *userDefaultsValuesDict = [NSMutableDictionary dictionary];
|
||||
|
||||
// Font defaults
|
||||
|
@ -497,11 +458,11 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
|
||||
[userDefaultsValuesDict setObject:[NSNumber numberWithBool:NO] forKey:@"resumePlaybackOnStartup"];
|
||||
|
||||
//Register and sync defaults
|
||||
// Register and sync defaults
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
//And if the existing feed URL is broken due to my ineptitude with the above defaults, fix it
|
||||
// And if the existing feed URL is broken due to my ineptitude with the above defaults, fix it
|
||||
NSSet<NSString *> *brokenFeedURLs = [NSSet setWithObjects:
|
||||
@"https://kode54.net/cog/stable.xml",
|
||||
@"https://kode54.net/cog/mercury.xml"
|
||||
|
@ -509,108 +470,97 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
@"https://f.losno.co/cog/mercury.xml",
|
||||
nil];
|
||||
NSString *feedURL = [[NSUserDefaults standardUserDefaults] stringForKey:@"SUFeedURL"];
|
||||
if ([brokenFeedURLs containsObject:feedURL]) {
|
||||
if([brokenFeedURLs containsObject:feedURL]) {
|
||||
[[NSUserDefaults standardUserDefaults] setValue:feedURLdefault forKey:@"SUFeedURL"];
|
||||
}
|
||||
|
||||
if ([[[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"] isEqualToString:@"BASSMIDI"]) {
|
||||
if([[[NSUserDefaults standardUserDefaults] stringForKey:@"midiPlugin"] isEqualToString:@"BASSMIDI"]) {
|
||||
[[NSUserDefaults standardUserDefaults] setValue:@"FluidSynth" forKey:@"midiPlugin"];
|
||||
}
|
||||
|
||||
NSString * oldMidiPlugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"];
|
||||
if (oldMidiPlugin) {
|
||||
NSString *oldMidiPlugin = [[NSUserDefaults standardUserDefaults] stringForKey:@"midi.plugin"];
|
||||
if(oldMidiPlugin) {
|
||||
[[NSUserDefaults standardUserDefaults] setValue:oldMidiPlugin forKey:@"midiPlugin"];
|
||||
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"midi.plugin"];
|
||||
}
|
||||
}
|
||||
|
||||
/* Unassign previous handler first, so dealloc can unregister it from the global map before the new instances are assigned */
|
||||
- (void)registerHotKeys
|
||||
{
|
||||
- (void)registerHotKeys {
|
||||
MASShortcutBinder *binder = [MASShortcutBinder sharedBinder];
|
||||
[binder bindShortcutWithDefaultsKey:CogPlayShortcutKey toAction:^{
|
||||
[binder bindShortcutWithDefaultsKey:CogPlayShortcutKey
|
||||
toAction:^{
|
||||
[self clickPlay];
|
||||
}];
|
||||
|
||||
[binder bindShortcutWithDefaultsKey:CogNextShortcutKey toAction:^{
|
||||
[binder bindShortcutWithDefaultsKey:CogNextShortcutKey
|
||||
toAction:^{
|
||||
[self clickNext];
|
||||
}];
|
||||
|
||||
[binder bindShortcutWithDefaultsKey:CogPrevShortcutKey toAction:^{
|
||||
[binder bindShortcutWithDefaultsKey:CogPrevShortcutKey
|
||||
toAction:^{
|
||||
[self clickPrev];
|
||||
}];
|
||||
|
||||
[binder bindShortcutWithDefaultsKey:CogSpamShortcutKey toAction:^{
|
||||
[binder bindShortcutWithDefaultsKey:CogSpamShortcutKey
|
||||
toAction:^{
|
||||
[self clickSpam];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)clickPlay
|
||||
{
|
||||
- (void)clickPlay {
|
||||
[playbackController playPauseResume:self];
|
||||
}
|
||||
|
||||
- (void)clickPause
|
||||
{
|
||||
- (void)clickPause {
|
||||
[playbackController pause:self];
|
||||
}
|
||||
|
||||
- (void)clickStop
|
||||
{
|
||||
- (void)clickStop {
|
||||
[playbackController stop:self];
|
||||
}
|
||||
|
||||
- (void)clickPrev
|
||||
{
|
||||
- (void)clickPrev {
|
||||
[playbackController prev:nil];
|
||||
}
|
||||
|
||||
- (void)clickNext
|
||||
{
|
||||
- (void)clickNext {
|
||||
[playbackController next:nil];
|
||||
}
|
||||
|
||||
- (void)clickSpam
|
||||
{
|
||||
- (void)clickSpam {
|
||||
[playbackController spam:nil];
|
||||
}
|
||||
|
||||
- (void)clickSeek:(NSTimeInterval)position
|
||||
{
|
||||
- (void)clickSeek:(NSTimeInterval)position {
|
||||
[playbackController seek:self toTime:position];
|
||||
}
|
||||
|
||||
- (void)changeFontSize:(float)size
|
||||
{
|
||||
- (void)changeFontSize:(float)size {
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
float fCurrentSize = [defaults floatForKey:@"fontSize"];
|
||||
NSNumber *newSize = [NSNumber numberWithFloat:(fCurrentSize + size)];
|
||||
[defaults setObject:newSize forKey:@"fontSize"];
|
||||
}
|
||||
|
||||
- (IBAction)increaseFontSize:(id)sender
|
||||
{
|
||||
- (IBAction)increaseFontSize:(id)sender {
|
||||
[self changeFontSize:1];
|
||||
}
|
||||
|
||||
- (IBAction)decreaseFontSize:(id)sender
|
||||
{
|
||||
- (IBAction)decreaseFontSize:(id)sender {
|
||||
[self changeFontSize:-1];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)toggleMiniMode:(id)sender
|
||||
{
|
||||
- (IBAction)toggleMiniMode:(id)sender {
|
||||
[self setMiniMode:(!miniMode)];
|
||||
}
|
||||
|
||||
- (BOOL)miniMode
|
||||
{
|
||||
- (BOOL)miniMode {
|
||||
return miniMode;
|
||||
}
|
||||
|
||||
- (void)setMiniMode:(BOOL)newMiniMode
|
||||
{
|
||||
- (void)setMiniMode:(BOOL)newMiniMode {
|
||||
miniMode = newMiniMode;
|
||||
[[NSUserDefaults standardUserDefaults] setBool:miniMode forKey:@"miniMode"];
|
||||
|
||||
|
@ -629,7 +579,7 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[[NSUserDefaults standardUserDefaults] setBool:full forKey:@"toolbarStyleFull"];
|
||||
DLog("Changed toolbar style: %@", (full ? @"full" : @"compact"));
|
||||
|
||||
if (@available(macOS 11.0, *)) {
|
||||
if(@available(macOS 11.0, *)) {
|
||||
NSWindowToolbarStyle style =
|
||||
full ? NSWindowToolbarStyleExpanded : NSWindowToolbarStyleUnified;
|
||||
mainWindow.toolbarStyle = style;
|
||||
|
@ -651,19 +601,17 @@ void* kAppControllerContext = &kAppControllerContext;
|
|||
[miniWindow setLevel:level];
|
||||
}
|
||||
|
||||
- (void)updateDockMenu:(NSNotification *)notification
|
||||
{
|
||||
- (void)updateDockMenu:(NSNotification *)notification {
|
||||
PlaylistEntry *pe = [playlistController currentEntry];
|
||||
|
||||
BOOL hideItem = NO;
|
||||
|
||||
if ([[notification name] isEqualToString:CogPlaybackDidStopNotficiation] || !pe || ![pe artist] || [[pe artist] isEqualToString:@""])
|
||||
if([[notification name] isEqualToString:CogPlaybackDidStopNotficiation] || !pe || ![pe artist] || [[pe artist] isEqualToString:@""])
|
||||
hideItem = YES;
|
||||
|
||||
if (hideItem && [dockMenu indexOfItem:currentArtistItem] == 0) {
|
||||
if(hideItem && [dockMenu indexOfItem:currentArtistItem] == 0) {
|
||||
[dockMenu removeItem:currentArtistItem];
|
||||
}
|
||||
else if (!hideItem && [dockMenu indexOfItem:currentArtistItem] < 0) {
|
||||
} else if(!hideItem && [dockMenu indexOfItem:currentArtistItem] < 0) {
|
||||
[dockMenu insertItem:currentArtistItem atIndex:0];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,58 +7,49 @@
|
|||
//
|
||||
|
||||
#import "DockIconController.h"
|
||||
#import <CogAudio/Status.h>
|
||||
#import "PlaybackController.h"
|
||||
#import <CogAudio/Status.h>
|
||||
|
||||
@implementation DockIconController
|
||||
|
||||
static NSString *DockIconPlaybackStatusObservationContext = @"DockIconPlaybackStatusObservationContext";
|
||||
|
||||
- (void)startObserving
|
||||
{
|
||||
[playbackController addObserver:self forKeyPath:@"playbackStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void * _Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[playbackController addObserver:self forKeyPath:@"progressBarStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void * _Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.colorfulDockIcons" options:0 context:(__bridge void * _Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
- (void)startObserving {
|
||||
[playbackController addObserver:self forKeyPath:@"playbackStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[playbackController addObserver:self forKeyPath:@"progressBarStatus" options:(NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial) context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.colorfulDockIcons" options:0 context:(__bridge void *_Nullable)(DockIconPlaybackStatusObservationContext)];
|
||||
}
|
||||
|
||||
- (void)stopObserving
|
||||
{
|
||||
- (void)stopObserving {
|
||||
[playbackController removeObserver:self forKeyPath:@"playbackStatus"];
|
||||
[playbackController removeObserver:self forKeyPath:@"progressBarStatus"];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.colorfulDockIcons"];
|
||||
}
|
||||
|
||||
static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
|
||||
{
|
||||
if (colorfulIcons)
|
||||
{
|
||||
static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons) {
|
||||
if(colorfulIcons) {
|
||||
return [baseName stringByAppendingString:@"Colorful"];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return baseName;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)refreshDockIcon:(NSInteger)playbackStatus withProgress:(double)progressStatus
|
||||
{
|
||||
- (void)refreshDockIcon:(NSInteger)playbackStatus withProgress:(double)progressStatus {
|
||||
BOOL displayChanged = NO;
|
||||
BOOL drawIcon = NO;
|
||||
BOOL removeProgress = NO;
|
||||
|
||||
if ( playbackStatus < 0 )
|
||||
if(playbackStatus < 0)
|
||||
playbackStatus = lastPlaybackStatus;
|
||||
else
|
||||
{
|
||||
else {
|
||||
lastPlaybackStatus = playbackStatus;
|
||||
drawIcon = YES;
|
||||
}
|
||||
|
||||
if ( progressStatus < -2 )
|
||||
if(progressStatus < -2)
|
||||
progressStatus = [lastProgressStatus doubleValue];
|
||||
else
|
||||
{
|
||||
if (progressStatus < 0 && [lastProgressStatus doubleValue] >= 0)
|
||||
else {
|
||||
if(progressStatus < 0 && [lastProgressStatus doubleValue] >= 0)
|
||||
removeProgress = YES;
|
||||
lastProgressStatus = [NSNumber numberWithDouble:progressStatus];
|
||||
}
|
||||
|
@ -69,18 +60,16 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
|
|||
|
||||
BOOL colorfulIcons = [[NSUserDefaults standardUserDefaults] boolForKey:@"colorfulDockIcons"];
|
||||
|
||||
if ((colorfulIcons && lastColorfulStatus < 1) ||
|
||||
(!colorfulIcons && lastColorfulStatus != 0))
|
||||
{
|
||||
if((colorfulIcons && lastColorfulStatus < 1) ||
|
||||
(!colorfulIcons && lastColorfulStatus != 0)) {
|
||||
lastColorfulStatus = colorfulIcons ? 1 : 0;
|
||||
drawIcon = YES;
|
||||
}
|
||||
|
||||
NSDockTile *dockTile = [NSApp dockTile];
|
||||
|
||||
if (drawIcon)
|
||||
{
|
||||
switch (playbackStatus) {
|
||||
if(drawIcon) {
|
||||
switch(playbackStatus) {
|
||||
case CogStatusPlaying:
|
||||
badgeImage = [NSImage imageNamed:getBadgeName(@"playDockBadge", colorfulIcons)];
|
||||
break;
|
||||
|
@ -100,7 +89,8 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
|
|||
|
||||
[badgeImage drawInRect:NSMakeRect(0, 0, 128, 128)
|
||||
fromRect:NSMakeRect(0, 0, badgeSize.width, badgeSize.height)
|
||||
operation:NSCompositingOperationSourceOver fraction:1.0];
|
||||
operation:NSCompositingOperationSourceOver
|
||||
fraction:1.0];
|
||||
|
||||
[newDockImage unlockFocus];
|
||||
|
||||
|
@ -121,17 +111,14 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
|
|||
displayChanged = YES;
|
||||
}
|
||||
|
||||
if (displayProgress)
|
||||
{
|
||||
if (!imageView)
|
||||
{
|
||||
if(displayProgress) {
|
||||
if(!imageView) {
|
||||
imageView = [[NSImageView alloc] init];
|
||||
[imageView setImage:[NSApp applicationIconImage]];
|
||||
[dockTile setContentView:imageView];
|
||||
}
|
||||
|
||||
if (!progressIndicator)
|
||||
{
|
||||
if(!progressIndicator) {
|
||||
progressIndicator = [[NSProgressIndicator alloc] initWithFrame:NSMakeRect(0.0, 0.0, dockTile.size.width, 10.0)];
|
||||
[progressIndicator setIndeterminate:NO];
|
||||
[progressIndicator setBezeled:YES];
|
||||
|
@ -147,47 +134,36 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
|
|||
displayChanged = YES;
|
||||
}
|
||||
|
||||
if (removeProgress)
|
||||
{
|
||||
if (progressIndicator)
|
||||
if(removeProgress) {
|
||||
if(progressIndicator)
|
||||
[progressIndicator setHidden:YES];
|
||||
|
||||
displayChanged = YES;
|
||||
}
|
||||
|
||||
if (displayChanged)
|
||||
if(displayChanged)
|
||||
[dockTile display];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
if ([DockIconPlaybackStatusObservationContext isEqual:(__bridge id)(context)])
|
||||
{
|
||||
if ([keyPath isEqualToString:@"playbackStatus"])
|
||||
{
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if([DockIconPlaybackStatusObservationContext isEqual:(__bridge id)(context)]) {
|
||||
if([keyPath isEqualToString:@"playbackStatus"]) {
|
||||
NSInteger playbackStatus = [[change objectForKey:NSKeyValueChangeNewKey] integerValue];
|
||||
|
||||
[self refreshDockIcon:playbackStatus withProgress:-10];
|
||||
}
|
||||
else if ([keyPath isEqualToString:@"progressBarStatus"])
|
||||
{
|
||||
} else if([keyPath isEqualToString:@"progressBarStatus"]) {
|
||||
double progressStatus = [[change objectForKey:NSKeyValueChangeNewKey] doubleValue];
|
||||
|
||||
[self refreshDockIcon:-1 withProgress:progressStatus];
|
||||
}
|
||||
else if ([keyPath isEqualToString:@"values.colorfulDockIcons"])
|
||||
{
|
||||
} else if([keyPath isEqualToString:@"values.colorfulDockIcons"]) {
|
||||
[self refreshDockIcon:-1 withProgress:-10];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
- (void)awakeFromNib {
|
||||
dockImage = [[NSImage imageNamed:@"icon_blank"] copy];
|
||||
lastColorfulStatus = -1;
|
||||
lastProgressStatus = [NSNumber numberWithDouble:-1];
|
||||
|
@ -196,8 +172,7 @@ static NSString *getBadgeName(NSString *baseName, BOOL colorfulIcons)
|
|||
[self startObserving];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[self stopObserving];
|
||||
}
|
||||
|
||||
|
|
|
@ -11,4 +11,3 @@
|
|||
@interface MediaKeysApplication : NSApplication
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
#import "AppController.h"
|
||||
#import "Logging.h"
|
||||
|
||||
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommandCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommand.h>
|
||||
#import <MediaPlayer/MPMediaItem.h>
|
||||
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommand.h>
|
||||
#import <MediaPlayer/MPRemoteCommandCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommandEvent.h>
|
||||
|
||||
@implementation MediaKeysApplication {
|
||||
|
@ -68,10 +68,9 @@
|
|||
return MPRemoteCommandHandlerStatusSuccess;
|
||||
}
|
||||
|
||||
- (MPRemoteCommandHandlerStatus)clickSeek: (MPChangePlaybackPositionCommandEvent*)event {
|
||||
- (MPRemoteCommandHandlerStatus)clickSeek:(MPChangePlaybackPositionCommandEvent *)event {
|
||||
[_appController clickSeek:event.positionTime];
|
||||
return MPRemoteCommandHandlerStatusSuccess;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "AppController.h"
|
||||
#import "AudioScrobbler.h"
|
||||
#import "CogAudio/AudioPlayer.h"
|
||||
#import "CogAudio/Status.h"
|
||||
#import "TrackingSlider.h"
|
||||
#import "AudioScrobbler.h"
|
||||
#import "AppController.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreAudio/CoreAudioTypes.h>
|
||||
|
||||
#import "AUPlayerView.h"
|
||||
|
@ -23,14 +23,13 @@ extern NSString *CogPlaybackDidPauseNotficiation;
|
|||
extern NSString *CogPlaybackDidResumeNotficiation;
|
||||
extern NSString *CogPlaybackDidStopNotficiation;
|
||||
|
||||
extern NSDictionary * makeRGInfo(PlaylistEntry *pe);
|
||||
extern NSDictionary *makeRGInfo(PlaylistEntry *pe);
|
||||
|
||||
@class PlaylistController;
|
||||
@class PlaylistView;
|
||||
@class PlaylistLoader;
|
||||
|
||||
@interface PlaybackController : NSObject
|
||||
{
|
||||
@interface PlaybackController : NSObject {
|
||||
IBOutlet AppController *appController;
|
||||
|
||||
IBOutlet PlaylistController *playlistController;
|
||||
|
@ -58,7 +57,7 @@ extern NSDictionary * makeRGInfo(PlaylistEntry *pe);
|
|||
BOOL _eqStubbed;
|
||||
AudioUnit _eq;
|
||||
AUPluginUI *_equi;
|
||||
}
|
||||
}
|
||||
|
||||
@property CogStatus playbackStatus;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#import "PlaybackController.h"
|
||||
#import "CogAudio/Helper.h"
|
||||
#import "CogAudio/Status.h"
|
||||
#import "PlaylistView.h"
|
||||
#import <Foundation/NSTimer.h>
|
||||
#import "CogAudio/Status.h"
|
||||
#import "CogAudio/Helper.h"
|
||||
|
||||
#import "PlaylistController.h"
|
||||
#import "PlaylistEntry.h"
|
||||
|
@ -11,10 +11,10 @@
|
|||
#import "MainWindow.h"
|
||||
#import "MiniWindow.h"
|
||||
|
||||
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommandCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommand.h>
|
||||
#import <MediaPlayer/MPMediaItem.h>
|
||||
#import <MediaPlayer/MPNowPlayingInfoCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommand.h>
|
||||
#import <MediaPlayer/MPRemoteCommandCenter.h>
|
||||
#import <MediaPlayer/MPRemoteCommandEvent.h>
|
||||
|
||||
#import "Logging.h"
|
||||
|
@ -32,16 +32,13 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
|
||||
@synthesize progressBarStatus;
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSeekable
|
||||
{
|
||||
return [NSSet setWithObjects:@"playlistController.currentEntry",@"playlistController.currentEntry.seekable",nil];
|
||||
+ (NSSet *)keyPathsForValuesAffectingSeekable {
|
||||
return [NSSet setWithObjects:@"playlistController.currentEntry", @"playlistController.currentEntry.seekable", nil];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
[self initDefaults];
|
||||
|
||||
seekable = NO;
|
||||
|
@ -53,14 +50,13 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
|
||||
audioPlayer = [[AudioPlayer alloc] init];
|
||||
[audioPlayer setDelegate:self];
|
||||
[self setPlaybackStatus: CogStatusStopped];
|
||||
[self setPlaybackStatus:CogStatusStopped];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)initDefaults
|
||||
{
|
||||
- (void)initDefaults {
|
||||
NSDictionary *defaultsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[NSNumber numberWithDouble:100.0], @"volume",
|
||||
[NSNumber numberWithBool:NO], @"GraphicEQenable",
|
||||
|
@ -73,10 +69,7 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
- (void)awakeFromNib {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
|
@ -88,48 +81,39 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
[self setSeekable:NO];
|
||||
}
|
||||
|
||||
- (IBAction)playPauseResume:(id)sender
|
||||
{
|
||||
if (playbackStatus == CogStatusStopped || playbackStatus == CogStatusStopping)
|
||||
{
|
||||
- (IBAction)playPauseResume:(id)sender {
|
||||
if(playbackStatus == CogStatusStopped || playbackStatus == CogStatusStopping) {
|
||||
[self play:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[self pauseResume:self];
|
||||
}
|
||||
|
||||
[self sendMetaData];
|
||||
}
|
||||
|
||||
- (IBAction)pauseResume:(id)sender
|
||||
{
|
||||
if (playbackStatus == CogStatusPaused)
|
||||
- (IBAction)pauseResume:(id)sender {
|
||||
if(playbackStatus == CogStatusPaused)
|
||||
[self resume:self];
|
||||
else
|
||||
[self pause:self];
|
||||
}
|
||||
|
||||
- (IBAction)pause:(id)sender
|
||||
{
|
||||
- (IBAction)pause:(id)sender {
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:CogStatusPaused forKey:@"lastPlaybackStatus"];
|
||||
|
||||
[audioPlayer pause];
|
||||
[self setPlaybackStatus: CogStatusPaused];
|
||||
[self setPlaybackStatus:CogStatusPaused];
|
||||
|
||||
[self sendMetaData];
|
||||
}
|
||||
|
||||
- (IBAction)resume:(id)sender
|
||||
{
|
||||
- (IBAction)resume:(id)sender {
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:CogStatusPlaying forKey:@"lastPlaybackStatus"];
|
||||
|
||||
[audioPlayer resume];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)stop:(id)sender
|
||||
{
|
||||
- (IBAction)stop:(id)sender {
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:CogStatusStopped forKey:@"lastPlaybackStatus"];
|
||||
|
||||
[self audioPlayer:audioPlayer removeEqualizer:_eq];
|
||||
|
@ -141,65 +125,56 @@ NSString *CogPlaybackDidStopNotficiation = @"CogPlaybackDidStopNotficiation";
|
|||
[self removeHDCD:nil];
|
||||
}
|
||||
|
||||
//called by double-clicking on table
|
||||
- (void)playEntryAtIndex:(NSInteger)i
|
||||
{
|
||||
// called by double-clicking on table
|
||||
- (void)playEntryAtIndex:(NSInteger)i {
|
||||
[self playEntryAtIndex:i startPaused:NO andSeekTo:[NSNumber numberWithDouble:0.0]];
|
||||
}
|
||||
|
||||
- (void)playEntryAtIndex:(NSInteger)i startPaused:(BOOL)paused
|
||||
{
|
||||
- (void)playEntryAtIndex:(NSInteger)i startPaused:(BOOL)paused {
|
||||
PlaylistEntry *pe = [playlistController entryAtIndex:i];
|
||||
|
||||
[self playEntry:pe startPaused:paused andSeekTo:[NSNumber numberWithDouble:0.0]];
|
||||
}
|
||||
|
||||
- (void)playEntryAtIndex:(NSInteger)i startPaused:(BOOL)paused andSeekTo:(id)offset
|
||||
{
|
||||
- (void)playEntryAtIndex:(NSInteger)i startPaused:(BOOL)paused andSeekTo:(id)offset {
|
||||
PlaylistEntry *pe = [playlistController entryAtIndex:i];
|
||||
|
||||
[self playEntry:pe startPaused:paused andSeekTo:offset];
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)play:(id)sender
|
||||
{
|
||||
if ([playlistView selectedRow] == -1)
|
||||
- (IBAction)play:(id)sender {
|
||||
if([playlistView selectedRow] == -1)
|
||||
[playlistView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO];
|
||||
|
||||
if ([playlistView selectedRow] > -1)
|
||||
if([playlistView selectedRow] > -1)
|
||||
[self playEntryAtIndex:(int)[playlistView selectedRow]];
|
||||
}
|
||||
|
||||
NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
||||
{
|
||||
NSMutableDictionary * dictionary = [NSMutableDictionary dictionary];
|
||||
if ([pe replayGainAlbumGain] != 0)
|
||||
NSDictionary *makeRGInfo(PlaylistEntry *pe) {
|
||||
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
|
||||
if([pe replayGainAlbumGain] != 0)
|
||||
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainAlbumGain]] forKey:@"replayGainAlbumGain"];
|
||||
if ([pe replayGainAlbumPeak] != 0)
|
||||
if([pe replayGainAlbumPeak] != 0)
|
||||
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainAlbumPeak]] forKey:@"replayGainAlbumPeak"];
|
||||
if ([pe replayGainTrackGain] != 0)
|
||||
if([pe replayGainTrackGain] != 0)
|
||||
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainTrackGain]] forKey:@"replayGainTrackGain"];
|
||||
if ([pe replayGainTrackPeak] != 0)
|
||||
if([pe replayGainTrackPeak] != 0)
|
||||
[dictionary setObject:[NSNumber numberWithFloat:[pe replayGainTrackPeak]] forKey:@"replayGainTrackPeak"];
|
||||
if ([pe volume] != 1)
|
||||
if([pe volume] != 1)
|
||||
[dictionary setObject:[NSNumber numberWithFloat:[pe volume]] forKey:@"volume"];
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
- (void)playEntry:(PlaylistEntry *)pe
|
||||
{
|
||||
- (void)playEntry:(PlaylistEntry *)pe {
|
||||
[self playEntry:pe startPaused:NO andSeekTo:[NSNumber numberWithDouble:0.0]];
|
||||
}
|
||||
|
||||
- (void)playEntry:(PlaylistEntry *)pe startPaused:(BOOL)paused
|
||||
{
|
||||
- (void)playEntry:(PlaylistEntry *)pe startPaused:(BOOL)paused {
|
||||
[self playEntry:pe startPaused:paused andSeekTo:[NSNumber numberWithDouble:0.0]];
|
||||
}
|
||||
|
||||
- (void)playEntry:(PlaylistEntry *)pe startPaused:(BOOL)paused andSeekTo:(id)offset
|
||||
{
|
||||
if (playbackStatus != CogStatusStopped && playbackStatus != CogStatusStopping)
|
||||
- (void)playEntry:(PlaylistEntry *)pe startPaused:(BOOL)paused andSeekTo:(id)offset {
|
||||
if(playbackStatus != CogStatusStopped && playbackStatus != CogStatusStopping)
|
||||
[self stop:self];
|
||||
|
||||
DLog(@"PLAYLIST CONTROLLER: %@", [playlistController class]);
|
||||
|
@ -209,12 +184,12 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
|
||||
[self setPosition:[offset doubleValue]];
|
||||
|
||||
if (pe == nil)
|
||||
if(pe == nil)
|
||||
return;
|
||||
|
||||
BOOL loadData = YES;
|
||||
NSString * urlScheme = [[pe URL] scheme];
|
||||
if ([urlScheme isEqualToString:@"http"] ||
|
||||
NSString *urlScheme = [[pe URL] scheme];
|
||||
if([urlScheme isEqualToString:@"http"] ||
|
||||
[urlScheme isEqualToString:@"https"])
|
||||
loadData = NO;
|
||||
|
||||
|
@ -225,13 +200,13 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
}
|
||||
#elif 0
|
||||
// Racing with this version is less likely to jam up the main thread
|
||||
if ([pe metadataLoaded] != YES) {
|
||||
NSArray * entries = @[pe];
|
||||
if([pe metadataLoaded] != YES) {
|
||||
NSArray *entries = @[pe];
|
||||
[playlistLoader loadInfoForEntries:entries];
|
||||
}
|
||||
#else
|
||||
// Let's do it this way instead
|
||||
if ([pe metadataLoaded] != YES && loadData == YES) {
|
||||
if([pe metadataLoaded] != YES && loadData == YES) {
|
||||
NSArray *entries = @[pe];
|
||||
[playlistLoader performSelectorInBackground:@selector(loadInfoForEntries:) withObject:entries];
|
||||
}
|
||||
|
@ -242,24 +217,21 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[audioPlayer play:[pe URL] withUserInfo:pe withRGInfo:makeRGInfo(pe) startPaused:paused andSeekTo:[offset doubleValue]];
|
||||
}
|
||||
|
||||
- (IBAction)next:(id)sender
|
||||
{
|
||||
if ([playlistController next] == NO)
|
||||
- (IBAction)next:(id)sender {
|
||||
if([playlistController next] == NO)
|
||||
return;
|
||||
|
||||
[self playEntry:[playlistController currentEntry]];
|
||||
}
|
||||
|
||||
- (IBAction)prev:(id)sender
|
||||
{
|
||||
if ([playlistController prev] == NO)
|
||||
- (IBAction)prev:(id)sender {
|
||||
if([playlistController prev] == NO)
|
||||
return;
|
||||
|
||||
[self playEntry:[playlistController currentEntry]];
|
||||
}
|
||||
|
||||
- (void)updatePosition:(id)sender
|
||||
{
|
||||
- (void)updatePosition:(id)sender {
|
||||
double pos = [audioPlayer amountPlayed];
|
||||
|
||||
[self setPosition:pos];
|
||||
|
@ -267,8 +239,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[playlistController currentEntry] setCurrentPosition:pos];
|
||||
}
|
||||
|
||||
- (IBAction)seek:(id)sender
|
||||
{
|
||||
- (IBAction)seek:(id)sender {
|
||||
double time = [sender doubleValue];
|
||||
|
||||
[audioPlayer seekToTime:time];
|
||||
|
@ -280,8 +251,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[playlistController currentEntry] setCurrentPosition:time];
|
||||
}
|
||||
|
||||
- (IBAction)seek:(id)sender toTime:(NSTimeInterval)position
|
||||
{
|
||||
- (IBAction)seek:(id)sender toTime:(NSTimeInterval)position {
|
||||
double time = (double)(position);
|
||||
|
||||
lastPosition = -10;
|
||||
|
@ -293,8 +263,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[playlistController currentEntry] setCurrentPosition:time];
|
||||
}
|
||||
|
||||
- (IBAction)spam:(id)sender
|
||||
{
|
||||
- (IBAction)spam:(id)sender {
|
||||
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
|
||||
|
||||
[pboard clearContents];
|
||||
|
@ -302,37 +271,30 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[pboard writeObjects:@[[[playlistController currentEntry] spam]]];
|
||||
}
|
||||
|
||||
- (IBAction)eventSeekForward:(id)sender
|
||||
{
|
||||
- (IBAction)eventSeekForward:(id)sender {
|
||||
[self seekForward:DEFAULT_SEEK];
|
||||
}
|
||||
|
||||
- (void)seekForward:(double)amount
|
||||
{
|
||||
- (void)seekForward:(double)amount {
|
||||
double seekTo = [audioPlayer amountPlayed] + amount;
|
||||
|
||||
if (seekTo > [[[playlistController currentEntry] length] doubleValue])
|
||||
{
|
||||
if(seekTo > [[[playlistController currentEntry] length] doubleValue]) {
|
||||
[self next:self];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
lastPosition = -10;
|
||||
[audioPlayer seekToTime:seekTo];
|
||||
[self setPosition:seekTo];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)eventSeekBackward:(id)sender
|
||||
{
|
||||
- (IBAction)eventSeekBackward:(id)sender {
|
||||
[self seekBackward:DEFAULT_SEEK];
|
||||
}
|
||||
|
||||
- (void)seekBackward:(double)amount
|
||||
{
|
||||
- (void)seekBackward:(double)amount {
|
||||
double seekTo = [audioPlayer amountPlayed] - amount;
|
||||
|
||||
if (seekTo < 0)
|
||||
if(seekTo < 0)
|
||||
seekTo = 0;
|
||||
|
||||
lastPosition = -10;
|
||||
|
@ -355,8 +317,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[playbackButtons setImage:img forSegment:1];
|
||||
}
|
||||
*/
|
||||
- (IBAction)changeVolume:(id)sender
|
||||
{
|
||||
- (IBAction)changeVolume:(id)sender {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
|
@ -370,84 +331,74 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
/* selector for NSTimer - gets passed the Timer object itself
|
||||
and the appropriate userInfo, which in this case is an NSNumber
|
||||
containing the current volume before we start fading. */
|
||||
- (void)audioFadeDown:(NSTimer *)audioTimer
|
||||
{
|
||||
- (void)audioFadeDown:(NSTimer *)audioTimer {
|
||||
double volume = [audioPlayer volume];
|
||||
double originalVolume = [[audioTimer userInfo] doubleValue];
|
||||
double down = originalVolume/10;
|
||||
double down = originalVolume / 10;
|
||||
|
||||
DLog(@"VOLUME IS %lf", volume);
|
||||
|
||||
if (volume > 0.0001) //YAY! Roundoff error!
|
||||
if(volume > 0.0001) // YAY! Roundoff error!
|
||||
{
|
||||
[audioPlayer volumeDown:down];
|
||||
}
|
||||
else // volume is at 0 or below, we are ready to release the timer and move on
|
||||
} else // volume is at 0 or below, we are ready to release the timer and move on
|
||||
{
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
[audioPlayer pause];
|
||||
[audioPlayer setVolume:originalVolume];
|
||||
[volumeSlider setDoubleValue: logarithmicToLinear(originalVolume, MAX_VOLUME)];
|
||||
[volumeSlider setDoubleValue:logarithmicToLinear(originalVolume, MAX_VOLUME)];
|
||||
[audioTimer invalidate];
|
||||
|
||||
fading = NO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)audioFadeUp:(NSTimer *)audioTimer
|
||||
{
|
||||
- (void)audioFadeUp:(NSTimer *)audioTimer {
|
||||
double volume = [audioPlayer volume];
|
||||
double originalVolume = [[audioTimer userInfo] doubleValue];
|
||||
double up = originalVolume/10;
|
||||
double up = originalVolume / 10;
|
||||
|
||||
DLog(@"VOLUME IS %lf", volume);
|
||||
|
||||
if (originalVolume - volume > 0.0001)
|
||||
{
|
||||
if ((volume + up) > originalVolume)
|
||||
if(originalVolume - volume > 0.0001) {
|
||||
if((volume + up) > originalVolume)
|
||||
[audioPlayer volumeUp:(originalVolume - volume)];
|
||||
else
|
||||
[audioPlayer volumeUp:up];
|
||||
}
|
||||
else // volume is at or near original level, we are ready to release the timer and move on
|
||||
} else // volume is at or near original level, we are ready to release the timer and move on
|
||||
{
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
[audioPlayer setVolume:originalVolume];
|
||||
[volumeSlider setDoubleValue: logarithmicToLinear(originalVolume, MAX_VOLUME)];
|
||||
[volumeSlider setDoubleValue:logarithmicToLinear(originalVolume, MAX_VOLUME)];
|
||||
[audioTimer invalidate];
|
||||
|
||||
fading = NO;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)fade:(id)sender
|
||||
{
|
||||
- (IBAction)fade:(id)sender {
|
||||
double time = 0.1;
|
||||
|
||||
// we can not allow multiple fade timers to be registered
|
||||
if (YES == fading)
|
||||
if(YES == fading)
|
||||
return;
|
||||
fading = YES;
|
||||
|
||||
NSNumber *originalVolume = [NSNumber numberWithDouble: [audioPlayer volume]];
|
||||
NSNumber *originalVolume = [NSNumber numberWithDouble:[audioPlayer volume]];
|
||||
NSTimer *fadeTimer;
|
||||
|
||||
if (playbackStatus == CogStatusPlaying) {
|
||||
if(playbackStatus == CogStatusPlaying) {
|
||||
fadeTimer = [NSTimer timerWithTimeInterval:time
|
||||
target:self
|
||||
selector:@selector(audioFadeDown:)
|
||||
userInfo:originalVolume
|
||||
repeats:YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer:fadeTimer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[audioPlayer setVolume:0];
|
||||
fadeTimer = [NSTimer timerWithTimeInterval:time
|
||||
target:self
|
||||
|
@ -459,8 +410,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
}
|
||||
}
|
||||
|
||||
- (IBAction)skipToNextAlbum:(id)sender
|
||||
{
|
||||
- (IBAction)skipToNextAlbum:(id)sender {
|
||||
BOOL found = NO;
|
||||
|
||||
NSInteger index = [[playlistController currentEntry] index];
|
||||
|
@ -471,37 +421,32 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
|
||||
PlaylistEntry *pe;
|
||||
|
||||
for (i = 1; i < [[playlistController arrangedObjects] count]; i++)
|
||||
{
|
||||
for(i = 1; i < [[playlistController arrangedObjects] count]; i++) {
|
||||
pe = [playlistController entryAtIndex:index + i];
|
||||
if (pe == nil)
|
||||
if(pe == nil)
|
||||
break;
|
||||
|
||||
curAlbum = [pe album];
|
||||
|
||||
// check for untagged files, and just play the first untagged one
|
||||
// we come across
|
||||
if (curAlbum == nil)
|
||||
{
|
||||
if(curAlbum == nil) {
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if ([curAlbum caseInsensitiveCompare:origAlbum] != NSOrderedSame)
|
||||
{
|
||||
if([curAlbum caseInsensitiveCompare:origAlbum] != NSOrderedSame) {
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found)
|
||||
{
|
||||
if(found) {
|
||||
[self playEntryAtIndex:i + index];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)skipToPreviousAlbum:(id)sender
|
||||
{
|
||||
- (IBAction)skipToPreviousAlbum:(id)sender {
|
||||
BOOL found = NO;
|
||||
BOOL foundAlbum = NO;
|
||||
|
||||
|
@ -513,48 +458,39 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
|
||||
PlaylistEntry *pe;
|
||||
|
||||
for (i = 1; i < [[playlistController arrangedObjects] count]; i++)
|
||||
{
|
||||
for(i = 1; i < [[playlistController arrangedObjects] count]; i++) {
|
||||
pe = [playlistController entryAtIndex:index - i];
|
||||
if (pe == nil)
|
||||
if(pe == nil)
|
||||
break;
|
||||
|
||||
curAlbum = [pe album];
|
||||
if (curAlbum == nil)
|
||||
{
|
||||
if(curAlbum == nil) {
|
||||
found = YES;
|
||||
break;
|
||||
}
|
||||
|
||||
if ([curAlbum caseInsensitiveCompare:origAlbum] != NSOrderedSame)
|
||||
{
|
||||
if (foundAlbum == NO)
|
||||
{
|
||||
if([curAlbum caseInsensitiveCompare:origAlbum] != NSOrderedSame) {
|
||||
if(foundAlbum == NO) {
|
||||
foundAlbum = YES;
|
||||
// now we need to move up to the first song in the album, so we'll
|
||||
// go till we either find index 0, or the first song in the album
|
||||
origAlbum = curAlbum;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
found = YES; // terminate loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found || foundAlbum)
|
||||
{
|
||||
if (foundAlbum == YES)
|
||||
if(found || foundAlbum) {
|
||||
if(foundAlbum == YES)
|
||||
i--;
|
||||
[self playEntryAtIndex:index - i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)volumeDown:(id)sender
|
||||
{
|
||||
- (IBAction)volumeDown:(id)sender {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
|
@ -562,11 +498,9 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[volumeSlider setDoubleValue:logarithmicToLinear(newVolume, MAX_VOLUME)];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)volumeUp:(id)sender
|
||||
{
|
||||
- (IBAction)volumeUp:(id)sender {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
|
@ -577,8 +511,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[NSUserDefaults standardUserDefaults] setDouble:[audioPlayer volume] forKey:@"volume"];
|
||||
}
|
||||
|
||||
- (void)eqAlloc
|
||||
{
|
||||
- (void)eqAlloc {
|
||||
// Show a stopped equalizer as a stub
|
||||
OSStatus err;
|
||||
AudioComponentDescription desc;
|
||||
|
@ -595,35 +528,30 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
desc.componentSubType = kAudioUnitSubType_GraphicEQ;
|
||||
|
||||
comp = AudioComponentFindNext(comp, &desc);
|
||||
if (!comp)
|
||||
if(!comp)
|
||||
return;
|
||||
|
||||
err = AudioComponentInstanceNew(comp, &_eq);
|
||||
if (err)
|
||||
if(err)
|
||||
return;
|
||||
|
||||
AudioUnitInitialize(_eq);
|
||||
}
|
||||
|
||||
- (void)eqDealloc
|
||||
{
|
||||
- (void)eqDealloc {
|
||||
AudioUnitUninitialize(_eq);
|
||||
AudioComponentInstanceDispose(_eq);
|
||||
_eq = nil;
|
||||
_eqStubbed = NO;
|
||||
}
|
||||
|
||||
- (IBAction)showEq:(id)sender
|
||||
{
|
||||
if (_eq)
|
||||
{
|
||||
if (_equi && [_equi isOpen])
|
||||
- (IBAction)showEq:(id)sender {
|
||||
if(_eq) {
|
||||
if(_equi && [_equi isOpen])
|
||||
[_equi bringToFront];
|
||||
else
|
||||
_equi = [[AUPluginUI alloc] initWithSampler:_eq bringToFront:YES orWindowNumber:0];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[self eqAlloc];
|
||||
_eqWasOpen = YES;
|
||||
[self audioPlayer:nil displayEqualizer:_eq];
|
||||
|
@ -631,23 +559,20 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
}
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq
|
||||
{
|
||||
if (_equi)
|
||||
{
|
||||
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq {
|
||||
if(_equi) {
|
||||
_eqWasOpen = [_equi isOpen];
|
||||
_equi = nil;
|
||||
}
|
||||
|
||||
if (_eq && _eq != eq) {
|
||||
if(_eq && _eq != eq) {
|
||||
OSStatus err;
|
||||
CFPropertyListRef classData;
|
||||
UInt32 size;
|
||||
|
||||
size = sizeof(classData);
|
||||
err = AudioUnitGetProperty(_eq, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, &size);
|
||||
if (err == noErr)
|
||||
{
|
||||
if(err == noErr) {
|
||||
CFPreferencesSetAppValue(CFSTR("GraphEQ_Preset"), classData, kCFPreferencesCurrentApplication);
|
||||
CFRelease(classData);
|
||||
}
|
||||
|
@ -668,20 +593,19 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
CFNumberRef cfnum;
|
||||
|
||||
classData = CFPreferencesCopyAppValue(CFSTR("GraphEQ_Preset"), kCFPreferencesCurrentApplication);
|
||||
if (classData)
|
||||
{
|
||||
dict = (CFDictionaryRef) classData;
|
||||
if(classData) {
|
||||
dict = (CFDictionaryRef)classData;
|
||||
|
||||
cfnum = (CFNumberRef) (CFDictionaryGetValue(dict, CFSTR("type")));
|
||||
cfnum = (CFNumberRef)(CFDictionaryGetValue(dict, CFSTR("type")));
|
||||
CFNumberGetValue(cfnum, kCFNumberSInt32Type, &cd.componentType);
|
||||
cfnum = (CFNumberRef) (CFDictionaryGetValue(dict, CFSTR("subtype")));
|
||||
cfnum = (CFNumberRef)(CFDictionaryGetValue(dict, CFSTR("subtype")));
|
||||
CFNumberGetValue(cfnum, kCFNumberSInt32Type, &cd.componentSubType);
|
||||
cfnum = (CFNumberRef) (CFDictionaryGetValue(dict, CFSTR("manufacturer")));
|
||||
cfnum = (CFNumberRef)(CFDictionaryGetValue(dict, CFSTR("manufacturer")));
|
||||
CFNumberGetValue(cfnum, kCFNumberSInt32Type, &cd.componentManufacturer);
|
||||
|
||||
if ((cd.componentType == kAudioUnitType_Effect ) &&
|
||||
if((cd.componentType == kAudioUnitType_Effect) &&
|
||||
(cd.componentSubType == kAudioUnitSubType_GraphicEQ) &&
|
||||
(cd.componentManufacturer == kAudioUnitManufacturer_Apple ))
|
||||
(cd.componentManufacturer == kAudioUnitManufacturer_Apple))
|
||||
err = AudioUnitSetProperty(eq, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, sizeof(classData));
|
||||
|
||||
CFRelease(classData);
|
||||
|
@ -690,81 +614,71 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
equalizerLoadPreset(eq);
|
||||
}
|
||||
|
||||
if (_eqWasOpen)
|
||||
{
|
||||
NSWindow * window = appController.miniMode ? appController.miniWindow : appController.mainWindow;
|
||||
if(_eqWasOpen) {
|
||||
NSWindow *window = appController.miniMode ? appController.miniWindow : appController.mainWindow;
|
||||
_equi = [[AUPluginUI alloc] initWithSampler:_eq bringToFront:NO orWindowNumber:window.windowNumber];
|
||||
_eqWasOpen = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player refreshEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)audioPlayer:(AudioPlayer *)player refreshEqualizer:(AudioUnit)eq {
|
||||
equalizerLoadPreset(eq);
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player removeEqualizer:(AudioUnit)eq
|
||||
{
|
||||
if (eq == _eq)
|
||||
{
|
||||
- (void)audioPlayer:(AudioPlayer *)player removeEqualizer:(AudioUnit)eq {
|
||||
if(eq == _eq) {
|
||||
OSStatus err;
|
||||
CFPropertyListRef classData;
|
||||
UInt32 size;
|
||||
|
||||
size = sizeof(classData);
|
||||
err = AudioUnitGetProperty(eq, kAudioUnitProperty_ClassInfo, kAudioUnitScope_Global, 0, &classData, &size);
|
||||
if (err == noErr)
|
||||
{
|
||||
if(err == noErr) {
|
||||
CFPreferencesSetAppValue(CFSTR("GraphEQ_Preset"), classData, kCFPreferencesCurrentApplication);
|
||||
CFRelease(classData);
|
||||
}
|
||||
|
||||
CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication);
|
||||
|
||||
if (_equi)
|
||||
{
|
||||
if(_equi) {
|
||||
_eqWasOpen = [_equi isOpen];
|
||||
}
|
||||
|
||||
_equi = nil;
|
||||
[self eqDealloc];
|
||||
|
||||
if (_eqWasOpen)
|
||||
{
|
||||
if(_eqWasOpen) {
|
||||
[self showEq:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player willEndStream:(id)userInfo
|
||||
{
|
||||
- (void)audioPlayer:(AudioPlayer *)player willEndStream:(id)userInfo {
|
||||
PlaylistEntry *curEntry = (PlaylistEntry *)userInfo;
|
||||
PlaylistEntry *pe;
|
||||
|
||||
if (curEntry.stopAfter)
|
||||
if(curEntry.stopAfter)
|
||||
pe = nil;
|
||||
else
|
||||
{
|
||||
else {
|
||||
pe = [playlistController getNextEntry:curEntry];
|
||||
if (pe && [pe metadataLoaded] != YES) {
|
||||
NSArray * entries = @[pe];
|
||||
if(pe && [pe metadataLoaded] != YES) {
|
||||
NSArray *entries = @[pe];
|
||||
[playlistLoader performSelectorInBackground:@selector(loadInfoForEntries:) withObject:entries];
|
||||
}
|
||||
}
|
||||
|
||||
if (pe)
|
||||
if(pe)
|
||||
[player setNextStream:[pe URL] withUserInfo:pe withRGInfo:makeRGInfo(pe)];
|
||||
else
|
||||
[player setNextStream:nil];
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player didBeginStream:(id)userInfo
|
||||
{
|
||||
- (void)audioPlayer:(AudioPlayer *)player didBeginStream:(id)userInfo {
|
||||
PlaylistEntry *pe = (PlaylistEntry *)userInfo;
|
||||
|
||||
[playlistController setCurrentEntry:pe];
|
||||
|
||||
if (_eq)
|
||||
if(_eq)
|
||||
equalizerApplyGenre(_eq, [pe genre]);
|
||||
|
||||
lastPosition = -10;
|
||||
|
@ -776,33 +690,26 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidBeginNotficiation object:pe];
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player didChangeStatus:(NSNumber *)s userInfo:(id)userInfo
|
||||
{
|
||||
- (void)audioPlayer:(AudioPlayer *)player didChangeStatus:(NSNumber *)s userInfo:(id)userInfo {
|
||||
int status = [s intValue];
|
||||
if (status == CogStatusStopped || status == CogStatusPaused)
|
||||
{
|
||||
if(status == CogStatusStopped || status == CogStatusPaused) {
|
||||
[self removeHDCD:nil];
|
||||
if (positionTimer)
|
||||
{
|
||||
if(positionTimer) {
|
||||
[positionTimer invalidate];
|
||||
positionTimer = NULL;
|
||||
}
|
||||
|
||||
if (status == CogStatusStopped)
|
||||
{
|
||||
if(status == CogStatusStopped) {
|
||||
[self setPosition:0];
|
||||
[self setSeekable:NO]; // the player stopped, disable the slider
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidStopNotficiation object:nil];
|
||||
}
|
||||
else // paused
|
||||
} else // paused
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidPauseNotficiation object:nil];
|
||||
}
|
||||
}
|
||||
else if (status == CogStatusPlaying)
|
||||
{
|
||||
if (!positionTimer) {
|
||||
} else if(status == CogStatusPlaying) {
|
||||
if(!positionTimer) {
|
||||
positionTimer = [NSTimer timerWithTimeInterval:0.2 target:self selector:@selector(updatePosition:) userInfo:nil repeats:YES];
|
||||
[[NSRunLoop currentRunLoop] addTimer:positionTimer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
@ -810,14 +717,13 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[NSNotificationCenter defaultCenter] postNotificationName:CogPlaybackDidResumeNotficiation object:nil];
|
||||
}
|
||||
|
||||
if (status == CogStatusStopped) {
|
||||
if(status == CogStatusStopped) {
|
||||
[playlistController setCurrentEntry:nil];
|
||||
[self setSeekable:NO]; // the player stopped, disable the slider
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[self setSeekable:YES];
|
||||
}
|
||||
switch (status) {
|
||||
switch(status) {
|
||||
case CogStatusPlaying:
|
||||
DLog(@"PLAYING!");
|
||||
break;
|
||||
|
@ -830,10 +736,10 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
break;
|
||||
}
|
||||
|
||||
if (status == CogStatusStopped) {
|
||||
if(status == CogStatusStopped) {
|
||||
status = CogStatusStopping;
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
if ([self playbackStatus] == CogStatusStopping)
|
||||
if([self playbackStatus] == CogStatusStopping)
|
||||
[self setPlaybackStatus:CogStatusStopped];
|
||||
});
|
||||
}
|
||||
|
@ -843,32 +749,27 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[self sendMetaData];
|
||||
}
|
||||
|
||||
- (void)audioPlayer:(AudioPlayer *)player sustainHDCD:(id)userInfo
|
||||
{
|
||||
MainWindow * mainWindow = (MainWindow *) appController.mainWindow;
|
||||
- (void)audioPlayer:(AudioPlayer *)player sustainHDCD:(id)userInfo {
|
||||
MainWindow *mainWindow = (MainWindow *)appController.mainWindow;
|
||||
[mainWindow showHDCDLogo:YES];
|
||||
MiniWindow * miniWindow = (MiniWindow *) appController.miniWindow;
|
||||
MiniWindow *miniWindow = (MiniWindow *)appController.miniWindow;
|
||||
[miniWindow showHDCDLogo:YES];
|
||||
}
|
||||
|
||||
- (void)removeHDCD:(id)sender
|
||||
{
|
||||
MainWindow * mainWindow = (MainWindow *) appController.mainWindow;
|
||||
- (void)removeHDCD:(id)sender {
|
||||
MainWindow *mainWindow = (MainWindow *)appController.mainWindow;
|
||||
[mainWindow showHDCDLogo:NO];
|
||||
MiniWindow * miniWindow = (MiniWindow *) appController.miniWindow;
|
||||
MiniWindow *miniWindow = (MiniWindow *)appController.miniWindow;
|
||||
[miniWindow showHDCDLogo:NO];
|
||||
}
|
||||
|
||||
- (void)playlistDidChange:(PlaylistController *)p
|
||||
{
|
||||
- (void)playlistDidChange:(PlaylistController *)p {
|
||||
[audioPlayer resetNextStreams];
|
||||
}
|
||||
|
||||
- (void)setPosition:(double)p
|
||||
{
|
||||
if (p > lastPosition && (p - lastPosition) >= 10.0)
|
||||
{
|
||||
PlaylistEntry * pe = [playlistController currentEntry];
|
||||
- (void)setPosition:(double)p {
|
||||
if(p > lastPosition && (p - lastPosition) >= 10.0) {
|
||||
PlaylistEntry *pe = [playlistController currentEntry];
|
||||
NSInteger lastTrackPlaying = [pe index];
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] setInteger:CogStatusPlaying forKey:@"lastPlaybackStatus"];
|
||||
|
@ -886,39 +787,37 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[[playlistController currentEntry] setCurrentPosition:p];
|
||||
}
|
||||
|
||||
- (double)position
|
||||
{
|
||||
- (double)position {
|
||||
return position;
|
||||
}
|
||||
|
||||
- (void)setSeekable:(BOOL)s
|
||||
{
|
||||
- (void)setSeekable:(BOOL)s {
|
||||
seekable = s;
|
||||
}
|
||||
|
||||
- (BOOL)seekable
|
||||
{
|
||||
- (BOOL)seekable {
|
||||
return seekable && [[playlistController currentEntry] seekable];
|
||||
}
|
||||
|
||||
- (void)sendMetaData {
|
||||
MPNowPlayingInfoCenter * defaultCenter = [MPNowPlayingInfoCenter defaultCenter];
|
||||
MPNowPlayingInfoCenter *defaultCenter = [MPNowPlayingInfoCenter defaultCenter];
|
||||
|
||||
PlaylistEntry * entry = [playlistController currentEntry];
|
||||
PlaylistEntry *entry = [playlistController currentEntry];
|
||||
NSMutableDictionary *songInfo = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (entry) {
|
||||
if ([entry title])
|
||||
if(entry) {
|
||||
if([entry title])
|
||||
[songInfo setObject:[entry title] forKey:MPMediaItemPropertyTitle];
|
||||
if ([entry artist])
|
||||
if([entry artist])
|
||||
[songInfo setObject:[entry artist] forKey:MPMediaItemPropertyArtist];
|
||||
if ([entry album])
|
||||
if([entry album])
|
||||
[songInfo setObject:[entry album] forKey:MPMediaItemPropertyAlbumTitle];
|
||||
if ([entry albumArt]) {
|
||||
if([entry albumArt]) {
|
||||
// can't do && with @available
|
||||
if (@available(macOS 10.13.2, *)) {
|
||||
if(@available(macOS 10.13.2, *)) {
|
||||
CGSize artworkSize = CGSizeMake(500, 500);
|
||||
MPMediaItemArtwork *mpArtwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artworkSize requestHandler:^NSImage * _Nonnull(CGSize size) {
|
||||
MPMediaItemArtwork *mpArtwork = [[MPMediaItemArtwork alloc] initWithBoundsSize:artworkSize
|
||||
requestHandler:^NSImage *_Nonnull(CGSize size) {
|
||||
return [entry albumArt];
|
||||
}];
|
||||
[songInfo setObject:mpArtwork forKey:MPMediaItemPropertyArtwork];
|
||||
|
@ -926,9 +825,9 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
}
|
||||
// I don't know what NPIC does with these since they aren't exposed in UI, but if we have them, use it.
|
||||
// There's a bunch of other metadata, but PlaylistEntry can't represent a lot of it.
|
||||
if ([entry genre])
|
||||
if([entry genre])
|
||||
[songInfo setObject:[entry genre] forKey:MPMediaItemPropertyGenre];
|
||||
if ([entry year]) {
|
||||
if([entry year]) {
|
||||
// If PlaylistEntry can represent a full date like some tag formats can do, change it
|
||||
NSCalendar *calendar = [NSCalendar currentCalendar];
|
||||
NSDate *releaseYear = [calendar dateWithEra:1 year:[[entry year] intValue] month:0 day:0 hour:0 minute:0 second:0 nanosecond:0];
|
||||
|
@ -939,7 +838,7 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[songInfo setObject:[NSNumber numberWithFloat:[entry index]] forKey:MPMediaItemPropertyPersistentID];
|
||||
}
|
||||
|
||||
switch (playbackStatus) {
|
||||
switch(playbackStatus) {
|
||||
case CogStatusPlaying:
|
||||
defaultCenter.playbackState = MPNowPlayingPlaybackStatePlaying;
|
||||
break;
|
||||
|
@ -955,7 +854,4 @@ NSDictionary * makeRGInfo(PlaylistEntry *pe)
|
|||
[defaultCenter setNowPlayingInfo:songInfo];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
@class AudioScrobbler;
|
||||
|
||||
@interface PlaybackEventController
|
||||
: NSObject <NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate> {
|
||||
: NSObject <NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate> {
|
||||
IBOutlet PlaybackController *playbackController;
|
||||
|
||||
IBOutlet NSWindow *mainWindow;
|
||||
|
|
|
@ -20,7 +20,9 @@ NSString *TrackLength = @"Total Time";
|
|||
NSString *TrackPath = @"Location";
|
||||
NSString *TrackState = @"Player State";
|
||||
|
||||
typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStopped };
|
||||
typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying,
|
||||
TrackPaused,
|
||||
TrackStopped };
|
||||
|
||||
@implementation PlaybackEventController {
|
||||
AudioScrobbler *scrobbler;
|
||||
|
@ -34,11 +36,11 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
|
||||
- (void)initDefaults {
|
||||
NSDictionary *defaultsDictionary = @{
|
||||
@"enableAudioScrobbler" : @YES,
|
||||
@"automaticallyLaunchLastFM" : @NO,
|
||||
@"notifications.enable" : @YES,
|
||||
@"notifications.itunes-style" : @YES,
|
||||
@"notifications.show-album-art" : @YES
|
||||
@"enableAudioScrobbler": @YES,
|
||||
@"automaticallyLaunchLastFM": @NO,
|
||||
@"notifications.enable": @YES,
|
||||
@"notifications.itunes-style": @YES,
|
||||
@"notifications.show-album-art": @YES
|
||||
};
|
||||
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
|
||||
|
@ -46,19 +48,19 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
if(self) {
|
||||
[self initDefaults];
|
||||
|
||||
didGainUN = NO;
|
||||
|
||||
if (@available(macOS 10.14, *)) {
|
||||
if(@available(macOS 10.14, *)) {
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center
|
||||
requestAuthorizationWithOptions:UNAuthorizationOptionAlert
|
||||
completionHandler:^(BOOL granted, NSError *_Nullable error) {
|
||||
self->didGainUN = granted;
|
||||
|
||||
if (granted) {
|
||||
if(granted) {
|
||||
UNNotificationAction *skipAction = [UNNotificationAction
|
||||
actionWithIdentifier:@"skip"
|
||||
title:@"Skip"
|
||||
|
@ -66,7 +68,7 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
|
||||
UNNotificationCategory *playCategory = [UNNotificationCategory
|
||||
categoryWithIdentifier:@"play"
|
||||
actions:@[ skipAction ]
|
||||
actions:@[skipAction]
|
||||
intentIdentifiers:@[]
|
||||
options:UNNotificationCategoryOptionNone];
|
||||
|
||||
|
@ -94,38 +96,38 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
willPresentNotification:(UNNotification *)notification
|
||||
withCompletionHandler:
|
||||
(void (^)(UNNotificationPresentationOptions options))completionHandler
|
||||
API_AVAILABLE(macos(10.14)) {
|
||||
API_AVAILABLE(macos(10.14)) {
|
||||
UNNotificationPresentationOptions presentationOptions = UNNotificationPresentationOptionAlert;
|
||||
|
||||
completionHandler(presentationOptions);
|
||||
}
|
||||
|
||||
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
||||
withCompletionHandler:(void (^)(void))completionHandler API_AVAILABLE(macos(10.14)) {
|
||||
if ([[response actionIdentifier] isEqualToString:@"skip"]) {
|
||||
if([[response actionIdentifier] isEqualToString:@"skip"]) {
|
||||
[playbackController next:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)fillNotificationDictionary:(PlaylistEntry *)pe status:(TrackStatus)status {
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
if (pe == nil) return dict;
|
||||
if(pe == nil) return dict;
|
||||
|
||||
[dict setObject:[[pe URL] absoluteString] forKey:TrackPath];
|
||||
if ([pe title]) [dict setObject:[pe title] forKey:TrackTitle];
|
||||
if ([pe artist]) [dict setObject:[pe artist] forKey:TrackArtist];
|
||||
if ([pe album]) [dict setObject:[pe album] forKey:TrackAlbum];
|
||||
if ([pe genre]) [dict setObject:[pe genre] forKey:TrackGenre];
|
||||
if ([pe track])
|
||||
if([pe title]) [dict setObject:[pe title] forKey:TrackTitle];
|
||||
if([pe artist]) [dict setObject:[pe artist] forKey:TrackArtist];
|
||||
if([pe album]) [dict setObject:[pe album] forKey:TrackAlbum];
|
||||
if([pe genre]) [dict setObject:[pe genre] forKey:TrackGenre];
|
||||
if([pe track])
|
||||
[dict setObject:[pe trackText] forKey:TrackNumber];
|
||||
if ([pe length])
|
||||
if([pe length])
|
||||
[dict setObject:[NSNumber numberWithInteger:(NSInteger)([[pe length] doubleValue] * 1000.0)]
|
||||
forKey:TrackLength];
|
||||
|
||||
NSString *state = nil;
|
||||
|
||||
switch (status) {
|
||||
switch(status) {
|
||||
case TrackPlaying:
|
||||
state = @"Playing";
|
||||
break;
|
||||
|
@ -145,7 +147,7 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
}
|
||||
|
||||
- (void)performPlaybackDidBeginActions:(PlaylistEntry *)pe {
|
||||
if (NO == [pe error]) {
|
||||
if(NO == [pe error]) {
|
||||
entry = pe;
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter]
|
||||
|
@ -156,14 +158,14 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
|
||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||
|
||||
if ([defaults boolForKey:@"notifications.enable"]) {
|
||||
if ([defaults boolForKey:@"enableAudioScrobbler"]) {
|
||||
if([defaults boolForKey:@"notifications.enable"]) {
|
||||
if([defaults boolForKey:@"enableAudioScrobbler"]) {
|
||||
[scrobbler start:pe];
|
||||
if ([AudioScrobbler isRunning]) return;
|
||||
if([AudioScrobbler isRunning]) return;
|
||||
}
|
||||
|
||||
if (@available(macOS 10.14, *)) {
|
||||
if (didGainUN) {
|
||||
if(@available(macOS 10.14, *)) {
|
||||
if(didGainUN) {
|
||||
UNUserNotificationCenter *center =
|
||||
[UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
|
@ -173,11 +175,11 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
content.title = @"Now Playing";
|
||||
|
||||
NSString *subtitle;
|
||||
if ([pe artist] && [pe album]) {
|
||||
if([pe artist] && [pe album]) {
|
||||
subtitle = [NSString stringWithFormat:@"%@ - %@", [pe artist], [pe album]];
|
||||
} else if ([pe artist]) {
|
||||
} else if([pe artist]) {
|
||||
subtitle = [pe artist];
|
||||
} else if ([pe album]) {
|
||||
} else if([pe album]) {
|
||||
subtitle = [pe album];
|
||||
} else {
|
||||
subtitle = @"";
|
||||
|
@ -188,14 +190,14 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
content.sound = nil;
|
||||
content.categoryIdentifier = @"play";
|
||||
|
||||
if ([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
if([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
[pe albumArt]) {
|
||||
NSError *error = nil;
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSURL *tmpSubFolderURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()]
|
||||
URLByAppendingPathComponent:@"cog-artworks-cache"
|
||||
isDirectory:true];
|
||||
if ([fileManager createDirectoryAtPath:[tmpSubFolderURL path]
|
||||
if([fileManager createDirectoryAtPath:[tmpSubFolderURL path]
|
||||
withIntermediateDirectories:true
|
||||
attributes:nil
|
||||
error:&error]) {
|
||||
|
@ -209,12 +211,12 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
context:nil
|
||||
hints:nil];
|
||||
|
||||
if (cgRef) {
|
||||
if(cgRef) {
|
||||
NSBitmapImageRep *newRep =
|
||||
[[NSBitmapImageRep alloc] initWithCGImage:cgRef];
|
||||
NSData *jpgData = [newRep
|
||||
representationUsingType:NSBitmapImageFileTypeJPEG
|
||||
properties:@{NSImageCompressionFactor : @0.5f}];
|
||||
properties:@{ NSImageCompressionFactor: @0.5f }];
|
||||
[jpgData writeToURL:fileURL atomically:YES];
|
||||
|
||||
UNNotificationAttachment *icon =
|
||||
|
@ -222,11 +224,11 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
URL:fileURL
|
||||
options:nil
|
||||
error:&error];
|
||||
if (error) {
|
||||
if(error) {
|
||||
// We have size limit of 10MB per image attachment.
|
||||
NSLog(@"%@", error.localizedDescription);
|
||||
} else {
|
||||
content.attachments = @[ icon ];
|
||||
content.attachments = @[icon];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,29 +249,29 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
notif.title = [pe title];
|
||||
|
||||
NSString *subtitle;
|
||||
if ([pe artist] && [pe album]) {
|
||||
if([pe artist] && [pe album]) {
|
||||
subtitle = [NSString stringWithFormat:@"%@ - %@", [pe artist], [pe album]];
|
||||
} else if ([pe artist]) {
|
||||
} else if([pe artist]) {
|
||||
subtitle = [pe artist];
|
||||
} else if ([pe album]) {
|
||||
} else if([pe album]) {
|
||||
subtitle = [pe album];
|
||||
} else {
|
||||
subtitle = @"";
|
||||
}
|
||||
|
||||
if ([defaults boolForKey:@"notifications.itunes-style"]) {
|
||||
if([defaults boolForKey:@"notifications.itunes-style"]) {
|
||||
notif.subtitle = subtitle;
|
||||
[notif setValue:@YES forKey:@"_showsButtons"];
|
||||
} else {
|
||||
notif.informativeText = subtitle;
|
||||
}
|
||||
|
||||
if ([notif respondsToSelector:@selector(setContentImage:)]) {
|
||||
if ([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
if([notif respondsToSelector:@selector(setContentImage:)]) {
|
||||
if([defaults boolForKey:@"notifications.show-album-art"] &&
|
||||
[pe albumArtInternal]) {
|
||||
NSImage *image = [pe albumArt];
|
||||
|
||||
if ([defaults boolForKey:@"notifications.itunes-style"]) {
|
||||
if([defaults boolForKey:@"notifications.itunes-style"]) {
|
||||
[notif setValue:image forKey:@"_identityImage"];
|
||||
} else {
|
||||
notif.contentImage = image;
|
||||
|
@ -292,7 +294,7 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
object:nil
|
||||
userInfo:[self fillNotificationDictionary:entry status:TrackPaused]
|
||||
deliverImmediately:YES];
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
|
||||
[scrobbler pause];
|
||||
}
|
||||
}
|
||||
|
@ -303,7 +305,7 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
object:nil
|
||||
userInfo:[self fillNotificationDictionary:entry status:TrackPlaying]
|
||||
deliverImmediately:YES];
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
|
||||
[scrobbler resume];
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +317,7 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
userInfo:[self fillNotificationDictionary:entry status:TrackStopped]
|
||||
deliverImmediately:YES];
|
||||
entry = nil;
|
||||
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"enableAudioScrobbler"]) {
|
||||
[scrobbler stop];
|
||||
}
|
||||
}
|
||||
|
@ -369,15 +371,13 @@ typedef NS_ENUM(NSInteger, TrackStatus) { TrackPlaying, TrackPaused, TrackStoppe
|
|||
|
||||
- (void)userNotificationCenter:(NSUserNotificationCenter *)center
|
||||
didActivateNotification:(NSUserNotification *)notification {
|
||||
switch (notification.activationType) {
|
||||
switch(notification.activationType) {
|
||||
case NSUserNotificationActivationTypeActionButtonClicked:
|
||||
[playbackController next:self];
|
||||
break;
|
||||
|
||||
case NSUserNotificationActivationTypeContentsClicked: {
|
||||
NSWindow *window = [[NSUserDefaults standardUserDefaults] boolForKey:@"miniMode"]
|
||||
? miniWindow
|
||||
: mainWindow;
|
||||
NSWindow *window = [[NSUserDefaults standardUserDefaults] boolForKey:@"miniMode"] ? miniWindow : mainWindow;
|
||||
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[window makeKeyAndOrderFront:self];
|
||||
|
|
|
@ -10,9 +10,8 @@
|
|||
#import <CogAudio/Plugin.h>
|
||||
|
||||
@interface AudioContainer : NSObject {
|
||||
|
||||
}
|
||||
|
||||
+ (NSArray *) urlsForContainerURL:(NSURL *)url;
|
||||
+ (NSArray *)urlsForContainerURL:(NSURL *)url;
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,8 +12,7 @@
|
|||
|
||||
@implementation AudioContainer
|
||||
|
||||
+ (NSArray *) urlsForContainerURL:(NSURL *)url
|
||||
{
|
||||
+ (NSArray *)urlsForContainerURL:(NSURL *)url {
|
||||
@autoreleasepool {
|
||||
return [[PluginController sharedPluginController] urlsForContainerURL:url];
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
@interface AudioDecoder : NSObject {
|
||||
}
|
||||
|
||||
+ (id<CogDecoder>)audioDecoderForSource:(id <CogSource>)source;
|
||||
+ (id<CogDecoder>)audioDecoderForSource:(id <CogSource>)source skipCue:(BOOL)skip;
|
||||
+ (id<CogDecoder>)audioDecoderForSource:(id<CogSource>)source;
|
||||
+ (id<CogDecoder>)audioDecoderForSource:(id<CogSource>)source skipCue:(BOOL)skip;
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
|
||||
@implementation AudioDecoder
|
||||
|
||||
+ (id<CogDecoder>) audioDecoderForSource:(id <CogSource>)source
|
||||
{
|
||||
+ (id<CogDecoder>)audioDecoderForSource:(id<CogSource>)source {
|
||||
return [[PluginController sharedPluginController] audioDecoderForSource:source skipCue:NO];
|
||||
}
|
||||
|
||||
+ (id<CogDecoder>) audioDecoderForSource:(id <CogSource>)source skipCue:(BOOL)skip
|
||||
{
|
||||
+ (id<CogDecoder>)audioDecoderForSource:(id<CogSource>)source skipCue:(BOOL)skip {
|
||||
return [[PluginController sharedPluginController] audioDecoderForSource:source skipCue:skip];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface AudioMetadataReader : NSObject {
|
||||
|
||||
}
|
||||
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url;
|
||||
|
|
|
@ -11,15 +11,13 @@
|
|||
|
||||
@implementation AudioMetadataReader
|
||||
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url
|
||||
{
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url {
|
||||
@autoreleasepool {
|
||||
return [[PluginController sharedPluginController] metadataForURL:url skipCue:NO];
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url skipCue:(BOOL)skip
|
||||
{
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url skipCue:(BOOL)skip {
|
||||
@autoreleasepool {
|
||||
return [[PluginController sharedPluginController] metadataForURL:url skipCue:skip];
|
||||
}
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface AudioMetadataWriter : NSObject {
|
||||
|
||||
}
|
||||
|
||||
+ (int)putMetadataInURL:(NSURL *)url;
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
#import "PluginController.h"
|
||||
|
||||
@implementation AudioMetadataWriter
|
||||
+ (int)putMetadataInURL:(NSURL *)url
|
||||
{
|
||||
+ (int)putMetadataInURL:(NSURL *)url {
|
||||
return [[PluginController sharedPluginController] putMetadataInURL:url];
|
||||
}
|
||||
@end
|
||||
|
|
|
@ -10,10 +10,10 @@
|
|||
|
||||
#import <CogAudio/Semaphore.h>
|
||||
|
||||
#import <CoreAudio/CoreAudio.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreAudio/CoreAudio.h>
|
||||
#import <CoreAudio/CoreAudioTypes.h>
|
||||
|
||||
#import <stdatomic.h>
|
||||
|
@ -21,8 +21,7 @@
|
|||
@class BufferChain;
|
||||
@class OutputNode;
|
||||
|
||||
@interface AudioPlayer : NSObject
|
||||
{
|
||||
@interface AudioPlayer : NSObject {
|
||||
BufferChain *bufferChain;
|
||||
OutputNode *output;
|
||||
|
||||
|
@ -57,9 +56,9 @@
|
|||
- (id)delegate;
|
||||
|
||||
- (void)play:(NSURL *)url;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi startPaused:(BOOL)paused;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi startPaused:(BOOL)paused andSeekTo:(double)time;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused;
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused andSeekTo:(double)time;
|
||||
|
||||
- (void)stop;
|
||||
- (void)pause;
|
||||
|
@ -74,7 +73,7 @@
|
|||
- (double)amountPlayed;
|
||||
|
||||
- (void)setNextStream:(NSURL *)url;
|
||||
- (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi;
|
||||
- (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi;
|
||||
- (void)resetNextStreams;
|
||||
|
||||
+ (NSArray *)fileTypes;
|
||||
|
@ -83,10 +82,10 @@
|
|||
|
||||
@end
|
||||
|
||||
@interface AudioPlayer (Private) //Dont use this stuff!
|
||||
@interface AudioPlayer (Private) // Dont use this stuff!
|
||||
|
||||
- (OutputNode *) output;
|
||||
- (BufferChain *) bufferChain;
|
||||
- (OutputNode *)output;
|
||||
- (BufferChain *)bufferChain;
|
||||
- (id)initWithDelegate:(id)d;
|
||||
|
||||
- (void)setPlaybackStatus:(int)status waitUntilDone:(BOOL)wait;
|
||||
|
@ -107,7 +106,7 @@
|
|||
//- (BufferChain *)bufferChain;
|
||||
- (void)launchOutputThread;
|
||||
- (void)endOfInputPlayed;
|
||||
- (void)sendDelegateMethod:(SEL)selector withVoid:(void*)obj waitUntilDone:(BOOL)wait;
|
||||
- (void)sendDelegateMethod:(SEL)selector withVoid:(void *)obj waitUntilDone:(BOOL)wait;
|
||||
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait;
|
||||
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj withObject:(id)obj2 waitUntilDone:(BOOL)wait;
|
||||
|
||||
|
@ -115,7 +114,7 @@
|
|||
@end
|
||||
|
||||
@protocol AudioPlayerDelegate
|
||||
- (void)audioPlayer:(AudioPlayer *)player willEndStream:(id)userInfo; //You must use setNextStream in this method
|
||||
- (void)audioPlayer:(AudioPlayer *)player willEndStream:(id)userInfo; // You must use setNextStream in this method
|
||||
- (void)audioPlayer:(AudioPlayer *)player didBeginStream:(id)userInfo;
|
||||
- (void)audioPlayer:(AudioPlayer *)player didChangeStatus:(id)status userInfo:(id)userInfo;
|
||||
- (void)audioPlayer:(AudioPlayer *)player displayEqualizer:(AudioUnit)eq;
|
||||
|
@ -124,4 +123,3 @@
|
|||
- (void)audioPlayer:(AudioPlayer *)player sustainHDCD:(id)userInfo;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -8,21 +8,18 @@
|
|||
|
||||
#import "AudioPlayer.h"
|
||||
#import "BufferChain.h"
|
||||
#import "OutputNode.h"
|
||||
#import "Status.h"
|
||||
#import "Helper.h"
|
||||
#import "OutputNode.h"
|
||||
#import "PluginController.h"
|
||||
|
||||
#import "Status.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation AudioPlayer
|
||||
|
||||
- (id)init
|
||||
{
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
output = NULL;
|
||||
bufferChain = nil;
|
||||
outputLaunched = NO;
|
||||
|
@ -39,8 +36,7 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)d
|
||||
{
|
||||
- (void)setDelegate:(id)d {
|
||||
delegate = d;
|
||||
}
|
||||
|
||||
|
@ -48,42 +44,36 @@
|
|||
return delegate;
|
||||
}
|
||||
|
||||
- (void)play:(NSURL *)url
|
||||
{
|
||||
- (void)play:(NSURL *)url {
|
||||
[self play:url withUserInfo:nil withRGInfo:nil startPaused:NO andSeekTo:0.0];
|
||||
}
|
||||
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi
|
||||
{
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
|
||||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:NO andSeekTo:0.0];
|
||||
}
|
||||
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused
|
||||
{
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused {
|
||||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:paused andSeekTo:0.0];
|
||||
}
|
||||
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused andSeekTo:(double)time
|
||||
{
|
||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused andSeekTo:(double)time {
|
||||
ALog(@"Opening file for playback: %@ at seek offset %f%@", url, time, (paused) ? @", starting paused" : @"");
|
||||
|
||||
[self waitUntilCallbacksExit];
|
||||
if (output) {
|
||||
if(output) {
|
||||
[output setShouldContinue:NO];
|
||||
output = nil;
|
||||
}
|
||||
output = [[OutputNode alloc] initWithController:self previous:nil];
|
||||
[output setup];
|
||||
[output setVolume: volume];
|
||||
[output setVolume:volume];
|
||||
@synchronized(chainQueue) {
|
||||
for (id anObject in chainQueue)
|
||||
{
|
||||
for(id anObject in chainQueue) {
|
||||
[anObject setShouldContinue:NO];
|
||||
}
|
||||
[chainQueue removeAllObjects];
|
||||
endOfInputReached = NO;
|
||||
if (bufferChain)
|
||||
{
|
||||
if(bufferChain) {
|
||||
[bufferChain setShouldContinue:NO];
|
||||
|
||||
bufferChain = nil;
|
||||
|
@ -93,15 +83,13 @@
|
|||
bufferChain = [[BufferChain alloc] initWithController:self];
|
||||
[self notifyStreamChanged:userInfo];
|
||||
|
||||
while (![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi])
|
||||
{
|
||||
while(![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi]) {
|
||||
bufferChain = nil;
|
||||
|
||||
[self requestNextStream: userInfo];
|
||||
[self requestNextStream:userInfo];
|
||||
|
||||
url = nextStream;
|
||||
if (url == nil)
|
||||
{
|
||||
if(url == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -115,8 +103,7 @@
|
|||
|
||||
[bufferChain setUserInfo:userInfo];
|
||||
|
||||
if (time > 0.0)
|
||||
{
|
||||
if(time > 0.0) {
|
||||
[output seek:time];
|
||||
[bufferChain seek:time];
|
||||
}
|
||||
|
@ -129,44 +116,38 @@
|
|||
|
||||
[bufferChain launchThreads];
|
||||
|
||||
if (paused)
|
||||
if(paused)
|
||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
//Set shouldoContinue to NO on all things
|
||||
- (void)stop {
|
||||
// Set shouldoContinue to NO on all things
|
||||
[self setShouldContinue:NO];
|
||||
[self setPlaybackStatus:CogStatusStopped waitUntilDone:YES];
|
||||
|
||||
@synchronized(chainQueue) {
|
||||
for (id anObject in chainQueue)
|
||||
{
|
||||
for(id anObject in chainQueue) {
|
||||
[anObject setShouldContinue:NO];
|
||||
}
|
||||
[chainQueue removeAllObjects];
|
||||
endOfInputReached = NO;
|
||||
if (bufferChain)
|
||||
{
|
||||
if(bufferChain) {
|
||||
bufferChain = nil;
|
||||
}
|
||||
}
|
||||
output = nil;
|
||||
}
|
||||
|
||||
- (void)pause
|
||||
{
|
||||
- (void)pause {
|
||||
[output pause];
|
||||
|
||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)resume
|
||||
{
|
||||
if (startedPaused)
|
||||
{
|
||||
- (void)resume {
|
||||
if(startedPaused) {
|
||||
startedPaused = NO;
|
||||
if (initialBufferFilled)
|
||||
if(initialBufferFilled)
|
||||
[self launchOutputThread];
|
||||
}
|
||||
|
||||
|
@ -175,18 +156,16 @@
|
|||
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)seekToTime:(double)time
|
||||
{
|
||||
if (endOfInputReached)
|
||||
{
|
||||
- (void)seekToTime:(double)time {
|
||||
if(endOfInputReached) {
|
||||
// This is a dirty hack in case the playback has finished with the track
|
||||
// that the user thinks they're seeking into
|
||||
CogStatus status = (CogStatus) currentPlaybackStatus;
|
||||
CogStatus status = (CogStatus)currentPlaybackStatus;
|
||||
NSURL *url;
|
||||
id userInfo;
|
||||
NSDictionary *rgi;
|
||||
|
||||
@synchronized (chainQueue) {
|
||||
@synchronized(chainQueue) {
|
||||
url = [bufferChain streamURL];
|
||||
userInfo = [bufferChain userInfo];
|
||||
rgi = [bufferChain rgInfo];
|
||||
|
@ -195,36 +174,29 @@
|
|||
[self stop];
|
||||
|
||||
[self play:url withUserInfo:userInfo withRGInfo:rgi startPaused:(status == CogStatusPaused) andSeekTo:time];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// Still decoding the current file, safe to seek within it
|
||||
[output seek:time];
|
||||
[bufferChain seek:time];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setVolume:(double)v
|
||||
{
|
||||
- (void)setVolume:(double)v {
|
||||
volume = v;
|
||||
|
||||
[output setVolume:v];
|
||||
}
|
||||
|
||||
- (double)volume
|
||||
{
|
||||
- (double)volume {
|
||||
return volume;
|
||||
}
|
||||
|
||||
|
||||
//This is called by the delegate DURING a requestNextStream request.
|
||||
- (void)setNextStream:(NSURL *)url
|
||||
{
|
||||
// This is called by the delegate DURING a requestNextStream request.
|
||||
- (void)setNextStream:(NSURL *)url {
|
||||
[self setNextStream:url withUserInfo:nil withRGInfo:nil];
|
||||
}
|
||||
|
||||
- (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi
|
||||
{
|
||||
- (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi {
|
||||
nextStream = url;
|
||||
|
||||
nextStreamUserInfo = userInfo;
|
||||
|
@ -233,76 +205,66 @@
|
|||
}
|
||||
|
||||
// Called when the playlist changed before we actually started playing a requested stream. We will re-request.
|
||||
- (void)resetNextStreams
|
||||
{
|
||||
- (void)resetNextStreams {
|
||||
[self waitUntilCallbacksExit];
|
||||
|
||||
@synchronized (chainQueue) {
|
||||
for (id anObject in chainQueue) {
|
||||
@synchronized(chainQueue) {
|
||||
for(id anObject in chainQueue) {
|
||||
[anObject setShouldContinue:NO];
|
||||
}
|
||||
[chainQueue removeAllObjects];
|
||||
|
||||
if (endOfInputReached) {
|
||||
if(endOfInputReached) {
|
||||
[self endOfInputReached:bufferChain];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s
|
||||
{
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
shouldContinue = s;
|
||||
|
||||
if (bufferChain)
|
||||
if(bufferChain)
|
||||
[bufferChain setShouldContinue:s];
|
||||
|
||||
if (output)
|
||||
if(output)
|
||||
[output setShouldContinue:s];
|
||||
}
|
||||
|
||||
- (double)amountPlayed
|
||||
{
|
||||
- (double)amountPlayed {
|
||||
return [output amountPlayed];
|
||||
}
|
||||
|
||||
- (void)launchOutputThread
|
||||
{
|
||||
- (void)launchOutputThread {
|
||||
initialBufferFilled = YES;
|
||||
if (outputLaunched == NO && startedPaused == NO) {
|
||||
if(outputLaunched == NO && startedPaused == NO) {
|
||||
[self setPlaybackStatus:CogStatusPlaying];
|
||||
[output launchThread];
|
||||
outputLaunched = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)requestNextStream:(id)userInfo
|
||||
{
|
||||
- (void)requestNextStream:(id)userInfo {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:willEndStream:) withObject:userInfo waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)notifyStreamChanged:(id)userInfo
|
||||
{
|
||||
- (void)notifyStreamChanged:(id)userInfo {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:didBeginStream:) withObject:userInfo waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)beginEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)beginEqualizer:(AudioUnit)eq {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:displayEqualizer:) withVoid:eq waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)refreshEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)refreshEqualizer:(AudioUnit)eq {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:refreshEqualizer:) withVoid:eq waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)endEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)endEqualizer:(AudioUnit)eq {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:removeEqualizer:) withVoid:eq waitUntilDone:YES];
|
||||
}
|
||||
|
||||
- (void)addChainToQueue:(BufferChain *)newChain
|
||||
{
|
||||
[newChain setUserInfo: nextStreamUserInfo];
|
||||
- (void)addChainToQueue:(BufferChain *)newChain {
|
||||
[newChain setUserInfo:nextStreamUserInfo];
|
||||
|
||||
[newChain setShouldContinue:YES];
|
||||
[newChain launchThreads];
|
||||
|
@ -310,29 +272,27 @@
|
|||
[chainQueue insertObject:newChain atIndex:[chainQueue count]];
|
||||
}
|
||||
|
||||
- (BOOL)endOfInputReached:(BufferChain *)sender //Sender is a BufferChain
|
||||
- (BOOL)endOfInputReached:(BufferChain *)sender // Sender is a BufferChain
|
||||
{
|
||||
BufferChain *newChain = nil;
|
||||
|
||||
if (atomic_load_explicit(&resettingNow, memory_order_relaxed))
|
||||
if(atomic_load_explicit(&resettingNow, memory_order_relaxed))
|
||||
return YES;
|
||||
|
||||
atomic_fetch_add(&refCount, 1);
|
||||
|
||||
@synchronized (chainQueue) {
|
||||
@synchronized(chainQueue) {
|
||||
// No point in constructing new chain for the next playlist entry
|
||||
// if there's already one at the head of chainQueue... r-r-right?
|
||||
for (BufferChain *chain in chainQueue)
|
||||
{
|
||||
if ([chain isRunning])
|
||||
{
|
||||
for(BufferChain *chain in chainQueue) {
|
||||
if([chain isRunning]) {
|
||||
atomic_fetch_sub(&refCount, 1);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
|
||||
// We don't want to do this, it may happen with a lot of short files
|
||||
//if ([chainQueue count] >= 5)
|
||||
// if ([chainQueue count] >= 5)
|
||||
//{
|
||||
// return YES;
|
||||
//}
|
||||
|
@ -340,22 +300,21 @@
|
|||
|
||||
double duration = 0.0;
|
||||
|
||||
@synchronized (chainQueue) {
|
||||
for (BufferChain *chain in chainQueue) {
|
||||
@synchronized(chainQueue) {
|
||||
for(BufferChain *chain in chainQueue) {
|
||||
duration += [chain secondsBuffered];
|
||||
}
|
||||
}
|
||||
|
||||
while (duration >= 30.0 && shouldContinue)
|
||||
{
|
||||
while(duration >= 30.0 && shouldContinue) {
|
||||
[semaphore wait];
|
||||
if (atomic_load_explicit(&resettingNow, memory_order_relaxed)) {
|
||||
if(atomic_load_explicit(&resettingNow, memory_order_relaxed)) {
|
||||
atomic_fetch_sub(&refCount, 1);
|
||||
return YES;
|
||||
}
|
||||
@synchronized (chainQueue) {
|
||||
@synchronized(chainQueue) {
|
||||
duration = 0.0;
|
||||
for (BufferChain *chain in chainQueue) {
|
||||
for(BufferChain *chain in chainQueue) {
|
||||
duration += [chain secondsBuffered];
|
||||
}
|
||||
}
|
||||
|
@ -366,49 +325,41 @@
|
|||
nextStreamRGInfo = [sender rgInfo];
|
||||
|
||||
// This call can sometimes lead to invoking a chainQueue block on another thread
|
||||
[self requestNextStream: nextStreamUserInfo];
|
||||
[self requestNextStream:nextStreamUserInfo];
|
||||
|
||||
if (!nextStream) {
|
||||
if(!nextStream) {
|
||||
atomic_fetch_sub(&refCount, 1);
|
||||
return YES;
|
||||
}
|
||||
|
||||
@synchronized (chainQueue) {
|
||||
@synchronized(chainQueue) {
|
||||
newChain = [[BufferChain alloc] initWithController:self];
|
||||
|
||||
endOfInputReached = YES;
|
||||
|
||||
BufferChain *lastChain = [chainQueue lastObject];
|
||||
if (lastChain == nil) {
|
||||
if(lastChain == nil) {
|
||||
lastChain = bufferChain;
|
||||
}
|
||||
|
||||
BOOL pathsEqual = NO;
|
||||
|
||||
if ([nextStream isFileURL] && [[lastChain streamURL] isFileURL])
|
||||
{
|
||||
if([nextStream isFileURL] && [[lastChain streamURL] isFileURL]) {
|
||||
NSMutableString *unixPathNext = [[nextStream path] mutableCopy];
|
||||
NSMutableString *unixPathPrev = [[[lastChain streamURL] path] mutableCopy];
|
||||
|
||||
if ([unixPathNext isEqualToString:unixPathPrev])
|
||||
if([unixPathNext isEqualToString:unixPathPrev])
|
||||
pathsEqual = YES;
|
||||
}
|
||||
|
||||
if (pathsEqual || ([[nextStream scheme] isEqualToString:[[lastChain streamURL] scheme]]
|
||||
&& (([nextStream host] == nil &&
|
||||
[[lastChain streamURL] host] == nil)
|
||||
|| [[nextStream host] isEqualToString:[[lastChain streamURL] host]])
|
||||
&& [[nextStream path] isEqualToString:[[lastChain streamURL] path]]))
|
||||
{
|
||||
if ([lastChain setTrack:nextStream]
|
||||
&& [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withRGInfo:nextStreamRGInfo])
|
||||
{
|
||||
if(pathsEqual || ([[nextStream scheme] isEqualToString:[[lastChain streamURL] scheme]] && (([nextStream host] == nil && [[lastChain streamURL] host] == nil) || [[nextStream host] isEqualToString:[[lastChain streamURL] host]]) && [[nextStream path] isEqualToString:[[lastChain streamURL] path]])) {
|
||||
if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withRGInfo:nextStreamRGInfo]) {
|
||||
[newChain setStreamURL:nextStream];
|
||||
[newChain setUserInfo:nextStreamUserInfo];
|
||||
|
||||
[self addChainToQueue:newChain];
|
||||
DLog(@"TRACK SET!!! %@", newChain);
|
||||
//Keep on-playin
|
||||
// Keep on-playin
|
||||
newChain = nil;
|
||||
|
||||
atomic_fetch_sub(&refCount, 1);
|
||||
|
@ -418,17 +369,15 @@
|
|||
|
||||
lastChain = nil;
|
||||
|
||||
while (shouldContinue && ![newChain open:nextStream withOutputFormat:[output format] withRGInfo:nextStreamRGInfo])
|
||||
{
|
||||
if (nextStream == nil)
|
||||
{
|
||||
while(shouldContinue && ![newChain open:nextStream withOutputFormat:[output format] withRGInfo:nextStreamRGInfo]) {
|
||||
if(nextStream == nil) {
|
||||
newChain = nil;
|
||||
atomic_fetch_sub(&refCount, 1);
|
||||
return YES;
|
||||
}
|
||||
|
||||
newChain = nil;
|
||||
[self requestNextStream: nextStreamUserInfo];
|
||||
[self requestNextStream:nextStreamUserInfo];
|
||||
|
||||
newChain = [[BufferChain alloc] initWithController:self];
|
||||
}
|
||||
|
@ -453,8 +402,7 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)endOfInputPlayed
|
||||
{
|
||||
- (void)endOfInputPlayed {
|
||||
// Once we get here:
|
||||
// - the buffer chain for the next playlist entry (started in endOfInputReached) have been working for some time
|
||||
// already, so that there is some decoded and converted data to play
|
||||
|
@ -463,9 +411,8 @@
|
|||
@synchronized(chainQueue) {
|
||||
endOfInputReached = NO;
|
||||
|
||||
if ([chainQueue count] <= 0)
|
||||
{
|
||||
//End of playlist
|
||||
if([chainQueue count] <= 0) {
|
||||
// End of playlist
|
||||
[self stop];
|
||||
|
||||
bufferChain = nil;
|
||||
|
@ -486,44 +433,40 @@
|
|||
[output setEndOfStream:NO];
|
||||
}
|
||||
|
||||
- (BOOL)chainQueueHasTracks
|
||||
{
|
||||
@synchronized (chainQueue) {
|
||||
- (BOOL)chainQueueHasTracks {
|
||||
@synchronized(chainQueue) {
|
||||
return [chainQueue count] > 0;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)sendDelegateMethod:(SEL)selector withVoid:(void*)obj waitUntilDone:(BOOL)wait
|
||||
{
|
||||
- (void)sendDelegateMethod:(SEL)selector withVoid:(void *)obj waitUntilDone:(BOOL)wait {
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:delegate];
|
||||
[invocation setSelector:selector];
|
||||
[invocation setArgument:(void*)&self atIndex:2];
|
||||
[invocation setArgument:(void *)&self atIndex:2];
|
||||
[invocation setArgument:&obj atIndex:3];
|
||||
[invocation retainArguments];
|
||||
|
||||
[invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:wait];
|
||||
}
|
||||
|
||||
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait
|
||||
{
|
||||
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait {
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:delegate];
|
||||
[invocation setSelector:selector];
|
||||
[invocation setArgument:(void*)&self atIndex:2];
|
||||
[invocation setArgument:(void *)&self atIndex:2];
|
||||
[invocation setArgument:&obj atIndex:3];
|
||||
[invocation retainArguments];
|
||||
|
||||
[invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:wait];
|
||||
}
|
||||
|
||||
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj withObject:(id)obj2 waitUntilDone:(BOOL)wait
|
||||
{
|
||||
- (void)sendDelegateMethod:(SEL)selector withObject:(id)obj withObject:(id)obj2 waitUntilDone:(BOOL)wait {
|
||||
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:selector]];
|
||||
[invocation setTarget:delegate];
|
||||
[invocation setSelector:selector];
|
||||
[invocation setArgument:(void*)&self atIndex:2];
|
||||
[invocation setArgument:(void *)&self atIndex:2];
|
||||
[invocation setArgument:&obj atIndex:3];
|
||||
[invocation setArgument:&obj2 atIndex:4];
|
||||
[invocation retainArguments];
|
||||
|
@ -531,41 +474,33 @@
|
|||
[invocation performSelectorOnMainThread:@selector(invoke) withObject:nil waitUntilDone:wait];
|
||||
}
|
||||
|
||||
|
||||
- (void)setPlaybackStatus:(int)status waitUntilDone:(BOOL)wait
|
||||
{
|
||||
- (void)setPlaybackStatus:(int)status waitUntilDone:(BOOL)wait {
|
||||
currentPlaybackStatus = status;
|
||||
|
||||
[self sendDelegateMethod:@selector(audioPlayer:didChangeStatus:userInfo:) withObject:[NSNumber numberWithInt:status] withObject:[bufferChain userInfo] waitUntilDone:wait];
|
||||
}
|
||||
|
||||
- (void)sustainHDCD
|
||||
{
|
||||
- (void)sustainHDCD {
|
||||
[self sendDelegateMethod:@selector(audioPlayer:sustainHDCD:) withObject:[bufferChain userInfo] waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (void)setPlaybackStatus:(int)status
|
||||
{
|
||||
- (void)setPlaybackStatus:(int)status {
|
||||
[self setPlaybackStatus:status waitUntilDone:NO];
|
||||
}
|
||||
|
||||
- (BufferChain *)bufferChain
|
||||
{
|
||||
- (BufferChain *)bufferChain {
|
||||
return bufferChain;
|
||||
}
|
||||
|
||||
- (OutputNode *) output
|
||||
{
|
||||
- (OutputNode *)output {
|
||||
return output;
|
||||
}
|
||||
|
||||
+ (NSArray *)containerTypes
|
||||
{
|
||||
+ (NSArray *)containerTypes {
|
||||
return [[[PluginController sharedPluginController] containers] allKeys];
|
||||
}
|
||||
|
||||
+ (NSArray *)fileTypes
|
||||
{
|
||||
+ (NSArray *)fileTypes {
|
||||
PluginController *pluginController = [PluginController sharedPluginController];
|
||||
|
||||
NSArray *containerTypes = [[pluginController containers] allKeys];
|
||||
|
@ -583,20 +518,18 @@
|
|||
return [types allObjects];
|
||||
}
|
||||
|
||||
+ (NSArray *)schemes
|
||||
{
|
||||
+ (NSArray *)schemes {
|
||||
PluginController *pluginController = [PluginController sharedPluginController];
|
||||
|
||||
return [[pluginController sources] allKeys];
|
||||
}
|
||||
|
||||
- (double)volumeUp:(double)amount
|
||||
{
|
||||
- (double)volumeUp:(double)amount {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
double newVolume = linearToLogarithmic(logarithmicToLinear(volume + amount, MAX_VOLUME), MAX_VOLUME);
|
||||
if (newVolume > MAX_VOLUME)
|
||||
if(newVolume > MAX_VOLUME)
|
||||
newVolume = MAX_VOLUME;
|
||||
|
||||
[self setVolume:newVolume];
|
||||
|
@ -606,13 +539,12 @@
|
|||
return newVolume;
|
||||
}
|
||||
|
||||
- (double)volumeDown:(double)amount
|
||||
{
|
||||
- (double)volumeDown:(double)amount {
|
||||
BOOL volumeLimit = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"volumeLimit"];
|
||||
const double MAX_VOLUME = (volumeLimit) ? 100.0 : 800.0;
|
||||
|
||||
double newVolume;
|
||||
if (amount > volume)
|
||||
if(amount > volume)
|
||||
newVolume = 0.0;
|
||||
else
|
||||
newVolume = linearToLogarithmic(logarithmicToLinear(volume - amount, MAX_VOLUME), MAX_VOLUME);
|
||||
|
@ -621,17 +553,16 @@
|
|||
return newVolume;
|
||||
}
|
||||
|
||||
- (void)waitUntilCallbacksExit
|
||||
{
|
||||
- (void)waitUntilCallbacksExit {
|
||||
// This sucks! And since the thread that's inside the function can be calling
|
||||
// event dispatches, we have to pump the message queue if we're on the main
|
||||
// thread. Damn.
|
||||
if (atomic_load_explicit(&refCount, memory_order_relaxed) != 0) {
|
||||
if(atomic_load_explicit(&refCount, memory_order_relaxed) != 0) {
|
||||
BOOL mainThread = (dispatch_queue_get_label(dispatch_get_main_queue()) == dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL));
|
||||
atomic_store(&resettingNow, true);
|
||||
while (atomic_load_explicit(&refCount, memory_order_relaxed) != 0) {
|
||||
while(atomic_load_explicit(&refCount, memory_order_relaxed) != 0) {
|
||||
[semaphore signal]; // Gotta poke this periodically
|
||||
if (mainThread)
|
||||
if(mainThread)
|
||||
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.001]];
|
||||
else
|
||||
usleep(500);
|
||||
|
@ -640,5 +571,4 @@
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface AudioPropertiesReader : NSObject {
|
||||
|
||||
}
|
||||
|
||||
+ (NSDictionary *)propertiesForURL:(NSURL *)url;
|
||||
|
|
|
@ -13,8 +13,7 @@
|
|||
|
||||
@implementation AudioPropertiesReader
|
||||
|
||||
+ (NSDictionary *)propertiesForURL:(NSURL *)url
|
||||
{
|
||||
+ (NSDictionary *)propertiesForURL:(NSURL *)url {
|
||||
@autoreleasepool {
|
||||
return [[PluginController sharedPluginController] propertiesForURL:url];
|
||||
}
|
||||
|
|
|
@ -13,6 +13,6 @@
|
|||
@interface AudioSource : NSObject {
|
||||
}
|
||||
|
||||
+ (id<CogSource>) audioSourceForURL:(NSURL *)url;
|
||||
+ (id<CogSource>)audioSourceForURL:(NSURL *)url;
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#import "AudioSource.h"
|
||||
|
||||
|
||||
@implementation AudioSource
|
||||
|
||||
+ (id<CogSource>) audioSourceForURL:(NSURL *)url
|
||||
{
|
||||
+ (id<CogSource>)audioSourceForURL:(NSURL *)url {
|
||||
return [[PluginController sharedPluginController] audioSourceForURL:url];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,14 +8,14 @@
|
|||
#ifndef AudioChunk_h
|
||||
#define AudioChunk_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreAudio/CoreAudio.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface AudioChunk : NSObject {
|
||||
AudioStreamBasicDescription format;
|
||||
NSMutableData * chunkData;
|
||||
NSMutableData *chunkData;
|
||||
BOOL formatAssigned;
|
||||
BOOL lossless;
|
||||
}
|
||||
|
@ -23,17 +23,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property AudioStreamBasicDescription format;
|
||||
@property BOOL lossless;
|
||||
|
||||
- (id) init;
|
||||
- (id)init;
|
||||
|
||||
- (void) assignSamples:(const void *)data frameCount:(size_t)count;
|
||||
- (void)assignSamples:(const void *)data frameCount:(size_t)count;
|
||||
|
||||
- (NSData *) removeSamples:(size_t)frameCount;
|
||||
- (NSData *)removeSamples:(size_t)frameCount;
|
||||
|
||||
- (BOOL) isEmpty;
|
||||
- (BOOL)isEmpty;
|
||||
|
||||
- (size_t) frameCount;
|
||||
- (size_t)frameCount;
|
||||
|
||||
- (double) duration;
|
||||
- (double)duration;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
|
||||
@implementation AudioChunk
|
||||
|
||||
- (id) init {
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
if(self) {
|
||||
chunkData = [[NSMutableData alloc] init];
|
||||
formatAssigned = NO;
|
||||
lossless = NO;
|
||||
|
@ -23,47 +23,47 @@
|
|||
|
||||
@synthesize lossless;
|
||||
|
||||
- (AudioStreamBasicDescription) format {
|
||||
- (AudioStreamBasicDescription)format {
|
||||
return format;
|
||||
}
|
||||
|
||||
- (void) setFormat:(AudioStreamBasicDescription)informat {
|
||||
- (void)setFormat:(AudioStreamBasicDescription)informat {
|
||||
formatAssigned = YES;
|
||||
format = informat;
|
||||
}
|
||||
|
||||
- (void) assignSamples:(const void *)data frameCount:(size_t)count {
|
||||
if (formatAssigned) {
|
||||
- (void)assignSamples:(const void *)data frameCount:(size_t)count {
|
||||
if(formatAssigned) {
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
[chunkData appendBytes:data length:bytesPerPacket * count];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSData *) removeSamples:(size_t)frameCount {
|
||||
if (formatAssigned) {
|
||||
- (NSData *)removeSamples:(size_t)frameCount {
|
||||
if(formatAssigned) {
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
const size_t byteCount = bytesPerPacket * frameCount;
|
||||
NSData * ret = [chunkData subdataWithRange:NSMakeRange(0, byteCount)];
|
||||
NSData *ret = [chunkData subdataWithRange:NSMakeRange(0, byteCount)];
|
||||
[chunkData replaceBytesInRange:NSMakeRange(0, byteCount) withBytes:NULL length:0];
|
||||
return ret;
|
||||
}
|
||||
return [NSData data];
|
||||
}
|
||||
|
||||
- (BOOL) isEmpty {
|
||||
- (BOOL)isEmpty {
|
||||
return [chunkData length] == 0;
|
||||
}
|
||||
|
||||
- (size_t) frameCount {
|
||||
if (formatAssigned) {
|
||||
- (size_t)frameCount {
|
||||
if(formatAssigned) {
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
return [chunkData length] / bytesPerPacket;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (double) duration {
|
||||
if (formatAssigned) {
|
||||
- (double)duration {
|
||||
if(formatAssigned) {
|
||||
const size_t bytesPerPacket = format.mBytesPerPacket;
|
||||
const double sampleRate = format.mSampleRate;
|
||||
return (double)([chunkData length] / bytesPerPacket) / sampleRate;
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "InputNode.h"
|
||||
#import "ConverterNode.h"
|
||||
#import "AudioPlayer.h"
|
||||
#import "ConverterNode.h"
|
||||
#import "InputNode.h"
|
||||
|
||||
@interface BufferChain : NSObject {
|
||||
InputNode *inputNode;
|
||||
|
@ -22,7 +22,7 @@
|
|||
id userInfo;
|
||||
NSDictionary *rgInfo;
|
||||
|
||||
id finalNode; //Final buffer in the chain.
|
||||
id finalNode; // Final buffer in the chain.
|
||||
|
||||
id controller;
|
||||
}
|
||||
|
@ -30,15 +30,15 @@
|
|||
- (id)initWithController:(id)c;
|
||||
- (void)buildChain;
|
||||
|
||||
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
|
||||
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
//Used when changing tracks to reuse the same decoder
|
||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
|
||||
// Used when changing tracks to reuse the same decoder
|
||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
//Used when resetting the decoder on seek
|
||||
// Used when resetting the decoder on seek
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
|
||||
withOutputFormat:(AudioStreamBasicDescription)outputFormat
|
||||
withRGInfo:(NSDictionary*)rgi;
|
||||
withRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
- (void)seek:(double)time;
|
||||
|
||||
|
@ -51,7 +51,7 @@
|
|||
- (id)userInfo;
|
||||
- (void)setUserInfo:(id)i;
|
||||
|
||||
- (NSDictionary*)rgInfo;
|
||||
- (NSDictionary *)rgInfo;
|
||||
- (void)setRGInfo:(NSDictionary *)rgi;
|
||||
|
||||
- (NSURL *)streamURL;
|
||||
|
|
|
@ -7,19 +7,17 @@
|
|||
//
|
||||
|
||||
#import "BufferChain.h"
|
||||
#import "OutputNode.h"
|
||||
#import "AudioSource.h"
|
||||
#import "CoreAudioUtils.h"
|
||||
#import "OutputNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation BufferChain
|
||||
|
||||
- (id)initWithController:(id)c
|
||||
{
|
||||
- (id)initWithController:(id)c {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
controller = c;
|
||||
streamURL = nil;
|
||||
userInfo = nil;
|
||||
|
@ -32,8 +30,7 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)buildChain
|
||||
{
|
||||
- (void)buildChain {
|
||||
inputNode = nil;
|
||||
converterNode = nil;
|
||||
|
||||
|
@ -43,27 +40,25 @@
|
|||
finalNode = converterNode;
|
||||
}
|
||||
|
||||
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi
|
||||
{
|
||||
- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi {
|
||||
[self setStreamURL:url];
|
||||
|
||||
[self buildChain];
|
||||
|
||||
id<CogSource> source = [AudioSource audioSourceForURL:url];
|
||||
DLog(@"Opening: %@", url);
|
||||
if (!source || ![source open:url])
|
||||
{
|
||||
if(!source || ![source open:url]) {
|
||||
DLog(@"Couldn't open source...");
|
||||
url = [NSURL URLWithString:@"silence://10"];
|
||||
source = [AudioSource audioSourceForURL:url];
|
||||
if (![source open:url])
|
||||
if(![source open:url])
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![inputNode openWithSource:source])
|
||||
if(![inputNode openWithSource:source])
|
||||
return NO;
|
||||
|
||||
NSDictionary * properties = [inputNode properties];
|
||||
NSDictionary *properties = [inputNode properties];
|
||||
|
||||
inputFormat = [inputNode nodeFormat];
|
||||
|
||||
|
@ -71,25 +66,24 @@
|
|||
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
|
||||
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
|
||||
|
||||
if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
if(![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
return NO;
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
||||
// return NO;
|
||||
// return NO;
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi
|
||||
{
|
||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi {
|
||||
DLog(@"New buffer chain!");
|
||||
[self buildChain];
|
||||
|
||||
if (![inputNode openWithDecoder:[i decoder]])
|
||||
if(![inputNode openWithDecoder:[i decoder]])
|
||||
return NO;
|
||||
|
||||
NSDictionary * properties = [inputNode properties];
|
||||
NSDictionary *properties = [inputNode properties];
|
||||
|
||||
inputFormat = [inputNode nodeFormat];
|
||||
|
||||
|
@ -98,7 +92,7 @@
|
|||
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
|
||||
|
||||
DLog(@"Input Properties: %@", properties);
|
||||
if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
if(![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
return NO;
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
@ -108,15 +102,15 @@
|
|||
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
|
||||
withOutputFormat:(AudioStreamBasicDescription)outputFormat
|
||||
withRGInfo:(NSDictionary*)rgi;
|
||||
withRGInfo:(NSDictionary *)rgi;
|
||||
{
|
||||
DLog(@"New buffer chain!");
|
||||
[self buildChain];
|
||||
|
||||
if (![inputNode openWithDecoder:decoder])
|
||||
if(![inputNode openWithDecoder:decoder])
|
||||
return NO;
|
||||
|
||||
NSDictionary * properties = [inputNode properties];
|
||||
NSDictionary *properties = [inputNode properties];
|
||||
|
||||
DLog(@"Input Properties: %@", properties);
|
||||
|
||||
|
@ -126,7 +120,7 @@
|
|||
outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame;
|
||||
outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket;
|
||||
|
||||
if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
if(![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]])
|
||||
return NO;
|
||||
|
||||
[self setRGInfo:rgi];
|
||||
|
@ -134,37 +128,31 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (void)launchThreads
|
||||
{
|
||||
- (void)launchThreads {
|
||||
DLog(@"Properties: %@", [inputNode properties]);
|
||||
|
||||
[inputNode launchThread];
|
||||
[converterNode launchThread];
|
||||
}
|
||||
|
||||
- (void)setUserInfo:(id)i
|
||||
{
|
||||
- (void)setUserInfo:(id)i {
|
||||
userInfo = i;
|
||||
}
|
||||
|
||||
- (id)userInfo
|
||||
{
|
||||
- (id)userInfo {
|
||||
return userInfo;
|
||||
}
|
||||
|
||||
- (void)setRGInfo:(NSDictionary *)rgi
|
||||
{
|
||||
- (void)setRGInfo:(NSDictionary *)rgi {
|
||||
rgInfo = rgi;
|
||||
[converterNode setRGInfo:rgi];
|
||||
}
|
||||
|
||||
- (NSDictionary *)rgInfo
|
||||
{
|
||||
- (NSDictionary *)rgInfo {
|
||||
return rgInfo;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[inputNode setShouldContinue:NO];
|
||||
[[inputNode exitAtTheEndOfTheStream] signal];
|
||||
[[inputNode semaphore] signal];
|
||||
|
@ -173,103 +161,85 @@
|
|||
DLog(@"Bufferchain dealloc");
|
||||
}
|
||||
|
||||
- (void)seek:(double)time
|
||||
{
|
||||
long frame = (long) round(time * [[[inputNode properties] objectForKey:@"sampleRate"] floatValue]);
|
||||
- (void)seek:(double)time {
|
||||
long frame = (long)round(time * [[[inputNode properties] objectForKey:@"sampleRate"] floatValue]);
|
||||
|
||||
[inputNode seek:frame];
|
||||
}
|
||||
|
||||
- (BOOL)endOfInputReached
|
||||
{
|
||||
- (BOOL)endOfInputReached {
|
||||
return [controller endOfInputReached:self];
|
||||
}
|
||||
|
||||
- (BOOL)setTrack: (NSURL *)track
|
||||
{
|
||||
- (BOOL)setTrack:(NSURL *)track {
|
||||
return [inputNode setTrack:track];
|
||||
}
|
||||
|
||||
- (void)initialBufferFilled:(id)sender
|
||||
{
|
||||
- (void)initialBufferFilled:(id)sender {
|
||||
DLog(@"INITIAL BUFFER FILLED");
|
||||
[controller launchOutputThread];
|
||||
}
|
||||
|
||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format
|
||||
{
|
||||
- (void)inputFormatDidChange:(AudioStreamBasicDescription)format {
|
||||
DLog(@"FORMAT DID CHANGE!");
|
||||
}
|
||||
|
||||
|
||||
- (InputNode *)inputNode
|
||||
{
|
||||
- (InputNode *)inputNode {
|
||||
return inputNode;
|
||||
}
|
||||
|
||||
- (id)finalNode
|
||||
{
|
||||
- (id)finalNode {
|
||||
return finalNode;
|
||||
}
|
||||
|
||||
- (NSURL *)streamURL
|
||||
{
|
||||
- (NSURL *)streamURL {
|
||||
return streamURL;
|
||||
}
|
||||
|
||||
- (void)setStreamURL:(NSURL *)url
|
||||
{
|
||||
- (void)setStreamURL:(NSURL *)url {
|
||||
streamURL = url;
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s
|
||||
{
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
[inputNode setShouldContinue:s];
|
||||
[converterNode setShouldContinue:s];
|
||||
}
|
||||
|
||||
- (BOOL)isRunning
|
||||
{
|
||||
- (BOOL)isRunning {
|
||||
InputNode *node = [self inputNode];
|
||||
if (nil != node && [node shouldContinue] && ![node endOfStream])
|
||||
{
|
||||
if(nil != node && [node shouldContinue] && ![node endOfStream]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)controller
|
||||
{
|
||||
- (id)controller {
|
||||
return controller;
|
||||
}
|
||||
|
||||
- (ConverterNode *)converter
|
||||
{
|
||||
- (ConverterNode *)converter {
|
||||
return converterNode;
|
||||
}
|
||||
|
||||
- (AudioStreamBasicDescription)inputFormat
|
||||
{
|
||||
- (AudioStreamBasicDescription)inputFormat {
|
||||
return inputFormat;
|
||||
}
|
||||
|
||||
- (double)secondsBuffered
|
||||
{
|
||||
- (double)secondsBuffered {
|
||||
double duration = 0.0;
|
||||
OutputNode * outputNode = (OutputNode *) [controller output];
|
||||
OutputNode *outputNode = (OutputNode *)[controller output];
|
||||
duration += [outputNode secondsBuffered];
|
||||
|
||||
Node * node = [self finalNode];
|
||||
while (node) {
|
||||
Node *node = [self finalNode];
|
||||
while(node) {
|
||||
duration += [node secondsBuffered];
|
||||
node = [node previousNode];
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
- (void)sustainHDCD
|
||||
{
|
||||
OutputNode * outputNode = (OutputNode *) [controller output];
|
||||
- (void)sustainHDCD {
|
||||
OutputNode *outputNode = (OutputNode *)[controller output];
|
||||
[outputNode sustainHDCD];
|
||||
[controller sustainHDCD];
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
// Created by Christopher Snowhill on 2/5/22.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreAudio/CoreAudio.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AudioChunk.h"
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
|||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ChunkList : NSObject {
|
||||
NSMutableArray<AudioChunk *> * chunkList;
|
||||
NSMutableArray<AudioChunk *> *chunkList;
|
||||
double listDuration;
|
||||
double maxDuration;
|
||||
|
||||
|
@ -24,18 +24,18 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
BOOL stopping;
|
||||
}
|
||||
|
||||
@property (readonly) double listDuration;
|
||||
@property (readonly) double maxDuration;
|
||||
@property(readonly) double listDuration;
|
||||
@property(readonly) double maxDuration;
|
||||
|
||||
- (id) initWithMaximumDuration:(double)duration;
|
||||
- (id)initWithMaximumDuration:(double)duration;
|
||||
|
||||
- (void) reset;
|
||||
- (void)reset;
|
||||
|
||||
- (BOOL) isEmpty;
|
||||
- (BOOL) isFull;
|
||||
- (BOOL)isEmpty;
|
||||
- (BOOL)isFull;
|
||||
|
||||
- (void) addChunk:(AudioChunk *)chunk;
|
||||
- (AudioChunk *) removeSamples:(size_t)maxFrameCount;
|
||||
- (void)addChunk:(AudioChunk *)chunk;
|
||||
- (AudioChunk *)removeSamples:(size_t)maxFrameCount;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
@synthesize listDuration;
|
||||
@synthesize maxDuration;
|
||||
|
||||
- (id) initWithMaximumDuration:(double)duration {
|
||||
- (id)initWithMaximumDuration:(double)duration {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
if(self) {
|
||||
chunkList = [[NSMutableArray alloc] init];
|
||||
listDuration = 0.0;
|
||||
maxDuration = duration;
|
||||
|
@ -28,32 +28,32 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
- (void)dealloc {
|
||||
stopping = YES;
|
||||
while (inAdder || inRemover) {
|
||||
while(inAdder || inRemover) {
|
||||
usleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
- (void) reset {
|
||||
@synchronized (chunkList) {
|
||||
- (void)reset {
|
||||
@synchronized(chunkList) {
|
||||
[chunkList removeAllObjects];
|
||||
listDuration = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) isEmpty {
|
||||
@synchronized (chunkList) {
|
||||
- (BOOL)isEmpty {
|
||||
@synchronized(chunkList) {
|
||||
return [chunkList count] == 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) isFull {
|
||||
- (BOOL)isFull {
|
||||
return listDuration >= maxDuration;
|
||||
}
|
||||
|
||||
- (void) addChunk:(AudioChunk *)chunk {
|
||||
if (stopping) return;
|
||||
- (void)addChunk:(AudioChunk *)chunk {
|
||||
if(stopping) return;
|
||||
|
||||
inAdder = YES;
|
||||
|
||||
|
@ -67,24 +67,24 @@
|
|||
inAdder = NO;
|
||||
}
|
||||
|
||||
- (AudioChunk *) removeSamples:(size_t)maxFrameCount {
|
||||
if (stopping) {
|
||||
- (AudioChunk *)removeSamples:(size_t)maxFrameCount {
|
||||
if(stopping) {
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
@synchronized (chunkList) {
|
||||
@synchronized(chunkList) {
|
||||
inRemover = YES;
|
||||
if (![chunkList count])
|
||||
if(![chunkList count])
|
||||
return [[AudioChunk alloc] init];
|
||||
AudioChunk * chunk = [chunkList objectAtIndex:0];
|
||||
if ([chunk frameCount] <= maxFrameCount) {
|
||||
AudioChunk *chunk = [chunkList objectAtIndex:0];
|
||||
if([chunk frameCount] <= maxFrameCount) {
|
||||
[chunkList removeObjectAtIndex:0];
|
||||
listDuration -= [chunk duration];
|
||||
inRemover = NO;
|
||||
return chunk;
|
||||
}
|
||||
NSData * removedData = [chunk removeSamples:maxFrameCount];
|
||||
AudioChunk * ret = [[AudioChunk alloc] init];
|
||||
NSData *removedData = [chunk removeSamples:maxFrameCount];
|
||||
AudioChunk *ret = [[AudioChunk alloc] init];
|
||||
[ret setFormat:[chunk format]];
|
||||
[ret assignSamples:[removedData bytes] frameCount:maxFrameCount];
|
||||
listDuration -= [ret duration];
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import <soxr.h>
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
|||
#import "HeadphoneFilter.h"
|
||||
|
||||
@interface ConverterNode : Node {
|
||||
NSDictionary * rgInfo;
|
||||
NSDictionary *rgInfo;
|
||||
|
||||
soxr_t soxr;
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -6,8 +6,8 @@
|
|||
// Copyright 2022 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreAudio/CoreAudio.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "HeadphoneFilter.h"
|
||||
|
||||
|
@ -18,9 +18,8 @@
|
|||
AudioStreamBasicDescription outputFormat;
|
||||
}
|
||||
|
||||
- (id) initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf;
|
||||
- (id)initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf;
|
||||
|
||||
- (void) process:(const void*)inBuffer frameCount:(size_t)frames output:(void *)outBuffer;
|
||||
- (void)process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -10,45 +10,59 @@
|
|||
|
||||
#import "Logging.h"
|
||||
|
||||
static const float STEREO_DOWNMIX[8-2][8][2]={
|
||||
static const float STEREO_DOWNMIX[8 - 2][8][2] = {
|
||||
/*3.0*/
|
||||
{
|
||||
{0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F}
|
||||
},
|
||||
{ 0.5858F, 0.0F },
|
||||
{ 0.0F, 0.5858F },
|
||||
{ 0.4142F, 0.4142F } },
|
||||
/*quadrophonic*/
|
||||
{
|
||||
{0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F}
|
||||
},
|
||||
{ 0.4226F, 0.0F },
|
||||
{ 0.0F, 0.4226F },
|
||||
{ 0.366F, 0.2114F },
|
||||
{ 0.2114F, 0.336F } },
|
||||
/*5.0*/
|
||||
{
|
||||
{0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F},
|
||||
{0.3254F,0.5636F}
|
||||
},
|
||||
{ 0.651F, 0.0F },
|
||||
{ 0.0F, 0.651F },
|
||||
{ 0.46F, 0.46F },
|
||||
{ 0.5636F, 0.3254F },
|
||||
{ 0.3254F, 0.5636F } },
|
||||
/*5.1*/
|
||||
{
|
||||
{0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F},
|
||||
{0.2645F,0.4582F}
|
||||
},
|
||||
{ 0.529F, 0.0F },
|
||||
{ 0.0F, 0.529F },
|
||||
{ 0.3741F, 0.3741F },
|
||||
{ 0.3741F, 0.3741F },
|
||||
{ 0.4582F, 0.2645F },
|
||||
{ 0.2645F, 0.4582F } },
|
||||
/*6.1*/
|
||||
{
|
||||
{0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F},
|
||||
{0.3943F,0.2277F},{0.2277F,0.3943F}
|
||||
},
|
||||
{ 0.4553F, 0.0F },
|
||||
{ 0.0F, 0.4553F },
|
||||
{ 0.322F, 0.322F },
|
||||
{ 0.322F, 0.322F },
|
||||
{ 0.2788F, 0.2788F },
|
||||
{ 0.3943F, 0.2277F },
|
||||
{ 0.2277F, 0.3943F } },
|
||||
/*7.1*/
|
||||
{
|
||||
{0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F},
|
||||
{0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F}
|
||||
}
|
||||
{ 0.3886F, 0.0F },
|
||||
{ 0.0F, 0.3886F },
|
||||
{ 0.2748F, 0.2748F },
|
||||
{ 0.2748F, 0.2748F },
|
||||
{ 0.3366F, 0.1943F },
|
||||
{ 0.1943F, 0.3366F },
|
||||
{ 0.3366F, 0.1943F },
|
||||
{ 0.1943F, 0.3366F } }
|
||||
};
|
||||
|
||||
static void downmix_to_stereo(const float * inBuffer, int channels, float * outBuffer, size_t count)
|
||||
{
|
||||
if (channels >= 3 && channels <= 8)
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
static void downmix_to_stereo(const float *inBuffer, int channels, float *outBuffer, size_t count) {
|
||||
if(channels >= 3 && channels <= 8)
|
||||
for(size_t i = 0; i < count; ++i) {
|
||||
float left = 0, right = 0;
|
||||
for (int j = 0; j < channels; ++j)
|
||||
{
|
||||
for(int j = 0; j < channels; ++j) {
|
||||
left += inBuffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0];
|
||||
right += inBuffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1];
|
||||
}
|
||||
|
@ -57,63 +71,48 @@ static void downmix_to_stereo(const float * inBuffer, int channels, float * outB
|
|||
}
|
||||
}
|
||||
|
||||
static void downmix_to_mono(const float * inBuffer, int channels, float * outBuffer, size_t count)
|
||||
{
|
||||
static void downmix_to_mono(const float *inBuffer, int channels, float *outBuffer, size_t count) {
|
||||
float tempBuffer[count * 2];
|
||||
if (channels >= 3 && channels <= 8)
|
||||
{
|
||||
if(channels >= 3 && channels <= 8) {
|
||||
downmix_to_stereo(inBuffer, channels, tempBuffer, count);
|
||||
inBuffer = tempBuffer;
|
||||
channels = 2;
|
||||
}
|
||||
float invchannels = 1.0 / (float)channels;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
for(size_t i = 0; i < count; ++i) {
|
||||
float sample = 0;
|
||||
for (int j = 0; j < channels; ++j)
|
||||
{
|
||||
for(int j = 0; j < channels; ++j) {
|
||||
sample += inBuffer[i * channels + j];
|
||||
}
|
||||
outBuffer[i] = sample * invchannels;
|
||||
}
|
||||
}
|
||||
|
||||
static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int outchannels, size_t count)
|
||||
{
|
||||
for (ssize_t i = 0; i < count; ++i)
|
||||
{
|
||||
if (inchannels == 1 && outchannels == 2)
|
||||
{
|
||||
static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int outchannels, size_t count) {
|
||||
for(ssize_t i = 0; i < count; ++i) {
|
||||
if(inchannels == 1 && outchannels == 2) {
|
||||
// upmix mono to stereo
|
||||
float sample = inBuffer[i];
|
||||
outBuffer[i * 2 + 0] = sample;
|
||||
outBuffer[i * 2 + 1] = sample;
|
||||
}
|
||||
else if (inchannels == 1 && outchannels == 4)
|
||||
{
|
||||
} else if(inchannels == 1 && outchannels == 4) {
|
||||
// upmix mono to quad
|
||||
float sample = inBuffer[i];
|
||||
outBuffer[i * 4 + 0] = sample;
|
||||
outBuffer[i * 4 + 1] = sample;
|
||||
outBuffer[i * 4 + 2] = 0;
|
||||
outBuffer[i * 4 + 3] = 0;
|
||||
}
|
||||
else if (inchannels == 1 && (outchannels == 3 || outchannels >= 5))
|
||||
{
|
||||
} else if(inchannels == 1 && (outchannels == 3 || outchannels >= 5)) {
|
||||
// upmix mono to center channel
|
||||
float sample = inBuffer[i];
|
||||
outBuffer[i * outchannels + 2] = sample;
|
||||
for (int j = 0; j < 2; ++j)
|
||||
{
|
||||
for(int j = 0; j < 2; ++j) {
|
||||
outBuffer[i * outchannels + j] = 0;
|
||||
}
|
||||
for (int j = 3; j < outchannels; ++j)
|
||||
{
|
||||
for(int j = 3; j < outchannels; ++j) {
|
||||
outBuffer[i * outchannels + j] = 0;
|
||||
}
|
||||
}
|
||||
else if (inchannels == 4 && outchannels >= 5)
|
||||
{
|
||||
} else if(inchannels == 4 && outchannels >= 5) {
|
||||
float fl = inBuffer[i * 4 + 0];
|
||||
float fr = inBuffer[i * 4 + 1];
|
||||
float bl = inBuffer[i * 4 + 2];
|
||||
|
@ -123,17 +122,13 @@ static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int
|
|||
outBuffer[i * outchannels + 1] = fr;
|
||||
outBuffer[i * outchannels + skipclfe + 2] = bl;
|
||||
outBuffer[i * outchannels + skipclfe + 3] = br;
|
||||
for (int j = 0; j < skipclfe; ++j)
|
||||
{
|
||||
for(int j = 0; j < skipclfe; ++j) {
|
||||
outBuffer[i * outchannels + 2 + j] = 0;
|
||||
}
|
||||
for (int j = 4 + skipclfe; j < outchannels; ++j)
|
||||
{
|
||||
for(int j = 4 + skipclfe; j < outchannels; ++j) {
|
||||
outBuffer[i * outchannels + j] = 0;
|
||||
}
|
||||
}
|
||||
else if (inchannels == 5 && outchannels >= 6)
|
||||
{
|
||||
} else if(inchannels == 5 && outchannels >= 6) {
|
||||
float fl = inBuffer[i * 5 + 0];
|
||||
float fr = inBuffer[i * 5 + 1];
|
||||
float c = inBuffer[i * 5 + 2];
|
||||
|
@ -145,13 +140,10 @@ static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int
|
|||
outBuffer[i * outchannels + 3] = 0;
|
||||
outBuffer[i * outchannels + 4] = bl;
|
||||
outBuffer[i * outchannels + 5] = br;
|
||||
for (int j = 6; j < outchannels; ++j)
|
||||
{
|
||||
for(int j = 6; j < outchannels; ++j) {
|
||||
outBuffer[i * outchannels + j] = 0;
|
||||
}
|
||||
}
|
||||
else if (inchannels == 7 && outchannels == 8)
|
||||
{
|
||||
} else if(inchannels == 7 && outchannels == 8) {
|
||||
float fl = inBuffer[i * 7 + 0];
|
||||
float fr = inBuffer[i * 7 + 1];
|
||||
float c = inBuffer[i * 7 + 2];
|
||||
|
@ -167,21 +159,16 @@ static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int
|
|||
outBuffer[i * 8 + 5] = bc;
|
||||
outBuffer[i * 8 + 6] = sl;
|
||||
outBuffer[i * 8 + 7] = sr;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
// upmix N channels to N channels plus silence the empty channels
|
||||
float samples[inchannels];
|
||||
for (int j = 0; j < inchannels; ++j)
|
||||
{
|
||||
for(int j = 0; j < inchannels; ++j) {
|
||||
samples[j] = inBuffer[i * inchannels + j];
|
||||
}
|
||||
for (int j = 0; j < inchannels; ++j)
|
||||
{
|
||||
for(int j = 0; j < inchannels; ++j) {
|
||||
outBuffer[i * outchannels + j] = samples[j];
|
||||
}
|
||||
for (int j = inchannels; j < outchannels; ++j)
|
||||
{
|
||||
for(int j = inchannels; j < outchannels; ++j) {
|
||||
outBuffer[i * outchannels + j] = 0;
|
||||
}
|
||||
}
|
||||
|
@ -190,18 +177,18 @@ static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int
|
|||
|
||||
@implementation DownmixProcessor
|
||||
|
||||
- (id) initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf {
|
||||
- (id)initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
if (inf.mFormatID != kAudioFormatLinearPCM ||
|
||||
if(self) {
|
||||
if(inf.mFormatID != kAudioFormatLinearPCM ||
|
||||
(inf.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != kAudioFormatFlagsNativeFloatPacked ||
|
||||
inf.mBitsPerChannel != 32 ||
|
||||
inf.mBytesPerFrame != (4 * inf.mChannelsPerFrame) ||
|
||||
inf.mBytesPerPacket != inf.mFramesPerPacket * inf.mBytesPerFrame)
|
||||
return nil;
|
||||
|
||||
if (outf.mFormatID != kAudioFormatLinearPCM ||
|
||||
if(outf.mFormatID != kAudioFormatLinearPCM ||
|
||||
(outf.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != kAudioFormatFlagsNativeFloatPacked ||
|
||||
outf.mBitsPerChannel != 32 ||
|
||||
outf.mBytesPerFrame != (4 * outf.mChannelsPerFrame) ||
|
||||
|
@ -220,39 +207,39 @@ static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc {
|
||||
- (void)dealloc {
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.headphoneVirtualization"];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.hrirPath"];
|
||||
}
|
||||
|
||||
- (void) setupVirt {
|
||||
- (void)setupVirt {
|
||||
@synchronized(hFilter) {
|
||||
hFilter = nil;
|
||||
}
|
||||
|
||||
BOOL hVirt = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"headphoneVirtualization"];
|
||||
|
||||
if (hVirt &&
|
||||
if(hVirt &&
|
||||
outputFormat.mChannelsPerFrame == 2 &&
|
||||
inputFormat.mChannelsPerFrame >= 1 &&
|
||||
inputFormat.mChannelsPerFrame <= 8) {
|
||||
NSString * userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"];
|
||||
NSString *userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"];
|
||||
|
||||
NSURL * presetUrl = nil;
|
||||
NSURL *presetUrl = nil;
|
||||
|
||||
if (userPreset && ![userPreset isEqualToString:@""]) {
|
||||
if(userPreset && ![userPreset isEqualToString:@""]) {
|
||||
presetUrl = [NSURL fileURLWithPath:userPreset];
|
||||
if (![HeadphoneFilter validateImpulseFile:presetUrl])
|
||||
if(![HeadphoneFilter validateImpulseFile:presetUrl])
|
||||
presetUrl = nil;
|
||||
}
|
||||
|
||||
if (!presetUrl) {
|
||||
if(!presetUrl) {
|
||||
presetUrl = [[NSBundle mainBundle] URLForResource:@"gsx" withExtension:@"wv"];
|
||||
if (![HeadphoneFilter validateImpulseFile:presetUrl])
|
||||
if(![HeadphoneFilter validateImpulseFile:presetUrl])
|
||||
presetUrl = nil;
|
||||
}
|
||||
|
||||
if (presetUrl) {
|
||||
if(presetUrl) {
|
||||
@synchronized(hFilter) {
|
||||
hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame];
|
||||
}
|
||||
|
@ -263,41 +250,32 @@ static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int
|
|||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context
|
||||
{
|
||||
context:(void *)context {
|
||||
DLog(@"SOMETHING CHANGED!");
|
||||
if ([keyPath isEqualToString:@"values.headphoneVirtualization"] ||
|
||||
if([keyPath isEqualToString:@"values.headphoneVirtualization"] ||
|
||||
[keyPath isEqualToString:@"values.hrirPath"]) {
|
||||
// Reset the converter, without rebuffering
|
||||
[self setupVirt];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer {
|
||||
@synchronized (hFilter) {
|
||||
if ( hFilter ) {
|
||||
[hFilter process:(const float *) inBuffer sampleCount:frames toBuffer:(float *) outBuffer];
|
||||
- (void)process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer {
|
||||
@synchronized(hFilter) {
|
||||
if(hFilter) {
|
||||
[hFilter process:(const float *)inBuffer sampleCount:frames toBuffer:(float *)outBuffer];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 )
|
||||
{
|
||||
downmix_to_stereo( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, frames );
|
||||
}
|
||||
else if ( inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1 )
|
||||
{
|
||||
downmix_to_mono( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, frames );
|
||||
}
|
||||
else if ( inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame )
|
||||
{
|
||||
upmix( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, outputFormat.mChannelsPerFrame, frames );
|
||||
}
|
||||
else if ( inputFormat.mChannelsPerFrame == outputFormat.mChannelsPerFrame )
|
||||
{
|
||||
if(inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2) {
|
||||
downmix_to_stereo((const float *)inBuffer, inputFormat.mChannelsPerFrame, (float *)outBuffer, frames);
|
||||
} else if(inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1) {
|
||||
downmix_to_mono((const float *)inBuffer, inputFormat.mChannelsPerFrame, (float *)outBuffer, frames);
|
||||
} else if(inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame) {
|
||||
upmix((const float *)inBuffer, inputFormat.mChannelsPerFrame, (float *)outBuffer, outputFormat.mChannelsPerFrame, frames);
|
||||
} else if(inputFormat.mChannelsPerFrame == outputFormat.mChannelsPerFrame) {
|
||||
memcpy(outBuffer, inBuffer, frames * outputFormat.mBytesPerPacket);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#ifndef HeadphoneFilter_h
|
||||
#define HeadphoneFilter_h
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Accelerate/Accelerate.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface HeadphoneFilter : NSObject {
|
||||
FFTSetup fftSetup;
|
||||
|
@ -24,18 +24,18 @@
|
|||
|
||||
COMPLEX_SPLIT signal_fft;
|
||||
COMPLEX_SPLIT input_filtered_signal_per_channel[2];
|
||||
COMPLEX_SPLIT * impulse_responses;
|
||||
COMPLEX_SPLIT *impulse_responses;
|
||||
|
||||
float * left_result;
|
||||
float * right_result;
|
||||
float *left_result;
|
||||
float *right_result;
|
||||
|
||||
float * left_mix_result;
|
||||
float * right_mix_result;
|
||||
float *left_mix_result;
|
||||
float *right_mix_result;
|
||||
|
||||
float * paddedSignal;
|
||||
float *paddedSignal;
|
||||
|
||||
float * prevOverlapLeft;
|
||||
float * prevOverlapRight;
|
||||
float *prevOverlapLeft;
|
||||
float *prevOverlapRight;
|
||||
|
||||
int prevOverlapLength;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
|||
|
||||
- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(size_t)channels;
|
||||
|
||||
- (void)process:(const float*)inBuffer sampleCount:(size_t)count toBuffer:(float *)outBuffer;
|
||||
- (void)process:(const float *)inBuffer sampleCount:(size_t)count toBuffer:(float *)outBuffer;
|
||||
|
||||
- (void)reset;
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
//
|
||||
|
||||
#import "HeadphoneFilter.h"
|
||||
#import "AudioSource.h"
|
||||
#import "AudioDecoder.h"
|
||||
#import "AudioSource.h"
|
||||
|
||||
#import <soxr.h>
|
||||
#import <mm_malloc.h>
|
||||
#import <soxr.h>
|
||||
|
||||
#import "lpc.h"
|
||||
#import "util.h"
|
||||
|
@ -59,22 +59,21 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
+ (BOOL)validateImpulseFile:(NSURL *)url {
|
||||
id<CogSource> source = [AudioSource audioSourceForURL:url];
|
||||
if (!source)
|
||||
if(!source)
|
||||
return NO;
|
||||
|
||||
if (![source open:url])
|
||||
if(![source open:url])
|
||||
return NO;
|
||||
|
||||
id<CogDecoder> decoder = [AudioDecoder audioDecoderForSource:source];
|
||||
|
||||
if (decoder == nil) {
|
||||
if(decoder == nil) {
|
||||
[source close];
|
||||
source = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (![decoder open:source])
|
||||
{
|
||||
if(![decoder open:source]) {
|
||||
decoder = nil;
|
||||
[source close];
|
||||
source = nil;
|
||||
|
@ -90,7 +89,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
int impulseChannels = [[properties objectForKey:@"channels"] intValue];
|
||||
|
||||
if ([[properties objectForKey:@"floatingPoint"] boolValue] != YES ||
|
||||
if([[properties objectForKey:@"floatingPoint"] boolValue] != YES ||
|
||||
[[properties objectForKey:@"bitsPerSample"] intValue] != 32 ||
|
||||
!([[properties objectForKey:@"endian"] isEqualToString:@"host"] ||
|
||||
[[properties objectForKey:@"endian"] isEqualToString:@"little"]) ||
|
||||
|
@ -103,24 +102,23 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(size_t)channels {
|
||||
self = [super init];
|
||||
|
||||
if (self) {
|
||||
if(self) {
|
||||
id<CogSource> source = [AudioSource audioSourceForURL:url];
|
||||
if (!source)
|
||||
if(!source)
|
||||
return nil;
|
||||
|
||||
if (![source open:url])
|
||||
if(![source open:url])
|
||||
return nil;
|
||||
|
||||
id<CogDecoder> decoder = [AudioDecoder audioDecoderForSource:source];
|
||||
|
||||
if (decoder == nil) {
|
||||
if(decoder == nil) {
|
||||
[source close];
|
||||
source = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (![decoder open:source])
|
||||
{
|
||||
if(![decoder open:source]) {
|
||||
decoder = nil;
|
||||
[source close];
|
||||
source = nil;
|
||||
|
@ -134,7 +132,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
int sampleCount = [[properties objectForKey:@"totalFrames"] intValue];
|
||||
int impulseChannels = [[properties objectForKey:@"channels"] intValue];
|
||||
|
||||
if ([[properties objectForKey:@"floatingPoint"] boolValue] != YES ||
|
||||
if([[properties objectForKey:@"floatingPoint"] boolValue] != YES ||
|
||||
[[properties objectForKey:@"bitsPerSample"] intValue] != 32 ||
|
||||
!([[properties objectForKey:@"endian"] isEqualToString:@"host"] ||
|
||||
[[properties objectForKey:@"endian"] isEqualToString:@"little"]) ||
|
||||
|
@ -146,8 +144,8 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
return nil;
|
||||
}
|
||||
|
||||
float * impulseBuffer = (float *) malloc(sampleCount * sizeof(float) * impulseChannels);
|
||||
if (!impulseBuffer) {
|
||||
float *impulseBuffer = (float *)malloc(sampleCount * sizeof(float) * impulseChannels);
|
||||
if(!impulseBuffer) {
|
||||
[decoder close];
|
||||
decoder = nil;
|
||||
[source close];
|
||||
|
@ -155,7 +153,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
return nil;
|
||||
}
|
||||
|
||||
if ([decoder readAudio:impulseBuffer frames:sampleCount] != sampleCount) {
|
||||
if([decoder readAudio:impulseBuffer frames:sampleCount] != sampleCount) {
|
||||
[decoder close];
|
||||
decoder = nil;
|
||||
[source close];
|
||||
|
@ -168,7 +166,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
[source close];
|
||||
source = nil;
|
||||
|
||||
if (sampleRateOfSource != sampleRate) {
|
||||
if(sampleRateOfSource != sampleRate) {
|
||||
double sampleRatio = sampleRate / sampleRateOfSource;
|
||||
int resampledCount = (int)ceil((double)sampleCount * sampleRatio);
|
||||
|
||||
|
@ -178,20 +176,20 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
soxr_error_t error;
|
||||
|
||||
unsigned long PRIME_LEN_ = MAX(sampleRateOfSource/20, 1024u);
|
||||
unsigned long PRIME_LEN_ = MAX(sampleRateOfSource / 20, 1024u);
|
||||
PRIME_LEN_ = MIN(PRIME_LEN_, 16384u);
|
||||
PRIME_LEN_ = MAX(PRIME_LEN_, 2*LPC_ORDER + 1);
|
||||
PRIME_LEN_ = MAX(PRIME_LEN_, 2 * LPC_ORDER + 1);
|
||||
|
||||
unsigned int N_samples_to_add_ = sampleRateOfSource;
|
||||
unsigned int N_samples_to_drop_ = sampleRate;
|
||||
|
||||
samples_len(&N_samples_to_add_, &N_samples_to_drop_, 20, 8192u);
|
||||
|
||||
int resamplerLatencyIn = (int) N_samples_to_add_;
|
||||
int resamplerLatencyOut = (int) N_samples_to_drop_;
|
||||
int resamplerLatencyIn = (int)N_samples_to_add_;
|
||||
int resamplerLatencyOut = (int)N_samples_to_drop_;
|
||||
|
||||
float * tempImpulse = (float *) realloc(impulseBuffer, (sampleCount + resamplerLatencyIn * 2 + 1024) * sizeof(float) * impulseChannels);
|
||||
if (!tempImpulse) {
|
||||
float *tempImpulse = (float *)realloc(impulseBuffer, (sampleCount + resamplerLatencyIn * 2 + 1024) * sizeof(float) * impulseChannels);
|
||||
if(!tempImpulse) {
|
||||
free(impulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
@ -200,15 +198,15 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
resampledCount += resamplerLatencyOut * 2 + 1024;
|
||||
|
||||
float * resampledImpulse = (float *) malloc(resampledCount * sizeof(float) * impulseChannels);
|
||||
if (!resampledImpulse) {
|
||||
float *resampledImpulse = (float *)malloc(resampledCount * sizeof(float) * impulseChannels);
|
||||
if(!resampledImpulse) {
|
||||
free(impulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
size_t prime = MIN(sampleCount, PRIME_LEN_);
|
||||
|
||||
void * extrapolate_buffer = NULL;
|
||||
void *extrapolate_buffer = NULL;
|
||||
size_t extrapolate_buffer_size = 0;
|
||||
|
||||
memmove(impulseBuffer + resamplerLatencyIn * impulseChannels, impulseBuffer, sampleCount * sizeof(float) * impulseChannels);
|
||||
|
@ -221,7 +219,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
error = soxr_oneshot(sampleRateOfSource, sampleRate, impulseChannels, impulseBuffer, sampleCount + N_samples_to_add_ * 2, &inputDone, resampledImpulse, resampledCount, &outputDone, &io_spec, &q_spec, &runtime_spec);
|
||||
|
||||
if (error) {
|
||||
if(error) {
|
||||
free(resampledImpulse);
|
||||
free(impulseBuffer);
|
||||
return nil;
|
||||
|
@ -233,7 +231,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
free(impulseBuffer);
|
||||
impulseBuffer = resampledImpulse;
|
||||
sampleCount = (int) outputDone;
|
||||
sampleCount = (int)outputDone;
|
||||
}
|
||||
|
||||
channelCount = channels;
|
||||
|
@ -242,16 +240,19 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
fftSize = sampleCount + bufferSize;
|
||||
|
||||
int pow = 1;
|
||||
while (fftSize > 2) { pow++; fftSize /= 2; }
|
||||
while(fftSize > 2) {
|
||||
pow++;
|
||||
fftSize /= 2;
|
||||
}
|
||||
fftSize = 2 << pow;
|
||||
|
||||
float * deinterleavedImpulseBuffer = (float *) _mm_malloc(fftSize * sizeof(float) * impulseChannels, 16);
|
||||
if (!deinterleavedImpulseBuffer) {
|
||||
float *deinterleavedImpulseBuffer = (float *)_mm_malloc(fftSize * sizeof(float) * impulseChannels, 16);
|
||||
if(!deinterleavedImpulseBuffer) {
|
||||
free(impulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < impulseChannels; ++i) {
|
||||
for(size_t i = 0; i < impulseChannels; ++i) {
|
||||
cblas_scopy(sampleCount, impulseBuffer + i, impulseChannels, deinterleavedImpulseBuffer + i * fftSize, 1);
|
||||
vDSP_vclr(deinterleavedImpulseBuffer + i * fftSize + sampleCount, 1, fftSize - sampleCount);
|
||||
}
|
||||
|
@ -264,53 +265,53 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
log2nhalf = log2n / 2;
|
||||
|
||||
fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2);
|
||||
if (!fftSetup) {
|
||||
if(!fftSetup) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
paddedSignal = (float *) _mm_malloc(sizeof(float) * paddedBufferSize, 16);
|
||||
if (!paddedSignal) {
|
||||
paddedSignal = (float *)_mm_malloc(sizeof(float) * paddedBufferSize, 16);
|
||||
if(!paddedSignal) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
signal_fft.realp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
signal_fft.imagp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
if (!signal_fft.realp || !signal_fft.imagp) {
|
||||
signal_fft.realp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
signal_fft.imagp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
if(!signal_fft.realp || !signal_fft.imagp) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
input_filtered_signal_per_channel[0].realp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
input_filtered_signal_per_channel[0].imagp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
if (!input_filtered_signal_per_channel[0].realp ||
|
||||
input_filtered_signal_per_channel[0].realp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
input_filtered_signal_per_channel[0].imagp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
if(!input_filtered_signal_per_channel[0].realp ||
|
||||
!input_filtered_signal_per_channel[0].imagp) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
input_filtered_signal_per_channel[1].realp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
input_filtered_signal_per_channel[1].imagp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
if (!input_filtered_signal_per_channel[1].realp ||
|
||||
input_filtered_signal_per_channel[1].realp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
input_filtered_signal_per_channel[1].imagp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
if(!input_filtered_signal_per_channel[1].realp ||
|
||||
!input_filtered_signal_per_channel[1].imagp) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
impulse_responses = (COMPLEX_SPLIT *) calloc(sizeof(COMPLEX_SPLIT), channels * 2);
|
||||
if (!impulse_responses) {
|
||||
impulse_responses = (COMPLEX_SPLIT *)calloc(sizeof(COMPLEX_SPLIT), channels * 2);
|
||||
if(!impulse_responses) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < channels; ++i) {
|
||||
impulse_responses[i * 2 + 0].realp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
impulse_responses[i * 2 + 0].imagp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
impulse_responses[i * 2 + 1].realp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
impulse_responses[i * 2 + 1].imagp = (float *) _mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
for(size_t i = 0; i < channels; ++i) {
|
||||
impulse_responses[i * 2 + 0].realp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
impulse_responses[i * 2 + 0].imagp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
impulse_responses[i * 2 + 1].realp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
impulse_responses[i * 2 + 1].imagp = (float *)_mm_malloc(sizeof(float) * fftSizeOver2, 16);
|
||||
|
||||
if (!impulse_responses[i * 2 + 0].realp || !impulse_responses[i * 2 + 0].imagp ||
|
||||
if(!impulse_responses[i * 2 + 0].realp || !impulse_responses[i * 2 + 0].imagp ||
|
||||
!impulse_responses[i * 2 + 1].realp || !impulse_responses[i * 2 + 1].imagp) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
|
@ -319,20 +320,19 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
int leftInChannel;
|
||||
int rightInChannel;
|
||||
|
||||
if (impulseChannels == 7) {
|
||||
leftInChannel = speakers_to_hesuvi_7[channels-1][0][i];
|
||||
rightInChannel = speakers_to_hesuvi_7[channels-1][1][i];
|
||||
}
|
||||
else {
|
||||
leftInChannel = speakers_to_hesuvi_14[channels-1][0][i];
|
||||
rightInChannel = speakers_to_hesuvi_14[channels-1][1][i];
|
||||
if(impulseChannels == 7) {
|
||||
leftInChannel = speakers_to_hesuvi_7[channels - 1][0][i];
|
||||
rightInChannel = speakers_to_hesuvi_7[channels - 1][1][i];
|
||||
} else {
|
||||
leftInChannel = speakers_to_hesuvi_14[channels - 1][0][i];
|
||||
rightInChannel = speakers_to_hesuvi_14[channels - 1][1][i];
|
||||
}
|
||||
|
||||
if (leftInChannel == -1 || rightInChannel == -1) {
|
||||
float * temp;
|
||||
if (impulseChannels == 7) {
|
||||
temp = (float *) malloc(sizeof(float) * fftSize);
|
||||
if (!temp) {
|
||||
if(leftInChannel == -1 || rightInChannel == -1) {
|
||||
float *temp;
|
||||
if(impulseChannels == 7) {
|
||||
temp = (float *)malloc(sizeof(float) * fftSize);
|
||||
if(!temp) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
@ -342,10 +342,9 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
vDSP_ctoz((DSPComplex *)temp, 2, &impulse_responses[i * 2 + 0], 1, fftSizeOver2);
|
||||
vDSP_ctoz((DSPComplex *)temp, 2, &impulse_responses[i * 2 + 1], 1, fftSizeOver2);
|
||||
}
|
||||
else {
|
||||
temp = (float *) malloc(sizeof(float) * fftSize * 2);
|
||||
if (!temp) {
|
||||
} else {
|
||||
temp = (float *)malloc(sizeof(float) * fftSize * 2);
|
||||
if(!temp) {
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
return nil;
|
||||
}
|
||||
|
@ -361,8 +360,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
}
|
||||
|
||||
free(temp);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + leftInChannel * fftSize), 2, &impulse_responses[i * 2 + 0], 1, fftSizeOver2);
|
||||
vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + rightInChannel * fftSize), 2, &impulse_responses[i * 2 + 1], 1, fftSizeOver2);
|
||||
}
|
||||
|
@ -373,19 +371,19 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
|
||||
_mm_free(deinterleavedImpulseBuffer);
|
||||
|
||||
left_result = (float *) _mm_malloc(sizeof(float) * fftSize, 16);
|
||||
right_result = (float *) _mm_malloc(sizeof(float) * fftSize, 16);
|
||||
if (!left_result || !right_result)
|
||||
left_result = (float *)_mm_malloc(sizeof(float) * fftSize, 16);
|
||||
right_result = (float *)_mm_malloc(sizeof(float) * fftSize, 16);
|
||||
if(!left_result || !right_result)
|
||||
return nil;
|
||||
|
||||
prevOverlapLeft = (float *) _mm_malloc(sizeof(float) * fftSize, 16);
|
||||
prevOverlapRight = (float *) _mm_malloc(sizeof(float) * fftSize, 16);
|
||||
if (!prevOverlapLeft || !prevOverlapRight)
|
||||
prevOverlapLeft = (float *)_mm_malloc(sizeof(float) * fftSize, 16);
|
||||
prevOverlapRight = (float *)_mm_malloc(sizeof(float) * fftSize, 16);
|
||||
if(!prevOverlapLeft || !prevOverlapRight)
|
||||
return nil;
|
||||
|
||||
left_mix_result = (float *) _mm_malloc(sizeof(float) * fftSize, 16);
|
||||
right_mix_result = (float *) _mm_malloc(sizeof(float) * fftSize, 16);
|
||||
if (!left_mix_result || !right_mix_result)
|
||||
left_mix_result = (float *)_mm_malloc(sizeof(float) * fftSize, 16);
|
||||
right_mix_result = (float *)_mm_malloc(sizeof(float) * fftSize, 16);
|
||||
if(!left_mix_result || !right_mix_result)
|
||||
return nil;
|
||||
|
||||
prevOverlapLength = 0;
|
||||
|
@ -395,7 +393,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
}
|
||||
|
||||
- (void)dealloc {
|
||||
if (fftSetup) vDSP_destroy_fftsetup(fftSetup);
|
||||
if(fftSetup) vDSP_destroy_fftsetup(fftSetup);
|
||||
|
||||
_mm_free(paddedSignal);
|
||||
|
||||
|
@ -407,8 +405,8 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
_mm_free(input_filtered_signal_per_channel[1].realp);
|
||||
_mm_free(input_filtered_signal_per_channel[1].imagp);
|
||||
|
||||
if (impulse_responses) {
|
||||
for (size_t i = 0; i < channelCount * 2; ++i) {
|
||||
if(impulse_responses) {
|
||||
for(size_t i = 0; i < channelCount * 2; ++i) {
|
||||
_mm_free(impulse_responses[i].realp);
|
||||
_mm_free(impulse_responses[i].imagp);
|
||||
}
|
||||
|
@ -425,16 +423,16 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
_mm_free(right_mix_result);
|
||||
}
|
||||
|
||||
- (void)process:(const float*)inBuffer sampleCount:(size_t)count toBuffer:(float *)outBuffer {
|
||||
- (void)process:(const float *)inBuffer sampleCount:(size_t)count toBuffer:(float *)outBuffer {
|
||||
const float scale = 1.0 / (8.0 * (float)fftSize);
|
||||
|
||||
while (count > 0) {
|
||||
while(count > 0) {
|
||||
size_t countToDo = (count > bufferSize) ? bufferSize : count;
|
||||
|
||||
vDSP_vclr(left_mix_result, 1, fftSize);
|
||||
vDSP_vclr(right_mix_result, 1, fftSize);
|
||||
|
||||
for (size_t i = 0; i < channelCount; ++i) {
|
||||
for(size_t i = 0; i < channelCount; ++i) {
|
||||
cblas_scopy((int)countToDo, inBuffer + i, (int)channelCount, paddedSignal, 1);
|
||||
|
||||
vDSP_vclr(paddedSignal + countToDo, 1, paddedBufferSize - countToDo);
|
||||
|
@ -474,7 +472,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = {
|
|||
}
|
||||
|
||||
// Integrate previous overlap
|
||||
if (prevOverlapLength) {
|
||||
if(prevOverlapLength) {
|
||||
vDSP_vadd(prevOverlapLeft, 1, left_mix_result, 1, left_mix_result, 1, prevOverlapLength);
|
||||
vDSP_vadd(prevOverlapRight, 1, right_mix_result, 1, right_mix_result, 1, prevOverlapLength);
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import "AudioDecoder.h"
|
||||
#import "Node.h"
|
||||
|
@ -36,16 +36,16 @@
|
|||
@property(readonly) Semaphore *exitAtTheEndOfTheStream;
|
||||
|
||||
- (BOOL)openWithSource:(id<CogSource>)source;
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>) d;
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)d;
|
||||
|
||||
- (void)process;
|
||||
- (NSDictionary *) properties;
|
||||
- (NSDictionary *)properties;
|
||||
- (void)seek:(long)frame;
|
||||
|
||||
- (void)registerObservers;
|
||||
|
||||
- (BOOL)setTrack:(NSURL *)track;
|
||||
|
||||
- (id<CogDecoder>) decoder;
|
||||
- (id<CogDecoder>)decoder;
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,40 +7,35 @@
|
|||
//
|
||||
|
||||
#import "InputNode.h"
|
||||
#import "BufferChain.h"
|
||||
#import "Plugin.h"
|
||||
#import "CoreAudioUtils.h"
|
||||
#import "AudioPlayer.h"
|
||||
#import "BufferChain.h"
|
||||
#import "CoreAudioUtils.h"
|
||||
#import "OutputNode.h"
|
||||
|
||||
#import "Plugin.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation InputNode
|
||||
@synthesize exitAtTheEndOfTheStream;
|
||||
|
||||
|
||||
- (id)initWithController:(id)c previous:(id)p {
|
||||
self = [super initWithController:c previous:p];
|
||||
if (self) {
|
||||
if(self) {
|
||||
exitAtTheEndOfTheStream = [[Semaphore alloc] init];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)openWithSource:(id<CogSource>)source
|
||||
{
|
||||
- (BOOL)openWithSource:(id<CogSource>)source {
|
||||
decoder = [AudioDecoder audioDecoderForSource:source];
|
||||
|
||||
if (decoder == nil)
|
||||
if(decoder == nil)
|
||||
return NO;
|
||||
|
||||
[self registerObservers];
|
||||
|
||||
if (![decoder open:source])
|
||||
{
|
||||
if(![decoder open:source]) {
|
||||
ALog(@"Couldn't open decoder...");
|
||||
return NO;
|
||||
}
|
||||
|
@ -60,8 +55,7 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>) d
|
||||
{
|
||||
- (BOOL)openWithDecoder:(id<CogDecoder>)d {
|
||||
DLog(@"Opening with old decoder: %@", d);
|
||||
decoder = d;
|
||||
|
||||
|
@ -83,9 +77,7 @@
|
|||
return YES;
|
||||
}
|
||||
|
||||
|
||||
- (void)registerObservers
|
||||
{
|
||||
- (void)registerObservers {
|
||||
DLog(@"REGISTERING OBSERVERS");
|
||||
[decoder addObserver:self
|
||||
forKeyPath:@"properties"
|
||||
|
@ -103,33 +95,28 @@
|
|||
- (void)observeValueForKeyPath:(NSString *)keyPath
|
||||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context
|
||||
{
|
||||
context:(void *)context {
|
||||
DLog(@"SOMETHING CHANGED!");
|
||||
if ([keyPath isEqual:@"properties"]) {
|
||||
if([keyPath isEqual:@"properties"]) {
|
||||
DLog(@"Input format changed");
|
||||
// Converter may need resetting, it'll do that when it reaches the new chunks
|
||||
NSDictionary * properties = [decoder properties];
|
||||
NSDictionary *properties = [decoder properties];
|
||||
nodeFormat = propertiesToASBD(properties);
|
||||
nodeLossless = [[properties valueForKey:@"encoding"] isEqualToString:@"lossless"];
|
||||
}
|
||||
else if ([keyPath isEqual:@"metadata"]) {
|
||||
//Inform something of metadata change
|
||||
} else if([keyPath isEqual:@"metadata"]) {
|
||||
// Inform something of metadata change
|
||||
}
|
||||
}
|
||||
|
||||
- (void)process
|
||||
{
|
||||
- (void)process {
|
||||
int amountInBuffer = 0;
|
||||
void *inputBuffer = malloc(CHUNK_SIZE);
|
||||
|
||||
BOOL shouldClose = YES;
|
||||
BOOL seekError = NO;
|
||||
|
||||
while ([self shouldContinue] == YES && [self endOfStream] == NO)
|
||||
{
|
||||
if (shouldSeek == YES)
|
||||
{
|
||||
while([self shouldContinue] == YES && [self endOfStream] == NO) {
|
||||
if(shouldSeek == YES) {
|
||||
ConverterNode *converter = [[[controller controller] bufferChain] converter];
|
||||
DLog(@"SEEKING! Resetting Buffer");
|
||||
|
||||
|
@ -149,47 +136,41 @@
|
|||
initialBufferFilled = NO;
|
||||
}
|
||||
|
||||
if (amountInBuffer < CHUNK_SIZE) {
|
||||
int framesToRead = (CHUNK_SIZE - amountInBuffer)/bytesPerFrame;
|
||||
if(amountInBuffer < CHUNK_SIZE) {
|
||||
int framesToRead = (CHUNK_SIZE - amountInBuffer) / bytesPerFrame;
|
||||
int framesRead = [decoder readAudio:((char *)inputBuffer) + amountInBuffer frames:framesToRead];
|
||||
|
||||
if (framesRead > 0 && !seekError)
|
||||
{
|
||||
if(framesRead > 0 && !seekError) {
|
||||
amountInBuffer += (framesRead * bytesPerFrame);
|
||||
[self writeData:inputBuffer amount:amountInBuffer];
|
||||
amountInBuffer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (initialBufferFilled == NO) {
|
||||
} else {
|
||||
if(initialBufferFilled == NO) {
|
||||
[controller initialBufferFilled:self];
|
||||
}
|
||||
|
||||
DLog(@"End of stream? %@", [self properties]);
|
||||
|
||||
endOfStream = YES;
|
||||
shouldClose = [controller endOfInputReached]; //Lets us know if we should keep going or not (occassionally, for track changes within a file)
|
||||
shouldClose = [controller endOfInputReached]; // Lets us know if we should keep going or not (occassionally, for track changes within a file)
|
||||
DLog(@"closing? is %i", shouldClose);
|
||||
|
||||
// wait before exiting, as we might still get seeking request
|
||||
DLog("InputNode: Before wait")
|
||||
[exitAtTheEndOfTheStream waitIndefinitely];
|
||||
DLog("InputNode: After wait, should seek = %d", shouldSeek)
|
||||
if (shouldSeek)
|
||||
{
|
||||
DLog("InputNode: After wait, should seek = %d", shouldSeek) if(shouldSeek) {
|
||||
endOfStream = NO;
|
||||
shouldClose = NO;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldClose)
|
||||
if(shouldClose)
|
||||
[decoder close];
|
||||
|
||||
free(inputBuffer);
|
||||
|
@ -199,22 +180,19 @@
|
|||
DLog("Input node thread stopping");
|
||||
}
|
||||
|
||||
- (void)seek:(long)frame
|
||||
{
|
||||
- (void)seek:(long)frame {
|
||||
seekFrame = frame;
|
||||
shouldSeek = YES;
|
||||
DLog(@"Should seek!");
|
||||
[semaphore signal];
|
||||
|
||||
if (endOfStream)
|
||||
{
|
||||
if(endOfStream) {
|
||||
[exitAtTheEndOfTheStream signal];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)setTrack:(NSURL *)track
|
||||
{
|
||||
if ([decoder respondsToSelector:@selector(setTrack:)] && [decoder setTrack:track]) {
|
||||
- (BOOL)setTrack:(NSURL *)track {
|
||||
if([decoder respondsToSelector:@selector(setTrack:)] && [decoder setTrack:track]) {
|
||||
DLog(@"SET TRACK!");
|
||||
|
||||
return YES;
|
||||
|
@ -223,41 +201,34 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (void)removeObservers
|
||||
{
|
||||
if (observersAdded)
|
||||
{
|
||||
- (void)removeObservers {
|
||||
if(observersAdded) {
|
||||
[decoder removeObserver:self forKeyPath:@"properties"];
|
||||
[decoder removeObserver:self forKeyPath:@"metadata"];
|
||||
observersAdded = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s
|
||||
{
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
[super setShouldContinue:s];
|
||||
if (!s)
|
||||
if(!s)
|
||||
[self removeObservers];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
DLog(@"Input Node dealloc");
|
||||
[self removeObservers];
|
||||
}
|
||||
|
||||
- (NSDictionary *) properties
|
||||
{
|
||||
- (NSDictionary *)properties {
|
||||
return [decoder properties];
|
||||
}
|
||||
|
||||
- (id<CogDecoder>) decoder
|
||||
{
|
||||
- (id<CogDecoder>)decoder {
|
||||
return decoder;
|
||||
}
|
||||
|
||||
- (double) secondsBuffered
|
||||
{
|
||||
- (double)secondsBuffered {
|
||||
return [buffer listDuration];
|
||||
}
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
// Copyright 2006 Vincent Spader. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "ChunkList.h"
|
||||
#import "Semaphore.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#define BUFFER_SIZE 1024 * 1024
|
||||
#define CHUNK_SIZE 16 * 1024
|
||||
|
@ -25,7 +25,7 @@
|
|||
BOOL shouldReset;
|
||||
|
||||
BOOL shouldContinue;
|
||||
BOOL endOfStream; //All data is now in buffer
|
||||
BOOL endOfStream; // All data is now in buffer
|
||||
BOOL initialBufferFilled;
|
||||
|
||||
AudioStreamBasicDescription nodeFormat;
|
||||
|
@ -36,7 +36,7 @@
|
|||
- (void)writeData:(const void *)ptr amount:(size_t)a;
|
||||
- (AudioChunk *)readChunk:(size_t)maxFrames;
|
||||
|
||||
- (void)process; //Should be overwriten by subclass
|
||||
- (void)process; // Should be overwriten by subclass
|
||||
- (void)threadEntry:(id)arg;
|
||||
|
||||
- (void)launchThread;
|
||||
|
@ -51,7 +51,7 @@
|
|||
- (void)setShouldContinue:(BOOL)s;
|
||||
|
||||
- (ChunkList *)buffer;
|
||||
- (void)resetBuffer; //WARNING! DANGER WILL ROBINSON!
|
||||
- (void)resetBuffer; // WARNING! DANGER WILL ROBINSON!
|
||||
|
||||
- (AudioStreamBasicDescription)nodeFormat;
|
||||
- (BOOL)nodeLossless;
|
||||
|
|
|
@ -8,16 +8,14 @@
|
|||
|
||||
#import "Node.h"
|
||||
|
||||
#import "Logging.h"
|
||||
#import "BufferChain.h"
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation Node
|
||||
|
||||
- (id)initWithController:(id)c previous:(id)p
|
||||
{
|
||||
- (id)initWithController:(id)c previous:(id)p {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:3.0];
|
||||
semaphore = [[Semaphore alloc] init];
|
||||
|
||||
|
@ -37,21 +35,18 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (AudioStreamBasicDescription)nodeFormat
|
||||
{
|
||||
- (AudioStreamBasicDescription)nodeFormat {
|
||||
return nodeFormat;
|
||||
}
|
||||
|
||||
- (BOOL)nodeLossless
|
||||
{
|
||||
- (BOOL)nodeLossless {
|
||||
return nodeLossless;
|
||||
}
|
||||
|
||||
- (void)writeData:(const void *)ptr amount:(size_t)amount
|
||||
{
|
||||
- (void)writeData:(const void *)ptr amount:(size_t)amount {
|
||||
[accessLock lock];
|
||||
|
||||
AudioChunk * chunk = [[AudioChunk alloc] init];
|
||||
AudioChunk *chunk = [[AudioChunk alloc] init];
|
||||
[chunk setFormat:nodeFormat];
|
||||
[chunk setLossless:nodeLossless];
|
||||
[chunk assignSamples:ptr frameCount:amount / nodeFormat.mBytesPerPacket];
|
||||
|
@ -59,17 +54,16 @@
|
|||
const double chunkDuration = [chunk duration];
|
||||
double durationLeft = [buffer maxDuration] - [buffer listDuration];
|
||||
|
||||
while (shouldContinue == YES && chunkDuration > durationLeft)
|
||||
{
|
||||
if (durationLeft < chunkDuration) {
|
||||
if (initialBufferFilled == NO) {
|
||||
while(shouldContinue == YES && chunkDuration > durationLeft) {
|
||||
if(durationLeft < chunkDuration) {
|
||||
if(initialBufferFilled == NO) {
|
||||
initialBufferFilled = YES;
|
||||
if ([controller respondsToSelector:@selector(initialBufferFilled:)])
|
||||
if([controller respondsToSelector:@selector(initialBufferFilled:)])
|
||||
[controller performSelector:@selector(initialBufferFilled:) withObject:self];
|
||||
}
|
||||
}
|
||||
|
||||
if (durationLeft < chunkDuration || shouldReset) {
|
||||
if(durationLeft < chunkDuration || shouldReset) {
|
||||
[accessLock unlock];
|
||||
[semaphore wait];
|
||||
[accessLock lock];
|
||||
|
@ -83,119 +77,100 @@
|
|||
[accessLock unlock];
|
||||
}
|
||||
|
||||
//Should be overwriten by subclass.
|
||||
- (void)process
|
||||
{
|
||||
// Should be overwriten by subclass.
|
||||
- (void)process {
|
||||
}
|
||||
|
||||
- (void)threadEntry:(id)arg
|
||||
{
|
||||
- (void)threadEntry:(id)arg {
|
||||
@autoreleasepool {
|
||||
[self process];
|
||||
}
|
||||
}
|
||||
|
||||
- (AudioChunk *)readChunk:(size_t)maxFrames
|
||||
{
|
||||
- (AudioChunk *)readChunk:(size_t)maxFrames {
|
||||
[accessLock lock];
|
||||
|
||||
if ([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES)
|
||||
{
|
||||
if([[previousNode buffer] isEmpty] && [previousNode endOfStream] == YES) {
|
||||
endOfStream = YES;
|
||||
[accessLock unlock];
|
||||
return [[AudioChunk alloc] init];
|
||||
}
|
||||
|
||||
if ([previousNode shouldReset] == YES) {
|
||||
if([previousNode shouldReset] == YES) {
|
||||
[buffer reset];
|
||||
|
||||
shouldReset = YES;
|
||||
[previousNode setShouldReset: NO];
|
||||
[previousNode setShouldReset:NO];
|
||||
|
||||
[[previousNode semaphore] signal];
|
||||
}
|
||||
|
||||
AudioChunk * ret = [[previousNode buffer] removeSamples:maxFrames];
|
||||
AudioChunk *ret = [[previousNode buffer] removeSamples:maxFrames];
|
||||
|
||||
[accessLock unlock];
|
||||
|
||||
if ([ret frameCount])
|
||||
{
|
||||
if([ret frameCount]) {
|
||||
[[previousNode semaphore] signal];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
- (void)launchThread
|
||||
{
|
||||
- (void)launchThread {
|
||||
[NSThread detachNewThreadSelector:@selector(threadEntry:) toTarget:self withObject:nil];
|
||||
}
|
||||
|
||||
- (void)setPreviousNode:(id)p
|
||||
{
|
||||
- (void)setPreviousNode:(id)p {
|
||||
previousNode = p;
|
||||
}
|
||||
|
||||
- (id)previousNode
|
||||
{
|
||||
- (id)previousNode {
|
||||
return previousNode;
|
||||
}
|
||||
|
||||
- (BOOL)shouldContinue
|
||||
{
|
||||
- (BOOL)shouldContinue {
|
||||
return shouldContinue;
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s
|
||||
{
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
shouldContinue = s;
|
||||
}
|
||||
|
||||
- (ChunkList *)buffer
|
||||
{
|
||||
- (ChunkList *)buffer {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
- (void)resetBuffer
|
||||
{
|
||||
shouldReset = YES; //Will reset on next write.
|
||||
if (previousNode == nil) {
|
||||
- (void)resetBuffer {
|
||||
shouldReset = YES; // Will reset on next write.
|
||||
if(previousNode == nil) {
|
||||
[accessLock lock];
|
||||
[buffer reset];
|
||||
[accessLock unlock];
|
||||
}
|
||||
}
|
||||
|
||||
- (Semaphore *)semaphore
|
||||
{
|
||||
- (Semaphore *)semaphore {
|
||||
return semaphore;
|
||||
}
|
||||
|
||||
- (BOOL)endOfStream
|
||||
{
|
||||
- (BOOL)endOfStream {
|
||||
return endOfStream;
|
||||
}
|
||||
|
||||
- (void)setEndOfStream:(BOOL)e
|
||||
{
|
||||
- (void)setEndOfStream:(BOOL)e {
|
||||
endOfStream = e;
|
||||
}
|
||||
|
||||
- (void)setShouldReset:(BOOL)s
|
||||
{
|
||||
- (void)setShouldReset:(BOOL)s {
|
||||
shouldReset = s;
|
||||
}
|
||||
- (BOOL)shouldReset
|
||||
{
|
||||
- (BOOL)shouldReset {
|
||||
return shouldReset;
|
||||
}
|
||||
|
||||
// Buffering nodes should implement this
|
||||
- (double)secondsBuffered
|
||||
{
|
||||
- (double)secondsBuffered {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import "Node.h"
|
||||
#import "OutputCoreAudio.h"
|
||||
|
@ -49,9 +49,9 @@
|
|||
- (AudioChunk *)readChunk:(size_t)amount;
|
||||
|
||||
- (void)setFormat:(AudioStreamBasicDescription *)f;
|
||||
- (AudioStreamBasicDescription) format;
|
||||
- (AudioStreamBasicDescription)format;
|
||||
|
||||
- (void)setVolume:(double) v;
|
||||
- (void)setVolume:(double)v;
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s;
|
||||
|
||||
|
|
|
@ -7,16 +7,15 @@
|
|||
//
|
||||
|
||||
#import "OutputNode.h"
|
||||
#import "OutputCoreAudio.h"
|
||||
#import "AudioPlayer.h"
|
||||
#import "BufferChain.h"
|
||||
#import "OutputCoreAudio.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation OutputNode
|
||||
|
||||
- (void)setup
|
||||
{
|
||||
- (void)setup {
|
||||
amountPlayed = 0.0;
|
||||
sampleRatio = 0.0;
|
||||
|
||||
|
@ -28,94 +27,79 @@
|
|||
[output setup];
|
||||
}
|
||||
|
||||
- (void)seek:(double)time
|
||||
{
|
||||
// [output pause];
|
||||
- (void)seek:(double)time {
|
||||
// [output pause];
|
||||
[self resetBuffer];
|
||||
|
||||
amountPlayed = time;
|
||||
}
|
||||
|
||||
- (void)process
|
||||
{
|
||||
- (void)process {
|
||||
paused = NO;
|
||||
[output start];
|
||||
}
|
||||
|
||||
- (void)pause
|
||||
{
|
||||
- (void)pause {
|
||||
paused = YES;
|
||||
[output pause];
|
||||
}
|
||||
|
||||
- (void)resume
|
||||
{
|
||||
- (void)resume {
|
||||
paused = NO;
|
||||
[output resume];
|
||||
}
|
||||
|
||||
- (void)incrementAmountPlayed:(double)seconds
|
||||
{
|
||||
- (void)incrementAmountPlayed:(double)seconds {
|
||||
amountPlayed += seconds;
|
||||
}
|
||||
|
||||
- (void)resetAmountPlayed
|
||||
{
|
||||
- (void)resetAmountPlayed {
|
||||
amountPlayed = 0;
|
||||
}
|
||||
|
||||
- (void)endOfInputPlayed
|
||||
{
|
||||
- (void)endOfInputPlayed {
|
||||
[controller endOfInputPlayed];
|
||||
}
|
||||
|
||||
- (BOOL)chainQueueHasTracks
|
||||
{
|
||||
- (BOOL)chainQueueHasTracks {
|
||||
return [controller chainQueueHasTracks];
|
||||
}
|
||||
|
||||
- (double)secondsBuffered
|
||||
{
|
||||
- (double)secondsBuffered {
|
||||
return [buffer listDuration];
|
||||
}
|
||||
|
||||
- (AudioChunk *)readChunk:(size_t)amount
|
||||
{
|
||||
- (AudioChunk *)readChunk:(size_t)amount {
|
||||
@autoreleasepool {
|
||||
[self setPreviousNode:[[controller bufferChain] finalNode]];
|
||||
|
||||
AudioChunk * ret = [super readChunk:amount];
|
||||
AudioChunk *ret = [super readChunk:amount];
|
||||
|
||||
/* if (n == 0) {
|
||||
/* if (n == 0) {
|
||||
DLog(@"Output Buffer dry!");
|
||||
}
|
||||
*/
|
||||
*/
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
- (double)amountPlayed
|
||||
{
|
||||
- (double)amountPlayed {
|
||||
return amountPlayed;
|
||||
}
|
||||
|
||||
- (AudioStreamBasicDescription) format
|
||||
{
|
||||
- (AudioStreamBasicDescription)format {
|
||||
return format;
|
||||
}
|
||||
|
||||
- (void)setFormat:(AudioStreamBasicDescription *)f
|
||||
{
|
||||
- (void)setFormat:(AudioStreamBasicDescription *)f {
|
||||
format = *f;
|
||||
// Calculate a ratio and add to double(seconds) instead, as format may change
|
||||
// double oldSampleRatio = sampleRatio;
|
||||
sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket);
|
||||
BufferChain *bufferChain = [controller bufferChain];
|
||||
if (bufferChain)
|
||||
{
|
||||
if(bufferChain) {
|
||||
ConverterNode *converter = [bufferChain converter];
|
||||
if (converter)
|
||||
{
|
||||
if(converter) {
|
||||
// This clears the resampler buffer, but not the input buffer
|
||||
// We also have to jump the play position ahead accounting for
|
||||
// the data we are flushing
|
||||
|
@ -132,47 +116,39 @@
|
|||
}
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
- (void)close {
|
||||
[output stop];
|
||||
output = nil;
|
||||
}
|
||||
|
||||
- (void)setVolume:(double) v
|
||||
{
|
||||
- (void)setVolume:(double)v {
|
||||
[output setVolume:v];
|
||||
}
|
||||
|
||||
- (void)setShouldContinue:(BOOL)s
|
||||
{
|
||||
- (void)setShouldContinue:(BOOL)s {
|
||||
[super setShouldContinue:s];
|
||||
|
||||
// if (s == NO)
|
||||
// [output stop];
|
||||
// if (s == NO)
|
||||
// [output stop];
|
||||
}
|
||||
|
||||
- (BOOL)isPaused
|
||||
{
|
||||
- (BOOL)isPaused {
|
||||
return paused;
|
||||
}
|
||||
|
||||
- (void)beginEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)beginEqualizer:(AudioUnit)eq {
|
||||
[controller beginEqualizer:eq];
|
||||
}
|
||||
|
||||
- (void)refreshEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)refreshEqualizer:(AudioUnit)eq {
|
||||
[controller refreshEqualizer:eq];
|
||||
}
|
||||
|
||||
- (void)endEqualizer:(AudioUnit)eq
|
||||
{
|
||||
- (void)endEqualizer:(AudioUnit)eq {
|
||||
[controller endEqualizer:eq];
|
||||
}
|
||||
|
||||
- (void)sustainHDCD
|
||||
{
|
||||
- (void)sustainHDCD {
|
||||
[output sustainHDCD];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
|
||||
#import "Node.h"
|
||||
#import "Plugin.h"
|
||||
|
@ -21,6 +21,6 @@
|
|||
// This node just slaps pre-converted data into its buffer for re-buffering
|
||||
}
|
||||
|
||||
- (void) setFormat:(AudioStreamBasicDescription)format;
|
||||
- (void)setFormat:(AudioStreamBasicDescription)format;
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,18 +6,16 @@
|
|||
// Copyright 2022 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Plugin.h"
|
||||
#import "RefillNode.h"
|
||||
#import "Plugin.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation RefillNode
|
||||
|
||||
- (id)initWithController:(id)c previous:(id)p
|
||||
{
|
||||
- (id)initWithController:(id)c previous:(id)p {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
// This special node should be able to handle up to four buffers
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:12.0];
|
||||
semaphore = [[Semaphore alloc] init];
|
||||
|
@ -36,13 +34,11 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
DLog(@"Refill Node dealloc");
|
||||
}
|
||||
|
||||
- (void)setFormat:(AudioStreamBasicDescription)format
|
||||
{
|
||||
- (void)setFormat:(AudioStreamBasicDescription)format {
|
||||
nodeFormat = format;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
//
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "Plugin.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface CogDecoderMulti : NSObject <CogDecoder> {
|
||||
NSArray *theDecoders;
|
||||
|
@ -15,7 +15,7 @@
|
|||
NSMutableArray *cachedObservers;
|
||||
}
|
||||
|
||||
-(id)initWithDecoders:(NSArray *)decoders;
|
||||
- (id)initWithDecoders:(NSArray *)decoders;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -8,12 +8,10 @@
|
|||
|
||||
#import "CogPluginMulti.h"
|
||||
|
||||
NSArray * sortClassesByPriority(NSArray * theClasses)
|
||||
{
|
||||
NSArray *sortClassesByPriority(NSArray *theClasses) {
|
||||
NSMutableArray *sortedClasses = [NSMutableArray arrayWithArray:theClasses];
|
||||
[sortedClasses sortUsingComparator:
|
||||
^NSComparisonResult(id obj1, id obj2)
|
||||
{
|
||||
^NSComparisonResult(id obj1, id obj2) {
|
||||
NSString *classString1 = (NSString *)obj1;
|
||||
NSString *classString2 = (NSString *)obj2;
|
||||
|
||||
|
@ -23,27 +21,27 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
|||
float priority1 = [class1 priority];
|
||||
float priority2 = [class2 priority];
|
||||
|
||||
if (priority1 == priority2) return NSOrderedSame;
|
||||
else if (priority1 > priority2) return NSOrderedAscending;
|
||||
else return NSOrderedDescending;
|
||||
if(priority1 == priority2)
|
||||
return NSOrderedSame;
|
||||
else if(priority1 > priority2)
|
||||
return NSOrderedAscending;
|
||||
else
|
||||
return NSOrderedDescending;
|
||||
}];
|
||||
return sortedClasses;
|
||||
}
|
||||
|
||||
@implementation CogDecoderMulti
|
||||
|
||||
+ (NSArray *)mimeTypes
|
||||
{
|
||||
+ (NSArray *)mimeTypes {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)fileTypes
|
||||
{
|
||||
+ (NSArray *)fileTypes {
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (float)priority
|
||||
{
|
||||
+ (float)priority {
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
|
@ -51,11 +49,9 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
|||
return nil;
|
||||
}
|
||||
|
||||
- (id)initWithDecoders:(NSArray *)decoders
|
||||
{
|
||||
- (id)initWithDecoders:(NSArray *)decoders {
|
||||
self = [super init];
|
||||
if ( self )
|
||||
{
|
||||
if(self) {
|
||||
theDecoders = sortClassesByPriority(decoders);
|
||||
theDecoder = nil;
|
||||
cachedObservers = [[NSMutableArray alloc] init];
|
||||
|
@ -63,52 +59,46 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)properties
|
||||
{
|
||||
if ( theDecoder != nil ) return [theDecoder properties];
|
||||
- (NSDictionary *)properties {
|
||||
if(theDecoder != nil) return [theDecoder properties];
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (int)readAudio:(void *)buffer frames:(UInt32)frames
|
||||
{
|
||||
if ( theDecoder != nil ) return [theDecoder readAudio:buffer frames:frames];
|
||||
- (int)readAudio:(void *)buffer frames:(UInt32)frames {
|
||||
if(theDecoder != nil) return [theDecoder readAudio:buffer frames:frames];
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (BOOL)open:(id<CogSource>)source
|
||||
{
|
||||
for (NSString *classString in theDecoders)
|
||||
{
|
||||
- (BOOL)open:(id<CogSource>)source {
|
||||
for(NSString *classString in theDecoders) {
|
||||
Class decoder = NSClassFromString(classString);
|
||||
theDecoder = [[decoder alloc] init];
|
||||
for (NSDictionary *obsItem in cachedObservers) {
|
||||
for(NSDictionary *obsItem in cachedObservers) {
|
||||
[theDecoder addObserver:[obsItem objectForKey:@"observer"]
|
||||
forKeyPath:[obsItem objectForKey:@"keyPath"]
|
||||
options:[[obsItem objectForKey:@"options"] unsignedIntegerValue]
|
||||
context:(__bridge void *)([obsItem objectForKey:@"context"])];
|
||||
}
|
||||
if ([theDecoder open:source])
|
||||
if([theDecoder open:source])
|
||||
return YES;
|
||||
for (NSDictionary *obsItem in cachedObservers) {
|
||||
for(NSDictionary *obsItem in cachedObservers) {
|
||||
[theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]];
|
||||
}
|
||||
if ([source seekable])
|
||||
if([source seekable])
|
||||
[source seek:0 whence:SEEK_SET];
|
||||
}
|
||||
theDecoder = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (long)seek:(long)frame
|
||||
{
|
||||
if ( theDecoder != nil ) return [theDecoder seek:frame];
|
||||
- (long)seek:(long)frame {
|
||||
if(theDecoder != nil) return [theDecoder seek:frame];
|
||||
return -1;
|
||||
}
|
||||
|
||||
- (void)close
|
||||
{
|
||||
if ( theDecoder != nil ) {
|
||||
for (NSDictionary *obsItem in cachedObservers) {
|
||||
- (void)close {
|
||||
if(theDecoder != nil) {
|
||||
for(NSDictionary *obsItem in cachedObservers) {
|
||||
[theDecoder removeObserver:[obsItem objectForKey:@"observer"] forKeyPath:[obsItem objectForKey:@"keyPath"]];
|
||||
}
|
||||
[cachedObservers removeAllObjects];
|
||||
|
@ -117,52 +107,46 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
|||
}
|
||||
}
|
||||
|
||||
- (BOOL)setTrack:(NSURL *)track
|
||||
{
|
||||
if ( theDecoder != nil && [theDecoder respondsToSelector: @selector(setTrack:)] ) return [theDecoder setTrack:track];
|
||||
- (BOOL)setTrack:(NSURL *)track {
|
||||
if(theDecoder != nil && [theDecoder respondsToSelector:@selector(setTrack:)]) return [theDecoder setTrack:track];
|
||||
return NO;
|
||||
}
|
||||
|
||||
/* By the current design, the core adds its observers to decoders before they are opened */
|
||||
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
|
||||
{
|
||||
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context {
|
||||
if(context != nil) {
|
||||
[cachedObservers addObject:[NSDictionary dictionaryWithObjectsAndKeys:observer, @"observer", keyPath, @"keyPath", @(options), @"options", context, @"context", nil]];
|
||||
} else {
|
||||
[cachedObservers addObject:[NSDictionary dictionaryWithObjectsAndKeys:observer, @"observer", keyPath, @"keyPath", @(options), @"options", nil]];
|
||||
}
|
||||
if (theDecoder) {
|
||||
if(theDecoder) {
|
||||
[theDecoder addObserver:observer forKeyPath:keyPath options:options context:context];
|
||||
}
|
||||
}
|
||||
|
||||
/* And this is currently called after the decoder is closed */
|
||||
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
|
||||
{
|
||||
for (NSDictionary *obsItem in cachedObservers) {
|
||||
if ([obsItem objectForKey:@"observer"] == observer && [keyPath isEqualToString:[obsItem objectForKey:@"keyPath"]]) {
|
||||
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath {
|
||||
for(NSDictionary *obsItem in cachedObservers) {
|
||||
if([obsItem objectForKey:@"observer"] == observer && [keyPath isEqualToString:[obsItem objectForKey:@"keyPath"]]) {
|
||||
[cachedObservers removeObject:obsItem];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (theDecoder) {
|
||||
if(theDecoder) {
|
||||
[theDecoder removeObserver:observer forKeyPath:keyPath];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
@implementation CogContainerMulti
|
||||
|
||||
+ (NSArray *)urlsForContainerURL:(NSURL *)url containers:(NSArray *)containers
|
||||
{
|
||||
NSArray * sortedContainers = sortClassesByPriority(containers);
|
||||
for (NSString *classString in sortedContainers)
|
||||
{
|
||||
+ (NSArray *)urlsForContainerURL:(NSURL *)url containers:(NSArray *)containers {
|
||||
NSArray *sortedContainers = sortClassesByPriority(containers);
|
||||
for(NSString *classString in sortedContainers) {
|
||||
Class container = NSClassFromString(classString);
|
||||
NSArray * urls = [container urlsForContainerURL:url];
|
||||
if ([urls count])
|
||||
NSArray *urls = [container urlsForContainerURL:url];
|
||||
if([urls count])
|
||||
return urls;
|
||||
}
|
||||
return nil;
|
||||
|
@ -172,14 +156,12 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
|||
|
||||
@implementation CogMetadataReaderMulti
|
||||
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url readers:(NSArray *)readers
|
||||
{
|
||||
NSArray * sortedReaders = sortClassesByPriority(readers);
|
||||
for (NSString *classString in sortedReaders)
|
||||
{
|
||||
+ (NSDictionary *)metadataForURL:(NSURL *)url readers:(NSArray *)readers {
|
||||
NSArray *sortedReaders = sortClassesByPriority(readers);
|
||||
for(NSString *classString in sortedReaders) {
|
||||
Class reader = NSClassFromString(classString);
|
||||
NSDictionary * data = [reader metadataForURL:url];
|
||||
if ([data count])
|
||||
NSDictionary *data = [reader metadataForURL:url];
|
||||
if([data count])
|
||||
return data;
|
||||
}
|
||||
return nil;
|
||||
|
@ -189,16 +171,14 @@ NSArray * sortClassesByPriority(NSArray * theClasses)
|
|||
|
||||
@implementation CogPropertiesReaderMulti
|
||||
|
||||
+ (NSDictionary *)propertiesForSource:(id<CogSource>)source readers:(NSArray *)readers
|
||||
{
|
||||
NSArray * sortedReaders = sortClassesByPriority(readers);
|
||||
for (NSString *classString in sortedReaders)
|
||||
{
|
||||
+ (NSDictionary *)propertiesForSource:(id<CogSource>)source readers:(NSArray *)readers {
|
||||
NSArray *sortedReaders = sortClassesByPriority(readers);
|
||||
for(NSString *classString in sortedReaders) {
|
||||
Class reader = NSClassFromString(classString);
|
||||
NSDictionary * data = [reader propertiesForSource:source];
|
||||
if ([data count])
|
||||
NSDictionary *data = [reader propertiesForSource:source];
|
||||
if([data count])
|
||||
return data;
|
||||
if ([source seekable])
|
||||
if([source seekable])
|
||||
[source seek:0 whence:SEEK_SET];
|
||||
}
|
||||
return nil;
|
||||
|
|
|
@ -7,21 +7,19 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "Helper.h"
|
||||
#include <math.h>
|
||||
|
||||
//These functions are helpers for the process of converting volume from a linear to logarithmic scale.
|
||||
//Numbers that goes in to audioPlayer should be logarithmic. Numbers that are displayed to the user should be linear.
|
||||
//Here's why: http://www.dr-lex.34sp.com/info-stuff/volumecontrols.html
|
||||
//We are using the approximation of X^4.
|
||||
//Input/Output values are in percents.
|
||||
double logarithmicToLinear(double logarithmic, double MAX_VOLUME)
|
||||
{
|
||||
return (MAX_VOLUME == 100.0) ? logarithmic : pow((logarithmic/MAX_VOLUME), 0.25) * 100.0;
|
||||
// These functions are helpers for the process of converting volume from a linear to logarithmic scale.
|
||||
// Numbers that goes in to audioPlayer should be logarithmic. Numbers that are displayed to the user should be linear.
|
||||
// Here's why: http://www.dr-lex.34sp.com/info-stuff/volumecontrols.html
|
||||
// We are using the approximation of X^4.
|
||||
// Input/Output values are in percents.
|
||||
double logarithmicToLinear(double logarithmic, double MAX_VOLUME) {
|
||||
return (MAX_VOLUME == 100.0) ? logarithmic : pow((logarithmic / MAX_VOLUME), 0.25) * 100.0;
|
||||
}
|
||||
|
||||
double linearToLogarithmic(double linear, double MAX_VOLUME)
|
||||
{
|
||||
return (MAX_VOLUME == 100.0) ? linear : (linear/100.0) * (linear/100.0) * (linear/100.0) * (linear/100.0) * MAX_VOLUME;
|
||||
double linearToLogarithmic(double linear, double MAX_VOLUME) {
|
||||
return (MAX_VOLUME == 100.0) ? linear : (linear / 100.0) * (linear / 100.0) * (linear / 100.0) * (linear / 100.0) * MAX_VOLUME;
|
||||
}
|
||||
//End helper volume function thingies. ONWARDS TO GLORY!
|
||||
// End helper volume function thingies. ONWARDS TO GLORY!
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
#import <AssertMacros.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <CoreAudio/CoreAudioTypes.h>
|
||||
|
||||
#import <stdatomic.h>
|
||||
|
@ -29,10 +29,10 @@
|
|||
@class OutputNode;
|
||||
|
||||
@interface OutputCoreAudio : NSObject {
|
||||
OutputNode * outputController;
|
||||
OutputNode *outputController;
|
||||
|
||||
Semaphore * writeSemaphore;
|
||||
Semaphore * readSemaphore;
|
||||
Semaphore *writeSemaphore;
|
||||
Semaphore *readSemaphore;
|
||||
|
||||
BOOL stopInvoked;
|
||||
BOOL running;
|
||||
|
@ -65,7 +65,7 @@
|
|||
|
||||
AudioUnit _eq;
|
||||
|
||||
DownmixProcessor * downmixer;
|
||||
DownmixProcessor *downmixer;
|
||||
|
||||
#ifdef OUTPUT_LOG
|
||||
FILE *_logFile;
|
||||
|
@ -82,7 +82,7 @@
|
|||
- (void)resume;
|
||||
- (void)stop;
|
||||
|
||||
- (void)setVolume:(double) v;
|
||||
- (void)setVolume:(double)v;
|
||||
|
||||
- (void)setEqualizerEnabled:(BOOL)enabled;
|
||||
|
||||
|
|
|
@ -11,22 +11,19 @@
|
|||
|
||||
#import "Logging.h"
|
||||
|
||||
extern void scale_by_volume(float * buffer, size_t count, float volume);
|
||||
extern void scale_by_volume(float *buffer, size_t count, float volume);
|
||||
|
||||
static NSString *CogPlaybackDidBeginNotficiation = @"CogPlaybackDidBeginNotficiation";
|
||||
|
||||
@implementation OutputCoreAudio
|
||||
|
||||
static void fillBuffers(AudioBufferList *ioData, float * inbuffer, size_t count, size_t offset)
|
||||
{
|
||||
static void fillBuffers(AudioBufferList *ioData, float *inbuffer, size_t count, size_t offset) {
|
||||
const size_t channels = ioData->mNumberBuffers;
|
||||
for (int i = 0; i < channels; ++i)
|
||||
{
|
||||
for(int i = 0; i < channels; ++i) {
|
||||
const size_t maxCount = (ioData->mBuffers[i].mDataByteSize / sizeof(float)) - offset;
|
||||
float * output = ((float *)ioData->mBuffers[i].mData) + offset;
|
||||
const float * input = inbuffer + i;
|
||||
for (size_t j = 0, k = (count > maxCount) ? maxCount : count; j < k; ++j)
|
||||
{
|
||||
float *output = ((float *)ioData->mBuffers[i].mData) + offset;
|
||||
const float *input = inbuffer + i;
|
||||
for(size_t j = 0, k = (count > maxCount) ? maxCount : count; j < k; ++j) {
|
||||
*output = *input;
|
||||
output++;
|
||||
input += channels;
|
||||
|
@ -35,29 +32,23 @@ static void fillBuffers(AudioBufferList *ioData, float * inbuffer, size_t count,
|
|||
}
|
||||
}
|
||||
|
||||
static void clearBuffers(AudioBufferList *ioData, size_t count, size_t offset)
|
||||
{
|
||||
for (int i = 0; i < ioData->mNumberBuffers; ++i)
|
||||
{
|
||||
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));
|
||||
ioData->mBuffers[i].mNumberChannels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void scaleBuffersByVolume(AudioBufferList *ioData, float volume)
|
||||
{
|
||||
if (volume != 1.0)
|
||||
{
|
||||
for (int i = 0; i < ioData->mNumberBuffers; ++i)
|
||||
{
|
||||
scale_by_volume((float*)ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize / sizeof(float), volume);
|
||||
static void scaleBuffersByVolume(AudioBufferList *ioData, float volume) {
|
||||
if(volume != 1.0) {
|
||||
for(int i = 0; i < ioData->mNumberBuffers; ++i) {
|
||||
scale_by_volume((float *)ioData->mBuffers[i].mData, ioData->mBuffers[i].mDataByteSize / sizeof(float), volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData )
|
||||
{
|
||||
OutputCoreAudio * _self = (__bridge OutputCoreAudio *) inRefCon;
|
||||
static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData) {
|
||||
OutputCoreAudio *_self = (__bridge OutputCoreAudio *)inRefCon;
|
||||
|
||||
const int channels = _self->deviceFormat.mChannelsPerFrame;
|
||||
const int bytesPerPacket = channels * sizeof(float);
|
||||
|
@ -66,8 +57,7 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
|
||||
amountToRead = inNumberFrames * bytesPerPacket;
|
||||
|
||||
if (_self->stopping == YES || [_self->outputController shouldContinue] == NO)
|
||||
{
|
||||
if(_self->stopping == YES || [_self->outputController shouldContinue] == NO) {
|
||||
// Chain is dead, fill out the serial number pointer forever with silence
|
||||
clearBuffers(ioData, amountToRead / bytesPerPacket, 0);
|
||||
atomic_fetch_add(&_self->bytesRendered, amountToRead);
|
||||
|
@ -75,21 +65,20 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
return 0;
|
||||
}
|
||||
|
||||
if ([[_self->outputController buffer] isEmpty] && ![_self->outputController chainQueueHasTracks])
|
||||
{
|
||||
if([[_self->outputController buffer] isEmpty] && ![_self->outputController chainQueueHasTracks]) {
|
||||
// Hit end of last track, pad with silence until queue event stops us
|
||||
clearBuffers(ioData, amountToRead / bytesPerPacket, 0);
|
||||
atomic_fetch_add(&_self->bytesRendered, amountToRead);
|
||||
return 0;
|
||||
}
|
||||
|
||||
AudioChunk * chunk = [[_self->outputController buffer] removeSamples:(amountToRead / bytesPerPacket)];
|
||||
AudioChunk *chunk = [[_self->outputController buffer] removeSamples:(amountToRead / bytesPerPacket)];
|
||||
|
||||
size_t frameCount = [chunk frameCount];
|
||||
AudioStreamBasicDescription format = [chunk format];
|
||||
|
||||
if (frameCount) {
|
||||
if (!_self->streamFormatStarted || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) {
|
||||
if(frameCount) {
|
||||
if(!_self->streamFormatStarted || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) {
|
||||
_self->streamFormat = format;
|
||||
_self->streamFormatStarted = YES;
|
||||
_self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:format andOutputFormat:_self->deviceFormat];
|
||||
|
@ -97,7 +86,7 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
|
||||
double chunkDuration = [chunk duration];
|
||||
|
||||
NSData * samples = [chunk removeSamples:frameCount];
|
||||
NSData *samples = [chunk removeSamples:frameCount];
|
||||
|
||||
float downmixedData[frameCount * channels];
|
||||
[_self->downmixer process:[samples bytes] frameCount:frameCount output:downmixedData];
|
||||
|
@ -111,20 +100,19 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
|
||||
// Try repeatedly! Buffer wraps can cause a slight data shortage, as can
|
||||
// unexpected track changes.
|
||||
while ((amountRead < amountToRead) && [_self->outputController shouldContinue] == YES)
|
||||
{
|
||||
while((amountRead < amountToRead) && [_self->outputController shouldContinue] == YES) {
|
||||
chunk = [[_self->outputController buffer] removeSamples:((amountToRead - amountRead) / bytesPerPacket)];
|
||||
frameCount = [chunk frameCount];
|
||||
format = [chunk format];
|
||||
if (frameCount) {
|
||||
if (!_self->streamFormatStarted || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) {
|
||||
if(frameCount) {
|
||||
if(!_self->streamFormatStarted || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) {
|
||||
_self->streamFormat = format;
|
||||
_self->streamFormatStarted = YES;
|
||||
_self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:format andOutputFormat:_self->deviceFormat];
|
||||
}
|
||||
atomic_fetch_add(&_self->bytesRendered, frameCount * bytesPerPacket);
|
||||
double chunkDuration = [chunk duration];
|
||||
NSData * samples = [chunk removeSamples:frameCount];
|
||||
NSData *samples = [chunk removeSamples:frameCount];
|
||||
float downmixedData[frameCount * channels];
|
||||
[_self->downmixer process:[samples bytes] frameCount:frameCount output:downmixedData];
|
||||
fillBuffers(ioData, downmixedData, frameCount, amountRead / bytesPerPacket);
|
||||
|
@ -133,19 +121,17 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
|
||||
amountRead += frameCount * bytesPerPacket;
|
||||
[_self->writeSemaphore signal];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[_self->readSemaphore timedWait:500];
|
||||
}
|
||||
}
|
||||
|
||||
float volumeScale = 1.0;
|
||||
long sustained = atomic_load_explicit(&_self->bytesHdcdSustained, memory_order_relaxed);
|
||||
if (sustained) {
|
||||
if (sustained < amountRead) {
|
||||
if(sustained) {
|
||||
if(sustained < amountRead) {
|
||||
atomic_store(&_self->bytesHdcdSustained, 0);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
atomic_fetch_sub(&_self->bytesHdcdSustained, amountRead);
|
||||
}
|
||||
volumeScale = 0.5;
|
||||
|
@ -153,8 +139,7 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
|
||||
scaleBuffersByVolume(ioData, _self->volume * volumeScale);
|
||||
|
||||
if (amountRead < amountToRead)
|
||||
{
|
||||
if(amountRead < amountToRead) {
|
||||
// Either underrun, or no data at all. Caller output tends to just
|
||||
// buffer loop if it doesn't get anything, so always produce a full
|
||||
// buffer, and silence anything we couldn't supply.
|
||||
|
@ -164,11 +149,9 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
return 0;
|
||||
};
|
||||
|
||||
- (id)initWithController:(OutputNode *)c
|
||||
{
|
||||
- (id)initWithController:(OutputNode *)c {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
outputController = c;
|
||||
_au = nil;
|
||||
_eq = NULL;
|
||||
|
@ -197,35 +180,29 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc
|
|||
}
|
||||
|
||||
static OSStatus
|
||||
default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData)
|
||||
{
|
||||
OutputCoreAudio *this = (__bridge OutputCoreAudio *) inUserData;
|
||||
default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const AudioObjectPropertyAddress *inAddresses, void *inUserData) {
|
||||
OutputCoreAudio *this = (__bridge OutputCoreAudio *)inUserData;
|
||||
return [this setOutputDeviceByID:-1];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
if ([keyPath isEqualToString:@"values.outputDevice"]) {
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if([keyPath isEqualToString:@"values.outputDevice"]) {
|
||||
NSDictionary *device = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"outputDevice"];
|
||||
|
||||
[self setOutputDeviceWithDeviceDict:device];
|
||||
}
|
||||
else if ([keyPath isEqualToString:@"values.GraphicEQenable"]) {
|
||||
} else if([keyPath isEqualToString:@"values.GraphicEQenable"]) {
|
||||
BOOL enabled = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"GraphicEQenable"] boolValue];
|
||||
|
||||
[self setEqualizerEnabled:enabled];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)signalEndOfStream
|
||||
{
|
||||
- (void)signalEndOfStream {
|
||||
[outputController resetAmountPlayed];
|
||||
[outputController endOfInputPlayed];
|
||||
}
|
||||
|
||||
- (void)threadEntry:(id)arg
|
||||
{
|
||||
- (void)threadEntry:(id)arg {
|
||||
running = YES;
|
||||
started = NO;
|
||||
stopNext = NO;
|
||||
|
@ -233,82 +210,77 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
atomic_store(&bytesRendered, 0);
|
||||
NSMutableArray *delayedEvents = [[NSMutableArray alloc] init];
|
||||
BOOL delayedEventsPopped = YES;
|
||||
while (!stopping) {
|
||||
if (++eventCount == 128) {
|
||||
while(!stopping) {
|
||||
if(++eventCount == 128) {
|
||||
[self updateDeviceFormat];
|
||||
eventCount = 0;
|
||||
}
|
||||
|
||||
if ([outputController shouldReset]) {
|
||||
if([outputController shouldReset]) {
|
||||
[[outputController buffer] reset];
|
||||
[outputController setShouldReset:NO];
|
||||
[delayedEvents removeAllObjects];
|
||||
delayedEventsPopped = YES;
|
||||
}
|
||||
|
||||
while ([delayedEvents count]) {
|
||||
while([delayedEvents count]) {
|
||||
size_t localBytesRendered = atomic_load_explicit(&bytesRendered, memory_order_relaxed);
|
||||
double secondsRendered = (double)localBytesRendered / (double)(deviceFormat.mBytesPerPacket * deviceFormat.mSampleRate);
|
||||
if (secondsRendered >= [[delayedEvents objectAtIndex:0] doubleValue]) {
|
||||
if ([outputController chainQueueHasTracks])
|
||||
if(secondsRendered >= [[delayedEvents objectAtIndex:0] doubleValue]) {
|
||||
if([outputController chainQueueHasTracks])
|
||||
delayedEventsPopped = YES;
|
||||
[self signalEndOfStream];
|
||||
[delayedEvents removeObjectAtIndex:0];
|
||||
}
|
||||
else break;
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
if (stopping)
|
||||
if(stopping)
|
||||
break;
|
||||
|
||||
size_t frameCount = 0;
|
||||
|
||||
if (![[outputController buffer] isFull]) {
|
||||
AudioChunk * chunk = [outputController readChunk:512];
|
||||
if(![[outputController buffer] isFull]) {
|
||||
AudioChunk *chunk = [outputController readChunk:512];
|
||||
frameCount = [chunk frameCount];
|
||||
if (frameCount) {
|
||||
if(frameCount) {
|
||||
[[outputController buffer] addChunk:chunk];
|
||||
}
|
||||
}
|
||||
|
||||
if (frameCount) {
|
||||
if(frameCount) {
|
||||
[readSemaphore signal];
|
||||
continue;
|
||||
}
|
||||
else if ([outputController shouldContinue] == NO)
|
||||
} else if([outputController shouldContinue] == NO)
|
||||
break;
|
||||
else if ([[outputController buffer] isFull]) {
|
||||
if (!started) {
|
||||
else if([[outputController buffer] isFull]) {
|
||||
if(!started) {
|
||||
started = YES;
|
||||
if (!paused) {
|
||||
if(!paused) {
|
||||
NSError *err;
|
||||
[_au startHardwareAndReturnError:&err];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// End of input possibly reached
|
||||
if (delayedEventsPopped && [outputController endOfStream] == YES)
|
||||
{
|
||||
if(delayedEventsPopped && [outputController endOfStream] == YES) {
|
||||
double secondsBuffered = [[outputController buffer] listDuration];
|
||||
size_t _bytesRendered = atomic_load_explicit(&bytesRendered, memory_order_relaxed);
|
||||
secondsBuffered += (double)_bytesRendered / (double)(deviceFormat.mBytesPerPacket * deviceFormat.mSampleRate);
|
||||
if ([outputController chainQueueHasTracks])
|
||||
{
|
||||
if (secondsBuffered <= 0.005)
|
||||
if([outputController chainQueueHasTracks]) {
|
||||
if(secondsBuffered <= 0.005)
|
||||
secondsBuffered = 0.0;
|
||||
else
|
||||
secondsBuffered -= 0.005;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
stopNext = YES;
|
||||
break;
|
||||
}
|
||||
[delayedEvents addObject:[NSNumber numberWithDouble:secondsBuffered]];
|
||||
delayedEventsPopped = NO;
|
||||
if (!started) {
|
||||
if(!started) {
|
||||
started = YES;
|
||||
if (!paused) {
|
||||
if(!paused) {
|
||||
NSError *err;
|
||||
[_au startHardwareAndReturnError:&err];
|
||||
}
|
||||
|
@ -319,12 +291,11 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
[writeSemaphore timedWait:5000];
|
||||
}
|
||||
stopped = YES;
|
||||
if (!stopInvoked)
|
||||
if(!stopInvoked)
|
||||
[self stop];
|
||||
}
|
||||
|
||||
- (OSStatus)setOutputDeviceByID:(AudioDeviceID)deviceID
|
||||
{
|
||||
- (OSStatus)setOutputDeviceByID:(AudioDeviceID)deviceID {
|
||||
OSStatus err;
|
||||
BOOL defaultDevice = NO;
|
||||
AudioObjectPropertyAddress theAddress = {
|
||||
|
@ -333,47 +304,46 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
.mElement = kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
if (deviceID == -1) {
|
||||
if(deviceID == -1) {
|
||||
defaultDevice = YES;
|
||||
UInt32 size = sizeof(AudioDeviceID);
|
||||
err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &size, &deviceID);
|
||||
|
||||
if (err != noErr) {
|
||||
if(err != noErr) {
|
||||
DLog(@"THERE'S NO DEFAULT OUTPUT DEVICE");
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (_au) {
|
||||
if(_au) {
|
||||
AudioObjectPropertyAddress defaultDeviceAddress = theAddress;
|
||||
|
||||
if (listenerapplied && !defaultDevice) {
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, default_device_changed, (__bridge void * _Nullable)(self));
|
||||
if(listenerapplied && !defaultDevice) {
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, default_device_changed, (__bridge void *_Nullable)(self));
|
||||
listenerapplied = NO;
|
||||
}
|
||||
|
||||
if (outputDeviceID != deviceID) {
|
||||
if(outputDeviceID != deviceID) {
|
||||
DLog(@"Device: %i\n", deviceID);
|
||||
outputDeviceID = deviceID;
|
||||
|
||||
NSError *nserr;
|
||||
[_au setDeviceID:outputDeviceID error:&nserr];
|
||||
if (nserr != nil) {
|
||||
if(nserr != nil) {
|
||||
return (OSErr)[nserr code];
|
||||
}
|
||||
}
|
||||
|
||||
if (!listenerapplied && defaultDevice) {
|
||||
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, default_device_changed, (__bridge void * _Nullable)(self));
|
||||
if(!listenerapplied && defaultDevice) {
|
||||
AudioObjectAddPropertyListener(kAudioObjectSystemObject, &defaultDeviceAddress, default_device_changed, (__bridge void *_Nullable)(self));
|
||||
listenerapplied = YES;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
err = noErr;
|
||||
}
|
||||
|
||||
if (err != noErr) {
|
||||
if(err != noErr) {
|
||||
DLog(@"No output device with ID %d could be found.", deviceID);
|
||||
|
||||
return err;
|
||||
|
@ -382,20 +352,19 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
return err;
|
||||
}
|
||||
|
||||
- (BOOL)setOutputDeviceWithDeviceDict:(NSDictionary *)deviceDict
|
||||
{
|
||||
- (BOOL)setOutputDeviceWithDeviceDict:(NSDictionary *)deviceDict {
|
||||
NSNumber *deviceIDNum = [deviceDict objectForKey:@"deviceID"];
|
||||
AudioDeviceID outputDeviceID = [deviceIDNum unsignedIntValue] ?: -1;
|
||||
|
||||
__block OSStatus err = [self setOutputDeviceByID:outputDeviceID];
|
||||
|
||||
if (err != noErr) {
|
||||
if(err != noErr) {
|
||||
// Try matching by name.
|
||||
NSString *userDeviceName = deviceDict[@"name"];
|
||||
|
||||
[self enumerateAudioOutputsUsingBlock:
|
||||
^(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop) {
|
||||
if ([deviceName isEqualToString:userDeviceName]) {
|
||||
if([deviceName isEqualToString:userDeviceName]) {
|
||||
err = [self setOutputDeviceByID:deviceID];
|
||||
|
||||
#if 0
|
||||
|
@ -415,8 +384,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
}];
|
||||
}
|
||||
|
||||
if (err != noErr) {
|
||||
|
||||
if(err != noErr) {
|
||||
ALog(@"No output device could be found, your random error code is %d. Have a nice day!", err);
|
||||
|
||||
return NO;
|
||||
|
@ -427,8 +395,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
|
||||
// The following is largely a copy pasta of -awakeFromNib from "OutputsArrayController.m".
|
||||
// TODO: Share the code. (How to do this across xcodeproj?)
|
||||
- (void)enumerateAudioOutputsUsingBlock:(void (NS_NOESCAPE ^ _Nonnull)(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop))block
|
||||
{
|
||||
- (void)enumerateAudioOutputsUsingBlock:(void(NS_NOESCAPE ^ _Nonnull)(NSString *deviceName, AudioDeviceID deviceID, AudioDeviceID systemDefaultID, BOOL *stop))block {
|
||||
UInt32 propsize;
|
||||
AudioObjectPropertyAddress theAddress = {
|
||||
.mSelector = kAudioHardwarePropertyDevices,
|
||||
|
@ -448,7 +415,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
|
||||
theAddress.mScope = kAudioDevicePropertyScopeOutput;
|
||||
|
||||
for (UInt32 i = 0; i < nDevices; ++i) {
|
||||
for(UInt32 i = 0; i < nDevices; ++i) {
|
||||
CFStringRef name = NULL;
|
||||
propsize = sizeof(name);
|
||||
theAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
||||
|
@ -458,17 +425,17 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
theAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
|
||||
__Verify_noErr(AudioObjectGetPropertyDataSize(devids[i], &theAddress, 0, NULL, &propsize));
|
||||
|
||||
if (propsize < sizeof(UInt32)) {
|
||||
if(propsize < sizeof(UInt32)) {
|
||||
CFRelease(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
AudioBufferList * bufferList = (AudioBufferList *) malloc(propsize);
|
||||
AudioBufferList *bufferList = (AudioBufferList *)malloc(propsize);
|
||||
__Verify_noErr(AudioObjectGetPropertyData(devids[i], &theAddress, 0, NULL, &propsize, bufferList));
|
||||
UInt32 bufferCount = bufferList->mNumberBuffers;
|
||||
free(bufferList);
|
||||
|
||||
if (!bufferCount) {
|
||||
if(!bufferCount) {
|
||||
CFRelease(name);
|
||||
continue;
|
||||
}
|
||||
|
@ -481,7 +448,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
|
||||
CFRelease(name);
|
||||
|
||||
if (stop) {
|
||||
if(stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -489,12 +456,10 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
free(devids);
|
||||
}
|
||||
|
||||
- (BOOL)updateDeviceFormat
|
||||
{
|
||||
- (BOOL)updateDeviceFormat {
|
||||
AVAudioFormat *format = _au.outputBusses[0].format;
|
||||
|
||||
if (!_deviceFormat || ![_deviceFormat isEqual:format])
|
||||
{
|
||||
if(!_deviceFormat || ![_deviceFormat isEqual:format]) {
|
||||
NSError *err;
|
||||
AVAudioFormat *renderFormat;
|
||||
|
||||
|
@ -504,20 +469,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
_deviceFormat = format;
|
||||
deviceFormat = *(format.streamDescription);
|
||||
|
||||
///Seems some 3rd party devices return incorrect stuff...or I just don't like noninterleaved data.
|
||||
/// Seems some 3rd party devices return incorrect stuff...or I just don't like noninterleaved data.
|
||||
deviceFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
|
||||
// deviceFormat.mFormatFlags &= ~kLinearPCMFormatFlagIsFloat;
|
||||
// deviceFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||
// We don't want more than 8 channels
|
||||
if (deviceFormat.mChannelsPerFrame > 8) {
|
||||
if(deviceFormat.mChannelsPerFrame > 8) {
|
||||
deviceFormat.mChannelsPerFrame = 8;
|
||||
}
|
||||
deviceFormat.mBytesPerFrame = deviceFormat.mChannelsPerFrame*(deviceFormat.mBitsPerChannel/8);
|
||||
deviceFormat.mBytesPerFrame = deviceFormat.mChannelsPerFrame * (deviceFormat.mBitsPerChannel / 8);
|
||||
deviceFormat.mBytesPerPacket = deviceFormat.mBytesPerFrame * deviceFormat.mFramesPerPacket;
|
||||
|
||||
/* Set the channel layout for the audio queue */
|
||||
AudioChannelLayoutTag tag = 0;
|
||||
switch (deviceFormat.mChannelsPerFrame) {
|
||||
switch(deviceFormat.mChannelsPerFrame) {
|
||||
case 1:
|
||||
tag = kAudioChannelLayoutTag_Mono;
|
||||
break;
|
||||
|
@ -546,7 +511,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
|
||||
renderFormat = [[AVAudioFormat alloc] initWithStreamDescription:&deviceFormat channelLayout:[[AVAudioChannelLayout alloc] initWithLayoutTag:tag]];
|
||||
[_au.inputBusses[0] setFormat:renderFormat error:&err];
|
||||
if (err != nil)
|
||||
if(err != nil)
|
||||
return NO;
|
||||
|
||||
[outputController setFormat:&deviceFormat];
|
||||
|
@ -555,15 +520,15 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
|
||||
asbd.mFormatFlags &= ~kAudioFormatFlagIsPacked;
|
||||
|
||||
AudioUnitSetProperty (_eq, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &asbd, sizeof (asbd));
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &asbd, sizeof(asbd));
|
||||
|
||||
AudioUnitSetProperty (_eq, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Output, 0, &asbd, sizeof (asbd));
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Output, 0);
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Output, 0, &asbd, sizeof(asbd));
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Output, 0);
|
||||
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Global, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Global, 0);
|
||||
|
||||
eqEnabled = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"GraphicEQenable"] boolValue];
|
||||
}
|
||||
|
@ -571,9 +536,8 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)setup
|
||||
{
|
||||
if (_au)
|
||||
- (BOOL)setup {
|
||||
if(_au)
|
||||
[self stop];
|
||||
|
||||
stopInvoked = NO;
|
||||
|
@ -596,21 +560,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
desc.componentFlagsMask = 0;
|
||||
|
||||
_au = [[AUAudioUnit alloc] initWithComponentDescription:desc error:&err];
|
||||
if (err != nil)
|
||||
if(err != nil)
|
||||
return NO;
|
||||
|
||||
// Setup the output device before mucking with settings
|
||||
NSDictionary *device = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"outputDevice"];
|
||||
if (device) {
|
||||
if(device) {
|
||||
BOOL ok = [self setOutputDeviceWithDeviceDict:device];
|
||||
if (!ok) {
|
||||
//Ruh roh.
|
||||
if(!ok) {
|
||||
// Ruh roh.
|
||||
[self setOutputDeviceWithDeviceDict:nil];
|
||||
|
||||
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] removeObjectForKey:@"outputDevice"];
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[self setOutputDeviceWithDeviceDict:nil];
|
||||
}
|
||||
|
||||
|
@ -622,11 +585,11 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
desc.componentSubType = kAudioUnitSubType_GraphicEQ;
|
||||
|
||||
comp = AudioComponentFindNext(comp, &desc);
|
||||
if (!comp)
|
||||
if(!comp)
|
||||
return NO;
|
||||
|
||||
OSStatus _err = AudioComponentInstanceNew(comp, &_eq);
|
||||
if (err)
|
||||
if(err)
|
||||
return NO;
|
||||
|
||||
[self updateDeviceFormat];
|
||||
|
@ -640,21 +603,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
__block FILE *logFile = _logFile;
|
||||
#endif
|
||||
|
||||
_au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags * _Nonnull actionFlags, const AudioTimeStamp * _Nonnull timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList * _Nonnull inputData)
|
||||
{
|
||||
_au.outputProvider = ^AUAudioUnitStatus(AudioUnitRenderActionFlags *_Nonnull actionFlags, const AudioTimeStamp *_Nonnull timestamp, AUAudioFrameCount frameCount, NSInteger inputBusNumber, AudioBufferList *_Nonnull inputData) {
|
||||
// This expects multiple buffers, so:
|
||||
int i;
|
||||
const int channels = format->mChannelsPerFrame;
|
||||
const int channelsminusone = channels - 1;
|
||||
float buffers[frameCount * format->mChannelsPerFrame];
|
||||
uint8_t bufferlistbuffer[sizeof(AudioBufferList) + sizeof(AudioBuffer) * channelsminusone];
|
||||
AudioBufferList * ioData = (AudioBufferList *)(bufferlistbuffer);
|
||||
AudioBufferList *ioData = (AudioBufferList *)(bufferlistbuffer);
|
||||
|
||||
ioData->mNumberBuffers = channels;
|
||||
|
||||
memset(buffers, 0, sizeof(buffers));
|
||||
|
||||
for (i = 0; i < channels; ++i) {
|
||||
for(i = 0; i < channels; ++i) {
|
||||
ioData->mBuffers[i].mNumberChannels = 1;
|
||||
ioData->mBuffers[i].mData = buffers + frameCount * i;
|
||||
ioData->mBuffers[i].mDataByteSize = frameCount * sizeof(float);
|
||||
|
@ -662,19 +624,19 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
|
||||
OSStatus ret;
|
||||
|
||||
if (*eqEnabled)
|
||||
ret = AudioUnitRender(eq, actionFlags, timestamp, (UInt32) inputBusNumber, frameCount, ioData);
|
||||
if(*eqEnabled)
|
||||
ret = AudioUnitRender(eq, actionFlags, timestamp, (UInt32)inputBusNumber, frameCount, ioData);
|
||||
else
|
||||
ret = renderCallback(refCon, actionFlags, timestamp, (UInt32) inputBusNumber, frameCount, ioData);
|
||||
ret = renderCallback(refCon, actionFlags, timestamp, (UInt32)inputBusNumber, frameCount, ioData);
|
||||
|
||||
if (ret)
|
||||
if(ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < channels; ++i) {
|
||||
float * outBuffer = ((float*)inputData->mBuffers[0].mData) + i;
|
||||
const float * inBuffer = ((float*)ioData->mBuffers[i].mData);
|
||||
for(i = 0; i < channels; ++i) {
|
||||
float *outBuffer = ((float *)inputData->mBuffers[0].mData) + i;
|
||||
const float *inBuffer = ((float *)ioData->mBuffers[i].mData);
|
||||
const int frameCount = ioData->mBuffers[i].mDataByteSize / sizeof(float);
|
||||
for (int j = 0; j < frameCount; ++j) {
|
||||
for(int j = 0; j < frameCount; ++j) {
|
||||
*outBuffer = *inBuffer;
|
||||
inBuffer++;
|
||||
outBuffer += channels;
|
||||
|
@ -682,7 +644,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
}
|
||||
|
||||
#ifdef OUTPUT_LOG
|
||||
if (logFile) {
|
||||
if(logFile) {
|
||||
fwrite(inputData->mBuffers[0].mData, 1, inputData->mBuffers[0].mDataByteSize, logFile);
|
||||
}
|
||||
#endif
|
||||
|
@ -698,26 +660,26 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
UInt32 size = sizeof(value);
|
||||
|
||||
value = CHUNK_SIZE;
|
||||
AudioUnitSetProperty (_eq, kAudioUnitProperty_MaximumFramesPerSlice,
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_MaximumFramesPerSlice,
|
||||
kAudioUnitScope_Global, 0, &value, size);
|
||||
|
||||
value = 127;
|
||||
AudioUnitSetProperty (_eq, kAudioUnitProperty_RenderQuality,
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_RenderQuality,
|
||||
kAudioUnitScope_Global, 0, &value, size);
|
||||
|
||||
AURenderCallbackStruct callbackStruct;
|
||||
callbackStruct.inputProcRefCon = (__bridge void *)self;
|
||||
callbackStruct.inputProc = renderCallback;
|
||||
AudioUnitSetProperty (_eq, kAudioUnitProperty_SetRenderCallback,
|
||||
AudioUnitSetProperty(_eq, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &callbackStruct, sizeof(callbackStruct));
|
||||
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Output, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Output, 0);
|
||||
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Global, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Global, 0);
|
||||
|
||||
_err = AudioUnitInitialize(_eq);
|
||||
if (_err)
|
||||
if(_err)
|
||||
return NO;
|
||||
|
||||
[self setEqualizerEnabled:[[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"GraphicEQenable"] boolValue]];
|
||||
|
@ -733,45 +695,41 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
return (err == nil);
|
||||
}
|
||||
|
||||
- (void)setVolume:(double)v
|
||||
{
|
||||
- (void)setVolume:(double)v {
|
||||
volume = v * 0.01f;
|
||||
}
|
||||
|
||||
- (void)setEqualizerEnabled:(BOOL)enabled
|
||||
{
|
||||
if (enabled && !eqEnabled) {
|
||||
if (_eq) {
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Output, 0);
|
||||
AudioUnitReset (_eq, kAudioUnitScope_Global, 0);
|
||||
- (void)setEqualizerEnabled:(BOOL)enabled {
|
||||
if(enabled && !eqEnabled) {
|
||||
if(_eq) {
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Input, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Output, 0);
|
||||
AudioUnitReset(_eq, kAudioUnitScope_Global, 0);
|
||||
}
|
||||
}
|
||||
|
||||
eqEnabled = enabled;
|
||||
}
|
||||
|
||||
- (void)start
|
||||
{
|
||||
- (void)start {
|
||||
[self threadEntry:nil];
|
||||
}
|
||||
|
||||
- (void)stop
|
||||
{
|
||||
- (void)stop {
|
||||
stopInvoked = YES;
|
||||
if (observersapplied) {
|
||||
if(observersapplied) {
|
||||
observersapplied = NO;
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.outputDevice"];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.GraphicEQenable"];
|
||||
}
|
||||
if (stopNext && started && !paused) {
|
||||
while (![[outputController buffer] isEmpty]) {
|
||||
if(stopNext && started && !paused) {
|
||||
while(![[outputController buffer] isEmpty]) {
|
||||
[writeSemaphore signal];
|
||||
[readSemaphore signal];
|
||||
usleep(500);
|
||||
}
|
||||
}
|
||||
if (stopNext) {
|
||||
if(stopNext) {
|
||||
stopNext = NO;
|
||||
[self signalEndOfStream];
|
||||
}
|
||||
|
@ -779,40 +737,36 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
paused = NO;
|
||||
[writeSemaphore signal];
|
||||
[readSemaphore signal];
|
||||
if (listenerapplied) {
|
||||
if(listenerapplied) {
|
||||
AudioObjectPropertyAddress theAddress = {
|
||||
.mSelector = kAudioHardwarePropertyDefaultOutputDevice,
|
||||
.mScope = kAudioObjectPropertyScopeGlobal,
|
||||
.mElement = kAudioObjectPropertyElementMaster
|
||||
};
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &theAddress, default_device_changed, (__bridge void * _Nullable)(self));
|
||||
AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &theAddress, default_device_changed, (__bridge void *_Nullable)(self));
|
||||
listenerapplied = NO;
|
||||
}
|
||||
if (_au) {
|
||||
if (started)
|
||||
if(_au) {
|
||||
if(started)
|
||||
[_au stopHardware];
|
||||
_au = nil;
|
||||
}
|
||||
if (running)
|
||||
while (!stopped)
|
||||
{
|
||||
if(running)
|
||||
while(!stopped) {
|
||||
stopping = YES;
|
||||
[readSemaphore signal];
|
||||
[writeSemaphore timedWait:5000];
|
||||
}
|
||||
if (_eq)
|
||||
{
|
||||
if(_eq) {
|
||||
// This takes the EQ and frees it after disposing of any present UIs
|
||||
[outputController endEqualizer:_eq];
|
||||
_eq = NULL;
|
||||
}
|
||||
if (downmixer)
|
||||
{
|
||||
if(downmixer) {
|
||||
downmixer = nil;
|
||||
}
|
||||
#ifdef OUTPUT_LOG
|
||||
if (_logFile)
|
||||
{
|
||||
if(_logFile) {
|
||||
fclose(_logFile);
|
||||
_logFile = NULL;
|
||||
}
|
||||
|
@ -820,29 +774,25 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const
|
|||
outputController = nil;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (!stopInvoked)
|
||||
- (void)dealloc {
|
||||
if(!stopInvoked)
|
||||
[self stop];
|
||||
}
|
||||
|
||||
- (void)pause
|
||||
{
|
||||
- (void)pause {
|
||||
paused = YES;
|
||||
if (started)
|
||||
if(started)
|
||||
[_au stopHardware];
|
||||
}
|
||||
|
||||
- (void)resume
|
||||
{
|
||||
- (void)resume {
|
||||
NSError *err;
|
||||
[_au startHardwareAndReturnError:&err];
|
||||
paused = NO;
|
||||
started = YES;
|
||||
}
|
||||
|
||||
- (void)sustainHDCD
|
||||
{
|
||||
- (void)sustainHDCD {
|
||||
atomic_store(&bytesHdcdSustained, deviceFormat.mSampleRate * 10 * sizeof(float) * 2);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//Plugins! HOORAY!
|
||||
// Plugins! HOORAY!
|
||||
|
||||
@protocol CogSource <NSObject>
|
||||
+ (NSArray *)schemes; //http, file, etc
|
||||
+ (NSArray *)schemes; // http, file, etc
|
||||
|
||||
- (NSURL *)url;
|
||||
- (NSString *)mimeType;
|
||||
|
@ -10,7 +10,7 @@
|
|||
- (BOOL)seekable;
|
||||
- (BOOL)seek:(long)position whence:(int)whence;
|
||||
- (long)tell;
|
||||
- (long)read:(void *)buffer amount:(long)amount; //reads UP TO amount, returns amount read.
|
||||
- (long)read:(void *)buffer amount:(long)amount; // reads UP TO amount, returns amount read.
|
||||
- (void)close;
|
||||
- (void)dealloc;
|
||||
@end
|
||||
|
@ -20,7 +20,7 @@
|
|||
@end
|
||||
|
||||
@protocol CogContainer <NSObject>
|
||||
+ (NSArray *)fileTypes; //mp3, ogg, etc
|
||||
+ (NSArray *)fileTypes; // mp3, ogg, etc
|
||||
+ (NSArray *)mimeTypes;
|
||||
+ (float)priority;
|
||||
|
||||
|
@ -30,11 +30,11 @@
|
|||
@protocol CogDecoder <NSObject>
|
||||
@required
|
||||
+ (NSArray *)mimeTypes;
|
||||
+ (NSArray *)fileTypes; //mp3, ogg, etc
|
||||
+ (NSArray *)fileTypes; // mp3, ogg, etc
|
||||
+ (NSArray *)fileTypeAssociations; // array of NSArray of NSString, where first item in array is the type name, the second is the icon name, and the rest are the extensions
|
||||
+ (float)priority; // should be 0.0 ... 1.0, higher means you get selected first, should default to 1.0 unless you know a reason why any of your extensions may behave badly, ie. greedily taking over some file type extension without performing any header validation on it
|
||||
|
||||
//For KVO
|
||||
// For KVO
|
||||
//- (void)setProperties:(NSDictionary *)p;
|
||||
- (NSDictionary *)properties;
|
||||
|
||||
|
@ -49,7 +49,7 @@
|
|||
|
||||
- (BOOL)setTrack:(NSURL *)track;
|
||||
|
||||
//These are in NSObject, so as long as you are a subclass of that, you are ok.
|
||||
// These are in NSObject, so as long as you are a subclass of that, you are ok.
|
||||
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
|
||||
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
|
||||
@end
|
||||
|
@ -87,14 +87,11 @@
|
|||
- (NSDictionary *)decodersByExtension;
|
||||
- (NSDictionary *)decodersByMimeType;
|
||||
|
||||
- (id<CogSource>) audioSourceForURL:(NSURL *)url;
|
||||
- (NSArray *) urlsForContainerURL:(NSURL *)url;
|
||||
- (NSDictionary *) metadataForURL:(NSURL *)url skipCue:(BOOL)skip;
|
||||
- (NSDictionary *) propertiesForURL:(NSURL *)url;
|
||||
- (id<CogDecoder>) audioDecoderForSource:(id<CogSource>)source skipCue:(BOOL)skip;
|
||||
- (id<CogSource>)audioSourceForURL:(NSURL *)url;
|
||||
- (NSArray *)urlsForContainerURL:(NSURL *)url;
|
||||
- (NSDictionary *)metadataForURL:(NSURL *)url skipCue:(BOOL)skip;
|
||||
- (NSDictionary *)propertiesForURL:(NSURL *)url;
|
||||
- (id<CogDecoder>)audioDecoderForSource:(id<CogSource>)source skipCue:(BOOL)skip;
|
||||
|
||||
- (int) putMetadataInURL:(NSURL *)url;
|
||||
- (int)putMetadataInURL:(NSURL *)url;
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
#import "Plugin.h"
|
||||
|
||||
//Singletonish
|
||||
@interface PluginController : NSObject <CogPluginController>
|
||||
{
|
||||
// Singletonish
|
||||
@interface PluginController : NSObject <CogPluginController> {
|
||||
NSMutableDictionary *sources;
|
||||
NSMutableDictionary *containers;
|
||||
NSMutableDictionary *metadataReaders;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#import "PluginController.h"
|
||||
#import "Plugin.h"
|
||||
#import "CogPluginMulti.h"
|
||||
#import "Plugin.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
|
@ -22,10 +22,9 @@
|
|||
|
||||
static PluginController *sharedPluginController = nil;
|
||||
|
||||
+ (id<CogPluginController>)sharedPluginController
|
||||
{
|
||||
+ (id<CogPluginController>)sharedPluginController {
|
||||
@synchronized(self) {
|
||||
if (sharedPluginController == nil) {
|
||||
if(sharedPluginController == nil) {
|
||||
sharedPluginController = [[self alloc] init];
|
||||
}
|
||||
}
|
||||
|
@ -33,10 +32,9 @@ static PluginController *sharedPluginController = nil;
|
|||
return sharedPluginController;
|
||||
}
|
||||
|
||||
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
if(self) {
|
||||
self.sources = [[NSMutableDictionary alloc] init];
|
||||
self.containers = [[NSMutableDictionary alloc] init];
|
||||
|
||||
|
@ -54,9 +52,8 @@ static PluginController *sharedPluginController = nil;
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)setup
|
||||
{
|
||||
if (self.configured == NO) {
|
||||
- (void)setup {
|
||||
if(self.configured == NO) {
|
||||
self.configured = YES;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(bundleDidLoad:) name:NSBundleDidLoadNotification object:nil];
|
||||
|
@ -66,63 +63,54 @@ static PluginController *sharedPluginController = nil;
|
|||
}
|
||||
}
|
||||
|
||||
- (void)bundleDidLoad:(NSNotification *)notification
|
||||
{
|
||||
- (void)bundleDidLoad:(NSNotification *)notification {
|
||||
NSArray *classNames = [[notification userInfo] objectForKey:@"NSLoadedClasses"];
|
||||
for (NSString *className in classNames)
|
||||
{
|
||||
for(NSString *className in classNames) {
|
||||
Class bundleClass = NSClassFromString(className);
|
||||
if ([bundleClass conformsToProtocol:@protocol(CogVersionCheck)]) {
|
||||
if([bundleClass conformsToProtocol:@protocol(CogVersionCheck)]) {
|
||||
DLog(@"Component has version check: %@", className);
|
||||
if (![bundleClass shouldLoadForOSVersion:[[NSProcessInfo processInfo] operatingSystemVersion]])
|
||||
{
|
||||
if(![bundleClass shouldLoadForOSVersion:[[NSProcessInfo processInfo] operatingSystemVersion]]) {
|
||||
DLog(@"Plugin fails OS version check, ignoring");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (NSString *className in classNames)
|
||||
{
|
||||
for(NSString *className in classNames) {
|
||||
DLog(@"Class loaded: %@", className);
|
||||
Class bundleClass = NSClassFromString(className);
|
||||
if ([bundleClass conformsToProtocol:@protocol(CogContainer)]) {
|
||||
if([bundleClass conformsToProtocol:@protocol(CogContainer)]) {
|
||||
[self setupContainer:className];
|
||||
}
|
||||
if ([bundleClass conformsToProtocol:@protocol(CogDecoder)]) {
|
||||
if([bundleClass conformsToProtocol:@protocol(CogDecoder)]) {
|
||||
[self setupDecoder:className];
|
||||
}
|
||||
if ([bundleClass conformsToProtocol:@protocol(CogMetadataReader)]) {
|
||||
if([bundleClass conformsToProtocol:@protocol(CogMetadataReader)]) {
|
||||
[self setupMetadataReader:className];
|
||||
}
|
||||
if ([bundleClass conformsToProtocol:@protocol(CogPropertiesReader)]) {
|
||||
if([bundleClass conformsToProtocol:@protocol(CogPropertiesReader)]) {
|
||||
[self setupPropertiesReader:className];
|
||||
}
|
||||
if ([bundleClass conformsToProtocol:@protocol(CogSource)]) {
|
||||
if([bundleClass conformsToProtocol:@protocol(CogSource)]) {
|
||||
[self setupSource:className];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadPluginsAtPath:(NSString *)path
|
||||
{
|
||||
|
||||
- (void)loadPluginsAtPath:(NSString *)path {
|
||||
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil];
|
||||
|
||||
for (NSString *pname in dirContents)
|
||||
{
|
||||
for(NSString *pname in dirContents) {
|
||||
NSString *ppath;
|
||||
ppath = [NSString pathWithComponents:@[path,pname]];
|
||||
ppath = [NSString pathWithComponents:@[path, pname]];
|
||||
|
||||
if ([[pname pathExtension] isEqualToString:@"bundle"])
|
||||
{
|
||||
if([[pname pathExtension] isEqualToString:@"bundle"]) {
|
||||
NSBundle *b = [NSBundle bundleWithPath:ppath];
|
||||
[b load];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)loadPlugins
|
||||
{
|
||||
- (void)loadPlugins {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString *basePath = [[paths firstObject] stringByAppendingPathComponent:@"Cog"];
|
||||
|
||||
|
@ -130,131 +118,106 @@ static PluginController *sharedPluginController = nil;
|
|||
[self loadPluginsAtPath:[basePath stringByAppendingPathComponent:@"Plugins"]];
|
||||
}
|
||||
|
||||
- (void)setupContainer:(NSString *)className
|
||||
{
|
||||
- (void)setupContainer:(NSString *)className {
|
||||
Class container = NSClassFromString(className);
|
||||
if (container && [container respondsToSelector:@selector(fileTypes)]) {
|
||||
for (id fileType in [container fileTypes])
|
||||
{
|
||||
if(container && [container respondsToSelector:@selector(fileTypes)]) {
|
||||
for(id fileType in [container fileTypes]) {
|
||||
NSString *ext = [fileType lowercaseString];
|
||||
NSMutableArray *containerSet;
|
||||
if (![containers objectForKey:ext])
|
||||
{
|
||||
if(![containers objectForKey:ext]) {
|
||||
containerSet = [[NSMutableArray alloc] init];
|
||||
[containers setObject:containerSet forKey:ext];
|
||||
}
|
||||
else
|
||||
} else
|
||||
containerSet = [containers objectForKey:ext];
|
||||
[containerSet addObject:className];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupDecoder:(NSString *)className
|
||||
{
|
||||
- (void)setupDecoder:(NSString *)className {
|
||||
Class decoder = NSClassFromString(className);
|
||||
if (decoder && [decoder respondsToSelector:@selector(fileTypes)]) {
|
||||
for (id fileType in [decoder fileTypes])
|
||||
{
|
||||
if(decoder && [decoder respondsToSelector:@selector(fileTypes)]) {
|
||||
for(id fileType in [decoder fileTypes]) {
|
||||
NSString *ext = [fileType lowercaseString];
|
||||
NSMutableArray *decoders;
|
||||
if (![decodersByExtension objectForKey:ext])
|
||||
{
|
||||
if(![decodersByExtension objectForKey:ext]) {
|
||||
decoders = [[NSMutableArray alloc] init];
|
||||
[decodersByExtension setObject:decoders forKey:ext];
|
||||
}
|
||||
else
|
||||
} else
|
||||
decoders = [decodersByExtension objectForKey:ext];
|
||||
[decoders addObject:className];
|
||||
}
|
||||
}
|
||||
|
||||
if (decoder && [decoder respondsToSelector:@selector(mimeTypes)]) {
|
||||
for (id mimeType in [decoder mimeTypes])
|
||||
{
|
||||
if(decoder && [decoder respondsToSelector:@selector(mimeTypes)]) {
|
||||
for(id mimeType in [decoder mimeTypes]) {
|
||||
NSString *mimetype = [mimeType lowercaseString];
|
||||
NSMutableArray *decoders;
|
||||
if (![decodersByMimeType objectForKey:mimetype])
|
||||
{
|
||||
if(![decodersByMimeType objectForKey:mimetype]) {
|
||||
decoders = [[NSMutableArray alloc] init];
|
||||
[decodersByMimeType setObject:decoders forKey:mimetype];
|
||||
}
|
||||
else
|
||||
} else
|
||||
decoders = [decodersByMimeType objectForKey:mimetype];
|
||||
[decoders addObject:className];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupMetadataReader:(NSString *)className
|
||||
{
|
||||
- (void)setupMetadataReader:(NSString *)className {
|
||||
Class metadataReader = NSClassFromString(className);
|
||||
if (metadataReader && [metadataReader respondsToSelector:@selector(fileTypes)]) {
|
||||
for (id fileType in [metadataReader fileTypes])
|
||||
{
|
||||
if(metadataReader && [metadataReader respondsToSelector:@selector(fileTypes)]) {
|
||||
for(id fileType in [metadataReader fileTypes]) {
|
||||
NSString *ext = [fileType lowercaseString];
|
||||
NSMutableArray *readers;
|
||||
if (![metadataReaders objectForKey:ext])
|
||||
{
|
||||
if(![metadataReaders objectForKey:ext]) {
|
||||
readers = [[NSMutableArray alloc] init];
|
||||
[metadataReaders setObject:readers forKey:ext];
|
||||
}
|
||||
else
|
||||
} else
|
||||
readers = [metadataReaders objectForKey:ext];
|
||||
[readers addObject:className];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupPropertiesReader:(NSString *)className
|
||||
{
|
||||
- (void)setupPropertiesReader:(NSString *)className {
|
||||
Class propertiesReader = NSClassFromString(className);
|
||||
if (propertiesReader && [propertiesReader respondsToSelector:@selector(fileTypes)]) {
|
||||
for (id fileType in [propertiesReader fileTypes])
|
||||
{
|
||||
if(propertiesReader && [propertiesReader respondsToSelector:@selector(fileTypes)]) {
|
||||
for(id fileType in [propertiesReader fileTypes]) {
|
||||
NSString *ext = [fileType lowercaseString];
|
||||
NSMutableArray *readers;
|
||||
if (![propertiesReadersByExtension objectForKey:ext])
|
||||
{
|
||||
if(![propertiesReadersByExtension objectForKey:ext]) {
|
||||
readers = [[NSMutableArray alloc] init];
|
||||
[propertiesReadersByExtension setObject:readers forKey:ext];
|
||||
}
|
||||
else
|
||||
} else
|
||||
readers = [propertiesReadersByExtension objectForKey:ext];
|
||||
[readers addObject:className];
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesReader && [propertiesReader respondsToSelector:@selector(mimeTypes)]) {
|
||||
for (id mimeType in [propertiesReader mimeTypes])
|
||||
{
|
||||
if(propertiesReader && [propertiesReader respondsToSelector:@selector(mimeTypes)]) {
|
||||
for(id mimeType in [propertiesReader mimeTypes]) {
|
||||
NSString *mimetype = [mimeType lowercaseString];
|
||||
NSMutableArray *readers;
|
||||
if (![propertiesReadersByMimeType objectForKey:mimetype])
|
||||
{
|
||||
if(![propertiesReadersByMimeType objectForKey:mimetype]) {
|
||||
readers = [[NSMutableArray alloc] init];
|
||||
[propertiesReadersByMimeType setObject:readers forKey:mimetype];
|
||||
}
|
||||
else
|
||||
} else
|
||||
readers = [propertiesReadersByMimeType objectForKey:mimetype];
|
||||
[readers addObject:className];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setupSource:(NSString *)className
|
||||
{
|
||||
- (void)setupSource:(NSString *)className {
|
||||
Class source = NSClassFromString(className);
|
||||
if (source && [source respondsToSelector:@selector(schemes)]) {
|
||||
for (id scheme in [source schemes])
|
||||
{
|
||||
if(source && [source respondsToSelector:@selector(schemes)]) {
|
||||
for(id scheme in [source schemes]) {
|
||||
[sources setObject:className forKey:scheme];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)printPluginInfo
|
||||
{
|
||||
- (void)printPluginInfo {
|
||||
ALog(@"Sources: %@", self.sources);
|
||||
ALog(@"Containers: %@", self.containers);
|
||||
ALog(@"Metadata Readers: %@", self.metadataReaders);
|
||||
|
@ -420,8 +383,7 @@ static PluginController *sharedPluginController = nil;
|
|||
#endif
|
||||
}
|
||||
|
||||
- (id<CogSource>) audioSourceForURL:(NSURL *)url
|
||||
{
|
||||
- (id<CogSource>)audioSourceForURL:(NSURL *)url {
|
||||
NSString *scheme = [url scheme];
|
||||
|
||||
Class source = NSClassFromString([sources objectForKey:scheme]);
|
||||
|
@ -429,20 +391,17 @@ static PluginController *sharedPluginController = nil;
|
|||
return [[source alloc] init];
|
||||
}
|
||||
|
||||
- (NSArray *) urlsForContainerURL:(NSURL *)url
|
||||
{
|
||||
- (NSArray *)urlsForContainerURL:(NSURL *)url {
|
||||
NSString *ext = [url pathExtension];
|
||||
NSArray *containerSet = [containers objectForKey:[ext lowercaseString]];
|
||||
NSString *classString;
|
||||
if (containerSet) {
|
||||
if ( [containerSet count] > 1 ) {
|
||||
if(containerSet) {
|
||||
if([containerSet count] > 1) {
|
||||
return [CogContainerMulti urlsForContainerURL:url containers:containerSet];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = [containerSet objectAtIndex:0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -451,20 +410,17 @@ static PluginController *sharedPluginController = nil;
|
|||
return [container urlsForContainerURL:url];
|
||||
}
|
||||
|
||||
//Note: Source is assumed to already be opened.
|
||||
- (id<CogDecoder>) audioDecoderForSource:(id <CogSource>)source skipCue:(BOOL)skip
|
||||
{
|
||||
// Note: Source is assumed to already be opened.
|
||||
- (id<CogDecoder>)audioDecoderForSource:(id<CogSource>)source skipCue:(BOOL)skip {
|
||||
NSString *ext = [[source url] pathExtension];
|
||||
NSArray *decoders = [decodersByExtension objectForKey:[ext lowercaseString]];
|
||||
NSString *classString;
|
||||
if (decoders) {
|
||||
if ( [decoders count] > 1 ) {
|
||||
if (skip)
|
||||
{
|
||||
NSMutableArray * _decoders = [decoders mutableCopy];
|
||||
for (int i = 0; i < [_decoders count];)
|
||||
{
|
||||
if ([[_decoders objectAtIndex:i] isEqualToString:@"CueSheetDecoder"])
|
||||
if(decoders) {
|
||||
if([decoders count] > 1) {
|
||||
if(skip) {
|
||||
NSMutableArray *_decoders = [decoders mutableCopy];
|
||||
for(int i = 0; i < [_decoders count];) {
|
||||
if([[_decoders objectAtIndex:i] isEqualToString:@"CueSheetDecoder"])
|
||||
[_decoders removeObjectAtIndex:i];
|
||||
else
|
||||
++i;
|
||||
|
@ -472,22 +428,18 @@ static PluginController *sharedPluginController = nil;
|
|||
return [[CogDecoderMulti alloc] initWithDecoders:_decoders];
|
||||
}
|
||||
return [[CogDecoderMulti alloc] initWithDecoders:decoders];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = [decoders objectAtIndex:0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
decoders = [decodersByMimeType objectForKey:[[source mimeType] lowercaseString]];
|
||||
if (decoders) {
|
||||
if ( [decoders count] > 1 ) {
|
||||
if(decoders) {
|
||||
if([decoders count] > 1) {
|
||||
return [[CogDecoderMulti alloc] initWithDecoders:decoders];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = [decoders objectAtIndex:0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = @"SilenceDecoder";
|
||||
}
|
||||
}
|
||||
|
@ -497,24 +449,21 @@ static PluginController *sharedPluginController = nil;
|
|||
return [[decoder alloc] init];
|
||||
}
|
||||
|
||||
- (NSDictionary *)metadataForURL:(NSURL *)url skipCue:(BOOL)skip
|
||||
{
|
||||
NSString * urlScheme = [url scheme];
|
||||
if ([urlScheme isEqualToString:@"http"] ||
|
||||
- (NSDictionary *)metadataForURL:(NSURL *)url skipCue:(BOOL)skip {
|
||||
NSString *urlScheme = [url scheme];
|
||||
if([urlScheme isEqualToString:@"http"] ||
|
||||
[urlScheme isEqualToString:@"https"])
|
||||
return nil;
|
||||
|
||||
NSString *ext = [url pathExtension];
|
||||
NSArray *readers = [metadataReaders objectForKey:[ext lowercaseString]];
|
||||
NSString *classString;
|
||||
if (readers) {
|
||||
if ( [readers count] > 1 ) {
|
||||
if (skip)
|
||||
{
|
||||
if(readers) {
|
||||
if([readers count] > 1) {
|
||||
if(skip) {
|
||||
NSMutableArray *_readers = [readers mutableCopy];
|
||||
for (int i = 0; i < [_readers count];)
|
||||
{
|
||||
if ([[_readers objectAtIndex:i] isEqualToString:@"CueSheetMetadataReader"])
|
||||
for(int i = 0; i < [_readers count];) {
|
||||
if([[_readers objectAtIndex:i] isEqualToString:@"CueSheetMetadataReader"])
|
||||
[_readers removeObjectAtIndex:i];
|
||||
else
|
||||
++i;
|
||||
|
@ -522,12 +471,10 @@ static PluginController *sharedPluginController = nil;
|
|||
return [CogMetadataReaderMulti metadataForURL:url readers:_readers];
|
||||
}
|
||||
return [CogMetadataReaderMulti metadataForURL:url readers:readers];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = [readers objectAtIndex:0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -536,12 +483,10 @@ static PluginController *sharedPluginController = nil;
|
|||
return [metadataReader metadataForURL:url];
|
||||
}
|
||||
|
||||
|
||||
//If no properties reader is defined, use the decoder's properties.
|
||||
- (NSDictionary *)propertiesForURL:(NSURL *)url
|
||||
{
|
||||
NSString * urlScheme = [url scheme];
|
||||
if ([urlScheme isEqualToString:@"http"] ||
|
||||
// If no properties reader is defined, use the decoder's properties.
|
||||
- (NSDictionary *)propertiesForURL:(NSURL *)url {
|
||||
NSString *urlScheme = [url scheme];
|
||||
if([urlScheme isEqualToString:@"http"] ||
|
||||
[urlScheme isEqualToString:@"https"])
|
||||
return nil;
|
||||
|
||||
|
@ -549,50 +494,43 @@ static PluginController *sharedPluginController = nil;
|
|||
NSString *ext = [url pathExtension];
|
||||
|
||||
id<CogSource> source = [self audioSourceForURL:url];
|
||||
if (![source open:url])
|
||||
if(![source open:url])
|
||||
return nil;
|
||||
|
||||
NSArray *readers = [propertiesReadersByExtension objectForKey:[ext lowercaseString]];
|
||||
NSString *classString = nil;
|
||||
if (readers)
|
||||
{
|
||||
if ( [readers count] > 1 ) {
|
||||
if(readers) {
|
||||
if([readers count] > 1) {
|
||||
properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers];
|
||||
if (properties != nil && [properties count])
|
||||
if(properties != nil && [properties count])
|
||||
return properties;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = [readers objectAtIndex:0];
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
readers = [propertiesReadersByMimeType objectForKey:[[source mimeType] lowercaseString]];
|
||||
if (readers)
|
||||
{
|
||||
if ( [readers count] > 1 ) {
|
||||
if(readers) {
|
||||
if([readers count] > 1) {
|
||||
properties = [CogPropertiesReaderMulti propertiesForSource:source readers:readers];
|
||||
if (properties != nil && [properties count])
|
||||
if(properties != nil && [properties count])
|
||||
return properties;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
classString = [readers objectAtIndex:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (classString)
|
||||
{
|
||||
if(classString) {
|
||||
Class propertiesReader = NSClassFromString(classString);
|
||||
|
||||
properties = [propertiesReader propertiesForSource:source];
|
||||
if (properties != nil && [properties count])
|
||||
if(properties != nil && [properties count])
|
||||
return properties;
|
||||
}
|
||||
|
||||
{
|
||||
id<CogDecoder> decoder = [self audioDecoderForSource:source skipCue:NO];
|
||||
if (![decoder open:source])
|
||||
{
|
||||
if(![decoder open:source]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -604,10 +542,8 @@ static PluginController *sharedPluginController = nil;
|
|||
}
|
||||
}
|
||||
|
||||
- (int)putMetadataInURL:(NSURL *)url
|
||||
{
|
||||
- (int)putMetadataInURL:(NSURL *)url {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -13,10 +13,10 @@
|
|||
semaphore_t semaphore;
|
||||
}
|
||||
|
||||
-(id)init;
|
||||
-(void)signal;
|
||||
-(void)timedWait:(int)microseconds;
|
||||
-(void)wait;
|
||||
-(void)waitIndefinitely;
|
||||
- (id)init;
|
||||
- (void)signal;
|
||||
- (void)timedWait:(int)microseconds;
|
||||
- (void)wait;
|
||||
- (void)waitIndefinitely;
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,40 +8,33 @@
|
|||
|
||||
#import "Semaphore.h"
|
||||
|
||||
|
||||
@implementation Semaphore
|
||||
|
||||
-(id)init
|
||||
{
|
||||
- (id)init {
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
semaphore_create(mach_task_self(), &semaphore, SYNC_POLICY_FIFO, 0);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)signal
|
||||
{
|
||||
- (void)signal {
|
||||
semaphore_signal_all(semaphore);
|
||||
}
|
||||
|
||||
-(void)timedWait:(int)microseconds
|
||||
{
|
||||
mach_timespec_t timeout = {0, microseconds * 1000UL};
|
||||
- (void)timedWait:(int)microseconds {
|
||||
mach_timespec_t timeout = { 0, microseconds * 1000UL };
|
||||
|
||||
semaphore_timedwait(semaphore, timeout);
|
||||
}
|
||||
|
||||
-(void)wait
|
||||
{
|
||||
mach_timespec_t t = {2.0, 0.0}; //2 second timeout
|
||||
- (void)wait {
|
||||
mach_timespec_t t = { 2.0, 0.0 }; // 2 second timeout
|
||||
semaphore_timedwait(semaphore, t);
|
||||
}
|
||||
|
||||
-(void)waitIndefinitely
|
||||
{
|
||||
- (void)waitIndefinitely {
|
||||
semaphore_wait(semaphore);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,7 @@
|
|||
|
||||
@class PlaylistEntry;
|
||||
|
||||
@interface AudioScrobbler : NSObject
|
||||
{
|
||||
@interface AudioScrobbler : NSObject {
|
||||
NSString *_pluginID;
|
||||
NSMutableArray *_queue;
|
||||
|
||||
|
@ -34,13 +33,13 @@
|
|||
semaphore_t _semaphore;
|
||||
}
|
||||
|
||||
+ (BOOL) isRunning;
|
||||
+ (BOOL)isRunning;
|
||||
|
||||
- (void) start:(PlaylistEntry *)pe;
|
||||
- (void) stop;
|
||||
- (void) pause;
|
||||
- (void) resume;
|
||||
- (void)start:(PlaylistEntry *)pe;
|
||||
- (void)stop;
|
||||
- (void)pause;
|
||||
- (void)resume;
|
||||
|
||||
- (void) shutdown;
|
||||
- (void)shutdown;
|
||||
|
||||
@end
|
||||
|
|
|
@ -28,14 +28,13 @@
|
|||
// ========================================
|
||||
// Symbolic Constants
|
||||
// ========================================
|
||||
NSString * const AudioScrobblerRunLoopMode = @"org.cogx.Cog.AudioScrobbler.RunLoopMode";
|
||||
NSString *const AudioScrobblerRunLoopMode = @"org.cogx.Cog.AudioScrobbler.RunLoopMode";
|
||||
|
||||
// ========================================
|
||||
// Helpers
|
||||
// ========================================
|
||||
static NSString *
|
||||
escapeForLastFM(NSString *string)
|
||||
{
|
||||
escapeForLastFM(NSString *string) {
|
||||
NSMutableString *result = [string mutableCopy];
|
||||
|
||||
[result replaceOccurrencesOfString:@"&"
|
||||
|
@ -48,27 +47,26 @@ escapeForLastFM(NSString *string)
|
|||
|
||||
@interface AudioScrobbler (Private)
|
||||
|
||||
- (NSMutableArray *) queue;
|
||||
- (NSString *) pluginID;
|
||||
- (NSMutableArray *)queue;
|
||||
- (NSString *)pluginID;
|
||||
|
||||
- (void) sendCommand:(NSString *)command;
|
||||
- (void)sendCommand:(NSString *)command;
|
||||
|
||||
- (BOOL) keepProcessingAudioScrobblerCommands;
|
||||
- (void) setKeepProcessingAudioScrobblerCommands:(BOOL)keepProcessingAudioScrobblerCommands;
|
||||
- (BOOL)keepProcessingAudioScrobblerCommands;
|
||||
- (void)setKeepProcessingAudioScrobblerCommands:(BOOL)keepProcessingAudioScrobblerCommands;
|
||||
|
||||
- (BOOL) audioScrobblerThreadCompleted;
|
||||
- (void) setAudioScrobblerThreadCompleted:(BOOL)audioScrobblerThreadCompleted;
|
||||
- (BOOL)audioScrobblerThreadCompleted;
|
||||
- (void)setAudioScrobblerThreadCompleted:(BOOL)audioScrobblerThreadCompleted;
|
||||
|
||||
- (semaphore_t) semaphore;
|
||||
- (semaphore_t)semaphore;
|
||||
|
||||
- (void) processAudioScrobblerCommands:(id)unused;
|
||||
- (void)processAudioScrobblerCommands:(id)unused;
|
||||
|
||||
@end
|
||||
|
||||
@implementation AudioScrobbler
|
||||
|
||||
+ (BOOL) isRunning
|
||||
{
|
||||
+ (BOOL)isRunning {
|
||||
NSArray *launchedApps = [[NSWorkspace sharedWorkspace] runningApplications];
|
||||
BOOL running = NO;
|
||||
for(NSRunningApplication *app in launchedApps) {
|
||||
|
@ -81,14 +79,11 @@ escapeForLastFM(NSString *string)
|
|||
return running;
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
- (id)init {
|
||||
if((self = [super init])) {
|
||||
|
||||
_pluginID = @"cog";
|
||||
|
||||
if([[NSUserDefaults standardUserDefaults] boolForKey:@"automaticallyLaunchLastFM"]) {
|
||||
|
||||
if(![AudioScrobbler isRunning]) {
|
||||
[[NSWorkspace sharedWorkspace] launchApplication:@"Last.fm.app"];
|
||||
}
|
||||
|
@ -110,18 +105,17 @@ escapeForLastFM(NSString *string)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
if([self keepProcessingAudioScrobblerCommands] || NO == [self audioScrobblerThreadCompleted])
|
||||
[self shutdown];
|
||||
|
||||
_queue = nil;
|
||||
|
||||
semaphore_destroy(mach_task_self(), _semaphore); _semaphore = 0;
|
||||
semaphore_destroy(mach_task_self(), _semaphore);
|
||||
_semaphore = 0;
|
||||
}
|
||||
|
||||
- (void) start:(PlaylistEntry *)pe
|
||||
{
|
||||
- (void)start:(PlaylistEntry *)pe {
|
||||
[self sendCommand:[NSString stringWithFormat:@"START c=%@&a=%@&t=%@&b=%@&m=%@&l=%i&p=%@\n",
|
||||
[self pluginID],
|
||||
escapeForLastFM([pe artist]),
|
||||
|
@ -129,27 +123,22 @@ escapeForLastFM(NSString *string)
|
|||
escapeForLastFM([pe album]),
|
||||
@"", // TODO: MusicBrainz support
|
||||
[[pe length] intValue],
|
||||
escapeForLastFM([[pe URL] path])
|
||||
]];
|
||||
escapeForLastFM([[pe URL] path])]];
|
||||
}
|
||||
|
||||
- (void) stop
|
||||
{
|
||||
- (void)stop {
|
||||
[self sendCommand:[NSString stringWithFormat:@"STOP c=%@\n", [self pluginID]]];
|
||||
}
|
||||
|
||||
- (void) pause
|
||||
{
|
||||
- (void)pause {
|
||||
[self sendCommand:[NSString stringWithFormat:@"PAUSE c=%@\n", [self pluginID]]];
|
||||
}
|
||||
|
||||
- (void) resume
|
||||
{
|
||||
- (void)resume {
|
||||
[self sendCommand:[NSString stringWithFormat:@"RESUME c=%@\n", [self pluginID]]];
|
||||
}
|
||||
|
||||
- (void) shutdown
|
||||
{
|
||||
- (void)shutdown {
|
||||
[self setKeepProcessingAudioScrobblerCommands:NO];
|
||||
semaphore_signal([self semaphore]);
|
||||
|
||||
|
@ -162,54 +151,45 @@ escapeForLastFM(NSString *string)
|
|||
|
||||
@implementation AudioScrobbler (Private)
|
||||
|
||||
- (NSMutableArray *) queue
|
||||
{
|
||||
- (NSMutableArray *)queue {
|
||||
if(nil == _queue)
|
||||
_queue = [[NSMutableArray alloc] init];
|
||||
|
||||
return _queue;
|
||||
}
|
||||
|
||||
- (NSString *) pluginID
|
||||
{
|
||||
- (NSString *)pluginID {
|
||||
return _pluginID;
|
||||
}
|
||||
|
||||
- (void) sendCommand:(NSString *)command
|
||||
{
|
||||
- (void)sendCommand:(NSString *)command {
|
||||
@synchronized([self queue]) {
|
||||
[[self queue] addObject:command];
|
||||
}
|
||||
semaphore_signal([self semaphore]);
|
||||
}
|
||||
|
||||
- (BOOL) keepProcessingAudioScrobblerCommands
|
||||
{
|
||||
- (BOOL)keepProcessingAudioScrobblerCommands {
|
||||
return _keepProcessingAudioScrobblerCommands;
|
||||
}
|
||||
|
||||
- (void) setKeepProcessingAudioScrobblerCommands:(BOOL)keepProcessingAudioScrobblerCommands
|
||||
{
|
||||
- (void)setKeepProcessingAudioScrobblerCommands:(BOOL)keepProcessingAudioScrobblerCommands {
|
||||
_keepProcessingAudioScrobblerCommands = keepProcessingAudioScrobblerCommands;
|
||||
}
|
||||
|
||||
- (BOOL) audioScrobblerThreadCompleted
|
||||
{
|
||||
- (BOOL)audioScrobblerThreadCompleted {
|
||||
return _audioScrobblerThreadCompleted;
|
||||
}
|
||||
|
||||
- (void) setAudioScrobblerThreadCompleted:(BOOL)audioScrobblerThreadCompleted
|
||||
{
|
||||
- (void)setAudioScrobblerThreadCompleted:(BOOL)audioScrobblerThreadCompleted {
|
||||
_audioScrobblerThreadCompleted = audioScrobblerThreadCompleted;
|
||||
}
|
||||
|
||||
- (semaphore_t) semaphore
|
||||
{
|
||||
- (semaphore_t)semaphore {
|
||||
return _semaphore;
|
||||
}
|
||||
|
||||
- (void) processAudioScrobblerCommands:(id)unused
|
||||
{
|
||||
- (void)processAudioScrobblerCommands:(id)unused {
|
||||
@autoreleasepool {
|
||||
AudioScrobblerClient *client = [[AudioScrobblerClient alloc] init];
|
||||
mach_timespec_t timeout = { 5, 0 };
|
||||
|
@ -219,10 +199,9 @@ escapeForLastFM(NSString *string)
|
|||
|
||||
while([self keepProcessingAudioScrobblerCommands]) {
|
||||
@autoreleasepool {
|
||||
|
||||
// Get the first command to be sent
|
||||
@synchronized([self queue]) {
|
||||
if ([[self queue] count]) {
|
||||
if([[self queue] count]) {
|
||||
command = [[self queue] objectAtIndex:0];
|
||||
[[self queue] removeObjectAtIndex:0];
|
||||
}
|
||||
|
@ -236,7 +215,7 @@ escapeForLastFM(NSString *string)
|
|||
command = nil;
|
||||
|
||||
response = [client receive];
|
||||
if(2 > [response length] || NSOrderedSame != [response compare:@"OK" options:NSLiteralSearch range:NSMakeRange(0,2)])
|
||||
if(2 > [response length] || NSOrderedSame != [response compare:@"OK" options:NSLiteralSearch range:NSMakeRange(0, 2)])
|
||||
ALog(@"AudioScrobbler error: %@", response);
|
||||
|
||||
[client shutdown];
|
||||
|
@ -247,7 +226,7 @@ escapeForLastFM(NSString *string)
|
|||
command = nil;
|
||||
|
||||
[client shutdown];
|
||||
// ALog(@"Exception: %@",exception);
|
||||
// ALog(@"Exception: %@",exception);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -262,7 +241,7 @@ escapeForLastFM(NSString *string)
|
|||
[client send:[NSString stringWithFormat:@"STOP c=%@\n", [self pluginID]]];
|
||||
|
||||
response = [client receive];
|
||||
if(2 > [response length] || NSOrderedSame != [response compare:@"OK" options:NSLiteralSearch range:NSMakeRange(0,2)])
|
||||
if(2 > [response length] || NSOrderedSame != [response compare:@"OK" options:NSLiteralSearch range:NSMakeRange(0, 2)])
|
||||
ALog(@"AudioScrobbler error: %@", response);
|
||||
|
||||
[client shutdown];
|
||||
|
|
|
@ -22,21 +22,20 @@
|
|||
|
||||
#include <netdb.h>
|
||||
|
||||
@interface AudioScrobblerClient : NSObject
|
||||
{
|
||||
@interface AudioScrobblerClient : NSObject {
|
||||
int _socket;
|
||||
BOOL _doPortStepping;
|
||||
in_port_t _port;
|
||||
}
|
||||
|
||||
- (BOOL) connectToHost:(NSString *)hostname port:(in_port_t)port;
|
||||
- (BOOL)connectToHost:(NSString *)hostname port:(in_port_t)port;
|
||||
|
||||
- (BOOL) isConnected;
|
||||
- (in_port_t) connectedPort;
|
||||
- (BOOL)isConnected;
|
||||
- (in_port_t)connectedPort;
|
||||
|
||||
- (void) send:(NSString *)data;
|
||||
- (NSString *) receive;
|
||||
- (void)send:(NSString *)data;
|
||||
- (NSString *)receive;
|
||||
|
||||
- (void) shutdown;
|
||||
- (void)shutdown;
|
||||
|
||||
@end
|
||||
|
|
|
@ -33,8 +33,7 @@
|
|||
#define kPortsToStep 5
|
||||
|
||||
static in_addr_t
|
||||
addressForHost(NSString *hostname)
|
||||
{
|
||||
addressForHost(NSString *hostname) {
|
||||
NSCParameterAssert(nil != hostname);
|
||||
|
||||
in_addr_t address;
|
||||
|
@ -56,13 +55,12 @@ addressForHost(NSString *hostname)
|
|||
}
|
||||
|
||||
@interface AudioScrobblerClient (Private)
|
||||
- (BOOL) connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port;
|
||||
- (BOOL)connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port;
|
||||
@end
|
||||
|
||||
@implementation AudioScrobblerClient
|
||||
|
||||
- (id) init
|
||||
{
|
||||
- (id)init {
|
||||
if((self = [super init])) {
|
||||
_socket = -1;
|
||||
_doPortStepping = YES;
|
||||
|
@ -70,8 +68,7 @@ addressForHost(NSString *hostname)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) connectToHost:(NSString *)hostname port:(in_port_t)port
|
||||
{
|
||||
- (BOOL)connectToHost:(NSString *)hostname port:(in_port_t)port {
|
||||
NSParameterAssert(nil != hostname);
|
||||
|
||||
in_addr_t remoteAddress = addressForHost(hostname);
|
||||
|
@ -82,20 +79,17 @@ addressForHost(NSString *hostname)
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL) isConnected
|
||||
{
|
||||
- (BOOL)isConnected {
|
||||
return (-1 != _socket);
|
||||
}
|
||||
|
||||
- (in_port_t) connectedPort
|
||||
{
|
||||
- (in_port_t)connectedPort {
|
||||
return _port;
|
||||
}
|
||||
|
||||
- (void) send:(NSString *)data
|
||||
{
|
||||
- (void)send:(NSString *)data {
|
||||
const char *utf8data = [data UTF8String];
|
||||
unsigned len = (unsigned int) strlen(utf8data);
|
||||
unsigned len = (unsigned int)strlen(utf8data);
|
||||
unsigned bytesToSend = len;
|
||||
unsigned totalBytesSent = 0;
|
||||
ssize_t bytesSent = 0;
|
||||
|
@ -115,9 +109,8 @@ addressForHost(NSString *hostname)
|
|||
}
|
||||
}
|
||||
|
||||
- (NSString *) receive
|
||||
{
|
||||
char buffer [ kBufferSize ];
|
||||
- (NSString *)receive {
|
||||
char buffer[kBufferSize];
|
||||
int readSize = kBufferSize - 1;
|
||||
ssize_t bytesRead = 0;
|
||||
BOOL keepGoing = YES;
|
||||
|
@ -148,10 +141,9 @@ addressForHost(NSString *hostname)
|
|||
return result;
|
||||
}
|
||||
|
||||
- (void) shutdown
|
||||
{
|
||||
- (void)shutdown {
|
||||
int result;
|
||||
char buffer [ kBufferSize ];
|
||||
char buffer[kBufferSize];
|
||||
ssize_t bytesRead;
|
||||
|
||||
if(NO == [self isConnected]) {
|
||||
|
@ -170,8 +162,7 @@ addressForHost(NSString *hostname)
|
|||
if(0 != bytesRead) {
|
||||
NSString *received = [[NSString alloc] initWithBytes:buffer length:bytesRead encoding:NSUTF8StringEncoding];
|
||||
ALog(@"Received unexpected bytes during shutdown: %@", received);
|
||||
}
|
||||
else
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -187,8 +178,7 @@ addressForHost(NSString *hostname)
|
|||
|
||||
@implementation AudioScrobblerClient (Private)
|
||||
|
||||
- (BOOL) connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port
|
||||
{
|
||||
- (BOOL)connectToSocket:(in_addr_t)remoteAddress port:(in_port_t)port {
|
||||
NSParameterAssert(INADDR_NONE != remoteAddress);
|
||||
|
||||
_port = port;
|
||||
|
@ -214,7 +204,7 @@ addressForHost(NSString *hostname)
|
|||
|
||||
_port++;
|
||||
}
|
||||
} while (YES == _doPortStepping && -1 == result && _port < (port + kPortsToStep));
|
||||
} while(YES == _doPortStepping && -1 == result && _port < (port + kPortsToStep));
|
||||
|
||||
return (-1 != result);
|
||||
}
|
||||
|
|
|
@ -6,16 +6,16 @@
|
|||
// Copyright 2005 Vincent Spader All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "FeedbackSocket.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface FeedbackController : NSWindowController<FeedbackSocketDelegate> {
|
||||
@interface FeedbackController : NSWindowController <FeedbackSocketDelegate> {
|
||||
IBOutlet NSTextField* fromView;
|
||||
IBOutlet NSTextField* subjectView;
|
||||
IBOutlet NSTextView* messageView;
|
||||
IBOutlet NSProgressIndicator *sendingIndicator;
|
||||
IBOutlet NSProgressIndicator* sendingIndicator;
|
||||
|
||||
FeedbackSocket *feedbackSocket;
|
||||
FeedbackSocket* feedbackSocket;
|
||||
}
|
||||
|
||||
- (IBAction)sendFeedback:(id)sender;
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
|
||||
@implementation FeedbackController
|
||||
|
||||
- (id)init
|
||||
{
|
||||
- (id)init {
|
||||
return [super initWithWindowNibName:@"Feedback"];
|
||||
}
|
||||
|
||||
- (IBAction)showWindow:(id)sender
|
||||
{
|
||||
- (IBAction)showWindow:(id)sender {
|
||||
[fromView setStringValue:@""];
|
||||
[subjectView setStringValue:@""];
|
||||
[messageView setString:@""];
|
||||
|
@ -26,16 +24,13 @@
|
|||
[super showWindow:sender];
|
||||
}
|
||||
|
||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
||||
{
|
||||
if ([(NSNumber *)CFBridgingRelease(contextInfo) boolValue]== YES)
|
||||
{
|
||||
- (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo {
|
||||
if([(NSNumber *)CFBridgingRelease(contextInfo) boolValue] == YES) {
|
||||
[[self window] close];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)feedbackDidNotSend:(FeedbackSocket *)feedback
|
||||
{
|
||||
- (void)feedbackDidNotSend:(FeedbackSocket *)feedback {
|
||||
ALog(@"Error sending feedback");
|
||||
|
||||
[sendingIndicator stopAnimation:self];
|
||||
|
@ -47,8 +42,7 @@
|
|||
[alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain([NSNumber numberWithBool:NO])];
|
||||
}
|
||||
|
||||
- (void)feedbackDidSend:(FeedbackSocket *)feedback
|
||||
{
|
||||
- (void)feedbackDidSend:(FeedbackSocket *)feedback {
|
||||
[sendingIndicator stopAnimation:self];
|
||||
|
||||
NSAlert *alert = [[NSAlert alloc] init];
|
||||
|
@ -58,12 +52,10 @@
|
|||
[alert beginSheetModalForWindow:[self window] modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain([NSNumber numberWithBool:YES])];
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)sendFeedback:(id)sender
|
||||
{
|
||||
- (IBAction)sendFeedback:(id)sender {
|
||||
[sendingIndicator startAnimation:self];
|
||||
|
||||
//Using this so that if its a bad connection, it doesnt sit there looking stupid..or should it
|
||||
// Using this so that if its a bad connection, it doesnt sit there looking stupid..or should it
|
||||
feedbackSocket = [[FeedbackSocket alloc] init];
|
||||
[feedbackSocket setDelegate:self];
|
||||
|
||||
|
@ -72,8 +64,7 @@
|
|||
[feedbackSocket sendFeedback:[fromView stringValue] subject:[subjectView stringValue] message:[messageView string] version:version];
|
||||
}
|
||||
|
||||
- (IBAction)cancel:(id)sender
|
||||
{
|
||||
- (IBAction)cancel:(id)sender {
|
||||
[[self window] close];
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
}
|
||||
|
||||
- (void)setDelegate:(id<FeedbackSocketDelegate>)d;
|
||||
- (void)sendFeedback: (NSString *)f subject:(NSString *)s message:(NSString *)m version:(NSString *)v;
|
||||
- (void)sendFeedback:(NSString *)f subject:(NSString *)s message:(NSString *)m version:(NSString *)v;
|
||||
|
||||
- (void)setFrom:(NSString *)f;
|
||||
- (void)setSubject:(NSString *)s;
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
@end
|
||||
|
||||
@protocol FeedbackSocketDelegate<NSObject>
|
||||
@protocol FeedbackSocketDelegate <NSObject>
|
||||
|
||||
- (void)feedbackDidSend:(FeedbackSocket *)feedback;
|
||||
- (void)feedbackDidNotSend:(FeedbackSocket *)feedback;
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
|
||||
@implementation FeedbackSocket
|
||||
|
||||
NSString *encodeForURL(NSString *s)
|
||||
{
|
||||
return (NSString*) CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)s, NULL, NULL, kCFStringEncodingUTF8));
|
||||
NSString *encodeForURL(NSString *s) {
|
||||
return (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)s, NULL, NULL, kCFStringEncodingUTF8));
|
||||
}
|
||||
|
||||
- (void)sendFeedbackThread:(id)sender
|
||||
{
|
||||
- (void)sendFeedbackThread:(id)sender {
|
||||
@autoreleasepool {
|
||||
NSString *f = encodeForURL(from);
|
||||
NSString *s = encodeForURL(subject);
|
||||
|
@ -36,26 +34,21 @@ NSString *encodeForURL(NSString *s)
|
|||
[post setHTTPMethod:@"POST"];
|
||||
[post setHTTPBody:postData];
|
||||
|
||||
NSError* error;
|
||||
NSURLResponse* response;
|
||||
NSData* resultData = [NSURLConnection sendSynchronousRequest:post returningResponse:&response error:&error];
|
||||
NSError *error;
|
||||
NSURLResponse *response;
|
||||
NSData *resultData = [NSURLConnection sendSynchronousRequest:post returningResponse:&response error:&error];
|
||||
NSString *resultString = [[NSString alloc] initWithData:resultData encoding:NSASCIIStringEncoding];
|
||||
//DLog(@"RESULT: %@", resultString);
|
||||
if ([resultString caseInsensitiveCompare:@"SUCCESS"] == NSOrderedSame)
|
||||
{
|
||||
// DLog(@"RESULT: %@", resultString);
|
||||
if([resultString caseInsensitiveCompare:@"SUCCESS"] == NSOrderedSame) {
|
||||
[self performSelectorOnMainThread:@selector(returnSuccess:) withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[self performSelectorOnMainThread:@selector(returnFailure:) withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)sendFeedback: (NSString *)f subject:(NSString *)s message:(NSString *)m version:(NSString *)v
|
||||
{
|
||||
if ([f isEqualToString:@""])
|
||||
{
|
||||
- (void)sendFeedback:(NSString *)f subject:(NSString *)s message:(NSString *)m version:(NSString *)v {
|
||||
if([f isEqualToString:@""]) {
|
||||
f = @"Anonymous";
|
||||
}
|
||||
[self setFrom:f];
|
||||
|
@ -66,43 +59,35 @@ NSString *encodeForURL(NSString *s)
|
|||
[NSThread detachNewThreadSelector:@selector(sendFeedbackThread:) toTarget:self withObject:nil];
|
||||
}
|
||||
|
||||
- (void)returnSuccess:(id)userInfo
|
||||
{
|
||||
if ([delegate respondsToSelector:@selector(feedbackDidSend:)]) {
|
||||
- (void)returnSuccess:(id)userInfo {
|
||||
if([delegate respondsToSelector:@selector(feedbackDidSend:)]) {
|
||||
[delegate feedbackDidSend:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)returnFailure:(id)userInfo
|
||||
{
|
||||
if ([delegate respondsToSelector:@selector(feedbackDidNotSend:)]) {
|
||||
- (void)returnFailure:(id)userInfo {
|
||||
if([delegate respondsToSelector:@selector(feedbackDidNotSend:)]) {
|
||||
[delegate feedbackDidNotSend:self];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)setDelegate:(id<FeedbackSocketDelegate>)d
|
||||
{
|
||||
- (void)setDelegate:(id<FeedbackSocketDelegate>)d {
|
||||
delegate = d;
|
||||
}
|
||||
|
||||
|
||||
- (void)setFrom:(NSString *)f
|
||||
{
|
||||
- (void)setFrom:(NSString *)f {
|
||||
from = f;
|
||||
}
|
||||
|
||||
- (void)setSubject:(NSString *)s
|
||||
{
|
||||
- (void)setSubject:(NSString *)s {
|
||||
subject = s;
|
||||
}
|
||||
|
||||
- (void)setMessage:(NSString *)m
|
||||
{
|
||||
- (void)setMessage:(NSString *)m {
|
||||
message = m;
|
||||
}
|
||||
|
||||
- (void)setVersion:(NSString *)v
|
||||
{
|
||||
- (void)setVersion:(NSString *)v {
|
||||
version = v;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#import "PathNode.h"
|
||||
|
||||
@interface ContainedNode : PathNode {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,36 +9,31 @@
|
|||
#import "ContainedNode.h"
|
||||
#import "CogAudio/AudioMetadataReader.h"
|
||||
|
||||
|
||||
@implementation ContainedNode
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
- (BOOL)isLeaf {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setURL:(NSURL *)u
|
||||
{
|
||||
- (void)setURL:(NSURL *)u {
|
||||
[super setURL:u];
|
||||
|
||||
if ([u fragment])
|
||||
{
|
||||
if([u fragment]) {
|
||||
NSDictionary *metadata = [AudioMetadataReader metadataForURL:u];
|
||||
NSString *title = nil;
|
||||
NSString *artist = nil;
|
||||
if (metadata)
|
||||
{
|
||||
if(metadata) {
|
||||
title = [metadata valueForKey:@"title"];
|
||||
artist = [metadata valueForKey:@"artist"];
|
||||
}
|
||||
|
||||
if (title && [title length])
|
||||
{
|
||||
if (artist && [artist length]) { display = [[u fragment] stringByAppendingFormat:@": %@ - %@", artist, title];}
|
||||
else { display = [[u fragment] stringByAppendingFormat:@": %@", title]; }
|
||||
if(title && [title length]) {
|
||||
if(artist && [artist length]) {
|
||||
display = [[u fragment] stringByAppendingFormat:@": %@ - %@", artist, title];
|
||||
} else {
|
||||
display = [[u fragment] stringByAppendingFormat:@": %@", title];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
display = [u fragment];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#import "PathNode.h"
|
||||
|
||||
@interface ContainerNode : PathNode {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,18 +15,15 @@
|
|||
|
||||
@implementation ContainerNode
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
- (BOOL)isLeaf {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)updatePath
|
||||
{
|
||||
- (void)updatePath {
|
||||
NSArray *urls = [AudioContainer urlsForContainerURL:url];
|
||||
|
||||
NSMutableArray *paths = [[NSMutableArray alloc] init];
|
||||
for (NSURL *u in urls)
|
||||
{
|
||||
for(NSURL *u in urls) {
|
||||
ContainedNode *node = [[ContainedNode alloc] initWithDataSource:dataSource url:u];
|
||||
DLog(@"Node: %@", u);
|
||||
[paths addObject:node];
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
// Copyright 2006 Vincent Spader. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "PathNode.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface DirectoryNode : PathNode
|
||||
{
|
||||
@interface DirectoryNode : PathNode {
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,24 +15,24 @@
|
|||
|
||||
@implementation DirectoryNode
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
- (BOOL)isLeaf {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)updatePath
|
||||
{
|
||||
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:url includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey] options:(NSDirectoryEnumerationSkipsSubdirectoryDescendants | NSDirectoryEnumerationSkipsPackageDescendants | NSDirectoryEnumerationSkipsHiddenFiles) errorHandler:^BOOL(NSURL *url, NSError *error) {
|
||||
- (void)updatePath {
|
||||
NSDirectoryEnumerator *enumerator = [[NSFileManager defaultManager] enumeratorAtURL:url
|
||||
includingPropertiesForKeys:@[NSURLNameKey, NSURLIsDirectoryKey]
|
||||
options:(NSDirectoryEnumerationSkipsSubdirectoryDescendants | NSDirectoryEnumerationSkipsPackageDescendants | NSDirectoryEnumerationSkipsHiddenFiles)
|
||||
errorHandler:^BOOL(NSURL *url, NSError *error) {
|
||||
return NO;
|
||||
}];
|
||||
NSMutableArray *fullPaths = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSURL * theUrl in enumerator)
|
||||
{
|
||||
for(NSURL *theUrl in enumerator) {
|
||||
[fullPaths addObject:[theUrl path]];
|
||||
}
|
||||
|
||||
[self processPaths: [fullPaths sortedArrayUsingSelector:@selector(finderCompare:)]];
|
||||
[self processPaths:[fullPaths sortedArrayUsingSelector:@selector(finderCompare:)]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,11 +6,10 @@
|
|||
// Copyright 2006 Vincent Spader. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "ImageTextCell.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface FileIconCell : ImageTextCell {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -11,13 +11,11 @@
|
|||
|
||||
@implementation FileIconCell
|
||||
|
||||
- (void)setObjectValue:(PathNode *)o
|
||||
{
|
||||
if ([o respondsToSelector:@selector(icon)] && [o respondsToSelector:@selector(display)]) {
|
||||
- (void)setObjectValue:(PathNode *)o {
|
||||
if([o respondsToSelector:@selector(icon)] && [o respondsToSelector:@selector(display)]) {
|
||||
[super setObjectValue:[o display]];
|
||||
[super setImage: [o icon]];
|
||||
}
|
||||
else {
|
||||
[super setImage:[o icon]];
|
||||
} else {
|
||||
[super setObjectValue:(id)o];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
// Copyright 2006 Vincent Spader. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "PathNode.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface FileNode : PathNode
|
||||
{
|
||||
|
||||
@interface FileNode : PathNode {
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -10,8 +10,7 @@
|
|||
|
||||
@implementation FileNode
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
- (BOOL)isLeaf {
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "FileTreeDataSource.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class SideViewController;
|
||||
@interface FileTreeController : NSObject {
|
||||
|
|
|
@ -6,86 +6,73 @@
|
|||
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "PlaylistController.h"
|
||||
#import "FileTreeController.h"
|
||||
#import "PlaylistController.h"
|
||||
#import "SideViewController.h"
|
||||
|
||||
|
||||
@implementation FileTreeController
|
||||
|
||||
- (IBAction)addToPlaylist:(id)sender
|
||||
{
|
||||
- (IBAction)addToPlaylist:(id)sender {
|
||||
[self doAddToPlaylist:sender origin:URLOriginInternal];
|
||||
}
|
||||
|
||||
- (void)doAddToPlaylist:(id)sender origin:(URLOrigin)origin
|
||||
{
|
||||
- (void)doAddToPlaylist:(id)sender origin:(URLOrigin)origin {
|
||||
NSUInteger index;
|
||||
NSIndexSet *selectedIndexes = [outlineView selectedRowIndexes];
|
||||
NSMutableArray *urls = [[NSMutableArray alloc] init];
|
||||
|
||||
for (index = [selectedIndexes firstIndex];
|
||||
index != NSNotFound; index = [selectedIndexes indexGreaterThanIndex: index])
|
||||
{
|
||||
for(index = [selectedIndexes firstIndex];
|
||||
index != NSNotFound; index = [selectedIndexes indexGreaterThanIndex:index]) {
|
||||
[urls addObject:[[outlineView itemAtRow:index] URL]];
|
||||
}
|
||||
|
||||
[controller doAddToPlaylist:urls origin:origin];
|
||||
}
|
||||
|
||||
- (void)addToPlaylistExternal:(id)sender
|
||||
{
|
||||
- (void)addToPlaylistExternal:(id)sender {
|
||||
[self doAddToPlaylist:sender origin:URLOriginExternal];
|
||||
}
|
||||
|
||||
- (IBAction)setAsPlaylist:(id)sender
|
||||
{
|
||||
- (IBAction)setAsPlaylist:(id)sender {
|
||||
[controller clear:sender];
|
||||
[self addToPlaylist:sender];
|
||||
}
|
||||
|
||||
- (IBAction)playPauseResume:(NSObject *)id
|
||||
{
|
||||
- (IBAction)playPauseResume:(NSObject *)id {
|
||||
[controller playPauseResume:id];
|
||||
}
|
||||
|
||||
- (IBAction)showEntryInFinder:(id)sender
|
||||
{
|
||||
- (IBAction)showEntryInFinder:(id)sender {
|
||||
NSUInteger index;
|
||||
NSWorkspace* ws = [NSWorkspace sharedWorkspace];
|
||||
NSWorkspace *ws = [NSWorkspace sharedWorkspace];
|
||||
NSIndexSet *selectedIndexes = [outlineView selectedRowIndexes];
|
||||
|
||||
for (index = [selectedIndexes firstIndex];
|
||||
index != NSNotFound; index = [selectedIndexes indexGreaterThanIndex: index])
|
||||
{
|
||||
for(index = [selectedIndexes firstIndex];
|
||||
index != NSNotFound; index = [selectedIndexes indexGreaterThanIndex:index]) {
|
||||
NSURL *url = [[outlineView itemAtRow:index] URL];
|
||||
[ws selectFile:[url path] inFileViewerRootedAtPath:[url path]];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)setAsRoot:(id)sender
|
||||
{
|
||||
- (IBAction)setAsRoot:(id)sender {
|
||||
NSUInteger index = [[outlineView selectedRowIndexes] firstIndex];
|
||||
|
||||
if (index != NSNotFound)
|
||||
{
|
||||
if(index != NSNotFound) {
|
||||
[dataSource changeURL:[[outlineView itemAtRow:index] URL]];
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL)validateMenuItem:(NSMenuItem*)menuItem
|
||||
{
|
||||
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
|
||||
SEL action = [menuItem action];
|
||||
|
||||
if ([outlineView numberOfSelectedRows] == 0)
|
||||
if([outlineView numberOfSelectedRows] == 0)
|
||||
return NO;
|
||||
|
||||
if (action == @selector(setAsRoot:))
|
||||
{
|
||||
if(action == @selector(setAsRoot:)) {
|
||||
BOOL isDir;
|
||||
NSInteger row = [outlineView selectedRow];
|
||||
|
||||
if ([outlineView numberOfSelectedRows] > 1)
|
||||
if([outlineView numberOfSelectedRows] > 1)
|
||||
return NO;
|
||||
|
||||
// Only let directories be Set as Root
|
||||
|
|
|
@ -21,7 +21,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
error:nil];
|
||||
}
|
||||
|
||||
@interface FileTreeDataSource()
|
||||
@interface FileTreeDataSource ()
|
||||
|
||||
@property NSURL *rootURL;
|
||||
|
||||
|
@ -33,7 +33,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
|
||||
+ (void)initialize {
|
||||
NSString *path = [defaultMusicDirectory() absoluteString];
|
||||
NSDictionary *userDefaultsValuesDict = @{@"fileTreeRootURL": path};
|
||||
NSDictionary *userDefaultsValuesDict = @{ @"fileTreeRootURL": path };
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
ofObject:(id)object
|
||||
change:(NSDictionary *)change
|
||||
context:(void *)context {
|
||||
if ([keyPath isEqualToString:@"values.fileTreeRootURL"]) {
|
||||
if([keyPath isEqualToString:@"values.fileTreeRootURL"]) {
|
||||
NSString *url =
|
||||
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] objectForKey:@"fileTreeRootURL"];
|
||||
DLog(@"File tree root URL: %@\n", url);
|
||||
|
@ -60,7 +60,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
}
|
||||
|
||||
- (void)changeURL:(NSURL *)url {
|
||||
if (url != nil) {
|
||||
if(url != nil) {
|
||||
[[[NSUserDefaultsController sharedUserDefaultsController] defaults] setObject:[url absoluteString]
|
||||
forKey:@"fileTreeRootURL"];
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
|
||||
- (void)pathControlAction:(id)sender {
|
||||
NSPathControlItem *item = [self.pathControl clickedPathItem];
|
||||
if (item != nil && item.URL != nil) {
|
||||
if(item != nil && item.URL != nil) {
|
||||
[self changeURL:item.URL];
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
}
|
||||
|
||||
- (void)setRootURL:(NSURL *)rootURL {
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) {
|
||||
if(![[NSFileManager defaultManager] fileExistsAtPath:[rootURL path]]) {
|
||||
rootURL = defaultMusicDirectory();
|
||||
}
|
||||
|
||||
|
@ -93,21 +93,20 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
NSString *relativePath = [[path stringByReplacingOccurrencesOfString:[[[self rootURL] path] stringByAppendingString:@"/"]
|
||||
withString:@""
|
||||
options:NSAnchoredSearch
|
||||
range:NSMakeRange(0, [path length])
|
||||
] stringByStandardizingPath];
|
||||
range:NSMakeRange(0, [path length])] stringByStandardizingPath];
|
||||
PathNode *node = rootNode;
|
||||
DLog(@"Root | Relative | Path: %@ | %@ | %@", [[self rootURL] path], relativePath, path);
|
||||
for (NSString *c in [relativePath pathComponents]) {
|
||||
for(NSString *c in [relativePath pathComponents]) {
|
||||
DLog(@"COMPONENT: %@", c);
|
||||
BOOL found = NO;
|
||||
for (PathNode *subnode in [node subpaths]) {
|
||||
if ([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) {
|
||||
for(PathNode *subnode in [node subpaths]) {
|
||||
if([[[[subnode URL] path] lastPathComponent] isEqualToString:c]) {
|
||||
node = subnode;
|
||||
found = YES;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
if(!found) {
|
||||
DLog(@"Not found!");
|
||||
return nil;
|
||||
}
|
||||
|
@ -118,7 +117,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
|
||||
- (void)pathDidChange:(NSString *)path {
|
||||
DLog(@"PATH DID CHANGE: %@", path);
|
||||
//Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node]
|
||||
// Need to find the corresponding node...and call [node reloadPath], then [self reloadPathNode:node]
|
||||
PathNode *node = [self nodeForPath:path];
|
||||
DLog(@"NODE IS: %@", node);
|
||||
[node updatePath];
|
||||
|
@ -128,7 +127,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
|
||||
PathNode *n = (item == nil ? rootNode : item);
|
||||
|
||||
return (int) [[n subpaths] count];
|
||||
return (int)[[n subpaths] count];
|
||||
}
|
||||
|
||||
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
|
||||
|
@ -140,7 +139,7 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item {
|
||||
PathNode *n = (item == nil ? rootNode : item);
|
||||
|
||||
return [n subpaths][(NSUInteger) index];
|
||||
return [n subpaths][(NSUInteger)index];
|
||||
}
|
||||
|
||||
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
|
||||
|
@ -149,19 +148,18 @@ static NSURL *defaultMusicDirectory(void) {
|
|||
return n;
|
||||
}
|
||||
|
||||
- (id <NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item {
|
||||
- (id<NSPasteboardWriting>)outlineView:(NSOutlineView *)outlineView pasteboardWriterForItem:(id)item {
|
||||
NSPasteboardItem *paste = [[NSPasteboardItem alloc] init];
|
||||
if (@available(macOS 10.13, *)) {
|
||||
if(@available(macOS 10.13, *)) {
|
||||
[paste setData:[[item URL] dataRepresentation] forType:NSPasteboardTypeFileURL];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[paste setPropertyList:@[[item URL]] forType:NSFilenamesPboardType];
|
||||
}
|
||||
return paste;
|
||||
}
|
||||
|
||||
- (void)reloadPathNode:(PathNode *)item {
|
||||
if (item == rootNode) {
|
||||
if(item == rootNode) {
|
||||
[self.outlineView reloadData];
|
||||
} else {
|
||||
[self.outlineView reloadItem:item reloadChildren:YES];
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface FileTreeOutlineView : NSOutlineView {
|
||||
}
|
||||
|
||||
|
|
|
@ -6,37 +6,31 @@
|
|||
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FileTreeController.h"
|
||||
#import "FileTreeOutlineView.h"
|
||||
#import "FileTreeController.h"
|
||||
#import "FileTreeViewController.h"
|
||||
#import "PlaybackController.h"
|
||||
|
||||
@implementation FileTreeOutlineView
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
- (void)awakeFromNib {
|
||||
[self setDoubleAction:@selector(addToPlaylistExternal:)];
|
||||
[self setTarget:[self delegate]];
|
||||
}
|
||||
|
||||
- (void)keyDown:(NSEvent *)e
|
||||
{
|
||||
- (void)keyDown:(NSEvent *)e {
|
||||
unsigned int modifiers = [e modifierFlags] & (NSEventModifierFlagCommand | NSEventModifierFlagShift | NSEventModifierFlagControl | NSEventModifierFlagOption);
|
||||
NSString *characters = [e characters];
|
||||
unichar c;
|
||||
|
||||
if ([characters length] == 1)
|
||||
{
|
||||
if([characters length] == 1) {
|
||||
c = [characters characterAtIndex:0];
|
||||
|
||||
if (modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter))
|
||||
{
|
||||
if(modifiers == 0 && (c == NSEnterCharacter || c == NSCarriageReturnCharacter)) {
|
||||
[(FileTreeController *)[self delegate] addToPlaylistExternal:self];
|
||||
|
||||
return;
|
||||
}
|
||||
else if (modifiers == 0 && c == ' ')
|
||||
{
|
||||
} else if(modifiers == 0 && c == ' ') {
|
||||
[(FileTreeController *)[self delegate] playPauseResume:self];
|
||||
return;
|
||||
}
|
||||
|
@ -48,25 +42,21 @@
|
|||
}
|
||||
|
||||
// enables right-click selection for "Show in Finder" contextual menu
|
||||
-(NSMenu*)menuForEvent:(NSEvent*)event
|
||||
{
|
||||
//Find which row is under the cursor
|
||||
- (NSMenu *)menuForEvent:(NSEvent *)event {
|
||||
// Find which row is under the cursor
|
||||
[[self window] makeFirstResponder:self];
|
||||
NSPoint menuPoint = [self convertPoint:[event locationInWindow] fromView:nil];
|
||||
NSInteger iRow = [self rowAtPoint:menuPoint];
|
||||
NSMenu* contextMenu = [self menu];
|
||||
NSMenu *contextMenu = [self menu];
|
||||
|
||||
/* Update the file tree selection before showing menu
|
||||
Preserves the selection if the row under the mouse is selected (to allow for
|
||||
multiple items to be selected), otherwise selects the row under the mouse */
|
||||
BOOL currentRowIsSelected = [[self selectedRowIndexes] containsIndex:iRow];
|
||||
|
||||
if (iRow == -1)
|
||||
{
|
||||
if(iRow == -1) {
|
||||
[self deselectAll:self];
|
||||
}
|
||||
else if (!currentRowIsSelected)
|
||||
{
|
||||
} else if(!currentRowIsSelected) {
|
||||
[self selectRowIndexes:[NSIndexSet indexSetWithIndex:iRow] byExtendingSelection:NO];
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "SideViewController.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@class PlaylistLoader;
|
||||
@class PlaybackController;
|
||||
|
@ -18,6 +18,6 @@
|
|||
IBOutlet FileTreeOutlineView *fileTreeOutlineView;
|
||||
}
|
||||
|
||||
- (FileTreeOutlineView*)outlineView;
|
||||
- (FileTreeOutlineView *)outlineView;
|
||||
|
||||
@end
|
||||
|
|
|
@ -7,44 +7,37 @@
|
|||
//
|
||||
|
||||
#import "FileTreeViewController.h"
|
||||
#import "PlaylistLoader.h"
|
||||
#import "PlaybackController.h"
|
||||
#import "PlaylistLoader.h"
|
||||
|
||||
@implementation FileTreeViewController
|
||||
|
||||
- (id)init
|
||||
{
|
||||
- (id)init {
|
||||
return [super initWithNibName:@"FileTree" bundle:[NSBundle mainBundle]];
|
||||
}
|
||||
|
||||
- (void)addToPlaylistInternal:(NSArray *)urls
|
||||
{
|
||||
- (void)addToPlaylistInternal:(NSArray *)urls {
|
||||
[self doAddToPlaylist:urls origin:URLOriginInternal];
|
||||
}
|
||||
|
||||
- (void)addToPlaylistExternal:(NSArray *)urls
|
||||
{
|
||||
- (void)addToPlaylistExternal:(NSArray *)urls {
|
||||
[self doAddToPlaylist:urls origin:URLOriginExternal];
|
||||
}
|
||||
|
||||
- (void)doAddToPlaylist:(NSArray *)urls origin:(URLOrigin)origin
|
||||
{
|
||||
- (void)doAddToPlaylist:(NSArray *)urls origin:(URLOrigin)origin {
|
||||
[playlistLoader willInsertURLs:urls origin:origin];
|
||||
[playlistLoader didInsertURLs:[playlistLoader addURLs:urls sort:YES] origin:origin];
|
||||
}
|
||||
|
||||
- (void)clear:(id)sender
|
||||
{
|
||||
- (void)clear:(id)sender {
|
||||
[playlistLoader clear:sender];
|
||||
}
|
||||
|
||||
- (void)playPauseResume:(NSObject *)id
|
||||
{
|
||||
- (void)playPauseResume:(NSObject *)id {
|
||||
[playbackController playPauseResume:id];
|
||||
}
|
||||
|
||||
- (FileTreeOutlineView*)outlineView
|
||||
{
|
||||
- (FileTreeOutlineView *)outlineView {
|
||||
return fileTreeOutlineView;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,11 @@
|
|||
|
||||
@class FileTreeDataSource;
|
||||
|
||||
@interface PathNode : NSObject
|
||||
{
|
||||
@interface PathNode : NSObject {
|
||||
FileTreeDataSource *dataSource;
|
||||
|
||||
NSURL *url;
|
||||
NSString *display; //The pretty path to display.
|
||||
NSString *display; // The pretty path to display.
|
||||
|
||||
NSImage *icon;
|
||||
|
||||
|
@ -27,7 +26,7 @@
|
|||
- (NSURL *)URL;
|
||||
- (void)setURL:(NSURL *)url;
|
||||
|
||||
- (void)processPaths: (NSArray *)contents;
|
||||
- (void)processPaths:(NSArray *)contents;
|
||||
|
||||
- (NSArray *)subpaths;
|
||||
- (void)setSubpaths:(NSArray *)s;
|
||||
|
@ -41,5 +40,4 @@
|
|||
|
||||
- (void)updatePath;
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,78 +12,67 @@
|
|||
|
||||
#import "FileTreeDataSource.h"
|
||||
|
||||
#import "FileNode.h"
|
||||
#import "DirectoryNode.h"
|
||||
#import "SmartFolderNode.h"
|
||||
#import "ContainerNode.h"
|
||||
#import "DirectoryNode.h"
|
||||
#import "FileNode.h"
|
||||
#import "SmartFolderNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation PathNode
|
||||
|
||||
//From http://developer.apple.com/documentation/Cocoa/Conceptual/LowLevelFileMgmt/Tasks/ResolvingAliases.html
|
||||
//Updated 2018-06-28
|
||||
NSURL *resolveAliases(NSURL *url)
|
||||
{
|
||||
// From http://developer.apple.com/documentation/Cocoa/Conceptual/LowLevelFileMgmt/Tasks/ResolvingAliases.html
|
||||
// Updated 2018-06-28
|
||||
NSURL *resolveAliases(NSURL *url) {
|
||||
CFErrorRef error;
|
||||
CFDataRef bookmarkRef = CFURLCreateBookmarkDataFromFile(kCFAllocatorDefault, (__bridge CFURLRef)url, &error);
|
||||
if (bookmarkRef)
|
||||
{
|
||||
if(bookmarkRef) {
|
||||
Boolean isStale;
|
||||
CFURLRef urlRef = CFURLCreateByResolvingBookmarkData(kCFAllocatorDefault, bookmarkRef, kCFURLBookmarkResolutionWithSecurityScope, NULL, NULL, &isStale, &error);
|
||||
|
||||
if (urlRef && !isStale)
|
||||
{
|
||||
if(urlRef && !isStale) {
|
||||
return (NSURL *)CFBridgingRelease(urlRef);
|
||||
}
|
||||
}
|
||||
|
||||
//DLog(@"Not resolved");
|
||||
// DLog(@"Not resolved");
|
||||
return url;
|
||||
}
|
||||
|
||||
- (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u
|
||||
{
|
||||
- (id)initWithDataSource:(FileTreeDataSource *)ds url:(NSURL *)u {
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
if(self) {
|
||||
dataSource = ds;
|
||||
[self setURL: u];
|
||||
[self setURL:u];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setURL:(NSURL *)u
|
||||
{
|
||||
- (void)setURL:(NSURL *)u {
|
||||
url = u;
|
||||
|
||||
display = [[NSFileManager defaultManager] displayNameAtPath:[u path]];
|
||||
|
||||
icon = [[NSWorkspace sharedWorkspace] iconForFile:[url path]];
|
||||
|
||||
[icon setSize: NSMakeSize(16.0, 16.0)];
|
||||
[icon setSize:NSMakeSize(16.0, 16.0)];
|
||||
}
|
||||
|
||||
- (NSURL *)URL
|
||||
{
|
||||
- (NSURL *)URL {
|
||||
return url;
|
||||
}
|
||||
|
||||
- (void)updatePath
|
||||
{
|
||||
- (void)updatePath {
|
||||
}
|
||||
|
||||
- (void)processPaths: (NSArray *)contents
|
||||
{
|
||||
- (void)processPaths:(NSArray *)contents {
|
||||
NSMutableArray *newSubpathsDirs = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *newSubpaths = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSString *s in contents)
|
||||
{
|
||||
if ([s characterAtIndex:0] == '.')
|
||||
{
|
||||
for(NSString *s in contents) {
|
||||
if([s characterAtIndex:0] == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -92,44 +81,35 @@ NSURL *resolveAliases(NSURL *url)
|
|||
|
||||
PathNode *newNode;
|
||||
|
||||
//DLog(@"Before: %@", u);
|
||||
// DLog(@"Before: %@", u);
|
||||
u = resolveAliases(u);
|
||||
//DLog(@"After: %@", u);
|
||||
// DLog(@"After: %@", u);
|
||||
|
||||
BOOL isDir;
|
||||
|
||||
if ([[s pathExtension] caseInsensitiveCompare:@"savedSearch"] == NSOrderedSame)
|
||||
{
|
||||
if([[s pathExtension] caseInsensitiveCompare:@"savedSearch"] == NSOrderedSame) {
|
||||
DLog(@"Smart folder!");
|
||||
newNode = [[SmartFolderNode alloc] initWithDataSource:dataSource url:u];
|
||||
isDir = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[[NSFileManager defaultManager] fileExistsAtPath:[u path] isDirectory:&isDir];
|
||||
|
||||
if (!isDir && ![[AudioPlayer fileTypes] containsObject:[[u pathExtension] lowercaseString]])
|
||||
{
|
||||
if(!isDir && ![[AudioPlayer fileTypes] containsObject:[[u pathExtension] lowercaseString]]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDir)
|
||||
{
|
||||
if(isDir) {
|
||||
newNode = [[DirectoryNode alloc] initWithDataSource:dataSource url:u];
|
||||
}
|
||||
else if ([[AudioPlayer containerTypes] containsObject:[[u pathExtension] lowercaseString]])
|
||||
{
|
||||
} else if([[AudioPlayer containerTypes] containsObject:[[u pathExtension] lowercaseString]]) {
|
||||
newNode = [[ContainerNode alloc] initWithDataSource:dataSource url:u];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
newNode = [[FileNode alloc] initWithDataSource:dataSource url:u];
|
||||
}
|
||||
}
|
||||
|
||||
[newNode setDisplay:displayName];
|
||||
|
||||
if (isDir)
|
||||
if(isDir)
|
||||
[newSubpathsDirs addObject:newNode];
|
||||
else
|
||||
[newSubpaths addObject:newNode];
|
||||
|
@ -139,39 +119,31 @@ NSURL *resolveAliases(NSURL *url)
|
|||
[self setSubpaths:newSubpathsDirs];
|
||||
}
|
||||
|
||||
- (NSArray *)subpaths
|
||||
{
|
||||
if (subpaths == nil)
|
||||
{
|
||||
- (NSArray *)subpaths {
|
||||
if(subpaths == nil) {
|
||||
[self updatePath];
|
||||
}
|
||||
|
||||
return subpaths;
|
||||
}
|
||||
|
||||
- (void)setSubpaths:(NSArray *)s
|
||||
{
|
||||
- (void)setSubpaths:(NSArray *)s {
|
||||
subpaths = s;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
- (BOOL)isLeaf {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setDisplay:(NSString *)s
|
||||
{
|
||||
- (void)setDisplay:(NSString *)s {
|
||||
display = s;
|
||||
}
|
||||
|
||||
- (NSString *)display
|
||||
{
|
||||
- (NSString *)display {
|
||||
return display;
|
||||
}
|
||||
|
||||
- (NSImage *)icon
|
||||
{
|
||||
- (NSImage *)icon {
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
|
||||
|
||||
@interface PathWatcher : NSObject {
|
||||
FSEventStreamRef stream;
|
||||
FSEventStreamContext *context;
|
||||
|
@ -20,7 +19,7 @@
|
|||
- (void)setDelegate:(id)d;
|
||||
- (id)delegate;
|
||||
|
||||
- (void)setPath:(NSString *)path; //Set the path to watch
|
||||
- (void)setPath:(NSString *)path; // Set the path to watch
|
||||
- (void)cleanUp;
|
||||
@end
|
||||
|
||||
|
|
|
@ -9,19 +9,18 @@
|
|||
#import "PathWatcher.h"
|
||||
|
||||
static void myFSEventCallback(
|
||||
ConstFSEventStreamRef streamRef,
|
||||
void *clientCallBackInfo,
|
||||
size_t numEvents,
|
||||
void *eventPaths,
|
||||
const FSEventStreamEventFlags eventFlags[],
|
||||
const FSEventStreamEventId eventIds[])
|
||||
{
|
||||
ConstFSEventStreamRef streamRef,
|
||||
void *clientCallBackInfo,
|
||||
size_t numEvents,
|
||||
void *eventPaths,
|
||||
const FSEventStreamEventFlags eventFlags[],
|
||||
const FSEventStreamEventId eventIds[]) {
|
||||
int i;
|
||||
char **paths = eventPaths;
|
||||
PathWatcher *pathWatcher = (__bridge PathWatcher *)clientCallBackInfo;
|
||||
|
||||
printf("Callback called\n");
|
||||
for (i=0; i<numEvents; i++) {
|
||||
for(i = 0; i < numEvents; i++) {
|
||||
NSString *pathString = [[NSString alloc] initWithUTF8String:paths[i]];
|
||||
[[pathWatcher delegate] pathDidChange:pathString];
|
||||
}
|
||||
|
@ -29,28 +28,26 @@ static void myFSEventCallback(
|
|||
|
||||
@implementation PathWatcher
|
||||
|
||||
- (void)cleanUp
|
||||
{
|
||||
if (stream) {
|
||||
- (void)cleanUp {
|
||||
if(stream) {
|
||||
FSEventStreamStop(stream);
|
||||
FSEventStreamInvalidate(stream);
|
||||
FSEventStreamRelease(stream);
|
||||
stream = NULL;
|
||||
}
|
||||
if (context) {
|
||||
if(context) {
|
||||
free(context);
|
||||
context = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setPath:(NSString *)path
|
||||
{
|
||||
- (void)setPath:(NSString *)path {
|
||||
[self cleanUp];
|
||||
|
||||
//Create FSEvent stream
|
||||
// Create FSEvent stream
|
||||
NSArray *pathsToWatch = @[path];
|
||||
|
||||
context = (FSEventStreamContext*)malloc(sizeof(FSEventStreamContext));
|
||||
context = (FSEventStreamContext *)malloc(sizeof(FSEventStreamContext));
|
||||
context->version = 0;
|
||||
context->info = (__bridge void *)self;
|
||||
context->retain = NULL;
|
||||
|
@ -62,7 +59,7 @@ static void myFSEventCallback(
|
|||
context,
|
||||
(__bridge CFArrayRef)pathsToWatch,
|
||||
kFSEventStreamEventIdSinceNow, // Or a previous event ID
|
||||
1.0, //latency in seconds
|
||||
1.0, // latency in seconds
|
||||
kFSEventStreamCreateFlagNone // Watch this and all its subdirectories
|
||||
);
|
||||
|
||||
|
@ -71,19 +68,15 @@ static void myFSEventCallback(
|
|||
FSEventStreamStart(stream);
|
||||
}
|
||||
|
||||
- (void)setDelegate:(id)d
|
||||
{
|
||||
- (void)setDelegate:(id)d {
|
||||
delegate = d;
|
||||
}
|
||||
- (id)delegate
|
||||
{
|
||||
- (id)delegate {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
[self cleanUp];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,13 +15,11 @@
|
|||
|
||||
@implementation SmartFolderNode
|
||||
|
||||
- (BOOL)isLeaf
|
||||
{
|
||||
- (BOOL)isLeaf {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)updatePath
|
||||
{
|
||||
- (void)updatePath {
|
||||
NSDictionary *doc = [NSDictionary dictionaryWithContentsOfFile:[url path]];
|
||||
NSString *rawQuery = [doc objectForKey:@"RawQuery"];
|
||||
NSArray *searchPaths = [[doc objectForKey:@"SearchCriteria"] objectForKey:@"CurrentFolderPath"];
|
||||
|
@ -32,46 +30,41 @@
|
|||
|
||||
MDQuerySetSearchScope(query, (CFArrayRef)searchPaths, 0);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryFinished:) name:(NSString*)kMDQueryDidFinishNotification object:(__bridge id)query];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryUpdate:) name:(NSString*)kMDQueryDidUpdateNotification object:(__bridge id)query];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryFinished:) name:(NSString *)kMDQueryDidFinishNotification object:(__bridge id)query];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(queryUpdate:) name:(NSString *)kMDQueryDidUpdateNotification object:(__bridge id)query];
|
||||
|
||||
DLog(@"Making query!");
|
||||
MDQueryExecute(query, kMDQueryWantsUpdates);
|
||||
|
||||
//Note: This is asynchronous!
|
||||
// Note: This is asynchronous!
|
||||
}
|
||||
|
||||
- (void)setSubpaths:(id)s
|
||||
{
|
||||
- (void)setSubpaths:(id)s {
|
||||
subpaths = s;
|
||||
}
|
||||
|
||||
- (unsigned int)countOfSubpaths
|
||||
{
|
||||
return (unsigned int) [[self subpaths] count];
|
||||
- (unsigned int)countOfSubpaths {
|
||||
return (unsigned int)[[self subpaths] count];
|
||||
}
|
||||
|
||||
- (PathNode *)objectInSubpathsAtIndex:(unsigned int)index
|
||||
{
|
||||
- (PathNode *)objectInSubpathsAtIndex:(unsigned int)index {
|
||||
return [[self subpaths] objectAtIndex:index];
|
||||
}
|
||||
|
||||
- (void)queryFinished:(NSNotification *)notification
|
||||
{
|
||||
- (void)queryFinished:(NSNotification *)notification {
|
||||
DLog(@"Query finished!");
|
||||
MDQueryRef query = (__bridge MDQueryRef)[notification object];
|
||||
|
||||
NSMutableArray *results = [NSMutableArray array];
|
||||
|
||||
MDQueryDisableUpdates(query);
|
||||
int c = (int) MDQueryGetResultCount(query);
|
||||
int c = (int)MDQueryGetResultCount(query);
|
||||
|
||||
int i;
|
||||
for (i = 0; i < c; i++)
|
||||
{
|
||||
for(i = 0; i < c; i++) {
|
||||
MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(query, i);
|
||||
|
||||
NSString *itemPath = (NSString *) CFBridgingRelease(MDItemCopyAttribute(item, kMDItemPath));
|
||||
NSString *itemPath = (NSString *)CFBridgingRelease(MDItemCopyAttribute(item, kMDItemPath));
|
||||
|
||||
[results addObject:itemPath];
|
||||
}
|
||||
|
@ -85,17 +78,13 @@
|
|||
[dataSource reloadPathNode:self];
|
||||
}
|
||||
|
||||
- (void)queryUpdate:(NSNotification *)notification
|
||||
{
|
||||
- (void)queryUpdate:(NSNotification *)notification {
|
||||
DLog(@"Query update!");
|
||||
[self queryFinished: notification];
|
||||
[self queryFinished:notification];
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
CFRelease(_query);
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -8,10 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface BlankZeroFormatter : NSFormatter {
|
||||
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#import "BlankZeroFormatter.h"
|
||||
|
||||
|
||||
@implementation BlankZeroFormatter
|
||||
|
||||
- (NSString *) stringForObjectValue:(id)object
|
||||
{
|
||||
- (NSString *)stringForObjectValue:(id)object {
|
||||
NSString *result = nil;
|
||||
int value;
|
||||
|
||||
|
@ -22,7 +20,7 @@
|
|||
|
||||
value = [object intValue];
|
||||
|
||||
if (value)
|
||||
if(value)
|
||||
result = [NSString stringWithFormat:@"%i", value];
|
||||
else
|
||||
result = [NSString string];
|
||||
|
@ -30,8 +28,7 @@
|
|||
return result;
|
||||
}
|
||||
|
||||
- (BOOL) getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error
|
||||
{
|
||||
- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error {
|
||||
if(NULL != object) {
|
||||
*object = [NSNumber numberWithInt:[string intValue]];
|
||||
|
||||
|
@ -41,8 +38,7 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (NSAttributedString *) attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes
|
||||
{
|
||||
- (NSAttributedString *)attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes {
|
||||
NSAttributedString *result = nil;
|
||||
|
||||
result = [[NSAttributedString alloc] initWithString:[self stringForObjectValue:object] attributes:attributes];
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface IndexFormatter : NSFormatter {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
|
||||
#import "IndexFormatter.h"
|
||||
|
||||
|
||||
@implementation IndexFormatter
|
||||
|
||||
- (NSString *) stringForObjectValue:(id)object
|
||||
{
|
||||
- (NSString *)stringForObjectValue:(id)object {
|
||||
NSString *result = nil;
|
||||
int value;
|
||||
|
||||
|
@ -27,8 +25,7 @@
|
|||
return result;
|
||||
}
|
||||
|
||||
- (BOOL) getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error
|
||||
{
|
||||
- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error {
|
||||
if(NULL != object) {
|
||||
*object = [NSNumber numberWithInt:[string intValue]];
|
||||
|
||||
|
@ -38,8 +35,7 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (NSAttributedString *) attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes
|
||||
{
|
||||
- (NSAttributedString *)attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes {
|
||||
NSAttributedString *result = nil;
|
||||
|
||||
result = [[NSAttributedString alloc] initWithString:[self stringForObjectValue:object] attributes:attributes];
|
||||
|
|
|
@ -20,8 +20,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface SecondsFormatter : NSFormatter
|
||||
{
|
||||
@interface SecondsFormatter : NSFormatter {
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -22,8 +22,7 @@
|
|||
|
||||
@implementation SecondsFormatter
|
||||
|
||||
- (NSString *) stringForObjectValue:(id)object
|
||||
{
|
||||
- (NSString *)stringForObjectValue:(id)object {
|
||||
NSString *result = nil;
|
||||
unsigned value;
|
||||
unsigned days = 0;
|
||||
|
@ -52,22 +51,18 @@
|
|||
|
||||
if(0 < days) {
|
||||
result = [NSString stringWithFormat:@"%u:%.2u:%.2u:%.2u", days, hours, minutes, seconds];
|
||||
}
|
||||
else if(0 < hours) {
|
||||
} else if(0 < hours) {
|
||||
result = [NSString stringWithFormat:@"%u:%.2u:%.2u", hours, minutes, seconds];
|
||||
}
|
||||
else if(0 < minutes) {
|
||||
} else if(0 < minutes) {
|
||||
result = [NSString stringWithFormat:@"%u:%.2u", minutes, seconds];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
result = [NSString stringWithFormat:@"0:%.2u", seconds];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (BOOL) getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error
|
||||
{
|
||||
- (BOOL)getObjectValue:(id *)object forString:(NSString *)string errorDescription:(NSString **)error {
|
||||
NSScanner *scanner = nil;
|
||||
BOOL result = NO;
|
||||
int value = 0;
|
||||
|
@ -76,7 +71,6 @@
|
|||
scanner = [NSScanner scannerWithString:string];
|
||||
|
||||
while(NO == [scanner isAtEnd]) {
|
||||
|
||||
// Grab a value
|
||||
if([scanner scanInt:&value]) {
|
||||
seconds *= 60;
|
||||
|
@ -90,16 +84,14 @@
|
|||
|
||||
if(result && NULL != object) {
|
||||
*object = [NSNumber numberWithUnsignedInt:seconds];
|
||||
}
|
||||
else if(NULL != error) {
|
||||
} else if(NULL != error) {
|
||||
*error = @"Couldn't convert value to seconds";
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (NSAttributedString *) attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes
|
||||
{
|
||||
- (NSAttributedString *)attributedStringForObjectValue:(id)object withDefaultAttributes:(NSDictionary *)attributes {
|
||||
NSAttributedString *result = nil;
|
||||
|
||||
result = [[NSAttributedString alloc] initWithString:[self stringForObjectValue:object] attributes:attributes];
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
// Copyright 2009 __MyCompanyName__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "AppController.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface InfoWindowController : NSWindowController
|
||||
{
|
||||
@interface InfoWindowController : NSWindowController {
|
||||
IBOutlet id playlistSelectionController;
|
||||
IBOutlet id currentEntryController;
|
||||
IBOutlet AppController *appController;
|
||||
|
|
|
@ -7,51 +7,46 @@
|
|||
//
|
||||
|
||||
#import "InfoWindowController.h"
|
||||
#import "AppController.h"
|
||||
#import "Logging.h"
|
||||
#import "MissingAlbumArtTransformer.h"
|
||||
#import "PlaylistEntry.h"
|
||||
#import "Logging.h"
|
||||
#import "AppController.h"
|
||||
|
||||
@implementation InfoWindowController
|
||||
|
||||
@synthesize valueToDisplay;
|
||||
|
||||
+ (void)initialize
|
||||
{
|
||||
+ (void)initialize {
|
||||
NSValueTransformer *missingAlbumArtTransformer = [[MissingAlbumArtTransformer alloc] init];
|
||||
[NSValueTransformer setValueTransformer:missingAlbumArtTransformer
|
||||
forName:@"MissingAlbumArtTransformer"];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
- (id)init {
|
||||
return [super initWithWindowNibName:@"InfoInspector"];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
- (void)awakeFromNib {
|
||||
[playlistSelectionController addObserver:self forKeyPath:@"selection" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[currentEntryController addObserver:self forKeyPath:@"content" options:NSKeyValueObservingOptionNew context:nil];
|
||||
[appController addObserver:self forKeyPath:@"miniMode" options:NSKeyValueObservingOptionNew context:nil];
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
|
||||
{
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
// Avoid "selection" because it creates a proxy that's hard to reason with when we don't need to write.
|
||||
PlaylistEntry* currentSelection = [[playlistSelectionController selectedObjects] firstObject];
|
||||
if (currentSelection != NULL) {
|
||||
[self setValueToDisplay: currentSelection];
|
||||
PlaylistEntry *currentSelection = [[playlistSelectionController selectedObjects] firstObject];
|
||||
if(currentSelection != NULL) {
|
||||
[self setValueToDisplay:currentSelection];
|
||||
} else {
|
||||
[self setValueToDisplay:[currentEntryController content]];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)toggleWindow:(id)sender
|
||||
{
|
||||
if ([[self window] isVisible])
|
||||
- (IBAction)toggleWindow:(id)sender {
|
||||
if([[self window] isVisible])
|
||||
[[self window] orderOut:self];
|
||||
else {
|
||||
if ([NSApp mainWindow]) {
|
||||
if([NSApp mainWindow]) {
|
||||
NSRect rect = [[NSApp mainWindow] frame];
|
||||
// Align Info Inspector HUD Panel to the right of Main Window.
|
||||
NSPoint point = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
|
||||
@interface MissingAlbumArtTransformer : NSValueTransformer {
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -8,15 +8,18 @@
|
|||
|
||||
#import "MissingAlbumArtTransformer.h"
|
||||
|
||||
|
||||
@implementation MissingAlbumArtTransformer
|
||||
|
||||
+ (Class)transformedValueClass { return [NSImage class]; }
|
||||
+ (BOOL)allowsReverseTransformation { return NO; }
|
||||
+ (Class)transformedValueClass {
|
||||
return [NSImage class];
|
||||
}
|
||||
+ (BOOL)allowsReverseTransformation {
|
||||
return NO;
|
||||
}
|
||||
|
||||
// Convert from NSImage to NSImage
|
||||
- (id)transformedValue:(id)value {
|
||||
if (value == nil) {
|
||||
if(value == nil) {
|
||||
return [NSImage imageNamed:@"missingArt"];
|
||||
}
|
||||
|
||||
|
|
8
Playlist/DNDArrayController.h
Executable file → Normal file
8
Playlist/DNDArrayController.h
Executable file → Normal file
|
@ -10,23 +10,23 @@ extern NSString *iTunesDropType;
|
|||
@property IBOutlet NSTableView *tableView;
|
||||
|
||||
// table view drag and drop support
|
||||
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
|
||||
- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView
|
||||
pasteboardWriterForRow:(NSInteger)row;
|
||||
- (void)tableView:(NSTableView *)tableView
|
||||
draggingSession:(NSDraggingSession *)session
|
||||
willBeginAtPoint:(NSPoint)screenPoint
|
||||
forRowIndexes:(NSIndexSet *)rowIndexes;
|
||||
- (NSDragOperation)tableView:(NSTableView *)tableView
|
||||
validateDrop:(id <NSDraggingInfo>)info
|
||||
validateDrop:(id<NSDraggingInfo>)info
|
||||
proposedRow:(NSInteger)row
|
||||
proposedDropOperation:(NSTableViewDropOperation)dropOperation;
|
||||
- (BOOL)tableView:(NSTableView *)tableView
|
||||
acceptDrop:(id <NSDraggingInfo>)info
|
||||
acceptDrop:(id<NSDraggingInfo>)info
|
||||
row:(NSInteger)row
|
||||
dropOperation:(NSTableViewDropOperation)dropOperation;
|
||||
|
||||
// utility methods
|
||||
-(void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
|
||||
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
|
||||
toIndex:(NSUInteger)insertIndex;
|
||||
// This is needed to undo the above
|
||||
- (void)moveObjectsFromIndex:(NSUInteger)fromIndex
|
||||
|
|
55
Playlist/DNDArrayController.m
Executable file → Normal file
55
Playlist/DNDArrayController.m
Executable file → Normal file
|
@ -13,10 +13,9 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
[super awakeFromNib];
|
||||
// register for drag and drop
|
||||
NSPasteboardType fileType;
|
||||
if (@available(macOS 10.13, *)) {
|
||||
if(@available(macOS 10.13, *)) {
|
||||
fileType = NSPasteboardTypeFileURL;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
fileType = NSFilenamesPboardType;
|
||||
}
|
||||
[self.tableView registerForDraggedTypes:@[CogDNDIndexType,
|
||||
|
@ -25,8 +24,7 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
iTunesDropType]];
|
||||
}
|
||||
|
||||
|
||||
- (id <NSPasteboardWriting>)tableView:(NSTableView *)tableView
|
||||
- (id<NSPasteboardWriting>)tableView:(NSTableView *)tableView
|
||||
pasteboardWriterForRow:(NSInteger)row {
|
||||
NSPasteboardItem *item = [[NSPasteboardItem alloc] init];
|
||||
[item setString:[@(row) stringValue] forType:CogDNDIndexType];
|
||||
|
@ -34,7 +32,6 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
return item;
|
||||
}
|
||||
|
||||
|
||||
- (void)tableView:(NSTableView *)tableView
|
||||
draggingSession:(NSDraggingSession *)session
|
||||
willBeginAtPoint:(NSPoint)screenPoint
|
||||
|
@ -42,14 +39,13 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
DLog(@"Drag session started with indexes: %@", rowIndexes);
|
||||
}
|
||||
|
||||
|
||||
- (NSDragOperation)tableView:(NSTableView *)tableView
|
||||
validateDrop:(id <NSDraggingInfo>)info
|
||||
validateDrop:(id<NSDraggingInfo>)info
|
||||
proposedRow:(NSInteger)row
|
||||
proposedDropOperation:(NSTableViewDropOperation)dropOperation {
|
||||
NSDragOperation dragOp = NSDragOperationCopy;
|
||||
|
||||
if ([info draggingSource] == tableView)
|
||||
if([info draggingSource] == tableView)
|
||||
dragOp = NSDragOperationMove;
|
||||
|
||||
DLog(@"VALIDATING DROP!");
|
||||
|
@ -60,26 +56,25 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
return dragOp;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)tableView:(NSTableView *)tableView
|
||||
acceptDrop:(id <NSDraggingInfo>)info
|
||||
acceptDrop:(id<NSDraggingInfo>)info
|
||||
row:(NSInteger)row
|
||||
dropOperation:(NSTableViewDropOperation)dropOperation {
|
||||
if (row < 0) {
|
||||
if(row < 0) {
|
||||
row = 0;
|
||||
}
|
||||
|
||||
NSArray<NSPasteboardItem *> *items = info.draggingPasteboard.pasteboardItems;
|
||||
// if drag source is self, it's a move
|
||||
if ([info draggingSource] == tableView || items == nil) {
|
||||
if([info draggingSource] == tableView || items == nil) {
|
||||
NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet];
|
||||
for (NSPasteboardItem *item in items) {
|
||||
[indexSet addIndex:(NSUInteger) [[item stringForType:CogDNDIndexType] intValue]];
|
||||
for(NSPasteboardItem *item in items) {
|
||||
[indexSet addIndex:(NSUInteger)[[item stringForType:CogDNDIndexType] intValue]];
|
||||
}
|
||||
if ([indexSet count] > 0) {
|
||||
if([indexSet count] > 0) {
|
||||
DLog(@"INDEX SET ON DROP: %@", indexSet);
|
||||
NSArray *selected = [[self arrangedObjects] objectsAtIndexes:indexSet];
|
||||
[self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:(unsigned int) row];
|
||||
[self moveObjectsInArrangedObjectsFromIndexes:indexSet toIndex:(unsigned int)row];
|
||||
|
||||
[self setSelectedObjects:selected];
|
||||
|
||||
|
@ -91,17 +86,16 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *)indexSet
|
||||
toIndex:(NSUInteger)insertIndex {
|
||||
__block NSUInteger rangeCount = 0;
|
||||
__block NSUInteger firstIndex = 0;
|
||||
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
if (++rangeCount == 1)
|
||||
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL *_Nonnull stop) {
|
||||
if(++rangeCount == 1)
|
||||
firstIndex = range.location;
|
||||
}];
|
||||
|
||||
if (rangeCount == 1 &&
|
||||
if(rangeCount == 1 &&
|
||||
(insertIndex >= firstIndex &&
|
||||
insertIndex < firstIndex + [indexSet count])) // Null operation
|
||||
return;
|
||||
|
@ -113,8 +107,8 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
id object;
|
||||
NSUInteger removeIndex;
|
||||
|
||||
while (NSNotFound != index) {
|
||||
if (index >= insertIndex) {
|
||||
while(NSNotFound != index) {
|
||||
if(index >= insertIndex) {
|
||||
removeIndex = index + aboveInsertIndexCount;
|
||||
aboveInsertIndexCount += 1;
|
||||
} else {
|
||||
|
@ -136,18 +130,18 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
__block NSUInteger rangeCount = 0;
|
||||
__block NSUInteger firstIndex = 0;
|
||||
__block NSUInteger _fromIndex = fromIndex;
|
||||
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL * _Nonnull stop) {
|
||||
if (++rangeCount == 1)
|
||||
[indexSet enumerateRangesUsingBlock:^(NSRange range, BOOL *_Nonnull stop) {
|
||||
if(++rangeCount == 1)
|
||||
firstIndex = range.location;
|
||||
if (_fromIndex >= range.location) {
|
||||
if (_fromIndex < range.location + range.length)
|
||||
if(_fromIndex >= range.location) {
|
||||
if(_fromIndex < range.location + range.length)
|
||||
_fromIndex = range.location;
|
||||
else
|
||||
_fromIndex -= range.length;
|
||||
}
|
||||
}];
|
||||
|
||||
if (rangeCount == 1 &&
|
||||
if(rangeCount == 1 &&
|
||||
(fromIndex >= firstIndex &&
|
||||
fromIndex < firstIndex + [indexSet count])) // Null operation
|
||||
return;
|
||||
|
@ -161,11 +155,11 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
id object;
|
||||
|
||||
fromIndex += [objects count];
|
||||
for (NSUInteger i = 0; i < [objects count]; i++) {
|
||||
for(NSUInteger i = 0; i < [objects count]; i++) {
|
||||
[self removeObjectAtArrangedObjectIndex:--fromIndex];
|
||||
}
|
||||
|
||||
while (NSNotFound != index) {
|
||||
while(NSNotFound != index) {
|
||||
object = objects[itemIndex++];
|
||||
|
||||
[self insertObject:object atArrangedObjectIndex:index];
|
||||
|
@ -174,5 +168,4 @@ NSString *iTunesDropType = @"com.apple.tv.metadata";
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
@ -6,9 +6,9 @@
|
|||
// Copyright 2005 Vincent Spader All rights reserved.
|
||||
//
|
||||
|
||||
#import "DNDArrayController.h"
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <Foundation/NSUndoManager.h>
|
||||
#import "DNDArrayController.h"
|
||||
|
||||
@class PlaylistLoader;
|
||||
@class PlaylistEntry;
|
||||
|
@ -26,7 +26,9 @@ static inline BOOL IsRepeatOneSet() {
|
|||
return [[NSUserDefaults standardUserDefaults] integerForKey:@"repeat"] == RepeatModeRepeatOne;
|
||||
}
|
||||
|
||||
typedef enum { ShuffleOff = 0, ShuffleAlbums, ShuffleAll } ShuffleMode;
|
||||
typedef enum { ShuffleOff = 0,
|
||||
ShuffleAlbums,
|
||||
ShuffleAll } ShuffleMode;
|
||||
|
||||
typedef NS_ENUM(NSInteger, URLOrigin) {
|
||||
URLOriginInternal = 0,
|
||||
|
@ -38,7 +40,7 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
|
|||
IBOutlet SpotlightWindowController *spotlightWindowController;
|
||||
IBOutlet PlaybackController *playbackController;
|
||||
|
||||
NSValueTransformer * statusImageTransformer;
|
||||
NSValueTransformer *statusImageTransformer;
|
||||
|
||||
NSMutableArray *shuffleList;
|
||||
NSMutableArray *queueList;
|
||||
|
@ -50,26 +52,26 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
|
|||
NSUndoManager *undoManager;
|
||||
}
|
||||
|
||||
@property(nonatomic, retain) PlaylistEntry * _Nullable currentEntry;
|
||||
@property(retain) NSString * _Nullable totalTime;
|
||||
@property(nonatomic, retain) PlaylistEntry *_Nullable currentEntry;
|
||||
@property(retain) NSString *_Nullable totalTime;
|
||||
|
||||
// Private Methods
|
||||
- (void)updateTotalTime;
|
||||
- (void)updatePlaylistIndexes;
|
||||
- (IBAction)stopAfterCurrent:(id _Nullable )sender;
|
||||
- (IBAction)stopAfterCurrent:(id _Nullable)sender;
|
||||
|
||||
// PUBLIC METHODS
|
||||
- (void)setShuffle:(ShuffleMode)s;
|
||||
- (ShuffleMode)shuffle;
|
||||
- (void)setRepeat:(RepeatMode)r;
|
||||
- (RepeatMode)repeat;
|
||||
- (NSArray * _Nullable)filterPlaylistOnAlbum:(NSString * _Nullable)album;
|
||||
- (NSArray *_Nullable)filterPlaylistOnAlbum:(NSString *_Nullable)album;
|
||||
|
||||
- (PlaylistEntry * _Nullable)getNextEntry:(PlaylistEntry * _Nullable)pe;
|
||||
- (PlaylistEntry * _Nullable)getPrevEntry:(PlaylistEntry * _Nullable)pe;
|
||||
- (PlaylistEntry *_Nullable)getNextEntry:(PlaylistEntry *_Nullable)pe;
|
||||
- (PlaylistEntry *_Nullable)getPrevEntry:(PlaylistEntry *_Nullable)pe;
|
||||
|
||||
/* Methods for undoing various actions */
|
||||
- (NSUndoManager * _Nullable)undoManager;
|
||||
- (NSUndoManager *_Nullable)undoManager;
|
||||
|
||||
- (IBAction)toggleShuffle:(id _Nullable)sender;
|
||||
|
||||
|
@ -98,25 +100,25 @@ typedef NS_ENUM(NSInteger, URLOrigin) {
|
|||
- (void)addShuffledListToFront;
|
||||
- (void)resetShuffleList;
|
||||
|
||||
- (PlaylistEntry * _Nullable)shuffledEntryAtIndex:(NSInteger)i;
|
||||
- (PlaylistEntry * _Nullable)entryAtIndex:(NSInteger)i;
|
||||
- (PlaylistEntry *_Nullable)shuffledEntryAtIndex:(NSInteger)i;
|
||||
- (PlaylistEntry *_Nullable)entryAtIndex:(NSInteger)i;
|
||||
|
||||
// Event inlets:
|
||||
- (void)willInsertURLs:(NSArray * _Nullable)urls origin:(URLOrigin)origin;
|
||||
- (void)didInsertURLs:(NSArray * _Nullable)urls origin:(URLOrigin)origin;
|
||||
- (void)willInsertURLs:(NSArray *_Nullable)urls origin:(URLOrigin)origin;
|
||||
- (void)didInsertURLs:(NSArray *_Nullable)urls origin:(URLOrigin)origin;
|
||||
|
||||
// queue methods
|
||||
- (IBAction)toggleQueued:(id _Nullable)sender;
|
||||
- (IBAction)emptyQueueList:(id _Nullable)sender;
|
||||
- (void)emptyQueueListUnsynced;
|
||||
- (NSMutableArray * _Nullable)queueList;
|
||||
- (NSMutableArray *_Nullable)queueList;
|
||||
|
||||
// reload metadata of selection
|
||||
- (IBAction)reloadTags:(id _Nullable)sender;
|
||||
|
||||
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet * _Nullable)indexSet
|
||||
- (void)moveObjectsInArrangedObjectsFromIndexes:(NSIndexSet *_Nullable)indexSet
|
||||
toIndex:(NSUInteger)insertIndex;
|
||||
|
||||
- (void)insertObjectsUnsynced:(NSArray * _Nullable)objects atArrangedObjectIndexes:(NSIndexSet * _Nullable)indexes;
|
||||
- (void)insertObjectsUnsynced:(NSArray *_Nullable)objects atArrangedObjectIndexes:(NSIndexSet *_Nullable)indexes;
|
||||
|
||||
@end
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,7 +8,7 @@
|
|||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface PlaylistEntry : NSObject<NSCopying> {
|
||||
@interface PlaylistEntry : NSObject <NSCopying> {
|
||||
NSInteger index;
|
||||
NSInteger shuffleIndex;
|
||||
NSInteger dbIndex;
|
||||
|
|
|
@ -67,89 +67,72 @@
|
|||
|
||||
// The following read-only keys depend on the values of other properties
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingDisplay
|
||||
{
|
||||
return [NSSet setWithObjects:@"artist",@"title",nil];
|
||||
+ (NSSet *)keyPathsForValuesAffectingDisplay {
|
||||
return [NSSet setWithObjects:@"artist", @"title", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingLength
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingLength {
|
||||
return [NSSet setWithObject:@"totalFrames"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingPath
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingPath {
|
||||
return [NSSet setWithObject:@"URL"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingFilename
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingFilename {
|
||||
return [NSSet setWithObject:@"URL"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingStatus
|
||||
{
|
||||
return [NSSet setWithObjects:@"current",@"queued", @"error", @"stopAfter", nil];
|
||||
+ (NSSet *)keyPathsForValuesAffectingStatus {
|
||||
return [NSSet setWithObjects:@"current", @"queued", @"error", @"stopAfter", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingStatusMessage
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingStatusMessage {
|
||||
return [NSSet setWithObjects:@"current", @"queued", @"queuePosition", @"error", @"errorMessage", @"stopAfter", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingSpam
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingSpam {
|
||||
return [NSSet setWithObjects:@"albumartist", @"artist", @"title", @"album", @"track", @"disc", @"totalFrames", @"currentPosition", @"bitrate", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingTrackText
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingTrackText {
|
||||
return [NSSet setWithObjects:@"track", @"disc", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingYearText
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingYearText {
|
||||
return [NSSet setWithObject:@"year"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingCuesheetPresent
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingCuesheetPresent {
|
||||
return [NSSet setWithObject:@"cuesheet"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingPositionText
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingPositionText {
|
||||
return [NSSet setWithObject:@"currentPosition"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingLengthText
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingLengthText {
|
||||
return [NSSet setWithObject:@"length"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingAlbumArt
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingAlbumArt {
|
||||
return [NSSet setWithObject:@"albumArtInternal"];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingGainCorrection
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingGainCorrection {
|
||||
return [NSSet setWithObjects:@"replayGainAlbumGain", @"replayGainAlbumPeak", @"replayGainTrackGain", @"replayGainTrackPeak", @"volume", nil];
|
||||
}
|
||||
|
||||
+ (NSSet *)keyPathsForValuesAffectingGainInfo
|
||||
{
|
||||
+ (NSSet *)keyPathsForValuesAffectingGainInfo {
|
||||
return [NSSet setWithObjects:@"replayGainAlbumGain", @"replayGainAlbumPeak", @"replayGainTrackGain", @"replayGainTrackPeak", @"volume", nil];
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
- (NSString *)description {
|
||||
return [NSString stringWithFormat:@"PlaylistEntry %li:(%@)", self.index, self.URL];
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init]) {
|
||||
- (id)init {
|
||||
if(self = [super init]) {
|
||||
self.replayGainAlbumGain = 0;
|
||||
self.replayGainAlbumPeak = 0;
|
||||
self.replayGainTrackGain = 0;
|
||||
|
@ -159,8 +142,7 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
- (void)dealloc {
|
||||
self.errorMessage = nil;
|
||||
|
||||
self.URL = nil;
|
||||
|
@ -183,25 +165,21 @@
|
|||
|
||||
// Get the URL if the title is blank
|
||||
@synthesize title;
|
||||
- (NSString *)title
|
||||
{
|
||||
if((title == nil || [title isEqualToString:@""]) && self.URL)
|
||||
{
|
||||
- (NSString *)title {
|
||||
if((title == nil || [title isEqualToString:@""]) && self.URL) {
|
||||
return [[self.URL path] lastPathComponent];
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
@synthesize rawTitle;
|
||||
- (NSString *)rawTitle
|
||||
{
|
||||
- (NSString *)rawTitle {
|
||||
return title;
|
||||
}
|
||||
|
||||
@dynamic display;
|
||||
- (NSString *)display
|
||||
{
|
||||
if ((self.artist == NULL) || ([self.artist isEqualToString:@""]))
|
||||
- (NSString *)display {
|
||||
if((self.artist == NULL) || ([self.artist isEqualToString:@""]))
|
||||
return self.title;
|
||||
else {
|
||||
return [NSString stringWithFormat:@"%@ - %@", self.artist, self.title];
|
||||
|
@ -209,8 +187,7 @@
|
|||
}
|
||||
|
||||
@dynamic spam;
|
||||
- (NSString *)spam
|
||||
{
|
||||
- (NSString *)spam {
|
||||
BOOL hasBitrate = (self.bitrate != 0);
|
||||
BOOL hasArtist = (self.artist != nil) && (![self.artist isEqualToString:@""]);
|
||||
BOOL hasAlbumArtist = (self.albumartist != nil) && (![self.albumartist isEqualToString:@""]);
|
||||
|
@ -222,11 +199,11 @@
|
|||
BOOL hasTitle = (title != nil) && (![title isEqualToString:@""]);
|
||||
BOOL hasCodec = (self.codec != nil) && (![self.codec isEqualToString:@""]);
|
||||
|
||||
NSMutableString * filename = [NSMutableString stringWithString:[self filename]];
|
||||
NSMutableString *filename = [NSMutableString stringWithString:[self filename]];
|
||||
NSRange dotPosition = [filename rangeOfString:@"." options:NSBackwardsSearch];
|
||||
NSString * extension = nil;
|
||||
NSString *extension = nil;
|
||||
|
||||
if (dotPosition.length > 0) {
|
||||
if(dotPosition.length > 0) {
|
||||
dotPosition.location++;
|
||||
dotPosition.length = [filename length] - dotPosition.location;
|
||||
extension = [filename substringWithRange:dotPosition];
|
||||
|
@ -236,17 +213,16 @@
|
|||
hasExtension = YES;
|
||||
}
|
||||
|
||||
NSMutableArray * elements = [NSMutableArray array];
|
||||
NSMutableArray *elements = [NSMutableArray array];
|
||||
|
||||
if (hasExtension) {
|
||||
if(hasExtension) {
|
||||
[elements addObject:@"["];
|
||||
if (hasCodec) {
|
||||
if(hasCodec) {
|
||||
[elements addObject:self.codec];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[elements addObject:[extension uppercaseString]];
|
||||
}
|
||||
if (hasBitrate) {
|
||||
if(hasBitrate) {
|
||||
[elements addObject:@"@"];
|
||||
[elements addObject:[NSString stringWithFormat:@"%u", self.bitrate]];
|
||||
[elements addObject:@"kbps"];
|
||||
|
@ -254,46 +230,44 @@
|
|||
[elements addObject:@"] "];
|
||||
}
|
||||
|
||||
if (hasArtist) {
|
||||
if (hasAlbumArtist) {
|
||||
if(hasArtist) {
|
||||
if(hasAlbumArtist) {
|
||||
[elements addObject:self.albumartist];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[elements addObject:self.artist];
|
||||
}
|
||||
[elements addObject:@" - "];
|
||||
}
|
||||
|
||||
if (hasAlbum) {
|
||||
if(hasAlbum) {
|
||||
[elements addObject:@"["];
|
||||
[elements addObject:self.album];
|
||||
if (hasTrack) {
|
||||
if(hasTrack) {
|
||||
[elements addObject:@" #"];
|
||||
[elements addObject:self.trackText];
|
||||
}
|
||||
[elements addObject:@"] "];
|
||||
}
|
||||
|
||||
if (hasTitle) {
|
||||
if(hasTitle) {
|
||||
[elements addObject:title];
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
[elements addObject:filename];
|
||||
}
|
||||
|
||||
if (hasAlbumArtist && hasArtist) {
|
||||
if(hasAlbumArtist && hasArtist) {
|
||||
[elements addObject:@" // "];
|
||||
[elements addObject:self.artist];
|
||||
}
|
||||
|
||||
if (hasCurrentPosition || hasLength) {
|
||||
if(hasCurrentPosition || hasLength) {
|
||||
SecondsFormatter *secondsFormatter = [[SecondsFormatter alloc] init];
|
||||
[elements addObject:@" ("];
|
||||
if (hasCurrentPosition) {
|
||||
if(hasCurrentPosition) {
|
||||
[elements addObject:[secondsFormatter stringForObjectValue:[NSNumber numberWithFloat:currentPosition]]];
|
||||
}
|
||||
if (hasLength) {
|
||||
if (hasCurrentPosition) {
|
||||
if(hasLength) {
|
||||
if(hasCurrentPosition) {
|
||||
[elements addObject:@" / "];
|
||||
}
|
||||
[elements addObject:[secondsFormatter stringForObjectValue:[self length]]];
|
||||
|
@ -305,116 +279,85 @@
|
|||
}
|
||||
|
||||
@dynamic trackText;
|
||||
-(NSString *)trackText
|
||||
{
|
||||
if ([self.track intValue])
|
||||
{
|
||||
if ([self.disc intValue])
|
||||
{
|
||||
- (NSString *)trackText {
|
||||
if([self.track intValue]) {
|
||||
if([self.disc intValue]) {
|
||||
return [NSString stringWithFormat:@"%@.%02u", self.disc, [self.track intValue]];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return [NSString stringWithFormat:@"%02u", [self.track intValue]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return @"";
|
||||
}
|
||||
}
|
||||
|
||||
@dynamic yearText;
|
||||
-(NSString *)yearText
|
||||
{
|
||||
if ([self.year intValue])
|
||||
{
|
||||
- (NSString *)yearText {
|
||||
if([self.year intValue]) {
|
||||
return [NSString stringWithFormat:@"%@", self.year];
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return @"";
|
||||
}
|
||||
}
|
||||
|
||||
@dynamic cuesheetPresent;
|
||||
-(NSString *)cuesheetPresent
|
||||
{
|
||||
if (cuesheet && [cuesheet length])
|
||||
{
|
||||
- (NSString *)cuesheetPresent {
|
||||
if(cuesheet && [cuesheet length]) {
|
||||
return @"yes";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return @"no";
|
||||
}
|
||||
}
|
||||
|
||||
@dynamic gainCorrection;
|
||||
- (NSString *)gainCorrection
|
||||
{
|
||||
if (replayGainAlbumGain)
|
||||
{
|
||||
if (replayGainAlbumPeak)
|
||||
- (NSString *)gainCorrection {
|
||||
if(replayGainAlbumGain) {
|
||||
if(replayGainAlbumPeak)
|
||||
return @"Album Gain plus Peak";
|
||||
else
|
||||
return @"Album Gain";
|
||||
}
|
||||
else if (replayGainTrackGain)
|
||||
{
|
||||
if (replayGainTrackPeak)
|
||||
} else if(replayGainTrackGain) {
|
||||
if(replayGainTrackPeak)
|
||||
return @"Track Gain plus Peak";
|
||||
else
|
||||
return @"Track Gain";
|
||||
}
|
||||
else if (volume && volume != 1)
|
||||
{
|
||||
} else if(volume && volume != 1) {
|
||||
return @"Volume scale";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return @"None";
|
||||
}
|
||||
}
|
||||
|
||||
@dynamic gainInfo;
|
||||
- (NSString *)gainInfo
|
||||
{
|
||||
NSMutableArray * gainItems = [[NSMutableArray alloc] init];
|
||||
if (replayGainAlbumGain)
|
||||
{
|
||||
- (NSString *)gainInfo {
|
||||
NSMutableArray *gainItems = [[NSMutableArray alloc] init];
|
||||
if(replayGainAlbumGain) {
|
||||
[gainItems addObject:[NSString stringWithFormat:@"Album Gain: %+.2f dB", replayGainAlbumGain]];
|
||||
}
|
||||
if (replayGainAlbumPeak)
|
||||
{
|
||||
if(replayGainAlbumPeak) {
|
||||
[gainItems addObject:[NSString stringWithFormat:@"Album Peak: %.6f", replayGainAlbumPeak]];
|
||||
}
|
||||
if (replayGainTrackGain)
|
||||
{
|
||||
if(replayGainTrackGain) {
|
||||
[gainItems addObject:[NSString stringWithFormat:@"Track Gain: %+.2f dB", replayGainTrackGain]];
|
||||
}
|
||||
if (replayGainTrackPeak)
|
||||
{
|
||||
if(replayGainTrackPeak) {
|
||||
[gainItems addObject:[NSString stringWithFormat:@"Track Peak: %.6f", replayGainTrackPeak]];
|
||||
}
|
||||
if (volume && volume != 1)
|
||||
{
|
||||
if(volume && volume != 1) {
|
||||
[gainItems addObject:[NSString stringWithFormat:@"Volume Scale: %.2f%C", volume, (unichar)0x00D7]];
|
||||
}
|
||||
return [gainItems componentsJoinedByString:@"\n"];
|
||||
}
|
||||
|
||||
@dynamic positionText;
|
||||
- (NSString *)positionText
|
||||
{
|
||||
- (NSString *)positionText {
|
||||
SecondsFormatter *secondsFormatter = [[SecondsFormatter alloc] init];
|
||||
NSString *time = [secondsFormatter stringForObjectValue:[NSNumber numberWithFloat:currentPosition]];
|
||||
return time;
|
||||
}
|
||||
|
||||
@dynamic lengthText;
|
||||
- (NSString *)lengthText
|
||||
{
|
||||
- (NSString *)lengthText {
|
||||
SecondsFormatter *secondsFormatter = [[SecondsFormatter alloc] init];
|
||||
NSString *time = [secondsFormatter stringForObjectValue:[self length]];
|
||||
return time;
|
||||
|
@ -423,15 +366,13 @@
|
|||
@synthesize albumArtInternal;
|
||||
|
||||
@dynamic albumArt;
|
||||
- (NSImage *)albumArt
|
||||
{
|
||||
if (!albumArtInternal) return nil;
|
||||
- (NSImage *)albumArt {
|
||||
if(!albumArtInternal) return nil;
|
||||
|
||||
NSString *imageCacheTag = [NSString stringWithFormat:@"%@-%@-%@-%@", album, artist, genre, year];
|
||||
NSImage *image = [NSImage imageNamed:imageCacheTag];
|
||||
|
||||
if (image == nil)
|
||||
{
|
||||
if(image == nil) {
|
||||
image = [[NSImage alloc] initWithData:albumArtInternal];
|
||||
[image setName:imageCacheTag];
|
||||
}
|
||||
|
@ -439,52 +380,39 @@
|
|||
return image;
|
||||
}
|
||||
|
||||
- (void)setAlbumArt:(id)data
|
||||
{
|
||||
if ([data isKindOfClass:[NSData class]])
|
||||
{
|
||||
- (void)setAlbumArt:(id)data {
|
||||
if([data isKindOfClass:[NSData class]]) {
|
||||
[self setAlbumArtInternal:data];
|
||||
}
|
||||
}
|
||||
|
||||
@dynamic length;
|
||||
- (NSNumber *)length
|
||||
{
|
||||
return [NSNumber numberWithDouble:(self.metadataLoaded)?((double)self.totalFrames / self.sampleRate):0.0];
|
||||
- (NSNumber *)length {
|
||||
return [NSNumber numberWithDouble:(self.metadataLoaded) ? ((double)self.totalFrames / self.sampleRate) : 0.0];
|
||||
}
|
||||
|
||||
@dynamic path;
|
||||
- (NSString *)path
|
||||
{
|
||||
if ([self.URL isFileURL])
|
||||
- (NSString *)path {
|
||||
if([self.URL isFileURL])
|
||||
return [[self.URL path] stringByAbbreviatingWithTildeInPath];
|
||||
else
|
||||
return [self.URL absoluteString];
|
||||
}
|
||||
|
||||
@dynamic filename;
|
||||
- (NSString *)filename
|
||||
{
|
||||
- (NSString *)filename {
|
||||
return [[self.URL path] lastPathComponent];
|
||||
}
|
||||
|
||||
@dynamic status;
|
||||
- (NSString *)status
|
||||
{
|
||||
if (self.stopAfter)
|
||||
{
|
||||
- (NSString *)status {
|
||||
if(self.stopAfter) {
|
||||
return @"stopAfter";
|
||||
}
|
||||
else if (self.current)
|
||||
{
|
||||
} else if(self.current) {
|
||||
return @"playing";
|
||||
}
|
||||
else if (self.queued)
|
||||
{
|
||||
} else if(self.queued) {
|
||||
return @"queued";
|
||||
}
|
||||
else if (self.error)
|
||||
{
|
||||
} else if(self.error) {
|
||||
return @"error";
|
||||
}
|
||||
|
||||
|
@ -492,37 +420,25 @@
|
|||
}
|
||||
|
||||
@dynamic statusMessage;
|
||||
- (NSString *)statusMessage
|
||||
{
|
||||
if (self.stopAfter)
|
||||
{
|
||||
- (NSString *)statusMessage {
|
||||
if(self.stopAfter) {
|
||||
return @"Stopping once finished...";
|
||||
}
|
||||
else if (self.current)
|
||||
{
|
||||
} else if(self.current) {
|
||||
return @"Playing...";
|
||||
}
|
||||
else if (self.queued)
|
||||
{
|
||||
} else if(self.queued) {
|
||||
return [NSString stringWithFormat:@"Queued: %li", self.queuePosition + 1];
|
||||
}
|
||||
else if (self.error)
|
||||
{
|
||||
} else if(self.error) {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setMetadata:(NSDictionary *)metadata
|
||||
{
|
||||
if (metadata == nil)
|
||||
{
|
||||
- (void)setMetadata:(NSDictionary *)metadata {
|
||||
if(metadata == nil) {
|
||||
self.error = YES;
|
||||
self.errorMessage = @"Unable to retrieve metadata.";
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
[self setValuesForKeysWithDictionary:metadata];
|
||||
}
|
||||
|
||||
|
@ -532,7 +448,7 @@
|
|||
- (id)copyWithZone:(NSZone *)zone {
|
||||
PlaylistEntry *pe = [[[self class] allocWithZone:zone] init];
|
||||
|
||||
if (pe) {
|
||||
if(pe) {
|
||||
pe->index = index;
|
||||
pe->shuffleIndex = shuffleIndex;
|
||||
pe->dbIndex = dbIndex;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue