Compare commits
No commits in common. "master" and "nbandeq" have entirely different histories.
|
|
@ -230,7 +230,7 @@
|
||||||
A1115929188D686000641365 /* Project object */ = {
|
A1115929188D686000641365 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 0710;
|
LastUpgradeCheck = 0500;
|
||||||
ORGANIZATIONNAME = "Thong Nguyen";
|
ORGANIZATIONNAME = "Thong Nguyen";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
A111594B188D686000641365 = {
|
A111594B188D686000641365 = {
|
||||||
|
|
@ -332,6 +332,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
|
@ -346,7 +347,6 @@
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
|
@ -366,7 +366,7 @@
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||||
);
|
);
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
|
|
@ -377,6 +377,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
|
@ -404,7 +405,7 @@
|
||||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||||
);
|
);
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
TARGETED_DEVICE_FAMILY = "1,2";
|
TARGETED_DEVICE_FAMILY = "1,2";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
|
|
@ -414,16 +415,15 @@
|
||||||
A111595E188D686000641365 /* Debug */ = {
|
A111595E188D686000641365 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
||||||
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
|
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
WRAPPER_EXTENSION = app;
|
WRAPPER_EXTENSION = app;
|
||||||
};
|
};
|
||||||
|
|
@ -432,6 +432,7 @@
|
||||||
A111595F188D686000641365 /* Release */ = {
|
A111595F188D686000641365 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
|
|
@ -440,7 +441,6 @@
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
WRAPPER_EXTENSION = app;
|
WRAPPER_EXTENSION = app;
|
||||||
};
|
};
|
||||||
|
|
@ -449,6 +449,7 @@
|
||||||
A1115961188D686000641365 /* Debug */ = {
|
A1115961188D686000641365 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||||
|
|
@ -462,7 +463,6 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
|
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||||
WRAPPER_EXTENSION = xctest;
|
WRAPPER_EXTENSION = xctest;
|
||||||
|
|
@ -472,6 +472,7 @@
|
||||||
A1115962188D686000641365 /* Release */ = {
|
A1115962188D686000641365 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||||
|
|
@ -481,7 +482,6 @@
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
||||||
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
|
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||||
WRAPPER_EXTENSION = xctest;
|
WRAPPER_EXTENSION = xctest;
|
||||||
|
|
|
||||||
|
|
@ -26,30 +26,26 @@
|
||||||
NSError* error;
|
NSError* error;
|
||||||
|
|
||||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
|
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
|
||||||
[[AVAudioSession sharedInstance] setActive:YES error:&error];
|
|
||||||
|
|
||||||
Float32 bufferLength = 0.1;
|
|
||||||
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(bufferLength), &bufferLength);
|
|
||||||
|
|
||||||
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
|
||||||
self.window.rootViewController = [[UIViewController alloc] init];
|
|
||||||
|
|
||||||
self.window.backgroundColor = [UIColor whiteColor];
|
self.window.backgroundColor = [UIColor whiteColor];
|
||||||
|
|
||||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
|
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
|
||||||
audioPlayer.meteringEnabled = YES;
|
audioPlayer.meteringEnabled = YES;
|
||||||
audioPlayer.volume = 1;
|
audioPlayer.volume = 0.1;
|
||||||
|
|
||||||
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
|
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds];
|
||||||
|
|
||||||
audioPlayerView.delegate = self;
|
audioPlayerView.delegate = self;
|
||||||
|
audioPlayerView.audioPlayer = audioPlayer;
|
||||||
|
|
||||||
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
||||||
[self becomeFirstResponder];
|
[self becomeFirstResponder];
|
||||||
|
|
||||||
|
[self.window addSubview:audioPlayerView];
|
||||||
|
|
||||||
[self.window makeKeyAndVisible];
|
[self.window makeKeyAndVisible];
|
||||||
|
|
||||||
[self.window.rootViewController.view addSubview:audioPlayerView];
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
@ -61,22 +57,13 @@
|
||||||
|
|
||||||
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView
|
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView
|
||||||
{
|
{
|
||||||
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||||
|
|
||||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
||||||
|
|
||||||
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) audioPlayerViewPlayFromIcecastSelected:(AudioPlayerView *)audioPlayerView
|
|
||||||
{
|
|
||||||
NSURL* url = [NSURL URLWithString:@"http://shoutmedia.abc.net.au:10326"];
|
|
||||||
|
|
||||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
|
||||||
|
|
||||||
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView
|
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView
|
||||||
{
|
{
|
||||||
NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"];
|
NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"];
|
||||||
|
|
@ -99,7 +86,7 @@
|
||||||
|
|
||||||
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView
|
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView
|
||||||
{
|
{
|
||||||
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/perfectly.wav"];
|
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/perfectly.wav"];
|
||||||
|
|
||||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@
|
||||||
|
|
||||||
@protocol AudioPlayerViewDelegate<NSObject>
|
@protocol AudioPlayerViewDelegate<NSObject>
|
||||||
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
|
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
|
||||||
-(void) audioPlayerViewPlayFromIcecastSelected:(AudioPlayerView*)audioPlayerView;
|
|
||||||
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView;
|
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView;
|
||||||
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView;
|
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView;
|
||||||
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView;
|
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView;
|
||||||
|
|
@ -52,13 +51,11 @@
|
||||||
UILabel* label;
|
UILabel* label;
|
||||||
UILabel* statusLabel;
|
UILabel* statusLabel;
|
||||||
UISlider* slider;
|
UISlider* slider;
|
||||||
UISwitch* enableEqSwitch;
|
|
||||||
UISwitch* repeatSwitch;
|
UISwitch* repeatSwitch;
|
||||||
UIButton* muteButton;
|
UIButton* muteButton;
|
||||||
UIButton* playButton;
|
UIButton* playButton;
|
||||||
UIButton* stopButton;
|
UIButton* stopButton;
|
||||||
UIButton* playFromHTTPButton;
|
UIButton* playFromHTTPButton;
|
||||||
UIButton* playFromIcecastButton;
|
|
||||||
UIButton* queueShortFileButton;
|
UIButton* queueShortFileButton;
|
||||||
UIButton* queuePcmWaveFileFromHTTPButton;
|
UIButton* queuePcmWaveFileFromHTTPButton;
|
||||||
UIButton* playFromLocalFileButton;
|
UIButton* playFromLocalFileButton;
|
||||||
|
|
@ -68,6 +65,4 @@
|
||||||
@property (readwrite, retain) STKAudioPlayer* audioPlayer;
|
@property (readwrite, retain) STKAudioPlayer* audioPlayer;
|
||||||
@property (readwrite, unsafe_unretained) id<AudioPlayerViewDelegate> delegate;
|
@property (readwrite, unsafe_unretained) id<AudioPlayerViewDelegate> delegate;
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayer;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -47,75 +47,63 @@
|
||||||
@implementation AudioPlayerView
|
@implementation AudioPlayerView
|
||||||
@synthesize audioPlayer, delegate;
|
@synthesize audioPlayer, delegate;
|
||||||
|
|
||||||
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayerIn
|
- (id)initWithFrame:(CGRect)frame
|
||||||
{
|
{
|
||||||
self = [super initWithFrame:frame];
|
self = [super initWithFrame:frame];
|
||||||
|
|
||||||
if (self)
|
if (self)
|
||||||
{
|
{
|
||||||
self.audioPlayer = audioPlayerIn;
|
|
||||||
|
|
||||||
CGSize size = CGSizeMake(220, 50);
|
CGSize size = CGSizeMake(220, 50);
|
||||||
|
|
||||||
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
playFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
|
playFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
|
||||||
[playFromHTTPButton addTarget:self action:@selector(playFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
[playFromHTTPButton addTarget:self action:@selector(playFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[playFromHTTPButton setTitle:@"Play from HTTP" forState:UIControlStateNormal];
|
[playFromHTTPButton setTitle:@"Play from HTTP" forState:UIControlStateNormal];
|
||||||
|
|
||||||
playFromIcecastButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
|
||||||
playFromIcecastButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 35, size.width, size.height);
|
|
||||||
[playFromIcecastButton addTarget:self action:@selector(playFromIcecasButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
|
||||||
[playFromIcecastButton setTitle:@"Play from Icecast" forState:UIControlStateNormal];
|
|
||||||
|
|
||||||
playFromLocalFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
playFromLocalFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
playFromLocalFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 70, size.width, size.height);
|
playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 50, size.width, size.height);
|
||||||
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
|
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
|
||||||
|
|
||||||
queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
queueShortFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 105, size.width, size.height);
|
queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 100, size.width, size.height);
|
||||||
[queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
[queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
|
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
|
||||||
|
|
||||||
queuePcmWaveFileFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
queuePcmWaveFileFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 140, size.width, size.height);
|
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 150, size.width, size.height);
|
||||||
[queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
[queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal];
|
[queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal];
|
||||||
|
|
||||||
size = CGSizeMake(90, 40);
|
size = CGSizeMake(90, 40);
|
||||||
|
|
||||||
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
playButton.frame = CGRectMake(30, 400, size.width, size.height);
|
playButton.frame = CGRectMake(30, 380, size.width, size.height);
|
||||||
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||||
|
|
||||||
stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
stopButton.frame = CGRectMake((frame.size.width - size.width) - 30, 400, size.width, size.height);
|
stopButton.frame = CGRectMake((320 - size.width) - 30, 380, size.width, size.height);
|
||||||
[stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
[stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[stopButton setTitle:@"Stop" forState:UIControlStateNormal];
|
[stopButton setTitle:@"Stop" forState:UIControlStateNormal];
|
||||||
|
|
||||||
muteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
muteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||||
muteButton.frame = CGRectMake((frame.size.width - size.width) - 30, 430, size.width, size.height);
|
muteButton.frame = CGRectMake((320 - size.width) - 30, 410, size.width, size.height);
|
||||||
[muteButton addTarget:self action:@selector(muteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
[muteButton addTarget:self action:@selector(muteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||||
[muteButton setTitle:@"Mute" forState:UIControlStateNormal];
|
[muteButton setTitle:@"Mute" forState:UIControlStateNormal];
|
||||||
|
|
||||||
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, queuePcmWaveFileFromHTTPButton.frame.origin.y + queuePcmWaveFileFromHTTPButton.frame.size.height + 20, 20)];
|
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)];
|
||||||
slider.continuous = YES;
|
slider.continuous = YES;
|
||||||
[slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged];
|
[slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged];
|
||||||
|
|
||||||
size = CGSizeMake(80, 50);
|
size = CGSizeMake(80, 50);
|
||||||
|
|
||||||
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(30, frame.size.height * 0.15 + 180, size.width, size.height)];
|
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 180, size.width, size.height)];
|
||||||
|
|
||||||
enableEqSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(frame.size.width - size.width - 30, frame.size.height * 0.15 + 180, size.width, size.height)];
|
|
||||||
enableEqSwitch.on = audioPlayer.equalizerEnabled;
|
|
||||||
|
|
||||||
[enableEqSwitch addTarget:self action:@selector(onEnableEqSwitch) forControlEvents:UIControlEventAllTouchEvents];
|
|
||||||
|
|
||||||
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 40, frame.size.width, 25)];
|
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 10, frame.size.width, 25)];
|
||||||
|
|
||||||
label.textAlignment = NSTextAlignmentCenter;
|
label.textAlignment = NSTextAlignmentCenter;
|
||||||
|
|
||||||
statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 50, frame.size.width, 50)];
|
statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 8, frame.size.width, 50)];
|
||||||
|
|
||||||
statusLabel.textAlignment = NSTextAlignmentCenter;
|
statusLabel.textAlignment = NSTextAlignmentCenter;
|
||||||
|
|
||||||
|
|
@ -126,7 +114,6 @@
|
||||||
[self addSubview:slider];
|
[self addSubview:slider];
|
||||||
[self addSubview:playButton];
|
[self addSubview:playButton];
|
||||||
[self addSubview:playFromHTTPButton];
|
[self addSubview:playFromHTTPButton];
|
||||||
[self addSubview:playFromIcecastButton];
|
|
||||||
[self addSubview:playFromLocalFileButton];
|
[self addSubview:playFromLocalFileButton];
|
||||||
[self addSubview:queueShortFileButton];
|
[self addSubview:queueShortFileButton];
|
||||||
[self addSubview:queuePcmWaveFileFromHTTPButton];
|
[self addSubview:queuePcmWaveFileFromHTTPButton];
|
||||||
|
|
@ -136,7 +123,6 @@
|
||||||
[self addSubview:stopButton];
|
[self addSubview:stopButton];
|
||||||
[self addSubview:meter];
|
[self addSubview:meter];
|
||||||
[self addSubview:muteButton];
|
[self addSubview:muteButton];
|
||||||
[self addSubview:enableEqSwitch];
|
|
||||||
|
|
||||||
[self setupTimer];
|
[self setupTimer];
|
||||||
[self updateControls];
|
[self updateControls];
|
||||||
|
|
@ -145,11 +131,6 @@
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) onEnableEqSwitch
|
|
||||||
{
|
|
||||||
audioPlayer.equalizerEnabled = self->enableEqSwitch.on;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) sliderChanged
|
-(void) sliderChanged
|
||||||
{
|
{
|
||||||
if (!audioPlayer)
|
if (!audioPlayer)
|
||||||
|
|
@ -180,17 +161,6 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioPlayer.currentlyPlayingQueueItemId == nil)
|
|
||||||
{
|
|
||||||
slider.value = 0;
|
|
||||||
slider.minimumValue = 0;
|
|
||||||
slider.maximumValue = 0;
|
|
||||||
|
|
||||||
label.text = @"";
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audioPlayer.duration != 0)
|
if (audioPlayer.duration != 0)
|
||||||
{
|
{
|
||||||
slider.minimumValue = 0;
|
slider.minimumValue = 0;
|
||||||
|
|
@ -205,7 +175,7 @@
|
||||||
slider.minimumValue = 0;
|
slider.minimumValue = 0;
|
||||||
slider.maximumValue = 0;
|
slider.maximumValue = 0;
|
||||||
|
|
||||||
label.text = [NSString stringWithFormat:@"Live stream %@", [self formatTimeFromSeconds:audioPlayer.progress]];
|
label.text = @"";
|
||||||
}
|
}
|
||||||
|
|
||||||
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
|
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
|
||||||
|
|
@ -220,11 +190,6 @@
|
||||||
[self.delegate audioPlayerViewPlayFromHTTPSelected:self];
|
[self.delegate audioPlayerViewPlayFromHTTPSelected:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) playFromIcecasButtonTouched
|
|
||||||
{
|
|
||||||
[self.delegate audioPlayerViewPlayFromIcecastSelected:self];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) playFromLocalFileButtonTouched
|
-(void) playFromLocalFileButtonTouched
|
||||||
{
|
{
|
||||||
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
|
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,16 @@
|
||||||
"size" : "29x29",
|
"size" : "29x29",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "29x29",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "40x40",
|
"size" : "40x40",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "40x40",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"size" : "60x60",
|
"size" : "60x60",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"size" : "60x60",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"idiom" : "ipad",
|
"idiom" : "ipad",
|
||||||
"size" : "29x29",
|
"size" : "29x29",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +1,5 @@
|
||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
|
||||||
"extent" : "full-screen",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"subtype" : "736h",
|
|
||||||
"filename" : "TX6sV.png",
|
|
||||||
"minimum-system-version" : "8.0",
|
|
||||||
"orientation" : "portrait",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"orientation" : "landscape",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"extent" : "full-screen",
|
|
||||||
"minimum-system-version" : "8.0",
|
|
||||||
"subtype" : "736h",
|
|
||||||
"scale" : "3x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"extent" : "full-screen",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"subtype" : "667h",
|
|
||||||
"filename" : "dBEHd.png",
|
|
||||||
"minimum-system-version" : "8.0",
|
|
||||||
"orientation" : "portrait",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"orientation" : "portrait",
|
"orientation" : "portrait",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
|
|
@ -34,12 +8,11 @@
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"extent" : "full-screen",
|
"orientation" : "portrait",
|
||||||
"idiom" : "iphone",
|
"idiom" : "iphone",
|
||||||
"subtype" : "retina4",
|
"subtype" : "retina4",
|
||||||
"filename" : "TX6sV-2.png",
|
"extent" : "full-screen",
|
||||||
"minimum-system-version" : "7.0",
|
"minimum-system-version" : "7.0",
|
||||||
"orientation" : "portrait",
|
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -69,26 +42,6 @@
|
||||||
"extent" : "full-screen",
|
"extent" : "full-screen",
|
||||||
"minimum-system-version" : "7.0",
|
"minimum-system-version" : "7.0",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
|
||||||
{
|
|
||||||
"orientation" : "portrait",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"extent" : "full-screen",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"orientation" : "portrait",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"extent" : "full-screen",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"orientation" : "portrait",
|
|
||||||
"idiom" : "iphone",
|
|
||||||
"filename" : "TX6sV-1.png",
|
|
||||||
"extent" : "full-screen",
|
|
||||||
"subtype" : "retina4",
|
|
||||||
"scale" : "2x"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 8.3 KiB |
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
|
|
|
||||||
|
|
@ -40,25 +40,17 @@
|
||||||
[[self.window contentView] addSubview:playFromHTTPButton];
|
[[self.window contentView] addSubview:playFromHTTPButton];
|
||||||
[[self.window contentView] addSubview:meter];
|
[[self.window contentView] addSubview:meter];
|
||||||
|
|
||||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} } ];
|
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = YES, .equalizerBandFrequencies = {0, 50, 100, 200, 400, 800, 1600, 2600, 16000} } ];
|
||||||
audioPlayer.delegate = self;
|
audioPlayer.delegate = self;
|
||||||
audioPlayer.meteringEnabled = YES;
|
audioPlayer.meteringEnabled = YES;
|
||||||
audioPlayer.volume = 0.1;
|
audioPlayer.volume = 0.1;
|
||||||
|
|
||||||
[self performSelector:@selector(test) withObject:nil afterDelay:4];
|
|
||||||
[self performSelector:@selector(test) withObject:nil afterDelay:8];
|
|
||||||
|
|
||||||
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
|
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) test
|
|
||||||
{
|
|
||||||
audioPlayer.equalizerEnabled = !audioPlayer.equalizerEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) playFromHTTP
|
-(void) playFromHTTP
|
||||||
{
|
{
|
||||||
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) tick:(NSTimer*)timer
|
-(void) tick:(NSTimer*)timer
|
||||||
|
|
@ -72,7 +64,7 @@
|
||||||
|
|
||||||
CGFloat meterWidth = 0;
|
CGFloat meterWidth = 0;
|
||||||
|
|
||||||
if (audioPlayer.currentlyPlayingQueueItemId != nil)
|
if (audioPlayer.duration != 0)
|
||||||
{
|
{
|
||||||
slider.minValue = 0;
|
slider.minValue = 0;
|
||||||
slider.maxValue = audioPlayer.duration;
|
slider.maxValue = audioPlayer.duration;
|
||||||
|
|
|
||||||
6
LICENSE
6
LICENSE
|
|
@ -4,7 +4,7 @@
|
||||||
Inspired by Matt Gallagher's AudioStreamer:
|
Inspired by Matt Gallagher's AudioStreamer:
|
||||||
https://github.com/mattgallagher/AudioStreamer
|
https://github.com/mattgallagher/AudioStreamer
|
||||||
|
|
||||||
Copyright (c) 2015 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
modification, are permitted provided that the following conditions are met:
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
@ -15,12 +15,12 @@
|
||||||
documentation and/or other materials provided with the distribution.
|
documentation and/or other materials provided with the distribution.
|
||||||
3. All advertising materials mentioning features or use of this software
|
3. All advertising materials mentioning features or use of this software
|
||||||
must display the following acknowledgement:
|
must display the following acknowledgement:
|
||||||
This product includes software developed by Thong Nguyen.
|
This product includes software developed by the <organization>.
|
||||||
4. Neither the name of the <organization> nor the
|
4. Neither the name of the <organization> nor the
|
||||||
names of its contributors may be used to endorse or promote products
|
names of its contributors may be used to endorse or promote products
|
||||||
derived from this software without specific prior written permission.
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THONG NGUYEN ''AS IS'' AND ANY
|
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
|
||||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||||
|
|
|
||||||
12
README.md
12
README.md
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
StreamingKit (formally Audjustable) is an audio playback and streaming library for iOS and Mac OSX. StreamingKit uses CoreAudio to decompress and playback audio (using hardware or software codecs) whilst providing a clean and simple object-oriented API.
|
StreamingKit (formally Audjustable) is an audio playback and streaming library for iOS and Mac OSX. StreamingKit uses CoreAudio to decompress and playback audio (using hardware or software codecs) whilst providing a clean and simple object-oriented API.
|
||||||
|
|
||||||
The primary motivation of this project was to decouple the input data sources from the actual player logic in order to allow advanced customizable input handling such as HTTP progressive download based streaming, encryption/decryption, auto-recovery, dynamic-buffering. StreamingKit is the only streaming and playback library that supports dead-easy [gapless playback](https://github.com/tumtumtum/StreamingKit/wiki/Gapless-playback) between audio files of differing formats.
|
The primary motivation of this project was to decouple the input data sources from the actual player logic in order to allow advanced customizable input handling such as HTTP streaming, encryption/decryption, auto-recovery, dynamic-buffering. StreamingKit is the only streaming and playback library that supports dead-easy [gapless playback](https://github.com/tumtumtum/StreamingKit/wiki/Gapless-playback) between audio files of differing formats.
|
||||||
|
|
||||||
## Main Features
|
## Main Features
|
||||||
|
|
||||||
|
|
@ -11,13 +11,11 @@ The primary motivation of this project was to decouple the input data sources fr
|
||||||
* Easy to read source.
|
* Easy to read source.
|
||||||
* Carefully multi-threaded to provide a responsive API that won't block your UI thread nor starve the audio buffers.
|
* Carefully multi-threaded to provide a responsive API that won't block your UI thread nor starve the audio buffers.
|
||||||
* Buffered and gapless playback between all format types.
|
* Buffered and gapless playback between all format types.
|
||||||
* Easy to implement audio data sources (Local, HTTP, AutoRecoveringHTTP DataSources are provided).
|
* Easy to implement audio data sources (Local, HTTP, AutoRecoveryingHTTP DataSources are provided).
|
||||||
* Easy to extend DataSource to support adaptive buffering, encryption, etc.
|
* Easy to extend DataSource to support adaptive buffering, encryption, etc.
|
||||||
* Optimised for low CPU/battery usage (0% - 1% CPU usage when streaming).
|
* Optimised for low CPU/battery usage (0% - 1% CPU usage when streaming).
|
||||||
* Optimised for linear data sources. Random access sources are required only for seeking.
|
* Optimised for linear data sources. Random access sources are required only for seeking.
|
||||||
* StreamingKit 0.2.0 uses the AudioUnit API rather than the slower AudioQueues API which allows real-time interception of the raw PCM data for features such as level metering, EQ, etc.
|
* StreamingKit 0.2.0 uses the AudioUnit API rather than the slower AudioQueues API which allows real-time interception of the raw PCM data for features such as level metering, EQ, etc.
|
||||||
* Power metering
|
|
||||||
* Inbuilt equalizer/EQ (iOS 5.0 and above, OSX 10.9 Mavericks and above) with support for dynamically changing/enabling/disabling EQ while playing.
|
|
||||||
* Example apps for iOS and Mac OSX provided.
|
* Example apps for iOS and Mac OSX provided.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
@ -34,7 +32,7 @@ There are two main classes. The `STKDataSource` class which is the abstract bas
|
||||||
```objective-c
|
```objective-c
|
||||||
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
||||||
|
|
||||||
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||||
```
|
```
|
||||||
|
|
||||||
### Gapless playback
|
### Gapless playback
|
||||||
|
|
@ -42,8 +40,8 @@ STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
||||||
```objective-c
|
```objective-c
|
||||||
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
||||||
|
|
||||||
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
[audioPlayer queue:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||||
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/airplane.aac"];
|
[audioPlayer queue:@"http://fs.bloom.fm/oss/audiosamples/airplane.aac"];
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
s.name = "StreamingKit"
|
s.name = "StreamingKit"
|
||||||
s.version = "0.1.29"
|
s.version = "0.1.19"
|
||||||
s.summary = "A fast and extensible audio streamer for iOS and OSX with support for gapless playback and custom (non-HTTP) sources."
|
s.summary = "A fast and extensible audio streamer for iOS and OSX with support for gapless playback and custom (non-HTTP) sources."
|
||||||
s.homepage = "https://github.com/tumtumtum/StreamingKit/"
|
s.homepage = "https://github.com/tumtumtum/StreamingKit/"
|
||||||
s.license = 'MIT'
|
s.license = 'MIT'
|
||||||
|
|
|
||||||
|
|
@ -10,29 +10,29 @@
|
||||||
<string>StreamingKit</string>
|
<string>StreamingKit</string>
|
||||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
|
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
|
||||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>IDESourceControlProjectPath</key>
|
<key>IDESourceControlProjectPath</key>
|
||||||
<string>StreamingKit.xcworkspace</string>
|
<string>StreamingKit.xcworkspace</string>
|
||||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
|
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
|
||||||
<string>..</string>
|
<string>..</string>
|
||||||
</dict>
|
</dict>
|
||||||
<key>IDESourceControlProjectURL</key>
|
<key>IDESourceControlProjectURL</key>
|
||||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||||
<key>IDESourceControlProjectVersion</key>
|
<key>IDESourceControlProjectVersion</key>
|
||||||
<integer>111</integer>
|
<integer>110</integer>
|
||||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||||
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
|
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
|
||||||
<key>IDESourceControlProjectWCConfigurations</key>
|
<key>IDESourceControlProjectWCConfigurations</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||||
<string>public.vcs.git</string>
|
<string>public.vcs.git</string>
|
||||||
<key>IDESourceControlWCCIdentifierKey</key>
|
<key>IDESourceControlWCCIdentifierKey</key>
|
||||||
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
|
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
|
||||||
<key>IDESourceControlWCCName</key>
|
<key>IDESourceControlWCCName</key>
|
||||||
<string>StreamingKit</string>
|
<string>StreamingKit</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,6 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F7188D5E550010896F /* STKDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD81A1140E4005675A0 /* STKLocalFileDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
5B949CD91A1140E4005675A0 /* STKQueueEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = A1BF65D0189A6582004DD08C /* STKQueueEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
|
||||||
95F80F4E1C68EE2300DB24B3 /* STKFloatConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 95F80F4C1C68EE2300DB24B3 /* STKFloatConverter.h */; };
|
|
||||||
95F80F4F1C68EE2300DB24B3 /* STKFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 95F80F4D1C68EE2300DB24B3 /* STKFloatConverter.m */; };
|
|
||||||
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; };
|
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; };
|
||||||
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; };
|
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; };
|
||||||
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
|
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
|
||||||
|
|
@ -93,8 +83,6 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
95F80F4C1C68EE2300DB24B3 /* STKFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKFloatConverter.h; sourceTree = "<group>"; };
|
|
||||||
95F80F4D1C68EE2300DB24B3 /* STKFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKFloatConverter.m; sourceTree = "<group>"; };
|
|
||||||
A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKitMac.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKitMac.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
A1A4996A189E744400E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
|
A1A4996A189E744400E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
|
||||||
A1A4996D189E744500E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
A1A4996D189E744500E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||||
|
|
@ -274,8 +262,6 @@
|
||||||
A1E7C4CD188D57F50010896F /* StreamingKit */ = {
|
A1E7C4CD188D57F50010896F /* StreamingKit */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
95F80F4C1C68EE2300DB24B3 /* STKFloatConverter.h */,
|
|
||||||
95F80F4D1C68EE2300DB24B3 /* STKFloatConverter.m */,
|
|
||||||
A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */,
|
A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */,
|
||||||
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */,
|
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */,
|
||||||
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */,
|
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */,
|
||||||
|
|
@ -328,22 +314,6 @@
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXHeadersBuildPhase section */
|
/* Begin PBXHeadersBuildPhase section */
|
||||||
5B949CD11A1140CF005675A0 /* Headers */ = {
|
|
||||||
isa = PBXHeadersBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */,
|
|
||||||
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */,
|
|
||||||
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */,
|
|
||||||
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */,
|
|
||||||
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */,
|
|
||||||
95F80F4E1C68EE2300DB24B3 /* STKFloatConverter.h in Headers */,
|
|
||||||
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */,
|
|
||||||
5B949CD81A1140E4005675A0 /* STKLocalFileDataSource.h in Headers */,
|
|
||||||
5B949CD91A1140E4005675A0 /* STKQueueEntry.h in Headers */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
A1A49967189E744400E2A2E2 /* Headers */ = {
|
A1A49967189E744400E2A2E2 /* Headers */ = {
|
||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
|
|
@ -396,7 +366,6 @@
|
||||||
A1E7C4C4188D57F50010896F /* Sources */,
|
A1E7C4C4188D57F50010896F /* Sources */,
|
||||||
A1E7C4C5188D57F50010896F /* Frameworks */,
|
A1E7C4C5188D57F50010896F /* Frameworks */,
|
||||||
A1E7C4C6188D57F50010896F /* CopyFiles */,
|
A1E7C4C6188D57F50010896F /* CopyFiles */,
|
||||||
5B949CD11A1140CF005675A0 /* Headers */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
|
@ -432,7 +401,7 @@
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
CLASSPREFIX = STK;
|
CLASSPREFIX = STK;
|
||||||
LastUpgradeCheck = 0710;
|
LastUpgradeCheck = 0500;
|
||||||
ORGANIZATIONNAME = "Thong Nguyen";
|
ORGANIZATIONNAME = "Thong Nguyen";
|
||||||
};
|
};
|
||||||
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
|
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
|
||||||
|
|
@ -532,7 +501,6 @@
|
||||||
A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */,
|
A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */,
|
||||||
A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */,
|
A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */,
|
||||||
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */,
|
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */,
|
||||||
95F80F4F1C68EE2300DB24B3 /* STKFloatConverter.m in Sources */,
|
|
||||||
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */,
|
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */,
|
||||||
A1E7C502188D5E550010896F /* STKDataSource.m in Sources */,
|
A1E7C502188D5E550010896F /* STKDataSource.m in Sources */,
|
||||||
A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */,
|
A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */,
|
||||||
|
|
@ -586,7 +554,6 @@
|
||||||
A1A49988189E744500E2A2E2 /* Debug */ = {
|
A1A49988189E744500E2A2E2 /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||||
|
|
@ -600,7 +567,6 @@
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = "";
|
MACOSX_DEPLOYMENT_TARGET = "";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
|
@ -608,7 +574,6 @@
|
||||||
A1A49989189E744500E2A2E2 /* Release */ = {
|
A1A49989189E744500E2A2E2 /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
@ -619,7 +584,6 @@
|
||||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||||
MACOSX_DEPLOYMENT_TARGET = "";
|
MACOSX_DEPLOYMENT_TARGET = "";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
@ -641,7 +605,6 @@
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
|
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
WRAPPER_EXTENSION = xctest;
|
WRAPPER_EXTENSION = xctest;
|
||||||
|
|
@ -662,7 +625,6 @@
|
||||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||||
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
|
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
WRAPPER_EXTENSION = xctest;
|
WRAPPER_EXTENSION = xctest;
|
||||||
|
|
@ -673,6 +635,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
|
@ -686,7 +649,6 @@
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
|
@ -701,7 +663,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
};
|
};
|
||||||
|
|
@ -711,6 +673,7 @@
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
|
|
@ -732,7 +695,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
|
|
@ -741,6 +704,7 @@
|
||||||
A1E7C4EC188D57F60010896F /* Debug */ = {
|
A1E7C4EC188D57F60010896F /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
DSTROOT = /tmp/StreamingKit.dst;
|
DSTROOT = /tmp/StreamingKit.dst;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
@ -750,7 +714,6 @@
|
||||||
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
|
|
@ -758,6 +721,7 @@
|
||||||
A1E7C4ED188D57F60010896F /* Release */ = {
|
A1E7C4ED188D57F60010896F /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
DSTROOT = /tmp/StreamingKit.dst;
|
DSTROOT = /tmp/StreamingKit.dst;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
@ -767,7 +731,6 @@
|
||||||
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
|
||||||
SKIP_INSTALL = YES;
|
SKIP_INSTALL = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|
@ -775,6 +738,7 @@
|
||||||
A1E7C4EF188D57F60010896F /* Debug */ = {
|
A1E7C4EF188D57F60010896F /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
@ -787,7 +751,6 @@
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
);
|
);
|
||||||
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
|
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
WRAPPER_EXTENSION = xctest;
|
WRAPPER_EXTENSION = xctest;
|
||||||
};
|
};
|
||||||
|
|
@ -796,6 +759,7 @@
|
||||||
A1E7C4F0188D57F60010896F /* Release */ = {
|
A1E7C4F0188D57F60010896F /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
|
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
|
@ -804,7 +768,6 @@
|
||||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||||
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
||||||
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
|
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
WRAPPER_EXTENSION = xctest;
|
WRAPPER_EXTENSION = xctest;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
|
|
@ -8,14 +8,10 @@
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface NSMutableArray (STKAudioPlayer)
|
@interface NSMutableArray (STKAudioPlayer)
|
||||||
-(void) enqueue:(id)obj;
|
-(void) enqueue:(id)obj;
|
||||||
-(void) skipQueue:(id)obj;
|
-(void) skipQueue:(id)obj;
|
||||||
-(void) skipQueueWithQueue:(NSMutableArray*)queue;
|
-(void) skipQueueWithQueue:(NSMutableArray*)queue;
|
||||||
-(nullable id) dequeue;
|
-(id) dequeue;
|
||||||
-(nullable id) peek;
|
-(id) peek;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -38,15 +38,13 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <pthread.h>
|
#import <pthread.h>
|
||||||
#import "STKDataSource.h"
|
#import "STKDataSource.h"
|
||||||
#import <AudioToolbox/AudioToolbox.h>
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
#include "UIKit/UIApplication.h"
|
#include "UIKit/UIApplication.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
typedef enum
|
||||||
|
|
||||||
typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
|
|
||||||
{
|
{
|
||||||
STKAudioPlayerStateReady,
|
STKAudioPlayerStateReady,
|
||||||
STKAudioPlayerStateRunning = 1,
|
STKAudioPlayerStateRunning = 1,
|
||||||
|
|
@ -56,19 +54,19 @@ typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
|
||||||
STKAudioPlayerStateStopped = (1 << 4),
|
STKAudioPlayerStateStopped = (1 << 4),
|
||||||
STKAudioPlayerStateError = (1 << 5),
|
STKAudioPlayerStateError = (1 << 5),
|
||||||
STKAudioPlayerStateDisposed = (1 << 6)
|
STKAudioPlayerStateDisposed = (1 << 6)
|
||||||
};
|
}
|
||||||
|
STKAudioPlayerState;
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, STKAudioPlayerStopReason)
|
typedef enum
|
||||||
{
|
{
|
||||||
STKAudioPlayerStopReasonNone = 0,
|
STKAudioPlayerStopReasonNone = 0,
|
||||||
STKAudioPlayerStopReasonEof,
|
STKAudioPlayerStopReasonEof,
|
||||||
STKAudioPlayerStopReasonUserAction,
|
STKAudioPlayerStopReasonUserAction,
|
||||||
STKAudioPlayerStopReasonPendingNext,
|
|
||||||
STKAudioPlayerStopReasonDisposed,
|
|
||||||
STKAudioPlayerStopReasonError = 0xffff
|
STKAudioPlayerStopReasonError = 0xffff
|
||||||
};
|
}
|
||||||
|
STKAudioPlayerStopReason;
|
||||||
|
|
||||||
typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode)
|
typedef enum
|
||||||
{
|
{
|
||||||
STKAudioPlayerErrorNone = 0,
|
STKAudioPlayerErrorNone = 0,
|
||||||
STKAudioPlayerErrorDataSource,
|
STKAudioPlayerErrorDataSource,
|
||||||
|
|
@ -77,13 +75,9 @@ typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode)
|
||||||
STKAudioPlayerErrorCodecError,
|
STKAudioPlayerErrorCodecError,
|
||||||
STKAudioPlayerErrorDataNotFound,
|
STKAudioPlayerErrorDataNotFound,
|
||||||
STKAudioPlayerErrorOther = 0xffff
|
STKAudioPlayerErrorOther = 0xffff
|
||||||
};
|
}
|
||||||
|
STKAudioPlayerErrorCode;
|
||||||
|
|
||||||
///
|
|
||||||
/// Options to initiailise the Audioplayer with.
|
|
||||||
/// By default if you set buffer size or seconds to 0, the non-zero default will be used
|
|
||||||
/// If you would like to disable the buffer option completely set to STK_DISABLE_BUFFER
|
|
||||||
///
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/// If YES then seeking a track will cause all pending items to be flushed from the queue
|
/// If YES then seeking a track will cause all pending items to be flushed from the queue
|
||||||
|
|
@ -95,19 +89,15 @@ typedef struct
|
||||||
/// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger.
|
/// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger.
|
||||||
UInt32 readBufferSize;
|
UInt32 readBufferSize;
|
||||||
/// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM)
|
/// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM)
|
||||||
float bufferSizeInSeconds;
|
UInt32 bufferSizeInSeconds;
|
||||||
/// Number of seconds of decompressed audio is required before playback first starts for each item (Default is 0.5 seconds. Must be larger than bufferSizeInSeconds)
|
/// Number of seconds of decompressed audio is required before playback first starts for each item (Default is 0.5 seconds. Must be larger than bufferSizeInSeconds)
|
||||||
Float32 secondsRequiredToStartPlaying;
|
Float32 secondsRequiredToStartPlaying;
|
||||||
/// Seconds after a seek is performed before data needs to come in (after which the state will change to playing/buffering)
|
|
||||||
Float32 gracePeriodAfterSeekInSeconds;
|
|
||||||
/// Number of seconds of decompressed audio required before playback resumes after a buffer underrun (Default is 5 seconds. Must be larger than bufferSizeinSeconds)
|
/// Number of seconds of decompressed audio required before playback resumes after a buffer underrun (Default is 5 seconds. Must be larger than bufferSizeinSeconds)
|
||||||
Float32 secondsRequiredToStartPlayingAfterBufferUnderun;
|
Float32 secondsRequiredToStartPlayingAfterBufferUnderun;
|
||||||
}
|
}
|
||||||
STKAudioPlayerOptions;
|
STKAudioPlayerOptions;
|
||||||
|
|
||||||
#define STK_DISABLE_BUFFER (0xffffffff)
|
typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames);
|
||||||
|
|
||||||
typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, float* frames);
|
|
||||||
|
|
||||||
@interface STKFrameFilterEntry : NSObject
|
@interface STKFrameFilterEntry : NSObject
|
||||||
@property (readonly) NSString* name;
|
@property (readonly) NSString* name;
|
||||||
|
|
@ -135,8 +125,6 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||||
/// Raised when items queued items are cleared (usually because of a call to play, setDataSource or stop)
|
/// Raised when items queued items are cleared (usually because of a call to play, setDataSource or stop)
|
||||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
|
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
|
||||||
|
|
||||||
-(void) plotGraphWithBuffer:(float*)buffer andLength:(UInt32)count;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
|
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
|
||||||
|
|
@ -152,16 +140,14 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||||
@property (readonly) double progress;
|
@property (readonly) double progress;
|
||||||
/// Enables or disables peak and average decibel meteting
|
/// Enables or disables peak and average decibel meteting
|
||||||
@property (readwrite) BOOL meteringEnabled;
|
@property (readwrite) BOOL meteringEnabled;
|
||||||
/// Enables or disables the EQ
|
|
||||||
@property (readwrite) BOOL equalizerEnabled;
|
|
||||||
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
|
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
|
||||||
@property (readonly, nullable) NSArray* frameFilters;
|
@property (readonly) NSArray* frameFilters;
|
||||||
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
|
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
|
||||||
@property (readonly) NSArray* pendingQueue;
|
@property (readonly) NSArray* pendingQueue;
|
||||||
/// The number of items pending to be played (includes buffering and upcoming items but does not include the current item)
|
/// The number of items pending to be played (includes buffering and upcoming items but does not include the current item)
|
||||||
@property (readonly) NSUInteger pendingQueueCount;
|
@property (readonly) NSUInteger pendingQueueCount;
|
||||||
/// Gets the most recently queued item that is still pending to play
|
/// Gets the most recently queued item that is still pending to play
|
||||||
@property (readonly, nullable) NSObject* mostRecentlyQueuedStillPendingItem;
|
@property (readonly) NSObject* mostRecentlyQueuedStillPendingItem;
|
||||||
/// Gets the current state of the player
|
/// Gets the current state of the player
|
||||||
@property (readwrite) STKAudioPlayerState state;
|
@property (readwrite) STKAudioPlayerState state;
|
||||||
/// Gets the options provided to the player on startup
|
/// Gets the options provided to the player on startup
|
||||||
|
|
@ -178,10 +164,10 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||||
+(STKDataSource*) dataSourceFromURL:(NSURL*)url;
|
+(STKDataSource*) dataSourceFromURL:(NSURL*)url;
|
||||||
|
|
||||||
/// Initializes a new STKAudioPlayer with the default options
|
/// Initializes a new STKAudioPlayer with the default options
|
||||||
-(instancetype) init;
|
-(id) init;
|
||||||
|
|
||||||
/// Initializes a new STKAudioPlayer with the given options
|
/// Initializes a new STKAudioPlayer with the given options
|
||||||
-(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn;
|
-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn;
|
||||||
|
|
||||||
/// Plays an item from the given URL string (all pending queued items are removed).
|
/// Plays an item from the given URL string (all pending queued items are removed).
|
||||||
/// The NSString is used as the queue item ID
|
/// The NSString is used as the queue item ID
|
||||||
|
|
@ -258,19 +244,17 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
|
||||||
|
|
||||||
/// Appends a frame filter with the given name and filter block just after the filter with the given name.
|
/// Appends a frame filter with the given name and filter block just after the filter with the given name.
|
||||||
/// If the given name is nil, the filter will be inserted at the beginning of the filter change
|
/// If the given name is nil, the filter will be inserted at the beginning of the filter change
|
||||||
-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(nullable NSString*)afterFilterWithName block:(STKFrameFilter)block;
|
-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block;
|
||||||
|
|
||||||
/// Reads the peak power in decibals for the given channel (0 or 1).
|
/// Reads the peak power in decibals for the given channel (0 or 1).
|
||||||
/// Return values are between -60 (low) and 0 (high).
|
/// Return values are between -60 (low) and 0 (high).
|
||||||
//-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||||
|
|
||||||
/// Reads the average power in decibals for the given channel (0 or 1)
|
/// Reads the average power in decibals for the given channel (0 or 1)
|
||||||
/// Return values are between -60 (low) and 0 (high).
|
/// Return values are between -60 (low) and 0 (high).
|
||||||
//-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||||
|
|
||||||
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
|
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
|
||||||
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
|
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -36,21 +36,10 @@
|
||||||
#import "STKHTTPDataSource.h"
|
#import "STKHTTPDataSource.h"
|
||||||
#import "STKDataSourceWrapper.h"
|
#import "STKDataSourceWrapper.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int watchdogPeriodSeconds;
|
|
||||||
int inactivePeriodBeforeReconnectSeconds;
|
|
||||||
}
|
|
||||||
STKAutoRecoveringHTTPDataSourceOptions;
|
|
||||||
|
|
||||||
@interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper
|
@interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper
|
||||||
|
|
||||||
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource;
|
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource;
|
||||||
|
|
||||||
@property (readonly) STKHTTPDataSource* innerDataSource;
|
@property (readonly) STKHTTPDataSource* innerDataSource;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
|
|
@ -38,38 +38,18 @@
|
||||||
#import <arpa/inet.h>
|
#import <arpa/inet.h>
|
||||||
#import <ifaddrs.h>
|
#import <ifaddrs.h>
|
||||||
#import <netdb.h>
|
#import <netdb.h>
|
||||||
#import "mach/mach_time.h"
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#import <SystemConfiguration/SystemConfiguration.h>
|
#import <SystemConfiguration/SystemConfiguration.h>
|
||||||
#import "STKAutoRecoveringHTTPDataSource.h"
|
#import "STKAutoRecoveringHTTPDataSource.h"
|
||||||
|
|
||||||
#define DEFAULT_WATCHDOG_PERIOD_SECONDS (8)
|
#define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (2)
|
||||||
#define DEFAULT_INACTIVE_PERIOD_BEFORE_RECONNECT_SECONDS (15)
|
|
||||||
|
|
||||||
static uint64_t GetTickCount(void)
|
|
||||||
{
|
|
||||||
static mach_timebase_info_data_t sTimebaseInfo;
|
|
||||||
uint64_t machTime = mach_absolute_time();
|
|
||||||
|
|
||||||
if (sTimebaseInfo.denom == 0 )
|
|
||||||
{
|
|
||||||
(void) mach_timebase_info(&sTimebaseInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t millis = ((machTime / 1000000) * sTimebaseInfo.numer) / sTimebaseInfo.denom;
|
|
||||||
|
|
||||||
return millis;
|
|
||||||
}
|
|
||||||
|
|
||||||
@interface STKAutoRecoveringHTTPDataSource()
|
@interface STKAutoRecoveringHTTPDataSource()
|
||||||
{
|
{
|
||||||
int serial;
|
int serial;
|
||||||
int waitSeconds;
|
int waitSeconds;
|
||||||
NSTimer* timeoutTimer;
|
|
||||||
BOOL waitingForNetwork;
|
BOOL waitingForNetwork;
|
||||||
uint64_t ticksWhenLastDataReceived;
|
|
||||||
SCNetworkReachabilityRef reachabilityRef;
|
SCNetworkReachabilityRef reachabilityRef;
|
||||||
STKAutoRecoveringHTTPDataSourceOptions options;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) reachabilityChanged;
|
-(void) reachabilityChanged;
|
||||||
|
|
@ -86,39 +66,19 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* options)
|
|
||||||
{
|
|
||||||
if (options->watchdogPeriodSeconds == 0)
|
|
||||||
{
|
|
||||||
options->watchdogPeriodSeconds = DEFAULT_WATCHDOG_PERIOD_SECONDS;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options->inactivePeriodBeforeReconnectSeconds == 0)
|
|
||||||
{
|
|
||||||
options->inactivePeriodBeforeReconnectSeconds = DEFAULT_INACTIVE_PERIOD_BEFORE_RECONNECT_SECONDS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation STKAutoRecoveringHTTPDataSource
|
@implementation STKAutoRecoveringHTTPDataSource
|
||||||
|
|
||||||
@dynamic innerDataSource;
|
|
||||||
|
|
||||||
-(STKHTTPDataSource*) innerHTTPDataSource
|
-(STKHTTPDataSource*) innerHTTPDataSource
|
||||||
{
|
{
|
||||||
return (STKHTTPDataSource*)self.innerDataSource;
|
return (STKHTTPDataSource*)self.innerDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(instancetype) initWithDataSource:(STKDataSource *)innerDataSource
|
-(id) initWithDataSource:(STKDataSource *)innerDataSource
|
||||||
{
|
{
|
||||||
return [self initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource];
|
return [self initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn
|
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn
|
||||||
{
|
|
||||||
return [self initWithHTTPDataSource:innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions){}];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions)optionsIn
|
|
||||||
{
|
{
|
||||||
if (self = [super initWithDataSource:innerDataSourceIn])
|
if (self = [super initWithDataSource:innerDataSourceIn])
|
||||||
{
|
{
|
||||||
|
|
@ -130,10 +90,6 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||||
zeroAddress.sin_family = AF_INET;
|
zeroAddress.sin_family = AF_INET;
|
||||||
|
|
||||||
PopulateOptionsWithDefault(&optionsIn);
|
|
||||||
|
|
||||||
self->options = optionsIn;
|
|
||||||
|
|
||||||
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
|
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,70 +117,14 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
[super registerForEvents:runLoop];
|
[super registerForEvents:runLoop];
|
||||||
[self startNotifierOnRunLoop:runLoop];
|
[self startNotifierOnRunLoop:runLoop];
|
||||||
|
|
||||||
if (timeoutTimer)
|
|
||||||
{
|
|
||||||
[timeoutTimer invalidate];
|
|
||||||
timeoutTimer = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
ticksWhenLastDataReceived = GetTickCount();
|
|
||||||
|
|
||||||
[self createTimeoutTimer];
|
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) unregisterForEvents
|
-(void) unregisterForEvents
|
||||||
{
|
{
|
||||||
[super unregisterForEvents];
|
[super unregisterForEvents];
|
||||||
|
|
||||||
[self stopNotifier];
|
[self stopNotifier];
|
||||||
|
|
||||||
[self destroyTimeoutTimer];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) timeoutTimerTick:(NSTimer*)timer
|
|
||||||
{
|
|
||||||
if (![self hasBytesAvailable])
|
|
||||||
{
|
|
||||||
if ([self hasGotNetworkConnection])
|
|
||||||
{
|
|
||||||
uint64_t currentTicks = GetTickCount();
|
|
||||||
|
|
||||||
if (((currentTicks - ticksWhenLastDataReceived) / 1000) >= options.inactivePeriodBeforeReconnectSeconds)
|
|
||||||
{
|
|
||||||
serial++;
|
|
||||||
|
|
||||||
NSLog(@"timeoutTimerTick %lld/%lld", self.position, self.length);
|
|
||||||
|
|
||||||
[self attemptReconnectWithSerial:@(serial)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) createTimeoutTimer
|
|
||||||
{
|
|
||||||
[self destroyTimeoutTimer];
|
|
||||||
|
|
||||||
NSRunLoop* runLoop = self.innerDataSource.eventsRunLoop;
|
|
||||||
|
|
||||||
if (runLoop == nil)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
timeoutTimer = [NSTimer timerWithTimeInterval:options.watchdogPeriodSeconds target:self selector:@selector(timeoutTimerTick:) userInfo:@(serial) repeats:YES];
|
|
||||||
|
|
||||||
[runLoop addTimer:timeoutTimer forMode:NSRunLoopCommonModes];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) destroyTimeoutTimer
|
|
||||||
{
|
|
||||||
if (timeoutTimer)
|
|
||||||
{
|
|
||||||
[timeoutTimer invalidate];
|
|
||||||
timeoutTimer = nil;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) stopNotifier
|
-(void) stopNotifier
|
||||||
|
|
@ -248,19 +148,6 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) seekToOffset:(int64_t)offset
|
|
||||||
{
|
|
||||||
ticksWhenLastDataReceived = GetTickCount();
|
|
||||||
|
|
||||||
[super seekToOffset:offset];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) close
|
|
||||||
{
|
|
||||||
[self destroyTimeoutTimer];
|
|
||||||
[super close];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) dealloc
|
-(void) dealloc
|
||||||
{
|
{
|
||||||
NSLog(@"STKAutoRecoveringHTTPDataSource dealloc");
|
NSLog(@"STKAutoRecoveringHTTPDataSource dealloc");
|
||||||
|
|
@ -268,7 +155,6 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
self.innerDataSource.delegate = nil;
|
self.innerDataSource.delegate = nil;
|
||||||
|
|
||||||
[self stopNotifier];
|
[self stopNotifier];
|
||||||
[self destroyTimeoutTimer];
|
|
||||||
|
|
||||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||||
|
|
||||||
|
|
@ -284,25 +170,15 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
{
|
{
|
||||||
waitingForNetwork = NO;
|
waitingForNetwork = NO;
|
||||||
|
|
||||||
NSLog(@"reachabilityChanged %lld/%lld", self.position, self.length);
|
|
||||||
|
|
||||||
serial++;
|
|
||||||
|
|
||||||
[self attemptReconnectWithSerial:@(serial)];
|
[self attemptReconnectWithSerial:@(serial)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
||||||
{
|
{
|
||||||
if (![self.innerDataSource hasBytesAvailable])
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
serial++;
|
serial++;
|
||||||
waitSeconds = 1;
|
waitSeconds = 1;
|
||||||
ticksWhenLastDataReceived = GetTickCount();
|
|
||||||
|
|
||||||
[super dataSourceDataAvailable:dataSource];
|
[super dataSourceDataAvailable:dataSource];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -315,10 +191,7 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
|
|
||||||
NSLog(@"attemptReconnect %lld/%lld", self.position, self.length);
|
NSLog(@"attemptReconnect %lld/%lld", self.position, self.length);
|
||||||
|
|
||||||
if (self.innerDataSource.eventsRunLoop)
|
[self seekToOffset:self.position];
|
||||||
{
|
|
||||||
[self.innerDataSource reconnect];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) attemptReconnectWithTimer:(NSTimer*)timer
|
-(void) attemptReconnectWithTimer:(NSTimer*)timer
|
||||||
|
|
@ -341,14 +214,10 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
|
|
||||||
if (runLoop == nil)
|
if (runLoop == nil)
|
||||||
{
|
{
|
||||||
// DataSource no longer used
|
[self performSelector:@selector(attemptReconnectWithSerial:) withObject:@(serial) afterDelay:waitSeconds];
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
serial++;
|
|
||||||
|
|
||||||
NSTimer* timer = [NSTimer timerWithTimeInterval:waitSeconds target:self selector:@selector(attemptReconnectWithTimer:) userInfo:@(serial) repeats:NO];
|
NSTimer* timer = [NSTimer timerWithTimeInterval:waitSeconds target:self selector:@selector(attemptReconnectWithTimer:) userInfo:@(serial) repeats:NO];
|
||||||
|
|
||||||
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
|
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
|
||||||
|
|
@ -371,15 +240,16 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
|
||||||
[self.delegate dataSourceEof:self];
|
[self.delegate dataSourceEof:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)dataSourceErrorOccured:(STKDataSource*)dataSource {
|
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource
|
||||||
|
{
|
||||||
NSLog(@"dataSourceErrorOccured");
|
NSLog(@"dataSourceErrorOccured");
|
||||||
|
|
||||||
/* Range out of bounds */
|
if (self.innerDataSource.httpStatusCode == 416 /* Range out of bounds */)
|
||||||
if (self.innerDataSource.httpStatusCode == 416) {
|
{
|
||||||
[super dataSourceEof:dataSource];
|
[super dataSourceEof:dataSource];
|
||||||
} else if ([self hasGotNetworkConnection]) {
|
}
|
||||||
[super dataSourceErrorOccured:dataSource];
|
else
|
||||||
} else {
|
{
|
||||||
[self processRetryOnError];
|
[self processRetryOnError];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,8 +34,6 @@
|
||||||
|
|
||||||
#import "STKDataSource.h"
|
#import "STKDataSource.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@class STKCoreFoundationDataSource;
|
@class STKCoreFoundationDataSource;
|
||||||
|
|
||||||
@interface CoreFoundationDataSourceClientInfo : NSObject
|
@interface CoreFoundationDataSourceClientInfo : NSObject
|
||||||
|
|
@ -45,10 +43,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
@interface STKCoreFoundationDataSource : STKDataSource
|
@interface STKCoreFoundationDataSource : STKDataSource
|
||||||
{
|
{
|
||||||
@public
|
|
||||||
CFReadStreamRef stream;
|
|
||||||
@protected
|
@protected
|
||||||
BOOL isInErrorState;
|
BOOL isInErrorState;
|
||||||
|
CFReadStreamRef stream;
|
||||||
NSRunLoop* eventsRunLoop;
|
NSRunLoop* eventsRunLoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,12 +54,9 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
-(BOOL) reregisterForEvents;
|
-(BOOL) reregisterForEvents;
|
||||||
|
|
||||||
-(void) open;
|
-(void) open;
|
||||||
-(void) openCompleted;
|
|
||||||
-(void) dataAvailable;
|
-(void) dataAvailable;
|
||||||
-(void) eof;
|
-(void) eof;
|
||||||
-(void) errorOccured;
|
-(void) errorOccured;
|
||||||
-(CFStreamStatus) status;
|
-(CFStreamStatus) status;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
|
|
@ -41,19 +41,14 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||||
switch (eventType)
|
switch (eventType)
|
||||||
{
|
{
|
||||||
case kCFStreamEventErrorOccurred:
|
case kCFStreamEventErrorOccurred:
|
||||||
{
|
|
||||||
[datasource errorOccured];
|
[datasource errorOccured];
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case kCFStreamEventEndEncountered:
|
case kCFStreamEventEndEncountered:
|
||||||
[datasource eof];
|
[datasource eof];
|
||||||
break;
|
break;
|
||||||
case kCFStreamEventHasBytesAvailable:
|
case kCFStreamEventHasBytesAvailable:
|
||||||
[datasource dataAvailable];
|
[datasource dataAvailable];
|
||||||
break;
|
break;
|
||||||
case kCFStreamEventOpenCompleted:
|
|
||||||
[datasource openCompleted];
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -106,11 +101,6 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||||
{
|
{
|
||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
if (eventsRunLoop)
|
|
||||||
{
|
|
||||||
[self unregisterForEvents];
|
|
||||||
}
|
|
||||||
|
|
||||||
CFReadStreamClose(stream);
|
CFReadStreamClose(stream);
|
||||||
CFRelease(stream);
|
CFRelease(stream);
|
||||||
|
|
||||||
|
|
@ -122,7 +112,7 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) seekToOffset:(SInt64)offset
|
-(void) seekToOffset:(long long)offset
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -158,10 +148,10 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||||
{
|
{
|
||||||
eventsRunLoop = runLoop;
|
eventsRunLoop = runLoop;
|
||||||
|
|
||||||
if (!stream)
|
if (!stream)
|
||||||
{
|
{
|
||||||
// Will register when they open or seek
|
[self open];
|
||||||
|
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,8 +184,4 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) openCompleted
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,6 @@
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
#include <AudioToolbox/AudioToolbox.h>
|
#include <AudioToolbox/AudioToolbox.h>
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@class STKDataSource;
|
@class STKDataSource;
|
||||||
|
|
||||||
@protocol STKDataSourceDelegate<NSObject>
|
@protocol STKDataSourceDelegate<NSObject>
|
||||||
|
|
@ -45,24 +43,24 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
-(void) dataSourceEof:(STKDataSource*)dataSource;
|
-(void) dataSourceEof:(STKDataSource*)dataSource;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
@protocol AudioDataSource<NSObject>
|
||||||
|
@property (readwrite) double averageBitRate;
|
||||||
|
@property (readwrite) long long audioDataOffset;
|
||||||
|
@end
|
||||||
|
|
||||||
@interface STKDataSource : NSObject
|
@interface STKDataSource : NSObject
|
||||||
|
|
||||||
@property (readonly) BOOL supportsSeek;
|
@property (readonly) long long position;
|
||||||
@property (readonly) SInt64 position;
|
@property (readonly) long long length;
|
||||||
@property (readonly) SInt64 length;
|
|
||||||
@property (readonly) BOOL hasBytesAvailable;
|
@property (readonly) BOOL hasBytesAvailable;
|
||||||
@property (nonatomic, readwrite, assign) double durationHint;
|
@property (readwrite, unsafe_unretained) id<STKDataSourceDelegate> delegate;
|
||||||
@property (readwrite, unsafe_unretained, nullable) id<STKDataSourceDelegate> delegate;
|
|
||||||
@property (nonatomic, strong, nullable) NSURL *recordToFileUrl;
|
|
||||||
|
|
||||||
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
|
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
|
||||||
-(void) unregisterForEvents;
|
-(void) unregisterForEvents;
|
||||||
-(void) close;
|
-(void) close;
|
||||||
|
|
||||||
-(void) seekToOffset:(SInt64)offset;
|
-(void) seekToOffset:(long long)offset;
|
||||||
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size;
|
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size;
|
||||||
-(AudioFileTypeID) audioFileTypeHint;
|
-(AudioFileTypeID) audioFileTypeHint;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,12 @@
|
||||||
@implementation STKDataSource
|
@implementation STKDataSource
|
||||||
@synthesize delegate;
|
@synthesize delegate;
|
||||||
|
|
||||||
-(SInt64) length
|
-(long long) length
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) seekToOffset:(SInt64)offset
|
-(void) seekToOffset:(long long)offset
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) position
|
-(long long) position
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -66,7 +66,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) close
|
-(void) close
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
-(BOOL) hasBytesAvailable
|
-(BOOL) hasBytesAvailable
|
||||||
|
|
@ -79,9 +79,4 @@
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(BOOL) supportsSeek
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,10 @@
|
||||||
|
|
||||||
#import "STKDataSource.h"
|
#import "STKDataSource.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface STKDataSourceWrapper : STKDataSource<STKDataSourceDelegate>
|
@interface STKDataSourceWrapper : STKDataSource<STKDataSourceDelegate>
|
||||||
|
|
||||||
-(instancetype) initWithDataSource:(STKDataSource*)innerDataSource;
|
-(id) initWithDataSource:(STKDataSource*)innerDataSource;
|
||||||
|
|
||||||
@property (readonly) STKDataSource* innerDataSource;
|
@property (readonly) STKDataSource* innerDataSource;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@
|
||||||
|
|
||||||
@implementation STKDataSourceWrapper
|
@implementation STKDataSourceWrapper
|
||||||
|
|
||||||
-(instancetype) initWithDataSource:(STKDataSource*)innerDataSourceIn
|
-(id) initWithDataSource:(STKDataSource*)innerDataSourceIn
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
{
|
{
|
||||||
|
|
@ -62,12 +62,12 @@
|
||||||
self.innerDataSource.delegate = nil;
|
self.innerDataSource.delegate = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) length
|
-(long long) length
|
||||||
{
|
{
|
||||||
return self.innerDataSource.length;
|
return self.innerDataSource.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) seekToOffset:(SInt64)offset
|
-(void) seekToOffset:(long long)offset
|
||||||
{
|
{
|
||||||
return [self.innerDataSource seekToOffset:offset];
|
return [self.innerDataSource seekToOffset:offset];
|
||||||
}
|
}
|
||||||
|
|
@ -77,7 +77,7 @@
|
||||||
return [self.innerDataSource readIntoBuffer:buffer withSize:size];
|
return [self.innerDataSource readIntoBuffer:buffer withSize:size];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) position
|
-(long long) position
|
||||||
{
|
{
|
||||||
return self.innerDataSource.position;
|
return self.innerDataSource.position;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,124 +0,0 @@
|
||||||
//
|
|
||||||
// AEFloatConverter.h
|
|
||||||
// The Amazing Audio Engine
|
|
||||||
//
|
|
||||||
// Created by Michael Tyson on 25/10/2012.
|
|
||||||
//
|
|
||||||
// This software is provided 'as-is', without any express or implied
|
|
||||||
// warranty. In no event will the authors be held liable for any damages
|
|
||||||
// arising from the use of this software.
|
|
||||||
//
|
|
||||||
// Permission is granted to anyone to use this software for any purpose,
|
|
||||||
// including commercial applications, and to alter it and redistribute it
|
|
||||||
// freely, subject to the following restrictions:
|
|
||||||
//
|
|
||||||
// 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
// claim that you wrote the original software. If you use this software
|
|
||||||
// in a product, an acknowledgment in the product documentation would be
|
|
||||||
// appreciated but is not required.
|
|
||||||
//
|
|
||||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
// misrepresented as being the original software.
|
|
||||||
//
|
|
||||||
// 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <AudioToolbox/AudioToolbox.h>
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Universal converter to float format
|
|
||||||
*
|
|
||||||
* Use this class to easily convert arbitrary audio formats to floating point
|
|
||||||
* for use with utilities like the Accelerate framework.
|
|
||||||
*/
|
|
||||||
@interface STKFloatConverter : NSObject
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Initialize
|
|
||||||
*
|
|
||||||
* @param sourceFormat The audio format to use
|
|
||||||
*/
|
|
||||||
- (id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Convert audio to floating-point
|
|
||||||
*
|
|
||||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
|
||||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
|
||||||
* convert it into a noninterleaved float array.
|
|
||||||
*
|
|
||||||
* @param converter Pointer to the converter object.
|
|
||||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
|
||||||
* @param targetBuffers An array of floating-point arrays to store the converted float audio into.
|
|
||||||
* Note that you must provide the correct number of arrays, to match the number of channels.
|
|
||||||
* @param frames The number of frames to convert.
|
|
||||||
* @return YES on success; NO on failure
|
|
||||||
*/
|
|
||||||
BOOL STKFloatConverterToFloat(STKFloatConverter* converter, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Convert audio to floating-point, in a buffer list
|
|
||||||
*
|
|
||||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
|
||||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
|
||||||
* convert it into a noninterleaved float format.
|
|
||||||
*
|
|
||||||
* @param converter Pointer to the converter object.
|
|
||||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
|
||||||
* @param targetBuffer An audio buffer list to store the converted floating-point audio.
|
|
||||||
* @param frames The number of frames to convert.
|
|
||||||
* @return YES on success; NO on failure
|
|
||||||
*/
|
|
||||||
BOOL STKFloatConverterToFloatBufferList(STKFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Convert audio from floating-point
|
|
||||||
*
|
|
||||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
|
||||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
|
||||||
* convert it into a float array.
|
|
||||||
*
|
|
||||||
* @param converter Pointer to the converter object.
|
|
||||||
* @param sourceBuffers An array of floating-point arrays containing the floating-point audio to convert.
|
|
||||||
* Note that you must provide the correct number of arrays, to match the number of channels.
|
|
||||||
* @param targetBuffer An audio buffer list to store the converted audio into.
|
|
||||||
* @param frames The number of frames to convert.
|
|
||||||
* @return YES on success; NO on failure
|
|
||||||
*/
|
|
||||||
BOOL STKFloatConverterFromFloat(STKFloatConverter* converter, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Convert audio from floating-point, in a buffer list
|
|
||||||
*
|
|
||||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
|
||||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
|
||||||
* convert it into a float array.
|
|
||||||
*
|
|
||||||
* @param converter Pointer to the converter object.
|
|
||||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
|
||||||
* @param targetBuffer An audio buffer list to store the converted audio into.
|
|
||||||
* @param frames The number of frames to convert.
|
|
||||||
* @return YES on success; NO on failure
|
|
||||||
*/
|
|
||||||
BOOL STKFloatConverterFromFloatBufferList(STKFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The AudioStreamBasicDescription representing the converted floating-point format
|
|
||||||
*/
|
|
||||||
@property (nonatomic, readonly) AudioStreamBasicDescription floatingPointAudioDescription;
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* The source audio format set at initialization
|
|
||||||
*/
|
|
||||||
@property (nonatomic, readonly) AudioStreamBasicDescription sourceFormat;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,211 +0,0 @@
|
||||||
//
|
|
||||||
// AEFloatConverter.m
|
|
||||||
// The Amazing Audio Engine
|
|
||||||
//
|
|
||||||
// Created by Michael Tyson on 25/10/2012.
|
|
||||||
//
|
|
||||||
// This software is provided 'as-is', without any express or implied
|
|
||||||
// warranty. In no event will the authors be held liable for any damages
|
|
||||||
// arising from the use of this software.
|
|
||||||
//
|
|
||||||
// Permission is granted to anyone to use this software for any purpose,
|
|
||||||
// including commercial applications, and to alter it and redistribute it
|
|
||||||
// freely, subject to the following restrictions:
|
|
||||||
//
|
|
||||||
// 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
// claim that you wrote the original software. If you use this software
|
|
||||||
// in a product, an acknowledgment in the product documentation would be
|
|
||||||
// appreciated but is not required.
|
|
||||||
//
|
|
||||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
// misrepresented as being the original software.
|
|
||||||
//
|
|
||||||
// 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "STKFloatConverter.h"
|
|
||||||
|
|
||||||
#define checkResult(result,operation) (_checkResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
|
|
||||||
static inline BOOL _checkResult(OSStatus result, const char *operation, const char* file, int line) {
|
|
||||||
if ( result != noErr ) {
|
|
||||||
NSLog(@"%s:%d: %s result %d %08X %4.4s", file, line, operation, (int)result, (int)result, (char*)&result);
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define kNoMoreDataErr -2222
|
|
||||||
|
|
||||||
struct complexInputDataProc_t {
|
|
||||||
AudioBufferList *sourceBuffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
@interface STKFloatConverter () {
|
|
||||||
AudioStreamBasicDescription _sourceAudioDescription;
|
|
||||||
AudioStreamBasicDescription _floatAudioDescription;
|
|
||||||
AudioConverterRef _toFloatConverter;
|
|
||||||
AudioConverterRef _fromFloatConverter;
|
|
||||||
AudioBufferList *_scratchFloatBufferList;
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
|
||||||
UInt32 *ioNumberDataPackets,
|
|
||||||
AudioBufferList *ioData,
|
|
||||||
AudioStreamPacketDescription **outDataPacketDescription,
|
|
||||||
void *inUserData);
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation STKFloatConverter
|
|
||||||
@synthesize sourceFormat = _sourceAudioDescription;
|
|
||||||
|
|
||||||
-(id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat {
|
|
||||||
if ( !(self = [super init]) ) return nil;
|
|
||||||
|
|
||||||
_floatAudioDescription.mFormatID = kAudioFormatLinearPCM;
|
|
||||||
_floatAudioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
|
|
||||||
_floatAudioDescription.mChannelsPerFrame = sourceFormat.mChannelsPerFrame;
|
|
||||||
_floatAudioDescription.mBytesPerPacket = sizeof(float);
|
|
||||||
_floatAudioDescription.mFramesPerPacket = 1;
|
|
||||||
_floatAudioDescription.mBytesPerFrame = sizeof(float);
|
|
||||||
_floatAudioDescription.mBitsPerChannel = 8 * sizeof(float);
|
|
||||||
_floatAudioDescription.mSampleRate = sourceFormat.mSampleRate;
|
|
||||||
|
|
||||||
_sourceAudioDescription = sourceFormat;
|
|
||||||
|
|
||||||
if ( memcmp(&sourceFormat, &_floatAudioDescription, sizeof(AudioStreamBasicDescription)) != 0 ) {
|
|
||||||
checkResult(AudioConverterNew(&sourceFormat, &_floatAudioDescription, &_toFloatConverter), "AudioConverterNew");
|
|
||||||
checkResult(AudioConverterNew(&_floatAudioDescription, &sourceFormat, &_fromFloatConverter), "AudioConverterNew");
|
|
||||||
_scratchFloatBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_floatAudioDescription.mChannelsPerFrame-1)*sizeof(AudioBuffer));
|
|
||||||
_scratchFloatBufferList->mNumberBuffers = _floatAudioDescription.mChannelsPerFrame;
|
|
||||||
for ( int i=0; i<_scratchFloatBufferList->mNumberBuffers; i++ ) {
|
|
||||||
_scratchFloatBufferList->mBuffers[i].mNumberChannels = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)dealloc {
|
|
||||||
if ( _toFloatConverter ) AudioConverterDispose(_toFloatConverter);
|
|
||||||
if ( _fromFloatConverter ) AudioConverterDispose(_fromFloatConverter);
|
|
||||||
if ( _scratchFloatBufferList ) free(_scratchFloatBufferList);
|
|
||||||
// [super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BOOL STKFloatConverterToFloat(STKFloatConverter* THIS, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames) {
|
|
||||||
if ( frames == 0 ) return YES;
|
|
||||||
|
|
||||||
if ( THIS->_toFloatConverter ) {
|
|
||||||
UInt32 priorDataByteSize = sourceBuffer->mBuffers[0].mDataByteSize;
|
|
||||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
|
||||||
sourceBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++ ) {
|
|
||||||
THIS->_scratchFloatBufferList->mBuffers[i].mData = targetBuffers[i];
|
|
||||||
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
|
|
||||||
}
|
|
||||||
|
|
||||||
OSStatus result = AudioConverterFillComplexBuffer(THIS->_toFloatConverter,
|
|
||||||
complexInputDataProc,
|
|
||||||
&(struct complexInputDataProc_t) { .sourceBuffer = sourceBuffer },
|
|
||||||
&frames,
|
|
||||||
THIS->_scratchFloatBufferList,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
|
||||||
sourceBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !checkResult(result, "AudioConverterConvertComplexBuffer") ) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
|
||||||
memcpy(targetBuffers[i], sourceBuffer->mBuffers[i].mData, frames * sizeof(float));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL STKFloatConverterToFloatBufferList(STKFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
|
|
||||||
assert(targetBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
|
|
||||||
|
|
||||||
float *targetBuffers[targetBuffer->mNumberBuffers];
|
|
||||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
|
||||||
targetBuffers[i] = (float*)targetBuffer->mBuffers[i].mData;
|
|
||||||
}
|
|
||||||
return STKFloatConverterToFloat(converter, sourceBuffer, targetBuffers, frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL STKFloatConverterFromFloat(STKFloatConverter* THIS, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames) {
|
|
||||||
if ( frames == 0 ) return YES;
|
|
||||||
|
|
||||||
if ( THIS->_fromFloatConverter ) {
|
|
||||||
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++ ) {
|
|
||||||
THIS->_scratchFloatBufferList->mBuffers[i].mData = sourceBuffers[i];
|
|
||||||
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
|
|
||||||
}
|
|
||||||
|
|
||||||
UInt32 priorDataByteSize = targetBuffer->mBuffers[0].mDataByteSize;
|
|
||||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
|
||||||
targetBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
OSStatus result = AudioConverterFillComplexBuffer(THIS->_fromFloatConverter,
|
|
||||||
complexInputDataProc,
|
|
||||||
&(struct complexInputDataProc_t) { .sourceBuffer = THIS->_scratchFloatBufferList },
|
|
||||||
&frames,
|
|
||||||
targetBuffer,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
|
||||||
targetBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !checkResult(result, "AudioConverterConvertComplexBuffer") ) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
|
||||||
memcpy(targetBuffer->mBuffers[i].mData, sourceBuffers[i], frames * sizeof(float));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL STKFloatConverterFromFloatBufferList(STKFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
|
|
||||||
assert(sourceBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
|
|
||||||
|
|
||||||
float *sourceBuffers[sourceBuffer->mNumberBuffers];
|
|
||||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
|
||||||
sourceBuffers[i] = (float*)sourceBuffer->mBuffers[i].mData;
|
|
||||||
}
|
|
||||||
return STKFloatConverterFromFloat(converter, sourceBuffers, targetBuffer, frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
|
||||||
UInt32 *ioNumberDataPackets,
|
|
||||||
AudioBufferList *ioData,
|
|
||||||
AudioStreamPacketDescription **outDataPacketDescription,
|
|
||||||
void *inUserData) {
|
|
||||||
struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
|
|
||||||
if ( !arg->sourceBuffer ) {
|
|
||||||
return kNoMoreDataErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(ioData, arg->sourceBuffer, sizeof(AudioBufferList) + (arg->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
|
|
||||||
arg->sourceBuffer = NULL;
|
|
||||||
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(AudioStreamBasicDescription)floatingPointAudioDescription {
|
|
||||||
return _floatAudioDescription;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
@ -34,27 +34,21 @@
|
||||||
|
|
||||||
#import "STKCoreFoundationDataSource.h"
|
#import "STKCoreFoundationDataSource.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@class STKHTTPDataSource;
|
@class STKHTTPDataSource;
|
||||||
|
|
||||||
typedef void(^STKURLBlock)(NSURL* url);
|
typedef void(^STKURLBlock)(NSURL* url);
|
||||||
typedef NSURL* _Nonnull (^STKURLProvider)();
|
typedef NSURL*(^STKURLProvider)();
|
||||||
typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock callback);
|
typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock callback);
|
||||||
|
|
||||||
@interface STKHTTPDataSource : STKCoreFoundationDataSource
|
@interface STKHTTPDataSource : STKCoreFoundationDataSource
|
||||||
|
|
||||||
@property (readonly, retain) NSURL* url;
|
@property (readonly, retain) NSURL* url;
|
||||||
@property (readonly) UInt32 httpStatusCode;
|
@property (readwrite) UInt32 httpStatusCode;
|
||||||
|
|
||||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
|
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
|
||||||
-(instancetype) initWithURL:(NSURL*)url;
|
-(id) initWithURL:(NSURL*)url;
|
||||||
-(instancetype) initWithURL:(NSURL*)url httpRequestHeaders:(NSDictionary*)httpRequestHeaders;
|
-(id) initWithURLProvider:(STKURLProvider)urlProvider;
|
||||||
-(instancetype) initWithURLProvider:(STKURLProvider)urlProvider;
|
-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider;
|
||||||
-(instancetype) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider;
|
-(NSRunLoop*) eventsRunLoop;
|
||||||
-(nullable NSRunLoop*) eventsRunLoop;
|
|
||||||
-(void) reconnect;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
|
|
@ -38,25 +38,14 @@
|
||||||
@interface STKHTTPDataSource()
|
@interface STKHTTPDataSource()
|
||||||
{
|
{
|
||||||
@private
|
@private
|
||||||
BOOL supportsSeek;
|
long long seekStart;
|
||||||
UInt32 httpStatusCode;
|
long long relativePosition;
|
||||||
SInt64 seekStart;
|
long long fileLength;
|
||||||
SInt64 relativePosition;
|
|
||||||
SInt64 fileLength;
|
|
||||||
int discontinuous;
|
int discontinuous;
|
||||||
int requestSerialNumber;
|
|
||||||
int prefixBytesRead;
|
|
||||||
NSData* prefixBytes;
|
|
||||||
NSMutableData* iceHeaderData;
|
|
||||||
BOOL iceHeaderSearchComplete;
|
|
||||||
BOOL iceHeaderAvailable;
|
|
||||||
BOOL httpHeaderNotAvailable;
|
|
||||||
|
|
||||||
NSURL* currentUrl;
|
NSURL* currentUrl;
|
||||||
STKAsyncURLProvider asyncUrlProvider;
|
STKAsyncURLProvider asyncUrlProvider;
|
||||||
NSDictionary* httpHeaders;
|
NSDictionary* httpHeaders;
|
||||||
AudioFileTypeID audioFileTypeHint;
|
AudioFileTypeID audioFileTypeHint;
|
||||||
NSDictionary* requestHeaders;
|
|
||||||
}
|
}
|
||||||
-(void) open;
|
-(void) open;
|
||||||
|
|
||||||
|
|
@ -64,19 +53,12 @@
|
||||||
|
|
||||||
@implementation STKHTTPDataSource
|
@implementation STKHTTPDataSource
|
||||||
|
|
||||||
-(instancetype) initWithURL:(NSURL*)urlIn
|
-(id) initWithURL:(NSURL*)urlIn
|
||||||
{
|
{
|
||||||
return [self initWithURLProvider:^NSURL* { return urlIn; }];
|
return [self initWithURLProvider:^NSURL* { return urlIn; }];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(instancetype) initWithURL:(NSURL *)urlIn httpRequestHeaders:(NSDictionary *)httpRequestHeaders
|
-(id) initWithURLProvider:(STKURLProvider)urlProviderIn
|
||||||
{
|
|
||||||
self = [self initWithURLProvider:^NSURL* { return urlIn; }];
|
|
||||||
self->requestHeaders = httpRequestHeaders;
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(instancetype) initWithURLProvider:(STKURLProvider)urlProviderIn
|
|
||||||
{
|
{
|
||||||
urlProviderIn = [urlProviderIn copy];
|
urlProviderIn = [urlProviderIn copy];
|
||||||
|
|
||||||
|
|
@ -86,7 +68,7 @@
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(instancetype) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProviderIn
|
-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProviderIn
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
{
|
{
|
||||||
|
|
@ -125,8 +107,6 @@
|
||||||
@"audio/mpg": @(kAudioFileMP3Type),
|
@"audio/mpg": @(kAudioFileMP3Type),
|
||||||
@"audio/mpeg": @(kAudioFileMP3Type),
|
@"audio/mpeg": @(kAudioFileMP3Type),
|
||||||
@"audio/wav": @(kAudioFileWAVEType),
|
@"audio/wav": @(kAudioFileWAVEType),
|
||||||
@"audio/x-wav": @(kAudioFileWAVEType),
|
|
||||||
@"audio/vnd.wav": @(kAudioFileWAVEType),
|
|
||||||
@"audio/aifc": @(kAudioFileAIFCType),
|
@"audio/aifc": @(kAudioFileAIFCType),
|
||||||
@"audio/aiff": @(kAudioFileAIFFType),
|
@"audio/aiff": @(kAudioFileAIFFType),
|
||||||
@"audio/x-m4a": @(kAudioFileM4AType),
|
@"audio/x-m4a": @(kAudioFileM4AType),
|
||||||
|
|
@ -134,18 +114,10 @@
|
||||||
@"audio/aacp": @(kAudioFileAAC_ADTSType),
|
@"audio/aacp": @(kAudioFileAAC_ADTSType),
|
||||||
@"audio/m4a": @(kAudioFileM4AType),
|
@"audio/m4a": @(kAudioFileM4AType),
|
||||||
@"audio/mp4": @(kAudioFileMPEG4Type),
|
@"audio/mp4": @(kAudioFileMPEG4Type),
|
||||||
@"video/mp4": @(kAudioFileMPEG4Type),
|
|
||||||
@"audio/caf": @(kAudioFileCAFType),
|
@"audio/caf": @(kAudioFileCAFType),
|
||||||
@"audio/x-caf": @(kAudioFileCAFType),
|
|
||||||
@"audio/aac": @(kAudioFileAAC_ADTSType),
|
@"audio/aac": @(kAudioFileAAC_ADTSType),
|
||||||
@"audio/aacp": @(kAudioFileAAC_ADTSType),
|
|
||||||
@"audio/ac3": @(kAudioFileAC3Type),
|
@"audio/ac3": @(kAudioFileAC3Type),
|
||||||
@"audio/3gp": @(kAudioFile3GPType),
|
@"audio/3gp": @(kAudioFile3GPType)
|
||||||
@"video/3gp": @(kAudioFile3GPType),
|
|
||||||
@"audio/3gpp": @(kAudioFile3GPType),
|
|
||||||
@"video/3gpp": @(kAudioFile3GPType),
|
|
||||||
@"audio/3gp2": @(kAudioFile3GP2Type),
|
|
||||||
@"video/3gp2": @(kAudioFile3GP2Type)
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -164,263 +136,87 @@
|
||||||
return audioFileTypeHint;
|
return audioFileTypeHint;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(NSDictionary*) parseIceHeader:(NSData*)headerData
|
|
||||||
{
|
|
||||||
NSMutableDictionary* retval = [[NSMutableDictionary alloc] init];
|
|
||||||
NSCharacterSet* characterSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
|
|
||||||
NSString* fullString = [[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding];
|
|
||||||
NSArray* strings = [fullString componentsSeparatedByCharactersInSet:characterSet];
|
|
||||||
|
|
||||||
httpHeaders = [NSMutableDictionary dictionary];
|
|
||||||
|
|
||||||
for (NSString* s in strings)
|
|
||||||
{
|
|
||||||
if (s.length == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([s hasPrefix:@"ICY "])
|
|
||||||
{
|
|
||||||
NSArray* parts = [s componentsSeparatedByString:@" "];
|
|
||||||
|
|
||||||
if (parts.count >= 2)
|
|
||||||
{
|
|
||||||
self->httpStatusCode = [parts[1] intValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSRange range = [s rangeOfString:@":"];
|
|
||||||
|
|
||||||
if (range.location == NSNotFound)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString* key = [s substringWithRange: (NSRange){.location = 0, .length = range.location}];
|
|
||||||
NSString* value = [s substringFromIndex:range.location + 1];
|
|
||||||
|
|
||||||
[retval setValue:value forKey:key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(BOOL) parseHttpHeader
|
|
||||||
{
|
|
||||||
if (!httpHeaderNotAvailable)
|
|
||||||
{
|
|
||||||
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
|
||||||
|
|
||||||
if (response)
|
|
||||||
{
|
|
||||||
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
|
||||||
|
|
||||||
if (httpHeaders.count == 0)
|
|
||||||
{
|
|
||||||
httpHeaderNotAvailable = YES;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->httpStatusCode = (UInt32)CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
|
||||||
}
|
|
||||||
|
|
||||||
CFRelease(response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (httpHeaderNotAvailable)
|
|
||||||
{
|
|
||||||
if (self->iceHeaderSearchComplete && !self->iceHeaderAvailable)
|
|
||||||
{
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self->iceHeaderSearchComplete)
|
|
||||||
{
|
|
||||||
UInt8 byte;
|
|
||||||
UInt8 terminal1[] = { '\n', '\n' };
|
|
||||||
UInt8 terminal2[] = { '\r', '\n', '\r', '\n' };
|
|
||||||
|
|
||||||
if (iceHeaderData == nil)
|
|
||||||
{
|
|
||||||
iceHeaderData = [NSMutableData dataWithCapacity:1024];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (![self hasBytesAvailable])
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read = [super readIntoBuffer:&byte withSize:1];
|
|
||||||
|
|
||||||
if (read <= 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
[iceHeaderData appendBytes:&byte length:read];
|
|
||||||
|
|
||||||
if (iceHeaderData.length >= sizeof(terminal1))
|
|
||||||
{
|
|
||||||
if (memcmp(&terminal1[0], [self->iceHeaderData bytes] + iceHeaderData.length - sizeof(terminal1), sizeof(terminal1)) == 0)
|
|
||||||
{
|
|
||||||
self->iceHeaderAvailable = YES;
|
|
||||||
self->iceHeaderSearchComplete = YES;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iceHeaderData.length >= sizeof(terminal2))
|
|
||||||
{
|
|
||||||
if (memcmp(&terminal2[0], [self->iceHeaderData bytes] + iceHeaderData.length - sizeof(terminal2), sizeof(terminal2)) == 0)
|
|
||||||
{
|
|
||||||
self->iceHeaderAvailable = YES;
|
|
||||||
self->iceHeaderSearchComplete = YES;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iceHeaderData.length >= 4)
|
|
||||||
{
|
|
||||||
if (memcmp([self->iceHeaderData bytes], "ICY ", 4) != 0 && memcmp([self->iceHeaderData bytes], "HTTP", 4) != 0)
|
|
||||||
{
|
|
||||||
self->iceHeaderAvailable = NO;
|
|
||||||
self->iceHeaderSearchComplete = YES;
|
|
||||||
prefixBytes = iceHeaderData;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self->iceHeaderSearchComplete)
|
|
||||||
{
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
httpHeaders = [self parseIceHeader:self->iceHeaderData];
|
|
||||||
|
|
||||||
self->iceHeaderData = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (([httpHeaders objectForKey:@"Accept-Ranges"] ?: [httpHeaders objectForKey:@"accept-ranges"]) != nil)
|
|
||||||
{
|
|
||||||
self->supportsSeek = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.httpStatusCode == 200)
|
|
||||||
{
|
|
||||||
if (seekStart == 0)
|
|
||||||
{
|
|
||||||
id value = [httpHeaders objectForKey:@"Content-Length"] ?: [httpHeaders objectForKey:@"content-length"];
|
|
||||||
|
|
||||||
fileLength = (SInt64)[value longLongValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"] ?: [httpHeaders objectForKey:@"content-type"] ;
|
|
||||||
AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType];
|
|
||||||
|
|
||||||
if (typeIdFromMimeType != 0)
|
|
||||||
{
|
|
||||||
audioFileTypeHint = typeIdFromMimeType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (self.httpStatusCode == 206)
|
|
||||||
{
|
|
||||||
NSString* contentRange = [httpHeaders objectForKey:@"Content-Range"] ?: [httpHeaders objectForKey:@"content-range"];
|
|
||||||
NSArray* components = [contentRange componentsSeparatedByString:@"/"];
|
|
||||||
|
|
||||||
if (components.count == 2)
|
|
||||||
{
|
|
||||||
fileLength = [[components objectAtIndex:1] integerValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (self.httpStatusCode == 416)
|
|
||||||
{
|
|
||||||
if (self.length >= 0)
|
|
||||||
{
|
|
||||||
seekStart = self.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self eof];
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
else if (self.httpStatusCode >= 300)
|
|
||||||
{
|
|
||||||
[self errorOccured];
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) dataAvailable
|
-(void) dataAvailable
|
||||||
{
|
{
|
||||||
if (stream == NULL)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.httpStatusCode == 0)
|
if (self.httpStatusCode == 0)
|
||||||
{
|
{
|
||||||
if ([self parseHttpHeader])
|
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
||||||
{
|
|
||||||
if ([self hasBytesAvailable])
|
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
||||||
{
|
|
||||||
[super dataAvailable];
|
self.httpStatusCode = CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
||||||
}
|
|
||||||
|
CFRelease(response);
|
||||||
return;
|
|
||||||
}
|
if (self.httpStatusCode == 200)
|
||||||
else
|
{
|
||||||
{
|
if (seekStart == 0)
|
||||||
return;
|
{
|
||||||
}
|
fileLength = (long long)[[httpHeaders objectForKey:@"Content-Length"] integerValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"];
|
||||||
|
AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType];
|
||||||
|
|
||||||
|
if (typeIdFromMimeType != 0)
|
||||||
|
{
|
||||||
|
audioFileTypeHint = typeIdFromMimeType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (self.httpStatusCode == 206)
|
||||||
|
{
|
||||||
|
NSString* contentRange = [httpHeaders objectForKey:@"Content-Range"];
|
||||||
|
NSArray* components = [contentRange componentsSeparatedByString:@"/"];
|
||||||
|
|
||||||
|
if (components.count == 2)
|
||||||
|
{
|
||||||
|
fileLength = [[components objectAtIndex:1] integerValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (self.httpStatusCode == 416)
|
||||||
|
{
|
||||||
|
if (self.length >= 0)
|
||||||
|
{
|
||||||
|
seekStart = self.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self eof];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (self.httpStatusCode >= 300)
|
||||||
|
{
|
||||||
|
[self errorOccured];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
[super dataAvailable];
|
||||||
[super dataAvailable];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) position
|
-(long long) position
|
||||||
{
|
{
|
||||||
return seekStart + relativePosition;
|
return seekStart + relativePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) length
|
-(long long) length
|
||||||
{
|
{
|
||||||
return fileLength >= 0 ? fileLength : 0;
|
return fileLength >= 0 ? fileLength : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) reconnect
|
-(void) seekToOffset:(long long)offset
|
||||||
{
|
{
|
||||||
NSRunLoop* savedEventsRunLoop = eventsRunLoop;
|
if (eventsRunLoop)
|
||||||
|
{
|
||||||
|
[self unregisterForEvents];
|
||||||
|
}
|
||||||
|
|
||||||
[self close];
|
if (stream)
|
||||||
|
{
|
||||||
eventsRunLoop = savedEventsRunLoop;
|
CFReadStreamClose(stream);
|
||||||
|
CFRelease(stream);
|
||||||
[self seekToOffset:self->supportsSeek ? self.position : 0];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
-(void) seekToOffset:(SInt64)offset
|
|
||||||
{
|
|
||||||
NSRunLoop* savedEventsRunLoop = eventsRunLoop;
|
|
||||||
|
|
||||||
[self close];
|
|
||||||
|
|
||||||
eventsRunLoop = savedEventsRunLoop;
|
|
||||||
|
|
||||||
NSAssert([NSRunLoop currentRunLoop] == eventsRunLoop, @"Seek called on wrong thread");
|
NSAssert([NSRunLoop currentRunLoop] == eventsRunLoop, @"Seek called on wrong thread");
|
||||||
|
|
||||||
|
|
@ -430,43 +226,17 @@
|
||||||
|
|
||||||
self->isInErrorState = NO;
|
self->isInErrorState = NO;
|
||||||
|
|
||||||
if (!self->supportsSeek && offset != self->relativePosition)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self openForSeek:YES];
|
[self openForSeek:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
|
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
|
||||||
{
|
|
||||||
return [self privateReadIntoBuffer:buffer withSize:size];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int) privateReadIntoBuffer:(UInt8*)buffer withSize:(int)size
|
|
||||||
{
|
{
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefixBytes != nil)
|
int read = (int)CFReadStreamRead(stream, buffer, size);
|
||||||
{
|
|
||||||
int count = MIN(size, (int)prefixBytes.length - prefixBytesRead);
|
|
||||||
|
|
||||||
[prefixBytes getBytes:buffer length:count];
|
|
||||||
|
|
||||||
prefixBytesRead += count;
|
|
||||||
|
|
||||||
if (prefixBytesRead >= prefixBytes.length)
|
|
||||||
{
|
|
||||||
prefixBytes = nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
int read = [super readIntoBuffer:buffer withSize:size];
|
|
||||||
|
|
||||||
if (read < 0)
|
if (read < 0)
|
||||||
{
|
{
|
||||||
|
|
@ -485,18 +255,8 @@
|
||||||
|
|
||||||
-(void) openForSeek:(BOOL)forSeek
|
-(void) openForSeek:(BOOL)forSeek
|
||||||
{
|
{
|
||||||
int localRequestSerialNumber;
|
|
||||||
|
|
||||||
requestSerialNumber++;
|
|
||||||
localRequestSerialNumber = requestSerialNumber;
|
|
||||||
|
|
||||||
asyncUrlProvider(self, forSeek, ^(NSURL* url)
|
asyncUrlProvider(self, forSeek, ^(NSURL* url)
|
||||||
{
|
{
|
||||||
if (localRequestSerialNumber != self->requestSerialNumber)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->currentUrl = url;
|
self->currentUrl = url;
|
||||||
|
|
||||||
if (url == nil)
|
if (url == nil)
|
||||||
|
|
@ -506,23 +266,13 @@
|
||||||
|
|
||||||
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1);
|
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1);
|
||||||
|
|
||||||
if (seekStart > 0 && supportsSeek)
|
if (seekStart > 0)
|
||||||
{
|
{
|
||||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%lld-", seekStart]);
|
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%lld-", seekStart]);
|
||||||
|
|
||||||
discontinuous = YES;
|
discontinuous = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (NSString* key in self->requestHeaders)
|
|
||||||
{
|
|
||||||
NSString* value = [self->requestHeaders objectForKey:key];
|
|
||||||
|
|
||||||
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)value);
|
|
||||||
}
|
|
||||||
|
|
||||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
|
|
||||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Ice-MetaData"), CFSTR("0"));
|
|
||||||
|
|
||||||
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
|
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
|
||||||
|
|
||||||
if (stream == nil)
|
if (stream == nil)
|
||||||
|
|
@ -533,8 +283,6 @@
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CFReadStreamSetProperty(stream, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground);
|
|
||||||
|
|
||||||
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
|
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
|
||||||
{
|
{
|
||||||
|
|
@ -546,15 +294,20 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Proxy support
|
// Proxy support
|
||||||
|
|
||||||
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
|
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
|
||||||
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
|
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
|
||||||
CFRelease(proxySettings);
|
CFRelease(proxySettings);
|
||||||
|
|
||||||
// SSL support
|
// SSL support
|
||||||
|
|
||||||
if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
||||||
{
|
{
|
||||||
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||||
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
|
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
|
||||||
|
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
|
||||||
|
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
|
||||||
|
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
|
||||||
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
|
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
|
||||||
[NSNull null], kCFStreamSSLPeerName,
|
[NSNull null], kCFStreamSSLPeerName,
|
||||||
nil];
|
nil];
|
||||||
|
|
@ -564,15 +317,14 @@
|
||||||
|
|
||||||
[self reregisterForEvents];
|
[self reregisterForEvents];
|
||||||
|
|
||||||
self->httpStatusCode = 0;
|
self.httpStatusCode = 0;
|
||||||
|
|
||||||
// Open
|
// Open
|
||||||
|
|
||||||
if (!CFReadStreamOpen(stream))
|
if (!CFReadStreamOpen(stream))
|
||||||
{
|
{
|
||||||
CFRelease(stream);
|
CFRelease(stream);
|
||||||
CFRelease(message);
|
CFRelease(message);
|
||||||
|
|
||||||
stream = 0;
|
|
||||||
|
|
||||||
[self errorOccured];
|
[self errorOccured];
|
||||||
|
|
||||||
|
|
@ -585,11 +337,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
-(UInt32) httpStatusCode
|
|
||||||
{
|
|
||||||
return self->httpStatusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSRunLoop*) eventsRunLoop
|
-(NSRunLoop*) eventsRunLoop
|
||||||
{
|
{
|
||||||
return self->eventsRunLoop;
|
return self->eventsRunLoop;
|
||||||
|
|
@ -600,9 +347,4 @@
|
||||||
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
|
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
|
||||||
}
|
}
|
||||||
|
|
||||||
-(BOOL) supportsSeek
|
|
||||||
{
|
|
||||||
return self->supportsSeek;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,10 @@
|
||||||
|
|
||||||
#import "STKCoreFoundationDataSource.h"
|
#import "STKCoreFoundationDataSource.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface STKLocalFileDataSource : STKCoreFoundationDataSource
|
@interface STKLocalFileDataSource : STKCoreFoundationDataSource
|
||||||
|
|
||||||
+(AudioFileTypeID) audioFileTypeHintFromFileExtension:(NSString*)fileExtension;
|
+(AudioFileTypeID) audioFileTypeHintFromFileExtension:(NSString*)fileExtension;
|
||||||
@property (readonly, copy) NSString* filePath;
|
@property (readonly, copy) NSString* filePath;
|
||||||
-(instancetype) initWithFilePath:(NSString*)filePath;
|
-(id) initWithFilePath:(NSString*)filePath;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
|
||||||
|
|
@ -36,8 +36,8 @@
|
||||||
|
|
||||||
@interface STKLocalFileDataSource()
|
@interface STKLocalFileDataSource()
|
||||||
{
|
{
|
||||||
SInt64 position;
|
long long position;
|
||||||
SInt64 length;
|
long long length;
|
||||||
AudioFileTypeID audioFileTypeHint;
|
AudioFileTypeID audioFileTypeHint;
|
||||||
}
|
}
|
||||||
@property (readwrite, copy) NSString* filePath;
|
@property (readwrite, copy) NSString* filePath;
|
||||||
|
|
@ -47,13 +47,13 @@
|
||||||
@implementation STKLocalFileDataSource
|
@implementation STKLocalFileDataSource
|
||||||
@synthesize filePath;
|
@synthesize filePath;
|
||||||
|
|
||||||
-(instancetype) initWithFilePath:(NSString*)filePathIn
|
-(id) initWithFilePath:(NSString*)filePathIn
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
{
|
{
|
||||||
self.filePath = filePathIn;
|
self.filePath = filePathIn;
|
||||||
|
|
||||||
audioFileTypeHint = [[self class] audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
|
audioFileTypeHint = [STKLocalFileDataSource audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -153,12 +153,12 @@
|
||||||
CFReadStreamOpen(stream);
|
CFReadStreamOpen(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) position
|
-(long long) position
|
||||||
{
|
{
|
||||||
return position;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(SInt64) length
|
-(long long) length
|
||||||
{
|
{
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +181,7 @@
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
-(void) seekToOffset:(SInt64)offset
|
-(void) seekToOffset:(long long)offset
|
||||||
{
|
{
|
||||||
CFStreamStatus status = kCFStreamStatusClosed;
|
CFStreamStatus status = kCFStreamStatusClosed;
|
||||||
|
|
||||||
|
|
@ -200,18 +200,6 @@
|
||||||
[self open];
|
[self open];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stream == 0)
|
|
||||||
{
|
|
||||||
CFRunLoopPerformBlock(eventsRunLoop.getCFRunLoop, NSRunLoopCommonModes, ^
|
|
||||||
{
|
|
||||||
[self errorOccured];
|
|
||||||
});
|
|
||||||
|
|
||||||
CFRunLoopWakeUp(eventsRunLoop.getCFRunLoop);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, (__bridge CFTypeRef)[NSNumber numberWithLongLong:offset]) != TRUE)
|
if (CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, (__bridge CFTypeRef)[NSNumber numberWithLongLong:offset]) != TRUE)
|
||||||
{
|
{
|
||||||
position = 0;
|
position = 0;
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,6 @@
|
||||||
#import "libkern/OSAtomic.h"
|
#import "libkern/OSAtomic.h"
|
||||||
#import "AudioToolbox/AudioToolbox.h"
|
#import "AudioToolbox/AudioToolbox.h"
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface STKQueueEntry : NSObject
|
@interface STKQueueEntry : NSObject
|
||||||
{
|
{
|
||||||
@public
|
@public
|
||||||
|
|
@ -24,27 +22,25 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
UInt64 audioDataByteCount;
|
UInt64 audioDataByteCount;
|
||||||
UInt32 packetBufferSize;
|
UInt32 packetBufferSize;
|
||||||
volatile Float64 seekTime;
|
volatile Float64 seekTime;
|
||||||
volatile SInt64 framesQueued;
|
volatile int64_t framesQueued;
|
||||||
volatile SInt64 framesPlayed;
|
volatile int64_t framesPlayed;
|
||||||
volatile SInt64 lastFrameQueued;
|
volatile int64_t lastFrameQueued;
|
||||||
volatile int processedPacketsCount;
|
volatile int processedPacketsCount;
|
||||||
volatile int processedPacketsSizeTotal;
|
volatile int processedPacketsSizeTotal;
|
||||||
AudioStreamBasicDescription audioStreamBasicDescription;
|
AudioStreamBasicDescription audioStreamBasicDescription;
|
||||||
double durationHint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@property (readonly) UInt64 audioDataLengthInBytes;
|
@property (readonly) UInt64 audioDataLengthInBytes;
|
||||||
@property (readwrite, retain) NSObject* queueItemId;
|
@property (readwrite, retain) NSObject* queueItemId;
|
||||||
@property (readwrite, retain) STKDataSource* dataSource;
|
@property (readwrite, retain) STKDataSource* dataSource;
|
||||||
|
|
||||||
-(instancetype) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
|
-(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
|
||||||
|
|
||||||
-(void) reset;
|
-(void) reset;
|
||||||
-(double) duration;
|
-(double) duration;
|
||||||
-(Float64) progressInFrames;
|
-(Float64) progressInFrames;
|
||||||
-(double) calculatedBitRate;
|
-(double) calculatedBitRate;
|
||||||
|
-(void) updateAudioDataSource;
|
||||||
-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription;
|
-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
@ -9,21 +9,17 @@
|
||||||
#import "STKQueueEntry.h"
|
#import "STKQueueEntry.h"
|
||||||
#import "STKDataSource.h"
|
#import "STKDataSource.h"
|
||||||
|
|
||||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN (2)
|
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64)
|
||||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED (64)
|
|
||||||
|
|
||||||
@implementation STKQueueEntry
|
@implementation STKQueueEntry
|
||||||
|
|
||||||
-(instancetype) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
|
-(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
|
||||||
{
|
{
|
||||||
if (self = [super init])
|
if (self = [super init])
|
||||||
{
|
{
|
||||||
self->spinLock = OS_SPINLOCK_INIT;
|
|
||||||
|
|
||||||
self.dataSource = dataSourceIn;
|
self.dataSource = dataSourceIn;
|
||||||
self.queueItemId = queueItemIdIn;
|
self.queueItemId = queueItemIdIn;
|
||||||
self->lastFrameQueued = -1;
|
self->lastFrameQueued = -1;
|
||||||
self->durationHint = dataSourceIn.durationHint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
|
|
@ -42,16 +38,13 @@
|
||||||
{
|
{
|
||||||
double retval;
|
double retval;
|
||||||
|
|
||||||
if (packetDuration > 0)
|
if (packetDuration && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS)
|
||||||
{
|
{
|
||||||
if (processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED || (audioStreamBasicDescription.mBytesPerFrame == 0 && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN))
|
double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount;
|
||||||
{
|
|
||||||
double averagePacketByteSize = (double)processedPacketsSizeTotal / (double)processedPacketsCount;
|
retval = averagePacketByteSize / packetDuration * 8;
|
||||||
|
|
||||||
retval = averagePacketByteSize / packetDuration * 8;
|
return retval;
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8;
|
retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8;
|
||||||
|
|
@ -59,10 +52,21 @@
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-(void) updateAudioDataSource
|
||||||
|
{
|
||||||
|
if ([self.dataSource conformsToProtocol:@protocol(AudioDataSource)])
|
||||||
|
{
|
||||||
|
double calculatedBitrate = [self calculatedBitRate];
|
||||||
|
|
||||||
|
id<AudioDataSource> audioDataSource = (id<AudioDataSource>)self.dataSource;
|
||||||
|
|
||||||
|
audioDataSource.averageBitRate = calculatedBitrate;
|
||||||
|
audioDataSource.audioDataOffset = audioDataOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
-(double) duration
|
-(double) duration
|
||||||
{
|
{
|
||||||
if (durationHint > 0.0) return durationHint;
|
|
||||||
|
|
||||||
if (self->sampleRate <= 0)
|
if (self->sampleRate <= 0)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -110,7 +114,7 @@
|
||||||
-(Float64) progressInFrames
|
-(Float64) progressInFrames
|
||||||
{
|
{
|
||||||
OSSpinLockLock(&self->spinLock);
|
OSSpinLockLock(&self->spinLock);
|
||||||
Float64 retval = (self->seekTime * self->audioStreamBasicDescription.mSampleRate) + self->framesPlayed;
|
Float64 retval = self->seekTime + self->framesPlayed;
|
||||||
OSSpinLockUnlock(&self->spinLock);
|
OSSpinLockUnlock(&self->spinLock);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
|
|
@ -121,4 +125,4 @@
|
||||||
return [[self queueItemId] description];
|
return [[self queueItemId] description];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>${EXECUTABLE_NAME}</string>
|
<string>${EXECUTABLE_NAME}</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue