Compare commits
222 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
6b6a5ad434 | |
|
|
7def768397 | |
|
|
532bac1e07 | |
|
|
15a9f6d395 | |
|
|
d13282d84e | |
|
|
dfe6c73621 | |
|
|
2d251d5150 | |
|
|
2043330287 | |
|
|
3cb6349c97 | |
|
|
b51035a267 | |
|
|
0d0280b631 | |
|
|
f9f8199015 | |
|
|
d9a6ba7248 | |
|
|
6264442f58 | |
|
|
830ed0f3db | |
|
|
6ef69bacf0 | |
|
|
a84db95be6 | |
|
|
d8b77ae214 | |
|
|
499e54731d | |
|
|
da71b04aaf | |
|
|
55a314b966 | |
|
|
8fa821a944 | |
|
|
0f69b7ea76 | |
|
|
4d0fccdd70 | |
|
|
5909657368 | |
|
|
50bec46acc | |
|
|
dec8b87498 | |
|
|
f872de223d | |
|
|
f84f1ef0bd | |
|
|
3bc3a85df3 | |
|
|
520f98a6b3 | |
|
|
4f72249c94 | |
|
|
cbdb01f0a9 | |
|
|
30815ae23a | |
|
|
7960ffcace | |
|
|
36a5f1cd22 | |
|
|
d52a2a39c2 | |
|
|
6f14432449 | |
|
|
726ec86d77 | |
|
|
15b0242305 | |
|
|
de99ec9d7a | |
|
|
39f0d8bdfe | |
|
|
8708e48395 | |
|
|
94e6cbf41b | |
|
|
a5b6360b1c | |
|
|
09e5602464 | |
|
|
4c7ce6c4ec | |
|
|
5784504e38 | |
|
|
e73df7fd86 | |
|
|
9a4b4a617d | |
|
|
7d4d7b847e | |
|
|
61cb8a8a7b | |
|
|
8e2e451e9c | |
|
|
88be5b33c6 | |
|
|
50a8b610c4 | |
|
|
c1b3b5d8cc | |
|
|
477b1f175f | |
|
|
000930a295 | |
|
|
269f335ee4 | |
|
|
162d964372 | |
|
|
ef7f42c97e | |
|
|
ea9e40b17a | |
|
|
9510d74e58 | |
|
|
c7e90e4d2e | |
|
|
cba7db8112 | |
|
|
1333f7f025 | |
|
|
ff779b669b | |
|
|
b07270910b | |
|
|
4d9cea0a31 | |
|
|
acdf65c7cb | |
|
|
ac951bfc7a | |
|
|
8c64914314 | |
|
|
a9dfb2eddf | |
|
|
3fcf054a23 | |
|
|
a615419404 | |
|
|
e43a4613f8 | |
|
|
ca928dfe1e | |
|
|
d7d583c3ba | |
|
|
7540045361 | |
|
|
972ae0e15b | |
|
|
e3ed6c6dee | |
|
|
3398e8c64e | |
|
|
f99201f54d | |
|
|
44b9e7d2d1 | |
|
|
7aae2bcb6b | |
|
|
5a8068b859 | |
|
|
728fc5bb21 | |
|
|
c4053c964e | |
|
|
c31df15a43 | |
|
|
923baf5b89 | |
|
|
9199785202 | |
|
|
0618027252 | |
|
|
188f880f5a | |
|
|
ae9cee68f0 | |
|
|
eeece64417 | |
|
|
243dc1f8a2 | |
|
|
8c608440ae | |
|
|
9aed1b082a | |
|
|
6eb149f83a | |
|
|
1243dbf0e1 | |
|
|
a15c2c27ff | |
|
|
aa441045aa | |
|
|
569764d869 | |
|
|
511b756694 | |
|
|
ab0c4d1315 | |
|
|
6216abb0ab | |
|
|
8c5b4fb298 | |
|
|
03e9b8b208 | |
|
|
60d48a0682 | |
|
|
dee6322751 | |
|
|
5e3048a7bf | |
|
|
ac7aabf746 | |
|
|
ce30f0de57 | |
|
|
a58269fcdb | |
|
|
3ecba8c8b4 | |
|
|
c98b673064 | |
|
|
13fc64baa2 | |
|
|
5e4b500785 | |
|
|
693c5ed059 | |
|
|
a15405b6c5 | |
|
|
ce6f2b9512 | |
|
|
63bb19747f | |
|
|
0a6e1d4534 | |
|
|
d49e2849eb | |
|
|
f725ed0b2c | |
|
|
55a96578b4 | |
|
|
16855e81dc | |
|
|
fed674d774 | |
|
|
c6e1b113dc | |
|
|
43a9f8b26f | |
|
|
db5b9d92ed | |
|
|
5398bfcedb | |
|
|
d63a99b5fb | |
|
|
1c78dc5867 | |
|
|
4df5a84569 | |
|
|
70368a09ba | |
|
|
018da7a2eb | |
|
|
d347e5e80c | |
|
|
4d73784338 | |
|
|
98c7d9b8f0 | |
|
|
56b2dd4ace | |
|
|
a9b1e143a0 | |
|
|
c4a7251d19 | |
|
|
ae63d04b87 | |
|
|
dd7165b3d2 | |
|
|
8c94b9ba5c | |
|
|
c2fe2c68b2 | |
|
|
83bc40d5fc | |
|
|
b903ee58cc | |
|
|
70fe37cd4a | |
|
|
f321f1a0d7 | |
|
|
a0a9553631 | |
|
|
ddc6cf4b2f | |
|
|
22e954a5e2 | |
|
|
e7a952d248 | |
|
|
511185c929 | |
|
|
15b18069e3 | |
|
|
6df11418f7 | |
|
|
9e04cc9e72 | |
|
|
cdb6f27a53 | |
|
|
b8528e30c5 | |
|
|
d5e531c602 | |
|
|
8d61f3a3e3 | |
|
|
b0466171f6 | |
|
|
10d799a35f | |
|
|
08eaa7e85b | |
|
|
cd0dd97ebf | |
|
|
b2eeb76c90 | |
|
|
5a6d93d7c1 | |
|
|
8dd611c2ac | |
|
|
4a0f944550 | |
|
|
0b5b6ada88 | |
|
|
f60e16c804 | |
|
|
f3f52cf1cd | |
|
|
53667f8a35 | |
|
|
6beebca443 | |
|
|
6af12221e5 | |
|
|
ca80243083 | |
|
|
804aa9dd6e | |
|
|
0fdb461d90 | |
|
|
313a55a5ca | |
|
|
ba8b29d106 | |
|
|
5b629665d6 | |
|
|
7608a42d29 | |
|
|
381795fed6 | |
|
|
bd1d3c9aea | |
|
|
6ada345b17 | |
|
|
e495130219 | |
|
|
e843a0b730 | |
|
|
b7f87aea98 | |
|
|
ca7abd75c1 | |
|
|
951f905516 | |
|
|
bd8e6a5da9 | |
|
|
48ce1f9e67 | |
|
|
257532c6c9 | |
|
|
f85464236a | |
|
|
b47771ae48 | |
|
|
48e082ab88 | |
|
|
b68e813750 | |
|
|
b4948882e4 | |
|
|
c100cb7202 | |
|
|
593a08a04f | |
|
|
1c9bd328e7 | |
|
|
dde738dffd | |
|
|
a9bd3557a5 | |
|
|
431dd5d7f3 | |
|
|
aaaed1f33f | |
|
|
3563fb7ea1 | |
|
|
c9ba30b692 | |
|
|
c68384ec50 | |
|
|
756504f33b | |
|
|
894dce8761 | |
|
|
c3a35fcdf3 | |
|
|
ce3f938309 | |
|
|
429c156d8b | |
|
|
0eb7687a63 | |
|
|
9b952de734 | |
|
|
93e6f4b6d9 | |
|
|
9ed755cda4 | |
|
|
7cc3f85a59 | |
|
|
a93d6867c5 | |
|
|
4660f0ce80 |
|
|
@ -23,6 +23,10 @@
|
|||
A1115967188D6AEE00641365 /* AudioPlayerView.m in Sources */ = {isa = PBXBuildFile; fileRef = A1115966188D6AEE00641365 /* AudioPlayerView.m */; };
|
||||
A111596C188D6C8100641365 /* sample.m4a in Resources */ = {isa = PBXBuildFile; fileRef = A111596B188D6C8100641365 /* sample.m4a */; };
|
||||
A111596F188D6DB100641365 /* SampleQueueId.m in Sources */ = {isa = PBXBuildFile; fileRef = A111596E188D6DB100641365 /* SampleQueueId.m */; };
|
||||
A142571D189079BE005F0129 /* airplane.aac in Resources */ = {isa = PBXBuildFile; fileRef = A142571C18907861005F0129 /* airplane.aac */; };
|
||||
A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */; };
|
||||
A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F5E491189EB3F20070B03F /* AVFoundation.framework */; };
|
||||
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -58,6 +62,11 @@
|
|||
A111596B188D6C8100641365 /* sample.m4a */ = {isa = PBXFileReference; lastKnownFileType = file; name = sample.m4a; path = Resources/sample.m4a; sourceTree = "<group>"; };
|
||||
A111596D188D6DB100641365 /* SampleQueueId.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SampleQueueId.h; sourceTree = "<group>"; };
|
||||
A111596E188D6DB100641365 /* SampleQueueId.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SampleQueueId.m; sourceTree = "<group>"; };
|
||||
A142571C18907861005F0129 /* airplane.aac */ = {isa = PBXFileReference; lastKnownFileType = file; path = airplane.aac; sourceTree = "<group>"; };
|
||||
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
|
||||
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
||||
A1F5E491189EB3F20070B03F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -65,6 +74,9 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */,
|
||||
A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */,
|
||||
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */,
|
||||
A1115964188D691500641365 /* libStreamingKit.a in Frameworks */,
|
||||
A1115937188D686000641365 /* CoreGraphics.framework in Frameworks */,
|
||||
A1115939188D686000641365 /* UIKit.framework in Frameworks */,
|
||||
|
|
@ -107,6 +119,10 @@
|
|||
A1115933188D686000641365 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */,
|
||||
A1F5E491189EB3F20070B03F /* AVFoundation.framework */,
|
||||
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */,
|
||||
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */,
|
||||
A1115963188D691500641365 /* libStreamingKit.a */,
|
||||
A1115934188D686000641365 /* Foundation.framework */,
|
||||
A1115936188D686000641365 /* CoreGraphics.framework */,
|
||||
|
|
@ -164,6 +180,7 @@
|
|||
A111596A188D6C4B00641365 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A142571C18907861005F0129 /* airplane.aac */,
|
||||
A111596B188D6C8100641365 /* sample.m4a */,
|
||||
);
|
||||
name = Resources;
|
||||
|
|
@ -213,7 +230,7 @@
|
|||
A1115929188D686000641365 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0500;
|
||||
LastUpgradeCheck = 0710;
|
||||
ORGANIZATIONNAME = "Thong Nguyen";
|
||||
TargetAttributes = {
|
||||
A111594B188D686000641365 = {
|
||||
|
|
@ -247,6 +264,7 @@
|
|||
A111593F188D686000641365 /* InfoPlist.strings in Resources */,
|
||||
A111596C188D6C8100641365 /* sample.m4a in Resources */,
|
||||
A1115947188D686000641365 /* Images.xcassets in Resources */,
|
||||
A142571D189079BE005F0129 /* airplane.aac in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -314,7 +332,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
|
|
@ -329,6 +346,7 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
|
|
@ -348,7 +366,7 @@
|
|||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
|
|
@ -359,7 +377,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
|
|
@ -387,7 +404,7 @@
|
|||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
|
@ -399,13 +416,14 @@
|
|||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
||||
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/StreamingKit-dabtccqthoyywldsgcvllbcjcmma/Build/Products/Debug-iphoneos",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
LLVM_LTO = YES;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
|
|
@ -419,10 +437,10 @@
|
|||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
||||
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/StreamingKit-dabtccqthoyywldsgcvllbcjcmma/Build/Products/Debug-iphoneos",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
LLVM_LTO = YES;
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
|
|
@ -431,7 +449,6 @@
|
|||
A1115961188D686000641365 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
|
|
@ -445,6 +462,7 @@
|
|||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
|
|
@ -454,7 +472,6 @@
|
|||
A1115962188D686000641365 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleApp.app/ExampleApp";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
|
|
@ -464,6 +481,7 @@
|
|||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
|
||||
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,9 @@
|
|||
#import "AppDelegate.h"
|
||||
#import "STKAudioPlayer.h"
|
||||
#import "AudioPlayerView.h"
|
||||
#import "STKAutoRecoveringHTTPDataSource.h"
|
||||
#import "SampleQueueId.h"
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@interface AppDelegate()
|
||||
{
|
||||
|
|
@ -19,40 +21,89 @@
|
|||
|
||||
@implementation AppDelegate
|
||||
|
||||
|
||||
-(BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
|
||||
{
|
||||
NSError* 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.rootViewController = [[UIViewController alloc] init];
|
||||
|
||||
self.window.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
audioPlayer = [[STKAudioPlayer alloc] init];
|
||||
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds];
|
||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
|
||||
audioPlayer.meteringEnabled = YES;
|
||||
audioPlayer.volume = 1;
|
||||
|
||||
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
|
||||
|
||||
audioPlayerView.delegate = self;
|
||||
audioPlayerView.audioPlayer = audioPlayer;
|
||||
|
||||
[self.window addSubview:audioPlayerView];
|
||||
|
||||
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
||||
[self becomeFirstResponder];
|
||||
|
||||
[self.window makeKeyAndVisible];
|
||||
|
||||
[self.window.rootViewController.view addSubview:audioPlayerView];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(BOOL) canBecomeFirstResponder
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
|
||||
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
||||
|
||||
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
||||
|
||||
[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
|
||||
{
|
||||
NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"];
|
||||
NSURL* url = [NSURL fileURLWithPath:path];
|
||||
|
||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
||||
|
||||
[audioPlayer queueDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
}
|
||||
|
||||
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView
|
||||
{
|
||||
NSString * path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"];
|
||||
NSString* path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"];
|
||||
NSURL* url = [NSURL fileURLWithPath:path];
|
||||
|
||||
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
||||
|
||||
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
}
|
||||
|
||||
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView
|
||||
{
|
||||
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/perfectly.wav"];
|
||||
|
||||
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
|
||||
|
||||
[audioPlayer queueDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -39,20 +39,35 @@
|
|||
|
||||
@protocol AudioPlayerViewDelegate<NSObject>
|
||||
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
|
||||
-(void) audioPlayerViewPlayFromIcecastSelected:(AudioPlayerView*)audioPlayerView;
|
||||
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView;
|
||||
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView;
|
||||
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView;
|
||||
@end
|
||||
|
||||
@interface AudioPlayerView : UIView<STKAudioPlayerDelegate>
|
||||
{
|
||||
@private
|
||||
NSTimer* timer;
|
||||
UILabel* label;
|
||||
UILabel* statusLabel;
|
||||
UISlider* slider;
|
||||
UISwitch* enableEqSwitch;
|
||||
UISwitch* repeatSwitch;
|
||||
UIButton* muteButton;
|
||||
UIButton* playButton;
|
||||
UIButton* stopButton;
|
||||
UIButton* playFromHTTPButton;
|
||||
UIButton* playFromIcecastButton;
|
||||
UIButton* queueShortFileButton;
|
||||
UIButton* queuePcmWaveFileFromHTTPButton;
|
||||
UIButton* playFromLocalFileButton;
|
||||
UIView* meter;
|
||||
}
|
||||
|
||||
@property (readwrite, retain) STKAudioPlayer* audioPlayer;
|
||||
@property (readwrite, unsafe_unretained) id<AudioPlayerViewDelegate> delegate;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayer;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -47,37 +47,97 @@
|
|||
@implementation AudioPlayerView
|
||||
@synthesize audioPlayer, delegate;
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayerIn
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
CGSize size = CGSizeMake(180, 50);
|
||||
self.audioPlayer = audioPlayerIn;
|
||||
|
||||
CGSize size = CGSizeMake(220, 50);
|
||||
|
||||
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
playFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15, size.width, size.height);
|
||||
playFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
|
||||
[playFromHTTPButton addTarget:self action:@selector(playFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||
[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.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 50, size.width, size.height);
|
||||
playFromLocalFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 70, size.width, size.height);
|
||||
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
|
||||
|
||||
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
playButton.frame = CGRectMake((320 - size.width) / 2, 350, size.width, size.height);
|
||||
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
queueShortFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 105, size.width, size.height);
|
||||
[queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
|
||||
|
||||
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 290, 280, 20)];
|
||||
queuePcmWaveFileFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 140, size.width, size.height);
|
||||
[queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
|
||||
[queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal];
|
||||
|
||||
size = CGSizeMake(90, 40);
|
||||
|
||||
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
playButton.frame = CGRectMake(30, 400, size.width, size.height);
|
||||
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
stopButton.frame = CGRectMake((frame.size.width - size.width) - 30, 400, size.width, size.height);
|
||||
[stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[stopButton setTitle:@"Stop" forState:UIControlStateNormal];
|
||||
|
||||
muteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
muteButton.frame = CGRectMake((frame.size.width - size.width) - 30, 430, size.width, size.height);
|
||||
[muteButton addTarget:self action:@selector(muteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
|
||||
[muteButton setTitle:@"Mute" forState:UIControlStateNormal];
|
||||
|
||||
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, queuePcmWaveFileFromHTTPButton.frame.origin.y + queuePcmWaveFileFromHTTPButton.frame.size.height + 20, 20)];
|
||||
slider.continuous = YES;
|
||||
[slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged];
|
||||
|
||||
size = CGSizeMake(80, 50);
|
||||
|
||||
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(30, 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.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.textAlignment = NSTextAlignmentCenter;
|
||||
|
||||
meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)];
|
||||
|
||||
meter.backgroundColor = [UIColor greenColor];
|
||||
|
||||
[self addSubview:slider];
|
||||
[self addSubview:playButton];
|
||||
[self addSubview:playFromHTTPButton];
|
||||
[self addSubview:playFromIcecastButton];
|
||||
[self addSubview:playFromLocalFileButton];
|
||||
|
||||
[self addSubview:queueShortFileButton];
|
||||
[self addSubview:queuePcmWaveFileFromHTTPButton];
|
||||
[self addSubview:repeatSwitch];
|
||||
[self addSubview:label];
|
||||
[self addSubview:statusLabel];
|
||||
[self addSubview:stopButton];
|
||||
[self addSubview:meter];
|
||||
[self addSubview:muteButton];
|
||||
[self addSubview:enableEqSwitch];
|
||||
|
||||
[self setupTimer];
|
||||
[self updateControls];
|
||||
}
|
||||
|
|
@ -85,6 +145,11 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
-(void) onEnableEqSwitch
|
||||
{
|
||||
audioPlayer.equalizerEnabled = self->enableEqSwitch.on;
|
||||
}
|
||||
|
||||
-(void) sliderChanged
|
||||
{
|
||||
if (!audioPlayer)
|
||||
|
|
@ -99,24 +164,55 @@
|
|||
|
||||
-(void) setupTimer
|
||||
{
|
||||
timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(tick) userInfo:nil repeats:YES];
|
||||
timer = [NSTimer timerWithTimeInterval:0.001 target:self selector:@selector(tick) userInfo:nil repeats:YES];
|
||||
|
||||
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
-(void) tick
|
||||
{
|
||||
if (!audioPlayer || audioPlayer.duration == 0)
|
||||
if (!audioPlayer)
|
||||
{
|
||||
slider.value = 0;
|
||||
label.text = @"";
|
||||
statusLabel.text = @"";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
slider.minimumValue = 0;
|
||||
slider.maximumValue = audioPlayer.duration;
|
||||
if (audioPlayer.currentlyPlayingQueueItemId == nil)
|
||||
{
|
||||
slider.value = 0;
|
||||
slider.minimumValue = 0;
|
||||
slider.maximumValue = 0;
|
||||
|
||||
label.text = @"";
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioPlayer.duration != 0)
|
||||
{
|
||||
slider.minimumValue = 0;
|
||||
slider.maximumValue = audioPlayer.duration;
|
||||
slider.value = audioPlayer.progress;
|
||||
|
||||
label.text = [NSString stringWithFormat:@"%@ - %@", [self formatTimeFromSeconds:audioPlayer.progress], [self formatTimeFromSeconds:audioPlayer.duration]];
|
||||
}
|
||||
else
|
||||
{
|
||||
slider.value = 0;
|
||||
slider.minimumValue = 0;
|
||||
slider.maximumValue = 0;
|
||||
|
||||
label.text = [NSString stringWithFormat:@"Live stream %@", [self formatTimeFromSeconds:audioPlayer.progress]];
|
||||
}
|
||||
|
||||
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
|
||||
|
||||
slider.value = audioPlayer.progress;
|
||||
CGFloat newWidth = 320 * (([audioPlayer averagePowerInDecibelsForChannel:1] + 60) / 60);
|
||||
|
||||
meter.frame = CGRectMake(0, 460, newWidth, 20);
|
||||
}
|
||||
|
||||
-(void) playFromHTTPButtonTouched
|
||||
|
|
@ -124,19 +220,53 @@
|
|||
[self.delegate audioPlayerViewPlayFromHTTPSelected:self];
|
||||
}
|
||||
|
||||
-(void) playFromIcecasButtonTouched
|
||||
{
|
||||
[self.delegate audioPlayerViewPlayFromIcecastSelected:self];
|
||||
}
|
||||
|
||||
-(void) playFromLocalFileButtonTouched
|
||||
{
|
||||
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
|
||||
}
|
||||
|
||||
-(void) queueShortFileButtonTouched
|
||||
{
|
||||
[self.delegate audioPlayerViewQueueShortFileSelected:self];
|
||||
}
|
||||
|
||||
-(void) queuePcmWaveFileButtonTouched
|
||||
{
|
||||
[self.delegate audioPlayerViewQueuePcmWaveFileSelected:self];
|
||||
}
|
||||
|
||||
-(void) muteButtonPressed
|
||||
{
|
||||
audioPlayer.muted = !audioPlayer.muted;
|
||||
|
||||
if (audioPlayer.muted)
|
||||
{
|
||||
[muteButton setTitle:@"Unmute" forState:UIControlStateNormal];
|
||||
}
|
||||
else
|
||||
{
|
||||
[muteButton setTitle:@"Mute" forState:UIControlStateNormal];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) stopButtonPressed
|
||||
{
|
||||
[audioPlayer stop];
|
||||
}
|
||||
|
||||
-(void) playButtonPressed
|
||||
{
|
||||
if (!audioPlayer)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (audioPlayer.state == AudioPlayerStatePaused)
|
||||
|
||||
if (audioPlayer.state == STKAudioPlayerStatePaused)
|
||||
{
|
||||
[audioPlayer resume];
|
||||
}
|
||||
|
|
@ -146,24 +276,36 @@
|
|||
}
|
||||
}
|
||||
|
||||
-(NSString*) formatTimeFromSeconds:(int)totalSeconds
|
||||
{
|
||||
|
||||
int seconds = totalSeconds % 60;
|
||||
int minutes = (totalSeconds / 60) % 60;
|
||||
int hours = totalSeconds / 3600;
|
||||
|
||||
return [NSString stringWithFormat:@"%02d:%02d:%02d", hours, minutes, seconds];
|
||||
}
|
||||
|
||||
-(void) updateControls
|
||||
{
|
||||
if (audioPlayer == nil)
|
||||
{
|
||||
[playButton setTitle:@"Play" forState:UIControlStateNormal];
|
||||
[playButton setTitle:@"" forState:UIControlStateNormal];
|
||||
}
|
||||
else if (audioPlayer.state == AudioPlayerStatePaused)
|
||||
else if (audioPlayer.state == STKAudioPlayerStatePaused)
|
||||
{
|
||||
[playButton setTitle:@"Resume" forState:UIControlStateNormal];
|
||||
}
|
||||
else if (audioPlayer.state == AudioPlayerStatePlaying)
|
||||
else if (audioPlayer.state & STKAudioPlayerStatePlaying)
|
||||
{
|
||||
[playButton setTitle:@"Pause" forState:UIControlStateNormal];
|
||||
}
|
||||
else
|
||||
{
|
||||
[playButton setTitle:@"Play" forState:UIControlStateNormal];
|
||||
[playButton setTitle:@"" forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
[self tick];
|
||||
}
|
||||
|
||||
-(void) setAudioPlayer:(STKAudioPlayer*)value
|
||||
|
|
@ -184,12 +326,12 @@
|
|||
return audioPlayer;
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(AudioPlayerState)state
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState
|
||||
{
|
||||
[self updateControls];
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(AudioPlayerErrorCode)errorCode
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode
|
||||
{
|
||||
[self updateControls];
|
||||
}
|
||||
|
|
@ -209,14 +351,17 @@
|
|||
|
||||
// This queues on the currently playing track to be buffered and played immediately after (gapless)
|
||||
|
||||
SampleQueueId* queueId = (SampleQueueId*)queueItemId;
|
||||
if (repeatSwitch.on)
|
||||
{
|
||||
SampleQueueId* queueId = (SampleQueueId*)queueItemId;
|
||||
|
||||
NSLog(@"Requeuing: %@", [queueId.url description]);
|
||||
NSLog(@"Requeuing: %@", [queueId.url description]);
|
||||
|
||||
[self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]];
|
||||
[self->audioPlayer queueDataSource:[STKAudioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration
|
||||
{
|
||||
[self updateControls];
|
||||
|
||||
|
|
@ -225,4 +370,9 @@
|
|||
NSLog(@"Finished: %@", [queueId.url description]);
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer *)audioPlayer logInfo:(NSString *)line
|
||||
{
|
||||
NSLog(@"%@", line);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
|
|
@ -24,6 +24,10 @@
|
|||
<string>1.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
|
|
|
|||
|
|
@ -5,16 +5,31 @@
|
|||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,31 @@
|
|||
{
|
||||
"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",
|
||||
"idiom" : "iphone",
|
||||
|
|
@ -8,11 +34,12 @@
|
|||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "retina4",
|
||||
"extent" : "full-screen",
|
||||
"filename" : "TX6sV-2.png",
|
||||
"minimum-system-version" : "7.0",
|
||||
"orientation" : "portrait",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
|
|
@ -42,6 +69,26 @@
|
|||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"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" : {
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
|
|
@ -36,4 +36,9 @@
|
|||
return [((SampleQueueId*)object).url isEqual: self.url] && ((SampleQueueId*)object).count == self.count;
|
||||
}
|
||||
|
||||
-(NSString*) description
|
||||
{
|
||||
return [self.url description];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -7,7 +7,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,537 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A17FFB6818A002BC00BAA7FF /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499ED189E793700E2A2E2 /* AudioUnit.framework */; };
|
||||
A1A499A5189E765800E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499A4189E765800E2A2E2 /* Cocoa.framework */; };
|
||||
A1A499AF189E765800E2A2E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1A499AD189E765800E2A2E2 /* InfoPlist.strings */; };
|
||||
A1A499B1189E765800E2A2E2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A499B0189E765800E2A2E2 /* main.m */; };
|
||||
A1A499B5189E765800E2A2E2 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = A1A499B3189E765800E2A2E2 /* Credits.rtf */; };
|
||||
A1A499B8189E765800E2A2E2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A499B7189E765800E2A2E2 /* AppDelegate.m */; };
|
||||
A1A499BB189E765800E2A2E2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A1A499B9189E765800E2A2E2 /* MainMenu.xib */; };
|
||||
A1A499BD189E765800E2A2E2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1A499BC189E765800E2A2E2 /* Images.xcassets */; };
|
||||
A1A499C4189E765800E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499C3189E765800E2A2E2 /* XCTest.framework */; };
|
||||
A1A499C5189E765800E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499A4189E765800E2A2E2 /* Cocoa.framework */; };
|
||||
A1A499CD189E765800E2A2E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1A499CB189E765800E2A2E2 /* InfoPlist.strings */; };
|
||||
A1A499CF189E765800E2A2E2 /* ExampleAppMacTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A499CE189E765800E2A2E2 /* ExampleAppMacTests.m */; };
|
||||
A1A499EC189E793300E2A2E2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499A9189E765800E2A2E2 /* Foundation.framework */; };
|
||||
A1A499F0189E793D00E2A2E2 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499EF189E793D00E2A2E2 /* CoreAudio.framework */; };
|
||||
A1A499F2189E799400E2A2E2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */; };
|
||||
A1A499F3189E799F00E2A2E2 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499ED189E793700E2A2E2 /* AudioUnit.framework */; };
|
||||
A1A499F5189E79CB00E2A2E2 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */; };
|
||||
A1A499F9189E7A3500E2A2E2 /* libStreamingKitMac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */; };
|
||||
A1A499FA189E7A5600E2A2E2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */; };
|
||||
A1A499FC189E7A6D00E2A2E2 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */; };
|
||||
A1A49A01189E82EC00E2A2E2 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A49A00189E82EC00E2A2E2 /* QuartzCore.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
A1A499C6189E765800E2A2E2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A1A49999189E765800E2A2E2 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = A1A499A0189E765800E2A2E2;
|
||||
remoteInfo = ExampleAppMac;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
A17FFB6618A002AD00BAA7FF /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
A1A499A1189E765800E2A2E2 /* ExampleAppMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleAppMac.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A1A499A4189E765800E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
A1A499A7189E765800E2A2E2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
A1A499A8189E765800E2A2E2 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
A1A499A9189E765800E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
A1A499AC189E765800E2A2E2 /* ExampleAppMac-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ExampleAppMac-Info.plist"; sourceTree = "<group>"; };
|
||||
A1A499AE189E765800E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A1A499B0189E765800E2A2E2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
A1A499B2189E765800E2A2E2 /* ExampleAppMac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExampleAppMac-Prefix.pch"; sourceTree = "<group>"; };
|
||||
A1A499B4189E765800E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
|
||||
A1A499B6189E765800E2A2E2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
A1A499B7189E765800E2A2E2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
A1A499BA189E765800E2A2E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
A1A499BC189E765800E2A2E2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
A1A499C2189E765800E2A2E2 /* ExampleAppMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleAppMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A1A499C3189E765800E2A2E2 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||
A1A499CA189E765800E2A2E2 /* ExampleAppMacTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ExampleAppMacTests-Info.plist"; sourceTree = "<group>"; };
|
||||
A1A499CC189E765800E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A1A499CE189E765800E2A2E2 /* ExampleAppMacTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleAppMacTests.m; sourceTree = "<group>"; };
|
||||
A1A499EA189E76BD00E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libStreamingKitMac.a; path = ../StreamingKit/build/Debug/libStreamingKitMac.a; sourceTree = "<group>"; };
|
||||
A1A499ED189E793700E2A2E2 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
||||
A1A499EF189E793D00E2A2E2 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||
A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; };
|
||||
A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libStreamingKitMac.a; path = ../StreamingKit/build/Debug/libStreamingKitMac.a; sourceTree = "<group>"; };
|
||||
A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
|
||||
A1A499FE189E82DD00E2A2E2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
|
||||
A1A49A00189E82EC00E2A2E2 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
A1A4999E189E765800E2A2E2 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A17FFB6818A002BC00BAA7FF /* AudioUnit.framework in Frameworks */,
|
||||
A1A49A01189E82EC00E2A2E2 /* QuartzCore.framework in Frameworks */,
|
||||
A1A499FC189E7A6D00E2A2E2 /* SystemConfiguration.framework in Frameworks */,
|
||||
A1A499FA189E7A5600E2A2E2 /* AudioToolbox.framework in Frameworks */,
|
||||
A1A499F9189E7A3500E2A2E2 /* libStreamingKitMac.a in Frameworks */,
|
||||
A1A499F0189E793D00E2A2E2 /* CoreAudio.framework in Frameworks */,
|
||||
A1A499EC189E793300E2A2E2 /* Foundation.framework in Frameworks */,
|
||||
A1A499A5189E765800E2A2E2 /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1A499BF189E765800E2A2E2 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A499F5189E79CB00E2A2E2 /* CoreAudioKit.framework in Frameworks */,
|
||||
A1A499F3189E799F00E2A2E2 /* AudioUnit.framework in Frameworks */,
|
||||
A1A499F2189E799400E2A2E2 /* AudioToolbox.framework in Frameworks */,
|
||||
A1A499C5189E765800E2A2E2 /* Cocoa.framework in Frameworks */,
|
||||
A1A499C4189E765800E2A2E2 /* XCTest.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
A1A49998189E765800E2A2E2 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499AA189E765800E2A2E2 /* ExampleAppMac */,
|
||||
A1A499C8189E765800E2A2E2 /* ExampleAppMacTests */,
|
||||
A1A499A3189E765800E2A2E2 /* Frameworks */,
|
||||
A1A499A2189E765800E2A2E2 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499A2189E765800E2A2E2 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499A1189E765800E2A2E2 /* ExampleAppMac.app */,
|
||||
A1A499C2189E765800E2A2E2 /* ExampleAppMacTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499A3189E765800E2A2E2 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A17FFB6618A002AD00BAA7FF /* AVFoundation.framework */,
|
||||
A1A49A00189E82EC00E2A2E2 /* QuartzCore.framework */,
|
||||
A1A499FE189E82DD00E2A2E2 /* CoreGraphics.framework */,
|
||||
A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */,
|
||||
A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */,
|
||||
A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */,
|
||||
A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */,
|
||||
A1A499EF189E793D00E2A2E2 /* CoreAudio.framework */,
|
||||
A1A499ED189E793700E2A2E2 /* AudioUnit.framework */,
|
||||
A1A499EA189E76BD00E2A2E2 /* libStreamingKitMac.a */,
|
||||
A1A499A4189E765800E2A2E2 /* Cocoa.framework */,
|
||||
A1A499C3189E765800E2A2E2 /* XCTest.framework */,
|
||||
A1A499A6189E765800E2A2E2 /* Other Frameworks */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499A6189E765800E2A2E2 /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499A7189E765800E2A2E2 /* AppKit.framework */,
|
||||
A1A499A8189E765800E2A2E2 /* CoreData.framework */,
|
||||
A1A499A9189E765800E2A2E2 /* Foundation.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499AA189E765800E2A2E2 /* ExampleAppMac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499B6189E765800E2A2E2 /* AppDelegate.h */,
|
||||
A1A499B7189E765800E2A2E2 /* AppDelegate.m */,
|
||||
A1A499B9189E765800E2A2E2 /* MainMenu.xib */,
|
||||
A1A499BC189E765800E2A2E2 /* Images.xcassets */,
|
||||
A1A499AB189E765800E2A2E2 /* Supporting Files */,
|
||||
);
|
||||
path = ExampleAppMac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499AB189E765800E2A2E2 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499AC189E765800E2A2E2 /* ExampleAppMac-Info.plist */,
|
||||
A1A499AD189E765800E2A2E2 /* InfoPlist.strings */,
|
||||
A1A499B0189E765800E2A2E2 /* main.m */,
|
||||
A1A499B2189E765800E2A2E2 /* ExampleAppMac-Prefix.pch */,
|
||||
A1A499B3189E765800E2A2E2 /* Credits.rtf */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499C8189E765800E2A2E2 /* ExampleAppMacTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499CE189E765800E2A2E2 /* ExampleAppMacTests.m */,
|
||||
A1A499C9189E765800E2A2E2 /* Supporting Files */,
|
||||
);
|
||||
path = ExampleAppMacTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499C9189E765800E2A2E2 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499CA189E765800E2A2E2 /* ExampleAppMacTests-Info.plist */,
|
||||
A1A499CB189E765800E2A2E2 /* InfoPlist.strings */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
A1A499A0189E765800E2A2E2 /* ExampleAppMac */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = A1A499D2189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMac" */;
|
||||
buildPhases = (
|
||||
A1A4999D189E765800E2A2E2 /* Sources */,
|
||||
A1A4999E189E765800E2A2E2 /* Frameworks */,
|
||||
A1A4999F189E765800E2A2E2 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = ExampleAppMac;
|
||||
productName = ExampleAppMac;
|
||||
productReference = A1A499A1189E765800E2A2E2 /* ExampleAppMac.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
A1A499C1189E765800E2A2E2 /* ExampleAppMacTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = A1A499D5189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMacTests" */;
|
||||
buildPhases = (
|
||||
A1A499BE189E765800E2A2E2 /* Sources */,
|
||||
A1A499BF189E765800E2A2E2 /* Frameworks */,
|
||||
A1A499C0189E765800E2A2E2 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
A1A499C7189E765800E2A2E2 /* PBXTargetDependency */,
|
||||
);
|
||||
name = ExampleAppMacTests;
|
||||
productName = ExampleAppMacTests;
|
||||
productReference = A1A499C2189E765800E2A2E2 /* ExampleAppMacTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
A1A49999189E765800E2A2E2 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0500;
|
||||
ORGANIZATIONNAME = "Thong Nguyen";
|
||||
TargetAttributes = {
|
||||
A1A499C1189E765800E2A2E2 = {
|
||||
TestTargetID = A1A499A0189E765800E2A2E2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = A1A4999C189E765800E2A2E2 /* Build configuration list for PBXProject "ExampleAppMac" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = A1A49998189E765800E2A2E2;
|
||||
productRefGroup = A1A499A2189E765800E2A2E2 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
A1A499A0189E765800E2A2E2 /* ExampleAppMac */,
|
||||
A1A499C1189E765800E2A2E2 /* ExampleAppMacTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
A1A4999F189E765800E2A2E2 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A499AF189E765800E2A2E2 /* InfoPlist.strings in Resources */,
|
||||
A1A499BD189E765800E2A2E2 /* Images.xcassets in Resources */,
|
||||
A1A499B5189E765800E2A2E2 /* Credits.rtf in Resources */,
|
||||
A1A499BB189E765800E2A2E2 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1A499C0189E765800E2A2E2 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A499CD189E765800E2A2E2 /* InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
A1A4999D189E765800E2A2E2 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A499B8189E765800E2A2E2 /* AppDelegate.m in Sources */,
|
||||
A1A499B1189E765800E2A2E2 /* main.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1A499BE189E765800E2A2E2 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A499CF189E765800E2A2E2 /* ExampleAppMacTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
A1A499C7189E765800E2A2E2 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = A1A499A0189E765800E2A2E2 /* ExampleAppMac */;
|
||||
targetProxy = A1A499C6189E765800E2A2E2 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
A1A499AD189E765800E2A2E2 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
A1A499AE189E765800E2A2E2 /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499B3189E765800E2A2E2 /* Credits.rtf */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
A1A499B4189E765800E2A2E2 /* en */,
|
||||
);
|
||||
name = Credits.rtf;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499B9189E765800E2A2E2 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
A1A499BA189E765800E2A2E2 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499CB189E765800E2A2E2 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
A1A499CC189E765800E2A2E2 /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
A1A499D0189E765800E2A2E2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A1A499D1189E765800E2A2E2 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.9;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
A1A499D3189E765800E2A2E2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A1A499D4189E765800E2A2E2 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
A1A499D6189E765800E2A2E2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleAppMac.app/Contents/MacOS/ExampleAppMac";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "ExampleAppMacTests/ExampleAppMacTests-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A1A499D7189E765800E2A2E2 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleAppMac.app/Contents/MacOS/ExampleAppMac";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch";
|
||||
INFOPLIST_FILE = "ExampleAppMacTests/ExampleAppMacTests-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUNDLE_LOADER)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
A1A4999C189E765800E2A2E2 /* Build configuration list for PBXProject "ExampleAppMac" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A1A499D0189E765800E2A2E2 /* Debug */,
|
||||
A1A499D1189E765800E2A2E2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
A1A499D2189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMac" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A1A499D3189E765800E2A2E2 /* Debug */,
|
||||
A1A499D4189E765800E2A2E2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
A1A499D5189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMacTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A1A499D6189E765800E2A2E2 /* Debug */,
|
||||
A1A499D7189E765800E2A2E2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = A1A49999189E765800E2A2E2 /* Project object */;
|
||||
}
|
||||
7
ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:ExampleAppMac.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>B1180E29-F9F8-4232-A985-F8E21716EF14</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>ExampleAppMac</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
|
||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
|
||||
<string>../../..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>110</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>StreamingKit</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
//
|
||||
// AppDelegate.h
|
||||
// ExampleAppMac
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "STKAudioPlayer.h"
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, STKAudioPlayerDelegate>
|
||||
|
||||
@property (assign) IBOutlet NSWindow *window;
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// AppDelegate.m
|
||||
// ExampleAppMac
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
#import "STKAudioPlayer.h"
|
||||
|
||||
@interface AppDelegate()
|
||||
{
|
||||
NSView* meter;
|
||||
NSSlider* slider;
|
||||
STKAudioPlayer* audioPlayer;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
-(void) applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
CGRect frame = [self.window.contentView frame];
|
||||
|
||||
NSButton* playFromHTTPButton = [[NSButton alloc] initWithFrame:CGRectMake(10, 10, frame.size.width - 20, 100)];
|
||||
|
||||
[playFromHTTPButton setTitle:@"Play from HTTP"];
|
||||
[playFromHTTPButton setAction:@selector(playFromHTTP)];
|
||||
|
||||
slider = [[NSSlider alloc] initWithFrame:CGRectMake(10, 140, frame.size.width - 20, 20)];
|
||||
[slider setAction:@selector(sliderChanged:)];
|
||||
|
||||
meter = [[NSView alloc] initWithFrame:CGRectMake(10, 200, 0, 20)];
|
||||
[meter setLayer:[CALayer new]];
|
||||
[meter setWantsLayer:YES];
|
||||
meter.layer.backgroundColor = [NSColor greenColor].CGColor;
|
||||
|
||||
[[self.window contentView] addSubview:slider];
|
||||
[[self.window contentView] addSubview:playFromHTTPButton];
|
||||
[[self.window contentView] addSubview:meter];
|
||||
|
||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} } ];
|
||||
audioPlayer.delegate = self;
|
||||
audioPlayer.meteringEnabled = YES;
|
||||
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];
|
||||
}
|
||||
|
||||
-(void) test
|
||||
{
|
||||
audioPlayer.equalizerEnabled = !audioPlayer.equalizerEnabled;
|
||||
}
|
||||
|
||||
-(void) playFromHTTP
|
||||
{
|
||||
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
||||
}
|
||||
|
||||
-(void) tick:(NSTimer*)timer
|
||||
{
|
||||
if (!audioPlayer)
|
||||
{
|
||||
slider.doubleValue = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat meterWidth = 0;
|
||||
|
||||
if (audioPlayer.currentlyPlayingQueueItemId != nil)
|
||||
{
|
||||
slider.minValue = 0;
|
||||
slider.maxValue = audioPlayer.duration;
|
||||
slider.doubleValue = audioPlayer.progress;
|
||||
|
||||
meterWidth = [self.window.contentView frame].size.width - 20;
|
||||
meterWidth *= (([audioPlayer averagePowerInDecibelsForChannel:0] + 60) / 60);
|
||||
}
|
||||
else
|
||||
{
|
||||
slider.doubleValue = 0;
|
||||
slider.minValue = 0;
|
||||
slider.maxValue = 0;
|
||||
|
||||
meterWidth = 0;
|
||||
}
|
||||
|
||||
CGRect frame = meter.frame;
|
||||
|
||||
frame.size.width = meterWidth;
|
||||
|
||||
meter.frame = frame;
|
||||
}
|
||||
|
||||
-(void) sliderChanged:(NSSlider*)sliderIn
|
||||
{
|
||||
[audioPlayer seekToTime:sliderIn.doubleValue];
|
||||
}
|
||||
|
||||
-(void) updateControls
|
||||
{
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId
|
||||
{
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId
|
||||
{
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState
|
||||
{
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration
|
||||
{
|
||||
}
|
||||
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,467 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4439" systemVersion="13A451" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4439"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" destination="58" id="142"/>
|
||||
<outlet property="delegate" destination="494" id="495"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder">
|
||||
<connections>
|
||||
<action selector="alignCenter:" destination="499" id="518"/>
|
||||
<action selector="alignJustified:" destination="500" id="523"/>
|
||||
<action selector="alignLeft:" destination="498" id="524"/>
|
||||
<action selector="alignRight:" destination="501" id="521"/>
|
||||
<action selector="arrangeInFront:" destination="5" id="39"/>
|
||||
<action selector="capitalizeWord:" destination="466" id="467"/>
|
||||
<action selector="centerSelectionInVisibleArea:" destination="210" id="245"/>
|
||||
<action selector="checkSpelling:" destination="201" id="225"/>
|
||||
<action selector="clearRecentDocuments:" destination="126" id="127"/>
|
||||
<action selector="copy:" destination="197" id="224"/>
|
||||
<action selector="copyFont:" destination="403" id="428"/>
|
||||
<action selector="copyRuler:" destination="506" id="522"/>
|
||||
<action selector="cut:" destination="199" id="228"/>
|
||||
<action selector="delete:" destination="202" id="235"/>
|
||||
<action selector="hide:" destination="134" id="367"/>
|
||||
<action selector="hideOtherApplications:" destination="145" id="368"/>
|
||||
<action selector="loosenKerning:" destination="419" id="435"/>
|
||||
<action selector="lowerBaseline:" destination="410" id="427"/>
|
||||
<action selector="lowercaseWord:" destination="465" id="468"/>
|
||||
<action selector="makeBaseWritingDirectionLeftToRight:" destination="511" id="526"/>
|
||||
<action selector="makeBaseWritingDirectionNatural:" destination="510" id="525"/>
|
||||
<action selector="makeBaseWritingDirectionRightToLeft:" destination="512" id="527"/>
|
||||
<action selector="makeTextWritingDirectionLeftToRight:" destination="516" id="529"/>
|
||||
<action selector="makeTextWritingDirectionNatural:" destination="515" id="528"/>
|
||||
<action selector="makeTextWritingDirectionRightToLeft:" destination="517" id="530"/>
|
||||
<action selector="newDocument:" destination="82" id="373"/>
|
||||
<action selector="openDocument:" destination="72" id="374"/>
|
||||
<action selector="orderFrontColorPanel:" destination="401" id="433"/>
|
||||
<action selector="orderFrontSubstitutionsPanel:" destination="457" id="458"/>
|
||||
<action selector="paste:" destination="203" id="226"/>
|
||||
<action selector="pasteAsPlainText:" destination="485" id="486"/>
|
||||
<action selector="pasteFont:" destination="404" id="436"/>
|
||||
<action selector="pasteRuler:" destination="507" id="519"/>
|
||||
<action selector="performClose:" destination="73" id="193"/>
|
||||
<action selector="performFindPanelAction:" destination="209" id="241"/>
|
||||
<action selector="performFindPanelAction:" destination="208" id="487"/>
|
||||
<action selector="performFindPanelAction:" destination="213" id="488"/>
|
||||
<action selector="performFindPanelAction:" destination="221" id="489"/>
|
||||
<action selector="performFindPanelAction:" destination="534" id="535"/>
|
||||
<action selector="performMiniaturize:" destination="23" id="37"/>
|
||||
<action selector="performZoom:" destination="239" id="240"/>
|
||||
<action selector="print:" destination="78" id="86"/>
|
||||
<action selector="raiseBaseline:" destination="409" id="426"/>
|
||||
<action selector="redo:" destination="215" id="231"/>
|
||||
<action selector="revertDocumentToSaved:" destination="112" id="364"/>
|
||||
<action selector="runPageLayout:" destination="77" id="87"/>
|
||||
<action selector="runToolbarCustomizationPalette:" destination="298" id="365"/>
|
||||
<action selector="saveDocument:" destination="75" id="362"/>
|
||||
<action selector="selectAll:" destination="198" id="232"/>
|
||||
<action selector="showGuessPanel:" destination="204" id="230"/>
|
||||
<action selector="showHelp:" destination="492" id="493"/>
|
||||
<action selector="startSpeaking:" destination="196" id="233"/>
|
||||
<action selector="stopSpeaking:" destination="195" id="227"/>
|
||||
<action selector="subscript:" destination="408" id="429"/>
|
||||
<action selector="superscript:" destination="407" id="430"/>
|
||||
<action selector="tightenKerning:" destination="418" id="431"/>
|
||||
<action selector="toggleAutomaticDashSubstitution:" destination="460" id="461"/>
|
||||
<action selector="toggleAutomaticLinkDetection:" destination="354" id="357"/>
|
||||
<action selector="toggleAutomaticQuoteSubstitution:" destination="351" id="356"/>
|
||||
<action selector="toggleAutomaticSpellingCorrection:" destination="454" id="456"/>
|
||||
<action selector="toggleAutomaticTextReplacement:" destination="462" id="463"/>
|
||||
<action selector="toggleContinuousSpellChecking:" destination="219" id="222"/>
|
||||
<action selector="toggleGrammarChecking:" destination="346" id="347"/>
|
||||
<action selector="toggleRuler:" destination="505" id="520"/>
|
||||
<action selector="toggleSmartInsertDelete:" destination="350" id="355"/>
|
||||
<action selector="toggleToolbarShown:" destination="297" id="366"/>
|
||||
<action selector="turnOffKerning:" destination="417" id="441"/>
|
||||
<action selector="turnOffLigatures:" destination="413" id="440"/>
|
||||
<action selector="underline:" destination="392" id="432"/>
|
||||
<action selector="undo:" destination="207" id="223"/>
|
||||
<action selector="unhideAllApplications:" destination="150" id="370"/>
|
||||
<action selector="unscript:" destination="406" id="437"/>
|
||||
<action selector="uppercaseWord:" destination="452" id="464"/>
|
||||
<action selector="useAllLigatures:" destination="414" id="434"/>
|
||||
<action selector="useStandardKerning:" destination="416" id="438"/>
|
||||
<action selector="useStandardLigatures:" destination="412" id="439"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-3" userLabel="Application">
|
||||
<connections>
|
||||
<action selector="terminate:" destination="136" id="449"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<menu title="AMainMenu" systemMenu="main" id="29">
|
||||
<items>
|
||||
<menuItem title="ExampleAppMac" id="56">
|
||||
<menu key="submenu" title="ExampleAppMac" systemMenu="apple" id="57">
|
||||
<items>
|
||||
<menuItem title="About ExampleAppMac" id="58">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="236">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="129"/>
|
||||
<menuItem isSeparatorItem="YES" id="143">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Services" id="131">
|
||||
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="144">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Hide ExampleAppMac" keyEquivalent="h" id="134"/>
|
||||
<menuItem title="Hide Others" keyEquivalent="h" id="145">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Show All" id="150"/>
|
||||
<menuItem isSeparatorItem="YES" id="149">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Quit ExampleAppMac" keyEquivalent="q" id="136"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="File" id="83">
|
||||
<menu key="submenu" title="File" id="81">
|
||||
<items>
|
||||
<menuItem title="New" keyEquivalent="n" id="82"/>
|
||||
<menuItem title="Open…" keyEquivalent="o" id="72"/>
|
||||
<menuItem title="Open Recent" id="124">
|
||||
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
|
||||
<items>
|
||||
<menuItem title="Clear Menu" id="126"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="79">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Close" keyEquivalent="w" id="73"/>
|
||||
<menuItem title="Save…" keyEquivalent="s" id="75"/>
|
||||
<menuItem title="Revert to Saved" id="112">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="74">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Page Setup..." keyEquivalent="P" id="77">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Print…" keyEquivalent="p" id="78"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Edit" id="217">
|
||||
<menu key="submenu" title="Edit" id="205">
|
||||
<items>
|
||||
<menuItem title="Undo" keyEquivalent="z" id="207"/>
|
||||
<menuItem title="Redo" keyEquivalent="Z" id="215">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="206">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Cut" keyEquivalent="x" id="199"/>
|
||||
<menuItem title="Copy" keyEquivalent="c" id="197"/>
|
||||
<menuItem title="Paste" keyEquivalent="v" id="203"/>
|
||||
<menuItem title="Paste and Match Style" keyEquivalent="V" id="485">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Delete" id="202"/>
|
||||
<menuItem title="Select All" keyEquivalent="a" id="198"/>
|
||||
<menuItem isSeparatorItem="YES" id="214">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Find" id="218">
|
||||
<menu key="submenu" title="Find" id="220">
|
||||
<items>
|
||||
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209"/>
|
||||
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="534">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
|
||||
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
|
||||
<menuItem title="Jump to Selection" keyEquivalent="j" id="210"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Spelling and Grammar" id="216">
|
||||
<menu key="submenu" title="Spelling and Grammar" id="200">
|
||||
<items>
|
||||
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="204"/>
|
||||
<menuItem title="Check Document Now" keyEquivalent=";" id="201"/>
|
||||
<menuItem isSeparatorItem="YES" id="453"/>
|
||||
<menuItem title="Check Spelling While Typing" id="219"/>
|
||||
<menuItem title="Check Grammar With Spelling" id="346"/>
|
||||
<menuItem title="Correct Spelling Automatically" id="454">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Substitutions" id="348">
|
||||
<menu key="submenu" title="Substitutions" id="349">
|
||||
<items>
|
||||
<menuItem title="Show Substitutions" id="457">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="459"/>
|
||||
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350"/>
|
||||
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351"/>
|
||||
<menuItem title="Smart Dashes" id="460">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
|
||||
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Text Replacement" id="462">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Transformations" id="450">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Transformations" id="451">
|
||||
<items>
|
||||
<menuItem title="Make Upper Case" id="452">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Make Lower Case" id="465">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Capitalize" id="466">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Speech" id="211">
|
||||
<menu key="submenu" title="Speech" id="212">
|
||||
<items>
|
||||
<menuItem title="Start Speaking" id="196"/>
|
||||
<menuItem title="Stop Speaking" id="195"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Format" id="375">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Format" id="376">
|
||||
<items>
|
||||
<menuItem title="Font" id="377">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Font" systemMenu="font" id="388">
|
||||
<items>
|
||||
<menuItem title="Show Fonts" keyEquivalent="t" id="389"/>
|
||||
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390"/>
|
||||
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391"/>
|
||||
<menuItem title="Underline" keyEquivalent="u" id="392"/>
|
||||
<menuItem isSeparatorItem="YES" id="393"/>
|
||||
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394"/>
|
||||
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395"/>
|
||||
<menuItem isSeparatorItem="YES" id="396"/>
|
||||
<menuItem title="Kern" id="397">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Kern" id="415">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="416">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Use None" id="417">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Tighten" id="418">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Loosen" id="419">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Ligatures" id="398">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Ligatures" id="411">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="412">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Use None" id="413">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Use All" id="414">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Baseline" id="399">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Baseline" id="405">
|
||||
<items>
|
||||
<menuItem title="Use Default" id="406">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Superscript" id="407">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Subscript" id="408">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Raise" id="409">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Lower" id="410">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="400"/>
|
||||
<menuItem title="Show Colors" keyEquivalent="C" id="401"/>
|
||||
<menuItem isSeparatorItem="YES" id="402"/>
|
||||
<menuItem title="Copy Style" keyEquivalent="c" id="403">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Paste Style" keyEquivalent="v" id="404">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Text" id="496">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Text" id="497">
|
||||
<items>
|
||||
<menuItem title="Align Left" keyEquivalent="{" id="498"/>
|
||||
<menuItem title="Center" keyEquivalent="|" id="499"/>
|
||||
<menuItem title="Justify" id="500">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Align Right" keyEquivalent="}" id="501"/>
|
||||
<menuItem isSeparatorItem="YES" id="502"/>
|
||||
<menuItem title="Writing Direction" id="503">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Writing Direction" id="508">
|
||||
<items>
|
||||
<menuItem title="Paragraph" enabled="NO" id="509">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="510">
|
||||
<string key="title"> Default</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="511">
|
||||
<string key="title"> Left to Right</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="512">
|
||||
<string key="title"> Right to Left</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="513"/>
|
||||
<menuItem title="Selection" enabled="NO" id="514">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="515">
|
||||
<string key="title"> Default</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="516">
|
||||
<string key="title"> Left to Right</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem id="517">
|
||||
<string key="title"> Right to Left</string>
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="504"/>
|
||||
<menuItem title="Show Ruler" id="505">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
</menuItem>
|
||||
<menuItem title="Copy Ruler" keyEquivalent="c" id="506">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Paste Ruler" keyEquivalent="v" id="507">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="View" id="295">
|
||||
<menu key="submenu" title="View" id="296">
|
||||
<items>
|
||||
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
|
||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Customize Toolbar…" id="298"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Window" id="19">
|
||||
<menu key="submenu" title="Window" systemMenu="window" id="24">
|
||||
<items>
|
||||
<menuItem title="Minimize" keyEquivalent="m" id="23"/>
|
||||
<menuItem title="Zoom" id="239"/>
|
||||
<menuItem isSeparatorItem="YES" id="92">
|
||||
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
|
||||
</menuItem>
|
||||
<menuItem title="Bring All to Front" id="5"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Help" id="490">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Help" systemMenu="help" id="491">
|
||||
<items>
|
||||
<menuItem title="ExampleAppMac Help" keyEquivalent="?" id="492"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
<window title="ExampleAppMac" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
|
||||
<view key="contentView" id="372">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</view>
|
||||
</window>
|
||||
<customObject id="494" customClass="AppDelegate">
|
||||
<connections>
|
||||
<outlet property="window" destination="371" id="532"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="420" customClass="NSFontManager">
|
||||
<connections>
|
||||
<action selector="addFontTrait:" destination="390" id="421"/>
|
||||
<action selector="addFontTrait:" destination="391" id="422"/>
|
||||
<action selector="modifyFont:" destination="395" id="423"/>
|
||||
<action selector="modifyFont:" destination="394" id="425"/>
|
||||
<action selector="orderFrontFontPanel:" destination="389" id="424"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
</objects>
|
||||
</document>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>${MACOSX_DEPLOYMENT_TARGET}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014 Thong Nguyen. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// Prefix header
|
||||
//
|
||||
// The contents of this file are implicitly included at the beginning of every source file.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "16x16",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "16x16",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "32x32",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "32x32",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "128x128",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "128x128",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "256x256",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "256x256",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "512x512",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "mac",
|
||||
"size" : "512x512",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;}
|
||||
{\colortbl;\red255\green255\blue255;}
|
||||
\paperw9840\paperh8400
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
|
||||
|
||||
\f0\b\fs24 \cf0 Engineering:
|
||||
\b0 \
|
||||
Some people\
|
||||
\
|
||||
|
||||
\b Human Interface Design:
|
||||
\b0 \
|
||||
Some other people\
|
||||
\
|
||||
|
||||
\b Testing:
|
||||
\b0 \
|
||||
Hopefully not nobody\
|
||||
\
|
||||
|
||||
\b Documentation:
|
||||
\b0 \
|
||||
Whoever\
|
||||
\
|
||||
|
||||
\b With special thanks to:
|
||||
\b0 \
|
||||
Mom\
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
//
|
||||
// main.m
|
||||
// ExampleAppMac
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// ExampleAppMacTests.m
|
||||
// ExampleAppMacTests
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface ExampleAppMacTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation ExampleAppMacTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testExample
|
||||
{
|
||||
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
6
LICENSE
6
LICENSE
|
|
@ -4,7 +4,7 @@
|
|||
Inspired by Matt Gallagher's AudioStreamer:
|
||||
https://github.com/mattgallagher/AudioStreamer
|
||||
|
||||
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
Copyright (c) 2015 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
|
@ -15,12 +15,12 @@
|
|||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by the <organization>.
|
||||
This product includes software developed by Thong Nguyen.
|
||||
4. Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
|
||||
THIS SOFTWARE IS PROVIDED BY THONG NGUYEN ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
|
|
|
|||
60
README.md
60
README.md
|
|
@ -1,44 +1,66 @@
|
|||
## StreamingKit
|
||||
|
||||
StreamingKit (formally Audjustable) is an audio streaming library for iOS and OSX. StreamingKit uses CoreAudio to decompress and playback audio 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 streaming, encryption/decryption, auto-recovery, dynamic-buffering. Along the way other features such as gapless playback were added.
|
||||
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.
|
||||
|
||||
## Main Features
|
||||
|
||||
* Simple OOP API
|
||||
* Easy to read source
|
||||
* Mostly asynchronous API
|
||||
* Buffered and gapless playback
|
||||
* Easy to implement audio data sources (HTTP and local file system DataSources provided)
|
||||
* Free OSS.
|
||||
* Simple API.
|
||||
* Easy to read source.
|
||||
* 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.
|
||||
* Easy to implement audio data sources (Local, HTTP, AutoRecoveringHTTP DataSources are provided).
|
||||
* Easy to extend DataSource to support adaptive buffering, encryption, etc.
|
||||
* Optimised for low CPU/battery usage
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
## Installation
|
||||
|
||||
StreamingKit is also available as a [Cocoapod](http://cocoapods.org/?q=StreamingKit) and a static lib. You can also simply manually copy all the source files located inside StreamingKit/StreamingKit/* into your project.
|
||||
StreamingKit is available as a [Cocoapod](http://cocoapods.org/?q=StreamingKit). You can also simply copy all the source files located inside StreamingKit/StreamingKit/* into your Xcode project.
|
||||
|
||||
## Example
|
||||
|
||||
There are two main classes. The `STKDataSource` class which is the abstract base class for the various compressed audio data sources (HTTP, local file are provided). The `STKAudioPlayer` class manages and renders audio from a queue DataSources.
|
||||
There are two main classes. The `STKDataSource` class which is the abstract base class for the various compressed audio data sources. The `STKAudioPlayer` class manages and renders audio from a queue DataSources. By default `STKAudioPlayer` will automatically parse URLs and create the appropriate data source internally.
|
||||
|
||||
### Play an MP3 over HTTP
|
||||
|
||||
|
||||
```objective-c
|
||||
|
||||
// Create AudioPlayer
|
||||
|
||||
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
||||
audioPlayer.delegate = self;
|
||||
|
||||
// Queue on a URL to play. Each queue item has a unique ID (item1) that to identify the related file in delegate callbacks
|
||||
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
||||
```
|
||||
|
||||
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"] withQueueItemId:@"item1"];
|
||||
### Gapless playback
|
||||
|
||||
```objective-c
|
||||
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
|
||||
|
||||
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
|
||||
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/airplane.aac"];
|
||||
|
||||
```
|
||||
|
||||
## Background Playback
|
||||
|
||||
Background playback on iOS is easily added to your application by calling the `AudioSessionInitialize` in your AppDelegate and adding audio to the `UIBackgroundModes` key in your plist file.
|
||||
### Intercept PCM data just before its played
|
||||
|
||||
```objective-c
|
||||
[audioPlayer appendFrameFilterWithName:@"MyCustomFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames)
|
||||
{
|
||||
...
|
||||
}];
|
||||
````
|
||||
|
||||
|
||||
## More
|
||||
|
||||
More documentation is available on the project [Wiki](https://github.com/tumtumtum/StreamingKit/wiki/_pages)
|
||||
|
||||
### Authors and Contributors
|
||||
Copyright (c) 2012-2014, Thong Nguyen (@tumtumtum)
|
||||
Copyright (c) 2012-2014, Thong Nguyen ([@tumtumtum](http://www.twitter.com/tumtumtum))
|
||||
|
|
|
|||
|
|
@ -5,9 +5,12 @@ Pod::Spec.new do |s|
|
|||
s.homepage = "https://github.com/tumtumtum/StreamingKit/"
|
||||
s.license = 'MIT'
|
||||
s.author = { "Thong Nguyen" => "tumtumtum@gmail.com" }
|
||||
s.source = { :git => "https://github.com/tumtumtum/StreamingKit.git"}
|
||||
s.source = { :git => "https://github.com/tumtumtum/StreamingKit.git" }
|
||||
s.platform = :ios
|
||||
s.requires_arc = true
|
||||
s.source_files = 'StreamingKit/StreamingKit/*.{h,m}'
|
||||
s.frameworks = 'AVFoundation', 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox'
|
||||
s.ios.deployment_target = '4.3'
|
||||
s.ios.frameworks = 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox'
|
||||
s.osx.deployment_target = '10.7'
|
||||
s.osx.frameworks = 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox', 'AudioUnit'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "StreamingKit"
|
||||
s.version = "0.0.13"
|
||||
s.version = "0.1.29"
|
||||
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.license = 'MIT'
|
||||
|
|
@ -9,5 +9,8 @@ Pod::Spec.new do |s|
|
|||
s.platform = :ios
|
||||
s.requires_arc = true
|
||||
s.source_files = 'StreamingKit/StreamingKit/*.{h,m}'
|
||||
s.frameworks = 'AVFoundation', 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox'
|
||||
s.ios.deployment_target = '4.3'
|
||||
s.ios.frameworks = 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox'
|
||||
s.osx.deployment_target = '10.7'
|
||||
s.osx.frameworks = 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox', 'AudioUnit'
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
<FileRef
|
||||
location = "group:ExampleApp/ExampleApp.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:StreamingKit/../ExampleAppMac/ExampleAppMac.xcodeproj">
|
||||
</FileRef>
|
||||
<FileRef
|
||||
location = "group:StreamingKit/StreamingKit.xcodeproj">
|
||||
</FileRef>
|
||||
|
|
|
|||
|
|
@ -5,36 +5,36 @@
|
|||
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
|
||||
<false/>
|
||||
<key>IDESourceControlProjectIdentifier</key>
|
||||
<string>A5188B80-95E0-46CF-BEC9-273724D5616A</string>
|
||||
<string>64C9BE2D-7DAD-476A-B9AC-1C4603CD4124</string>
|
||||
<key>IDESourceControlProjectName</key>
|
||||
<string>StreamingKit</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</key>
|
||||
<string>https://github.com/tumtumtum/audjustable.git</string>
|
||||
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
|
||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>StreamingKit.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</key>
|
||||
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
|
||||
<string>..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/tumtumtum/audjustable.git</string>
|
||||
<string>https://github.com/tumtumtum/StreamingKit.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>110</integer>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</string>
|
||||
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>821E27F3-2BD4-4B06-BB39-F4C3ECC1BBAD</string>
|
||||
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>audjustable</string>
|
||||
<string>StreamingKit</string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
|
|
|
|||
|
|
@ -7,23 +7,70 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; };
|
||||
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 */; };
|
||||
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; };
|
||||
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
|
||||
A1A4997C189E744500E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; };
|
||||
A1A4997F189E744500E2A2E2 /* libStreamingKitMac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */; };
|
||||
A1A49985189E744500E2A2E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1A49983189E744500E2A2E2 /* InfoPlist.strings */; };
|
||||
A1A49987189E744500E2A2E2 /* StreamingKitMacTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */; };
|
||||
A1A4998E189E745900E2A2E2 /* STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */; };
|
||||
A1A4998F189E745C00E2A2E2 /* STKAutoRecoveringHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */; };
|
||||
A1A49991189E746000E2A2E2 /* STKCoreFoundationDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */; };
|
||||
A1A49992189E746300E2A2E2 /* STKDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F8188D5E550010896F /* STKDataSource.m */; };
|
||||
A1A49993189E746500E2A2E2 /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; };
|
||||
A1A49994189E746900E2A2E2 /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; };
|
||||
A1A49995189E746B00E2A2E2 /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; };
|
||||
A1A49996189E746E00E2A2E2 /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; };
|
||||
A1A49997189E747000E2A2E2 /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; };
|
||||
A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; };
|
||||
A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; };
|
||||
A1E7C4DA188D57F60010896F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
|
||||
A1E7C4DB188D57F60010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; };
|
||||
A1E7C4E0188D57F60010896F /* libStreamingKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4C8188D57F50010896F /* libStreamingKit.a */; };
|
||||
A1E7C4E6188D57F60010896F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1E7C4E4188D57F60010896F /* InfoPlist.strings */; };
|
||||
A1E7C4E8188D57F60010896F /* StreamingKitTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4E7188D57F60010896F /* StreamingKitTests.m */; };
|
||||
A1E7C4FF188D5E550010896F /* STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */; };
|
||||
A1E7C500188D5E550010896F /* STKAutoRecoveringHttpDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */; };
|
||||
A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */; };
|
||||
A1E7C501188D5E550010896F /* STKCoreFoundationDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */; };
|
||||
A1E7C502188D5E550010896F /* STKDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F8188D5E550010896F /* STKDataSource.m */; };
|
||||
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; };
|
||||
A1E7C504188D5E550010896F /* STKHttpDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */; };
|
||||
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; };
|
||||
A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; };
|
||||
A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C507188D62D20010896F /* UIKit.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
A1A4997D189E744500E2A2E2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A1E7C4C0188D57F50010896F /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = A1A49968189E744400E2A2E2;
|
||||
remoteInfo = StreamingKitMac;
|
||||
};
|
||||
A1A499E6189E769A00E2A2E2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = A1A499A1189E765800E2A2E2;
|
||||
remoteInfo = ExampleAppMac;
|
||||
};
|
||||
A1A499E8189E769A00E2A2E2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = A1A499C2189E765800E2A2E2;
|
||||
remoteInfo = ExampleAppMacTests;
|
||||
};
|
||||
A1E7C4DE188D57F60010896F /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A1E7C4C0188D57F50010896F /* Project object */;
|
||||
|
|
@ -46,6 +93,27 @@
|
|||
/* End PBXCopyFilesBuildPhase 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; };
|
||||
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; };
|
||||
A1A4996E189E744500E2A2E2 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
A1A4996F189E744500E2A2E2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
A1A49972189E744500E2A2E2 /* StreamingKitMac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamingKitMac-Prefix.pch"; sourceTree = "<group>"; };
|
||||
A1A49973189E744500E2A2E2 /* StreamingKitMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StreamingKitMac.h; sourceTree = "<group>"; };
|
||||
A1A49974189E744500E2A2E2 /* StreamingKitMac.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitMac.m; sourceTree = "<group>"; };
|
||||
A1A4997A189E744500E2A2E2 /* StreamingKitMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StreamingKitMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A1A49982189E744500E2A2E2 /* StreamingKitMacTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "StreamingKitMacTests-Info.plist"; sourceTree = "<group>"; };
|
||||
A1A49984189E744500E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitMacTests.m; sourceTree = "<group>"; };
|
||||
A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ExampleAppMac.xcodeproj; path = ../ExampleAppMac/ExampleAppMac.xcodeproj; sourceTree = "<group>"; };
|
||||
A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
|
||||
A1BF65D0189A6582004DD08C /* STKQueueEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKQueueEntry.h; sourceTree = "<group>"; };
|
||||
A1BF65D1189A6582004DD08C /* STKQueueEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKQueueEntry.m; sourceTree = "<group>"; };
|
||||
A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+STKAudioPlayer.h"; sourceTree = "<group>"; };
|
||||
A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+STKAudioPlayer.m"; sourceTree = "<group>"; };
|
||||
A1C9767618981BFE0057F881 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
||||
A1E7C4C8188D57F50010896F /* libStreamingKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKit.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A1E7C4CB188D57F50010896F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
A1E7C4CF188D57F50010896F /* StreamingKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamingKit-Prefix.pch"; sourceTree = "<group>"; };
|
||||
|
|
@ -56,28 +124,44 @@
|
|||
A1E7C4E7188D57F60010896F /* StreamingKitTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitTests.m; sourceTree = "<group>"; };
|
||||
A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAudioPlayer.h; sourceTree = "<group>"; };
|
||||
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAudioPlayer.m; sourceTree = "<group>"; };
|
||||
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHttpDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAutoRecoveringHttpDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAutoRecoveringHttpDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKAutoRecoveringHTTPDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKAutoRecoveringHTTPDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKCoreFoundationDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKCoreFoundationDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C4F7188D5E550010896F /* STKDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4F8188D5E550010896F /* STKDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKDataSourceWrapper.h; sourceTree = "<group>"; };
|
||||
A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKDataSourceWrapper.m; sourceTree = "<group>"; };
|
||||
A1E7C4FB188D5E550010896F /* STKHttpDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKHttpDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKHttpDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKHTTPDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKHTTPDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKLocalFileDataSource.h; sourceTree = "<group>"; };
|
||||
A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKLocalFileDataSource.m; sourceTree = "<group>"; };
|
||||
A1E7C507188D62D20010896F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
A1A49966189E744400E2A2E2 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1A49977189E744500E2A2E2 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A4997C189E744500E2A2E2 /* Cocoa.framework in Frameworks */,
|
||||
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */,
|
||||
A1A4997F189E744500E2A2E2 /* libStreamingKitMac.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1E7C4C5188D57F50010896F /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */,
|
||||
A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -94,13 +178,71 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
A1A4996C189E744500E2A2E2 /* Other Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A4996D189E744500E2A2E2 /* Foundation.framework */,
|
||||
A1A4996E189E744500E2A2E2 /* CoreData.framework */,
|
||||
A1A4996F189E744500E2A2E2 /* AppKit.framework */,
|
||||
);
|
||||
name = "Other Frameworks";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A49970189E744500E2A2E2 /* StreamingKitMac */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A49973189E744500E2A2E2 /* StreamingKitMac.h */,
|
||||
A1A49974189E744500E2A2E2 /* StreamingKitMac.m */,
|
||||
A1A49971189E744500E2A2E2 /* Supporting Files */,
|
||||
);
|
||||
path = StreamingKitMac;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A49971189E744500E2A2E2 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A49972189E744500E2A2E2 /* StreamingKitMac-Prefix.pch */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A49980189E744500E2A2E2 /* StreamingKitMacTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */,
|
||||
A1A49981189E744500E2A2E2 /* Supporting Files */,
|
||||
);
|
||||
path = StreamingKitMacTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A49981189E744500E2A2E2 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A49982189E744500E2A2E2 /* StreamingKitMacTests-Info.plist */,
|
||||
A1A49983189E744500E2A2E2 /* InfoPlist.strings */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1A499E2189E769A00E2A2E2 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499E7189E769A00E2A2E2 /* ExampleAppMac.app */,
|
||||
A1A499E9189E769A00E2A2E2 /* ExampleAppMacTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1E7C4BF188D57F50010896F = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1E7C4CD188D57F50010896F /* StreamingKit */,
|
||||
A1E7C4E1188D57F60010896F /* StreamingKitTests */,
|
||||
A1A49970189E744500E2A2E2 /* StreamingKitMac */,
|
||||
A1A49980189E744500E2A2E2 /* StreamingKitMacTests */,
|
||||
A1E7C4CA188D57F50010896F /* Frameworks */,
|
||||
A1E7C4C9188D57F50010896F /* Products */,
|
||||
A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
|
|
@ -109,6 +251,8 @@
|
|||
children = (
|
||||
A1E7C4C8188D57F50010896F /* libStreamingKit.a */,
|
||||
A1E7C4D8188D57F60010896F /* StreamingKitTests.xctest */,
|
||||
A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */,
|
||||
A1A4997A189E744500E2A2E2 /* StreamingKitMacTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -116,9 +260,13 @@
|
|||
A1E7C4CA188D57F50010896F /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */,
|
||||
A1C9767618981BFE0057F881 /* AudioUnit.framework */,
|
||||
A1E7C507188D62D20010896F /* UIKit.framework */,
|
||||
A1E7C4CB188D57F50010896F /* Foundation.framework */,
|
||||
A1E7C4D9188D57F60010896F /* XCTest.framework */,
|
||||
A1A4996A189E744400E2A2E2 /* Cocoa.framework */,
|
||||
A1A4996C189E744500E2A2E2 /* Other Frameworks */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -126,20 +274,26 @@
|
|||
A1E7C4CD188D57F50010896F /* StreamingKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
95F80F4C1C68EE2300DB24B3 /* STKFloatConverter.h */,
|
||||
95F80F4D1C68EE2300DB24B3 /* STKFloatConverter.m */,
|
||||
A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */,
|
||||
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */,
|
||||
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHttpDataSource.h */,
|
||||
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHttpDataSource.m */,
|
||||
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */,
|
||||
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */,
|
||||
A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */,
|
||||
A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */,
|
||||
A1E7C4F7188D5E550010896F /* STKDataSource.h */,
|
||||
A1E7C4F8188D5E550010896F /* STKDataSource.m */,
|
||||
A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */,
|
||||
A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */,
|
||||
A1E7C4FB188D5E550010896F /* STKHttpDataSource.h */,
|
||||
A1E7C4FC188D5E550010896F /* STKHttpDataSource.m */,
|
||||
A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */,
|
||||
A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */,
|
||||
A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */,
|
||||
A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */,
|
||||
A1BF65D0189A6582004DD08C /* STKQueueEntry.h */,
|
||||
A1BF65D1189A6582004DD08C /* STKQueueEntry.m */,
|
||||
A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */,
|
||||
A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */,
|
||||
A1E7C4CE188D57F50010896F /* Supporting Files */,
|
||||
);
|
||||
path = StreamingKit;
|
||||
|
|
@ -173,7 +327,68 @@
|
|||
};
|
||||
/* End PBXGroup 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 */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
A1A49968189E744400E2A2E2 /* StreamingKitMac */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = A1A4998C189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMac" */;
|
||||
buildPhases = (
|
||||
A1A49965189E744400E2A2E2 /* Sources */,
|
||||
A1A49966189E744400E2A2E2 /* Frameworks */,
|
||||
A1A49967189E744400E2A2E2 /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = StreamingKitMac;
|
||||
productName = StreamingKitMac;
|
||||
productReference = A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
A1A49979189E744500E2A2E2 /* StreamingKitMacTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = A1A4998D189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMacTests" */;
|
||||
buildPhases = (
|
||||
A1A49976189E744500E2A2E2 /* Sources */,
|
||||
A1A49977189E744500E2A2E2 /* Frameworks */,
|
||||
A1A49978189E744500E2A2E2 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
A1A4997E189E744500E2A2E2 /* PBXTargetDependency */,
|
||||
);
|
||||
name = StreamingKitMacTests;
|
||||
productName = StreamingKitMacTests;
|
||||
productReference = A1A4997A189E744500E2A2E2 /* StreamingKitMacTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
A1E7C4C7188D57F50010896F /* StreamingKit */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = A1E7C4EB188D57F60010896F /* Build configuration list for PBXNativeTarget "StreamingKit" */;
|
||||
|
|
@ -181,6 +396,7 @@
|
|||
A1E7C4C4188D57F50010896F /* Sources */,
|
||||
A1E7C4C5188D57F50010896F /* Frameworks */,
|
||||
A1E7C4C6188D57F50010896F /* CopyFiles */,
|
||||
5B949CD11A1140CF005675A0 /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -216,7 +432,7 @@
|
|||
isa = PBXProject;
|
||||
attributes = {
|
||||
CLASSPREFIX = STK;
|
||||
LastUpgradeCheck = 0500;
|
||||
LastUpgradeCheck = 0710;
|
||||
ORGANIZATIONNAME = "Thong Nguyen";
|
||||
};
|
||||
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
|
||||
|
|
@ -229,15 +445,48 @@
|
|||
mainGroup = A1E7C4BF188D57F50010896F;
|
||||
productRefGroup = A1E7C4C9188D57F50010896F /* Products */;
|
||||
projectDirPath = "";
|
||||
projectReferences = (
|
||||
{
|
||||
ProductGroup = A1A499E2189E769A00E2A2E2 /* Products */;
|
||||
ProjectRef = A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */;
|
||||
},
|
||||
);
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
A1E7C4C7188D57F50010896F /* StreamingKit */,
|
||||
A1E7C4D7188D57F60010896F /* StreamingKitTests */,
|
||||
A1A49968189E744400E2A2E2 /* StreamingKitMac */,
|
||||
A1A49979189E744500E2A2E2 /* StreamingKitMacTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXReferenceProxy section */
|
||||
A1A499E7189E769A00E2A2E2 /* ExampleAppMac.app */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.application;
|
||||
path = ExampleAppMac.app;
|
||||
remoteRef = A1A499E6189E769A00E2A2E2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
A1A499E9189E769A00E2A2E2 /* ExampleAppMacTests.xctest */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = wrapper.cfbundle;
|
||||
path = ExampleAppMacTests.xctest;
|
||||
remoteRef = A1A499E8189E769A00E2A2E2 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
A1A49978189E744500E2A2E2 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A49985189E744500E2A2E2 /* InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1E7C4D6188D57F60010896F /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -249,6 +498,31 @@
|
|||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
A1A49965189E744400E2A2E2 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A4998F189E745C00E2A2E2 /* STKAutoRecoveringHTTPDataSource.m in Sources */,
|
||||
A1A49994189E746900E2A2E2 /* STKHTTPDataSource.m in Sources */,
|
||||
A1A49991189E746000E2A2E2 /* STKCoreFoundationDataSource.m in Sources */,
|
||||
A1A49995189E746B00E2A2E2 /* STKLocalFileDataSource.m in Sources */,
|
||||
A1A49997189E747000E2A2E2 /* NSMutableArray+STKAudioPlayer.m in Sources */,
|
||||
A1A4998E189E745900E2A2E2 /* STKAudioPlayer.m in Sources */,
|
||||
A1A49993189E746500E2A2E2 /* STKDataSourceWrapper.m in Sources */,
|
||||
A1A49992189E746300E2A2E2 /* STKDataSource.m in Sources */,
|
||||
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */,
|
||||
A1A49996189E746E00E2A2E2 /* STKQueueEntry.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1A49976189E744500E2A2E2 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1A49987189E744500E2A2E2 /* StreamingKitMacTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
A1E7C4C4188D57F50010896F /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
|
|
@ -256,10 +530,13 @@
|
|||
A1E7C501188D5E550010896F /* STKCoreFoundationDataSource.m in Sources */,
|
||||
A1E7C4FF188D5E550010896F /* STKAudioPlayer.m in Sources */,
|
||||
A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */,
|
||||
A1E7C504188D5E550010896F /* STKHttpDataSource.m in Sources */,
|
||||
A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */,
|
||||
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */,
|
||||
95F80F4F1C68EE2300DB24B3 /* STKFloatConverter.m in Sources */,
|
||||
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */,
|
||||
A1E7C502188D5E550010896F /* STKDataSource.m in Sources */,
|
||||
A1E7C500188D5E550010896F /* STKAutoRecoveringHttpDataSource.m in Sources */,
|
||||
A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */,
|
||||
A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -274,6 +551,11 @@
|
|||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
A1A4997E189E744500E2A2E2 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = A1A49968189E744400E2A2E2 /* StreamingKitMac */;
|
||||
targetProxy = A1A4997D189E744500E2A2E2 /* PBXContainerItemProxy */;
|
||||
};
|
||||
A1E7C4DF188D57F60010896F /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = A1E7C4C7188D57F50010896F /* StreamingKit */;
|
||||
|
|
@ -282,6 +564,14 @@
|
|||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
A1A49983189E744500E2A2E2 /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
A1A49984189E744500E2A2E2 /* en */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
A1E7C4E4188D57F60010896F /* InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
|
|
@ -293,11 +583,96 @@
|
|||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
A1A49988189E744500E2A2E2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A1A49989189E744500E2A2E2 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
);
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||
MACOSX_DEPLOYMENT_TARGET = "";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
A1A4998A189E744500E2A2E2 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
A1A4998B189E744500E2A2E2 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
|
||||
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.8;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
A1E7C4E9188D57F60010896F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
|
|
@ -311,6 +686,7 @@
|
|||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
|
|
@ -325,7 +701,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
|
|
@ -335,7 +711,6 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
|
|
@ -357,7 +732,7 @@
|
|||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
|
@ -375,6 +750,7 @@
|
|||
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
|
|
@ -391,6 +767,7 @@
|
|||
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
|
|
@ -398,7 +775,6 @@
|
|||
A1E7C4EF188D57F60010896F /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
|
|
@ -411,6 +787,7 @@
|
|||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
|
|
@ -419,7 +796,6 @@
|
|||
A1E7C4F0188D57F60010896F /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
|
|
@ -428,6 +804,7 @@
|
|||
GCC_PRECOMPILE_PREFIX_HEADER = YES;
|
||||
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
|
||||
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = xctest;
|
||||
};
|
||||
|
|
@ -436,6 +813,24 @@
|
|||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
A1A4998C189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMac" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A1A49988189E744500E2A2E2 /* Debug */,
|
||||
A1A49989189E744500E2A2E2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
A1A4998D189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMacTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
A1A4998A189E744500E2A2E2 /* Debug */,
|
||||
A1A4998B189E744500E2A2E2 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
|
|
@ -452,6 +847,7 @@
|
|||
A1E7C4ED188D57F60010896F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
A1E7C4EE188D57F60010896F /* Build configuration list for PBXNativeTarget "StreamingKitTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
|
|
@ -460,6 +856,7 @@
|
|||
A1E7C4F0188D57F60010896F /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
|
|
|||
7
StreamingKit/StreamingKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
StreamingKit/StreamingKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
//
|
||||
// NSMutableArray+STKAudioPlayer.h
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 30/01/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface NSMutableArray (STKAudioPlayer)
|
||||
-(void) enqueue:(id)obj;
|
||||
-(void) skipQueue:(id)obj;
|
||||
-(void) skipQueueWithQueue:(NSMutableArray*)queue;
|
||||
-(nullable id) dequeue;
|
||||
-(nullable id) peek;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// NSMutableArray+STKAudioPlayer.m
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 30/01/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NSMutableArray+STKAudioPlayer.h"
|
||||
|
||||
@implementation NSMutableArray (STKAudioPlayer)
|
||||
|
||||
-(void) enqueue:(id)obj
|
||||
{
|
||||
[self insertObject:obj atIndex:0];
|
||||
}
|
||||
|
||||
-(void) skipQueue:(id)obj
|
||||
{
|
||||
[self addObject:obj];
|
||||
}
|
||||
|
||||
-(void) skipQueueWithQueue:(NSMutableArray*)queue
|
||||
{
|
||||
for (id item in queue)
|
||||
{
|
||||
[self addObject:item];
|
||||
}
|
||||
}
|
||||
|
||||
-(id) dequeue
|
||||
{
|
||||
if ([self count] == 0)
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
id retval = [self lastObject];
|
||||
|
||||
[self removeLastObject];
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
-(id) peek
|
||||
{
|
||||
return [self lastObject];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
AudioPlayer.m
|
||||
|
||||
Created by Thong Nguyen on 14/05/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
https://github.com/tumtumtum/StreamingKit
|
||||
|
||||
Inspired by Matt Gallagher's AudioStreamer:
|
||||
https://github.com/mattgallagher/AudioStreamer
|
||||
|
||||
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
|
@ -38,177 +38,239 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <pthread.h>
|
||||
#import "STKDataSource.h"
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#include "UIKit/UIApplication.h"
|
||||
#endif
|
||||
|
||||
#define AudioPlayerDefaultNumberOfAudioQueueBuffers (2 * 1024)
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef enum
|
||||
typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
|
||||
{
|
||||
AudioPlayerInternalStateInitialised = 0,
|
||||
AudioPlayerInternalStateRunning = 1,
|
||||
AudioPlayerInternalStatePlaying = (1 << 1) | AudioPlayerInternalStateRunning,
|
||||
AudioPlayerInternalStateStartingThread = (1 << 2) | AudioPlayerInternalStateRunning,
|
||||
AudioPlayerInternalStateWaitingForData = (1 << 3) | AudioPlayerInternalStateRunning,
|
||||
AudioPlayerInternalStateWaitingForQueueToStart = (1 << 4) | AudioPlayerInternalStateRunning,
|
||||
AudioPlayerInternalStatePaused = (1 << 5) | AudioPlayerInternalStateRunning,
|
||||
AudioPlayerInternalStateRebuffering = (1 << 6) | AudioPlayerInternalStateRunning,
|
||||
AudioPlayerInternalStateStopping = (1 << 7),
|
||||
AudioPlayerInternalStateStopped = (1 << 8),
|
||||
AudioPlayerInternalStateDisposed = (1 << 9),
|
||||
AudioPlayerInternalStateError = (1 << 10)
|
||||
}
|
||||
AudioPlayerInternalState;
|
||||
STKAudioPlayerStateReady,
|
||||
STKAudioPlayerStateRunning = 1,
|
||||
STKAudioPlayerStatePlaying = (1 << 1) | STKAudioPlayerStateRunning,
|
||||
STKAudioPlayerStateBuffering = (1 << 2) | STKAudioPlayerStateRunning,
|
||||
STKAudioPlayerStatePaused = (1 << 3) | STKAudioPlayerStateRunning,
|
||||
STKAudioPlayerStateStopped = (1 << 4),
|
||||
STKAudioPlayerStateError = (1 << 5),
|
||||
STKAudioPlayerStateDisposed = (1 << 6)
|
||||
};
|
||||
|
||||
typedef enum
|
||||
typedef NS_ENUM(NSInteger, STKAudioPlayerStopReason)
|
||||
{
|
||||
AudioPlayerStateReady,
|
||||
AudioPlayerStateRunning = 1,
|
||||
AudioPlayerStatePlaying = (1 << 1) | AudioPlayerStateRunning,
|
||||
AudioPlayerStatePaused = (1 << 2) | AudioPlayerStateRunning,
|
||||
AudioPlayerStateStopped = (1 << 3),
|
||||
AudioPlayerStateError = (1 << 4),
|
||||
AudioPlayerStateDisposed = (1 << 5)
|
||||
}
|
||||
AudioPlayerState;
|
||||
STKAudioPlayerStopReasonNone = 0,
|
||||
STKAudioPlayerStopReasonEof,
|
||||
STKAudioPlayerStopReasonUserAction,
|
||||
STKAudioPlayerStopReasonPendingNext,
|
||||
STKAudioPlayerStopReasonDisposed,
|
||||
STKAudioPlayerStopReasonError = 0xffff
|
||||
};
|
||||
|
||||
typedef enum
|
||||
typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode)
|
||||
{
|
||||
AudioPlayerStopReasonNoStop = 0,
|
||||
AudioPlayerStopReasonEof,
|
||||
AudioPlayerStopReasonUserAction,
|
||||
AudioPlayerStopReasonUserActionFlushStop
|
||||
}
|
||||
AudioPlayerStopReason;
|
||||
STKAudioPlayerErrorNone = 0,
|
||||
STKAudioPlayerErrorDataSource,
|
||||
STKAudioPlayerErrorStreamParseBytesFailed,
|
||||
STKAudioPlayerErrorAudioSystemError,
|
||||
STKAudioPlayerErrorCodecError,
|
||||
STKAudioPlayerErrorDataNotFound,
|
||||
STKAudioPlayerErrorOther = 0xffff
|
||||
};
|
||||
|
||||
typedef enum
|
||||
///
|
||||
/// 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
|
||||
{
|
||||
AudioPlayerErrorNone = 0,
|
||||
AudioPlayerErrorDataSource,
|
||||
AudioPlayerErrorStreamParseBytesFailed,
|
||||
AudioPlayerErrorDataNotFound,
|
||||
AudioPlayerErrorQueueStartFailed,
|
||||
AudioPlayerErrorQueuePauseFailed,
|
||||
AudioPlayerErrorUnknownBuffer,
|
||||
AudioPlayerErrorQueueStopFailed,
|
||||
AudioPlayerErrorOther
|
||||
/// If YES then seeking a track will cause all pending items to be flushed from the queue
|
||||
BOOL flushQueueOnSeek;
|
||||
/// If YES then volume control will be enabled on iOS
|
||||
BOOL enableVolumeMixer;
|
||||
/// A pointer to a 0 terminated array of band frequencies (iOS 5.0 and later, OSX 10.9 and later)
|
||||
Float32 equalizerBandFrequencies[24];
|
||||
/// 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;
|
||||
/// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM)
|
||||
float 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;
|
||||
/// 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)
|
||||
Float32 secondsRequiredToStartPlayingAfterBufferUnderun;
|
||||
}
|
||||
AudioPlayerErrorCode;
|
||||
STKAudioPlayerOptions;
|
||||
|
||||
#define STK_DISABLE_BUFFER (0xffffffff)
|
||||
|
||||
typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, float* frames);
|
||||
|
||||
@interface STKFrameFilterEntry : NSObject
|
||||
@property (readonly) NSString* name;
|
||||
@property (readonly) STKFrameFilter filter;
|
||||
@end
|
||||
|
||||
@class STKAudioPlayer;
|
||||
|
||||
@protocol STKAudioPlayerDelegate <NSObject>
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(AudioPlayerState)state;
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(AudioPlayerErrorCode)errorCode;
|
||||
|
||||
/// Raised when an item has started playing
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId;
|
||||
/// Raised when an item has finished buffering (may or may not be the currently playing item)
|
||||
/// This event may be raised multiple times for the same item if seek is invoked on the player
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId;
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(AudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration;
|
||||
/// Raised when the state of the player has changed
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState;
|
||||
/// Raised when an item has finished playing
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration;
|
||||
/// Raised when an unexpected and possibly unrecoverable error has occured (usually best to recreate the STKAudioPlauyer)
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode;
|
||||
@optional
|
||||
/// Optionally implemented to get logging information from the STKAudioPlayer (used internally for debugging)
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer logInfo:(NSString*)line;
|
||||
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer internalStateChanged:(AudioPlayerInternalState)state;
|
||||
-(void) audioPlayer: (STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
|
||||
/// 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) plotGraphWithBuffer:(float*)buffer andLength:(UInt32)count;
|
||||
|
||||
@end
|
||||
|
||||
@class QueueEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AudioQueueBufferRef ref;
|
||||
int bufferIndex;
|
||||
}
|
||||
AudioQueueBufferRefLookupEntry;
|
||||
|
||||
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
|
||||
{
|
||||
@private
|
||||
UInt8* readBuffer;
|
||||
int readBufferSize;
|
||||
|
||||
NSOperationQueue* fastApiQueue;
|
||||
|
||||
QueueEntry* currentlyPlayingEntry;
|
||||
QueueEntry* currentlyReadingEntry;
|
||||
|
||||
NSMutableArray* upcomingQueue;
|
||||
NSMutableArray* bufferingQueue;
|
||||
|
||||
AudioQueueBufferRef* audioQueueBuffer;
|
||||
AudioQueueBufferRefLookupEntry* audioQueueBufferLookup;
|
||||
unsigned int audioQueueBufferRefLookupCount;
|
||||
unsigned int audioQueueBufferCount;
|
||||
AudioStreamPacketDescription* packetDescs;
|
||||
bool* bufferUsed;
|
||||
int numberOfBuffersUsed;
|
||||
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription currentAudioStreamBasicDescription;
|
||||
|
||||
NSThread* playbackThread;
|
||||
NSRunLoop* playbackThreadRunLoop;
|
||||
NSConditionLock* threadFinishedCondLock;
|
||||
|
||||
AudioFileStreamID audioFileStream;
|
||||
|
||||
BOOL discontinuous;
|
||||
|
||||
int bytesFilled;
|
||||
int packetsFilled;
|
||||
|
||||
int fillBufferIndex;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
UIBackgroundTaskIdentifier backgroundTaskId;
|
||||
#endif
|
||||
|
||||
AudioPlayerErrorCode errorCode;
|
||||
AudioPlayerStopReason stopReason;
|
||||
|
||||
int currentlyPlayingLock;
|
||||
pthread_mutex_t playerMutex;
|
||||
pthread_mutex_t queueBuffersMutex;
|
||||
pthread_cond_t queueBufferReadyCondition;
|
||||
|
||||
volatile BOOL waiting;
|
||||
volatile BOOL disposeWasRequested;
|
||||
volatile BOOL seekToTimeWasRequested;
|
||||
volatile BOOL newFileToPlay;
|
||||
volatile double requestedSeekTime;
|
||||
volatile BOOL audioQueueFlushing;
|
||||
volatile SInt64 audioPacketsReadCount;
|
||||
volatile SInt64 audioPacketsPlayedCount;
|
||||
|
||||
BOOL meteringEnabled;
|
||||
AudioQueueLevelMeterState* levelMeterState;
|
||||
NSInteger numberOfChannels;
|
||||
}
|
||||
|
||||
/// Gets or sets the volume (ranges 0 - 1.0).
|
||||
/// On iOS the STKAudioPlayerOptionEnableMultichannelMixer option must be enabled for volume to work.
|
||||
@property (readwrite) Float32 volume;
|
||||
/// Gets or sets the player muted state
|
||||
@property (readwrite) BOOL muted;
|
||||
/// Gets the current item duration in seconds
|
||||
@property (readonly) double duration;
|
||||
/// Gets the current item progress in seconds
|
||||
@property (readonly) double progress;
|
||||
@property (readwrite) AudioPlayerState state;
|
||||
@property (readonly) AudioPlayerStopReason stopReason;
|
||||
@property (readwrite, unsafe_unretained) id<STKAudioPlayerDelegate> delegate;
|
||||
/// Enables or disables peak and average decibel meteting
|
||||
@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
|
||||
@property (readonly, nullable) NSArray* frameFilters;
|
||||
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
|
||||
@property (readonly) NSArray* pendingQueue;
|
||||
/// The number of items pending to be played (includes buffering and upcoming items but does not include the current item)
|
||||
@property (readonly) NSUInteger pendingQueueCount;
|
||||
/// Gets the most recently queued item that is still pending to play
|
||||
@property (readonly, nullable) NSObject* mostRecentlyQueuedStillPendingItem;
|
||||
/// Gets the current state of the player
|
||||
@property (readwrite) STKAudioPlayerState state;
|
||||
/// Gets the options provided to the player on startup
|
||||
@property (readonly) STKAudioPlayerOptions options;
|
||||
/// Gets the reason why the player is stopped (if any)
|
||||
@property (readonly) STKAudioPlayerStopReason stopReason;
|
||||
/// Gets and sets the delegate used for receiving events from the STKAudioPlayer
|
||||
@property (readwrite, unsafe_unretained) id<STKAudioPlayerDelegate> delegate;
|
||||
|
||||
-(id) init;
|
||||
-(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn;
|
||||
-(DataSource*) dataSourceFromURL:(NSURL*)url;
|
||||
-(void) play:(NSURL*)url;
|
||||
-(void) queueDataSource:(DataSource*)dataSource withQueueItemId:(NSObject*)queueItemId;
|
||||
-(void) setDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId;
|
||||
/// Creates a datasource from a given URL.
|
||||
/// URLs with FILE schemes will return an STKLocalFileDataSource.
|
||||
/// URLs with HTTP schemes will return an STKHTTPDataSource wrapped within an STKAutoRecoveringHTTPDataSource.
|
||||
/// URLs with unrecognised schemes will return nil.
|
||||
+(STKDataSource*) dataSourceFromURL:(NSURL*)url;
|
||||
|
||||
/// Initializes a new STKAudioPlayer with the default options
|
||||
-(instancetype) init;
|
||||
|
||||
/// Initializes a new STKAudioPlayer with the given options
|
||||
-(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn;
|
||||
|
||||
/// Plays an item from the given URL string (all pending queued items are removed).
|
||||
/// The NSString is used as the queue item ID
|
||||
-(void) play:(NSString*)urlString;
|
||||
|
||||
/// Plays an item from the given URL (all pending queued items are removed)
|
||||
-(void) play:(NSString*)urlString withQueueItemID:(NSObject*)queueItemId;
|
||||
|
||||
/// Plays an item from the given URL (all pending queued items are removed)
|
||||
/// The NSURL is used as the queue item ID
|
||||
-(void) playURL:(NSURL*)url;
|
||||
|
||||
/// Plays an item from the given URL (all pending queued items are removed)
|
||||
-(void) playURL:(NSURL*)url withQueueItemID:(NSObject*)queueItemId;
|
||||
|
||||
/// Plays the given item (all pending queued items are removed)
|
||||
/// The STKDataSource is used as the queue item ID
|
||||
-(void) playDataSource:(STKDataSource*)dataSource;
|
||||
|
||||
/// Plays the given item (all pending queued items are removed)
|
||||
-(void) playDataSource:(STKDataSource*)dataSource withQueueItemID:(NSObject*)queueItemId;
|
||||
|
||||
/// Queues the URL string for playback and uses the NSString as the queueItemID
|
||||
-(void) queue:(NSString*)urlString;
|
||||
|
||||
/// Queues the URL string for playback with the given queueItemID
|
||||
-(void) queue:(NSString*)urlString withQueueItemId:(NSObject*)queueItemId;
|
||||
|
||||
/// Queues the URL for playback and uses the NSURL as the queueItemID
|
||||
-(void) queueURL:(NSURL*)url;
|
||||
|
||||
/// Queues the URL for playback with the given queueItemID
|
||||
-(void) queueURL:(NSURL*)url withQueueItemId:(NSObject*)queueItemId;
|
||||
|
||||
/// Queues a DataSource with the given queueItemId
|
||||
-(void) queueDataSource:(STKDataSource*)dataSource withQueueItemId:(NSObject*)queueItemId;
|
||||
|
||||
/// Plays the given item (all pending queued items are removed)
|
||||
-(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId;
|
||||
|
||||
/// Seeks to a specific time (in seconds)
|
||||
-(void) seekToTime:(double)value;
|
||||
|
||||
/// Clears any upcoming items already queued for playback (does not stop the current item).
|
||||
/// The didCancelItems event will be raised for the items removed from the queue.
|
||||
-(void) clearQueue;
|
||||
|
||||
/// Pauses playback
|
||||
-(void) pause;
|
||||
|
||||
/// Resumes playback from pause
|
||||
-(void) resume;
|
||||
|
||||
/// Stops playback of the current file, flushes all the buffers and removes any pending queued items
|
||||
-(void) stop;
|
||||
-(void) flushStop;
|
||||
|
||||
/// Mutes playback
|
||||
-(void) mute;
|
||||
|
||||
/// Unmutes playback
|
||||
-(void) unmute;
|
||||
|
||||
/// Disposes the STKAudioPlayer and frees up all resources before returning
|
||||
-(void) dispose;
|
||||
|
||||
/// The QueueItemId of the currently playing item
|
||||
-(NSObject*) currentlyPlayingQueueItemId;
|
||||
-(void) updateMeters;
|
||||
-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||
|
||||
/// Removes a frame filter with the given name
|
||||
-(void) removeFrameFilterWithName:(NSString*)name;
|
||||
|
||||
/// Appends a frame filter with the given name and filter block to the end of the filter chain
|
||||
-(void) appendFrameFilterWithName:(NSString*)name block:(STKFrameFilter)block;
|
||||
|
||||
/// 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
|
||||
-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(nullable NSString*)afterFilterWithName block:(STKFrameFilter)block;
|
||||
|
||||
/// Reads the peak power in decibals for the given channel (0 or 1).
|
||||
/// Return values are between -60 (low) and 0 (high).
|
||||
//-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||
|
||||
/// Reads the average power in decibals for the given channel (0 or 1)
|
||||
/// Return values are between -60 (low) and 0 (high).
|
||||
//-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
|
||||
|
||||
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
|
||||
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -4,7 +4,7 @@
|
|||
Created by Thong Nguyen on 16/10/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
|
@ -33,13 +33,24 @@
|
|||
**********************************************************************************/
|
||||
|
||||
#import "STKDataSource.h"
|
||||
#import "STKHttpDataSource.h"
|
||||
#import "STKHTTPDataSource.h"
|
||||
#import "STKDataSourceWrapper.h"
|
||||
|
||||
@interface STKAutoRecoveringHttpDataSource : DataSourceWrapper
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
-(id) initWithHttpDataSource:(HttpDataSource*)innerDataSource;
|
||||
typedef struct
|
||||
{
|
||||
int watchdogPeriodSeconds;
|
||||
int inactivePeriodBeforeReconnectSeconds;
|
||||
}
|
||||
STKAutoRecoveringHTTPDataSourceOptions;
|
||||
|
||||
@property (readonly) HttpDataSource* innerDataSource;
|
||||
@interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper
|
||||
|
||||
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource;
|
||||
|
||||
@property (readonly) STKHTTPDataSource* innerDataSource;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,392 @@
|
|||
/**********************************************************************************
|
||||
AudioPlayer.m
|
||||
|
||||
Created by Thong Nguyen on 16/10/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
|
||||
4. Neither the name of Thong Nguyen nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Thong Nguyen''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**********************************************************************************/
|
||||
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
#import "mach/mach_time.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import "STKAutoRecoveringHTTPDataSource.h"
|
||||
|
||||
#define DEFAULT_WATCHDOG_PERIOD_SECONDS (8)
|
||||
#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()
|
||||
{
|
||||
int serial;
|
||||
int waitSeconds;
|
||||
NSTimer* timeoutTimer;
|
||||
BOOL waitingForNetwork;
|
||||
uint64_t ticksWhenLastDataReceived;
|
||||
SCNetworkReachabilityRef reachabilityRef;
|
||||
STKAutoRecoveringHTTPDataSourceOptions options;
|
||||
}
|
||||
|
||||
-(void) reachabilityChanged;
|
||||
|
||||
@end
|
||||
|
||||
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
STKAutoRecoveringHTTPDataSource* dataSource = (__bridge STKAutoRecoveringHTTPDataSource*)info;
|
||||
|
||||
[dataSource reachabilityChanged];
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@dynamic innerDataSource;
|
||||
|
||||
-(STKHTTPDataSource*) innerHTTPDataSource
|
||||
{
|
||||
return (STKHTTPDataSource*)self.innerDataSource;
|
||||
}
|
||||
|
||||
-(instancetype) initWithDataSource:(STKDataSource *)innerDataSource
|
||||
{
|
||||
return [self initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource];
|
||||
}
|
||||
|
||||
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn
|
||||
{
|
||||
return [self initWithHTTPDataSource:innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions){}];
|
||||
}
|
||||
|
||||
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions)optionsIn
|
||||
{
|
||||
if (self = [super initWithDataSource:innerDataSourceIn])
|
||||
{
|
||||
self.innerDataSource.delegate = self;
|
||||
|
||||
struct sockaddr_in zeroAddress;
|
||||
|
||||
bzero(&zeroAddress, sizeof(zeroAddress));
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
PopulateOptionsWithDefault(&optionsIn);
|
||||
|
||||
self->options = optionsIn;
|
||||
|
||||
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(BOOL) startNotifierOnRunLoop:(NSRunLoop*)runLoop
|
||||
{
|
||||
BOOL retVal = NO;
|
||||
SCNetworkReachabilityContext context = { 0, (__bridge void*)self, NULL, NULL, NULL };
|
||||
|
||||
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
|
||||
{
|
||||
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, runLoop.getCFRunLoop, kCFRunLoopDefaultMode))
|
||||
{
|
||||
retVal = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
-(BOOL) registerForEvents:(NSRunLoop*)runLoop
|
||||
{
|
||||
[super registerForEvents:runLoop];
|
||||
[self startNotifierOnRunLoop:runLoop];
|
||||
|
||||
if (timeoutTimer)
|
||||
{
|
||||
[timeoutTimer invalidate];
|
||||
timeoutTimer = nil;
|
||||
}
|
||||
|
||||
ticksWhenLastDataReceived = GetTickCount();
|
||||
|
||||
[self createTimeoutTimer];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) unregisterForEvents
|
||||
{
|
||||
[super unregisterForEvents];
|
||||
[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
|
||||
{
|
||||
if (reachabilityRef != NULL)
|
||||
{
|
||||
SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, [self.innerDataSource.eventsRunLoop getCFRunLoop], kCFRunLoopDefaultMode);
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL) hasGotNetworkConnection
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
|
||||
{
|
||||
return ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(int64_t)offset
|
||||
{
|
||||
ticksWhenLastDataReceived = GetTickCount();
|
||||
|
||||
[super seekToOffset:offset];
|
||||
}
|
||||
|
||||
-(void) close
|
||||
{
|
||||
[self destroyTimeoutTimer];
|
||||
[super close];
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
NSLog(@"STKAutoRecoveringHTTPDataSource dealloc");
|
||||
|
||||
self.innerDataSource.delegate = nil;
|
||||
|
||||
[self stopNotifier];
|
||||
[self destroyTimeoutTimer];
|
||||
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
|
||||
if (reachabilityRef!= NULL)
|
||||
{
|
||||
CFRelease(reachabilityRef);
|
||||
}
|
||||
}
|
||||
|
||||
-(void) reachabilityChanged
|
||||
{
|
||||
if (waitingForNetwork)
|
||||
{
|
||||
waitingForNetwork = NO;
|
||||
|
||||
NSLog(@"reachabilityChanged %lld/%lld", self.position, self.length);
|
||||
|
||||
serial++;
|
||||
|
||||
[self attemptReconnectWithSerial:@(serial)];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
||||
{
|
||||
if (![self.innerDataSource hasBytesAvailable])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
serial++;
|
||||
waitSeconds = 1;
|
||||
ticksWhenLastDataReceived = GetTickCount();
|
||||
|
||||
[super dataSourceDataAvailable:dataSource];
|
||||
}
|
||||
|
||||
-(void) attemptReconnectWithSerial:(NSNumber*)serialIn
|
||||
{
|
||||
if (serialIn.intValue != self->serial)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"attemptReconnect %lld/%lld", self.position, self.length);
|
||||
|
||||
if (self.innerDataSource.eventsRunLoop)
|
||||
{
|
||||
[self.innerDataSource reconnect];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) attemptReconnectWithTimer:(NSTimer*)timer
|
||||
{
|
||||
[self attemptReconnectWithSerial:(NSNumber*)timer.userInfo];
|
||||
}
|
||||
|
||||
-(void) processRetryOnError
|
||||
{
|
||||
if (![self hasGotNetworkConnection])
|
||||
{
|
||||
waitingForNetwork = YES;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
waitingForNetwork = NO;
|
||||
|
||||
NSRunLoop* runLoop = self.innerDataSource.eventsRunLoop;
|
||||
|
||||
if (runLoop == nil)
|
||||
{
|
||||
// DataSource no longer used
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
serial++;
|
||||
|
||||
NSTimer* timer = [NSTimer timerWithTimeInterval:waitSeconds target:self selector:@selector(attemptReconnectWithTimer:) userInfo:@(serial) repeats:NO];
|
||||
|
||||
[runLoop addTimer:timer forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
|
||||
waitSeconds = MIN(waitSeconds + 1, 5);
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource
|
||||
{
|
||||
NSLog(@"dataSourceEof");
|
||||
|
||||
if ([self position] < [self length])
|
||||
{
|
||||
[self processRetryOnError];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[self.delegate dataSourceEof:self];
|
||||
}
|
||||
|
||||
- (void)dataSourceErrorOccured:(STKDataSource*)dataSource {
|
||||
NSLog(@"dataSourceErrorOccured");
|
||||
|
||||
/* Range out of bounds */
|
||||
if (self.innerDataSource.httpStatusCode == 416) {
|
||||
[super dataSourceEof:dataSource];
|
||||
} else if ([self hasGotNetworkConnection]) {
|
||||
[super dataSourceErrorOccured:dataSource];
|
||||
} else {
|
||||
[self processRetryOnError];
|
||||
}
|
||||
}
|
||||
|
||||
-(NSString*) description
|
||||
{
|
||||
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,236 +0,0 @@
|
|||
/**********************************************************************************
|
||||
AudioPlayer.m
|
||||
|
||||
Created by Thong Nguyen on 16/10/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
|
||||
4. Neither the name of Thong Nguyen nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Thong Nguyen''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**********************************************************************************/
|
||||
|
||||
#import <sys/socket.h>
|
||||
#import <netinet/in.h>
|
||||
#import <netinet6/in6.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <ifaddrs.h>
|
||||
#import <netdb.h>
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import "STKAutoRecoveringHttpDataSource.h"
|
||||
|
||||
#define MAX_IMMEDIATE_RECONNECT_ATTEMPTS (8)
|
||||
#define MAX_ATTEMPTS_WITH_SERVER_ERROR (MAX_IMMEDIATE_RECONNECT_ATTEMPTS + 2)
|
||||
|
||||
@interface STKAutoRecoveringHttpDataSource()
|
||||
{
|
||||
int reconnectAttempts;
|
||||
BOOL waitingForNetwork;
|
||||
SCNetworkReachabilityRef reachabilityRef;
|
||||
}
|
||||
|
||||
-(void) reachabilityChanged;
|
||||
|
||||
@end
|
||||
|
||||
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
STKAutoRecoveringHttpDataSource* dataSource = (__bridge STKAutoRecoveringHttpDataSource*)info;
|
||||
|
||||
[dataSource reachabilityChanged];
|
||||
}
|
||||
}
|
||||
|
||||
@implementation STKAutoRecoveringHttpDataSource
|
||||
|
||||
-(HttpDataSource*) innerHttpDataSource
|
||||
{
|
||||
return (HttpDataSource*)self.innerDataSource;
|
||||
}
|
||||
|
||||
-(id) initWithHttpDataSource:(HttpDataSource*)innerDataSourceIn
|
||||
{
|
||||
if (self = [super initWithDataSource:innerDataSourceIn])
|
||||
{
|
||||
self.innerDataSource.delegate = self;
|
||||
|
||||
struct sockaddr_in zeroAddress;
|
||||
|
||||
bzero(&zeroAddress, sizeof(zeroAddress));
|
||||
zeroAddress.sin_len = sizeof(zeroAddress);
|
||||
zeroAddress.sin_family = AF_INET;
|
||||
|
||||
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(BOOL) startNotifierOnRunLoop:(NSRunLoop*)runLoop
|
||||
{
|
||||
BOOL retVal = NO;
|
||||
SCNetworkReachabilityContext context = { 0, (__bridge void*)self, NULL, NULL, NULL };
|
||||
|
||||
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context))
|
||||
{
|
||||
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, runLoop.getCFRunLoop, kCFRunLoopDefaultMode))
|
||||
{
|
||||
retVal = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
-(BOOL) registerForEvents:(NSRunLoop*)runLoop
|
||||
{
|
||||
[super registerForEvents:runLoop];
|
||||
[self startNotifierOnRunLoop:runLoop];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) unregisterForEvents
|
||||
{
|
||||
[self stopNotifier];
|
||||
}
|
||||
|
||||
-(void) stopNotifier
|
||||
{
|
||||
if (reachabilityRef != NULL)
|
||||
{
|
||||
SCNetworkReachabilitySetCallback(reachabilityRef, NULL, NULL);
|
||||
SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL) hasGotNetworkConnection
|
||||
{
|
||||
SCNetworkReachabilityFlags flags;
|
||||
|
||||
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
|
||||
{
|
||||
return ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
self.innerDataSource.delegate = nil;
|
||||
|
||||
[self stopNotifier];
|
||||
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
|
||||
if (reachabilityRef!= NULL)
|
||||
{
|
||||
CFRelease(reachabilityRef);
|
||||
}
|
||||
}
|
||||
|
||||
-(void) reachabilityChanged
|
||||
{
|
||||
if (waitingForNetwork)
|
||||
{
|
||||
waitingForNetwork = NO;
|
||||
|
||||
[self attemptReconnect];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSource
|
||||
{
|
||||
reconnectAttempts = 0;
|
||||
|
||||
[super dataSourceDataAvailable:dataSource];
|
||||
}
|
||||
|
||||
-(void) attemptReconnect
|
||||
{
|
||||
reconnectAttempts++;
|
||||
|
||||
[self seekToOffset:self.position];
|
||||
}
|
||||
|
||||
-(void) processRetryOnError
|
||||
{
|
||||
if (![self hasGotNetworkConnection])
|
||||
{
|
||||
waitingForNetwork = YES;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(self.innerDataSource.httpStatusCode >= 200 && self.innerDataSource.httpStatusCode <= 299) && reconnectAttempts >= MAX_ATTEMPTS_WITH_SERVER_ERROR)
|
||||
{
|
||||
[super dataSourceErrorOccured:self];
|
||||
}
|
||||
else if (reconnectAttempts > MAX_IMMEDIATE_RECONNECT_ATTEMPTS)
|
||||
{
|
||||
[self performSelector:@selector(attemptReconnect) withObject:nil afterDelay:5];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self attemptReconnect];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(DataSource*)dataSource
|
||||
{
|
||||
if ([self position] != [self length])
|
||||
{
|
||||
[self processRetryOnError];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
[self.delegate dataSourceEof:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSource
|
||||
{
|
||||
if (self.innerDataSource.httpStatusCode == 416 /* Range out of bounds */)
|
||||
{
|
||||
[super dataSourceEof:dataSource];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self processRetryOnError];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-(NSString*) description
|
||||
{
|
||||
return [NSString stringWithFormat:@"Auto-recovering HTTP data source with file length: %lld and position: %lld", self.length, self.position];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -34,6 +34,8 @@
|
|||
|
||||
#import "STKDataSource.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class STKCoreFoundationDataSource;
|
||||
|
||||
@interface CoreFoundationDataSourceClientInfo : NSObject
|
||||
|
|
@ -41,17 +43,26 @@
|
|||
@property (readwrite, retain) STKCoreFoundationDataSource* datasource;
|
||||
@end
|
||||
|
||||
@interface STKCoreFoundationDataSource : DataSource
|
||||
@interface STKCoreFoundationDataSource : STKDataSource
|
||||
{
|
||||
@protected
|
||||
@public
|
||||
CFReadStreamRef stream;
|
||||
@protected
|
||||
BOOL isInErrorState;
|
||||
NSRunLoop* eventsRunLoop;
|
||||
}
|
||||
|
||||
@property (readonly) BOOL isInErrorState;
|
||||
|
||||
-(BOOL) reregisterForEvents;
|
||||
|
||||
-(void) open;
|
||||
-(void) openCompleted;
|
||||
-(void) dataAvailable;
|
||||
-(void) eof;
|
||||
-(void) errorOccured;
|
||||
-(CFStreamStatus) status;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
|
|
@ -41,14 +41,19 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
switch (eventType)
|
||||
{
|
||||
case kCFStreamEventErrorOccurred:
|
||||
{
|
||||
[datasource errorOccured];
|
||||
break;
|
||||
}
|
||||
case kCFStreamEventEndEncountered:
|
||||
[datasource eof];
|
||||
break;
|
||||
case kCFStreamEventHasBytesAvailable:
|
||||
[datasource dataAvailable];
|
||||
break;
|
||||
case kCFStreamEventOpenCompleted:
|
||||
[datasource openCompleted];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -60,6 +65,11 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
|
||||
@implementation STKCoreFoundationDataSource
|
||||
|
||||
-(BOOL) isInErrorState
|
||||
{
|
||||
return self->isInErrorState;
|
||||
}
|
||||
|
||||
-(void) dataAvailable
|
||||
{
|
||||
[self.delegate dataSourceDataAvailable:self];
|
||||
|
|
@ -72,6 +82,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
|
||||
-(void) errorOccured
|
||||
{
|
||||
self->isInErrorState = YES;
|
||||
|
||||
[self.delegate dataSourceErrorOccured:self];
|
||||
}
|
||||
|
||||
|
|
@ -79,7 +91,10 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
{
|
||||
if (stream)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
if (eventsRunLoop)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
}
|
||||
|
||||
[self close];
|
||||
|
||||
|
|
@ -91,6 +106,11 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
{
|
||||
if (stream)
|
||||
{
|
||||
if (eventsRunLoop)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
}
|
||||
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(stream);
|
||||
|
||||
|
|
@ -98,7 +118,11 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
}
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) open
|
||||
{
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -133,19 +157,21 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
-(BOOL) registerForEvents:(NSRunLoop*)runLoop
|
||||
{
|
||||
eventsRunLoop = runLoop;
|
||||
|
||||
if (stream)
|
||||
{
|
||||
CFStreamClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||
|
||||
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, ReadStreamCallbackProc, &context);
|
||||
|
||||
CFReadStreamScheduleWithRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
|
||||
|
||||
if (!stream)
|
||||
{
|
||||
// Will register when they open or seek
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
CFStreamClientContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||
|
||||
return NO;
|
||||
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, ReadStreamCallbackProc, &context);
|
||||
|
||||
CFReadStreamScheduleWithRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(BOOL) hasBytesAvailable
|
||||
|
|
@ -158,4 +184,18 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
|
|||
return CFReadStreamHasBytesAvailable(stream);
|
||||
}
|
||||
|
||||
-(CFStreamStatus) status
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
return CFReadStreamGetStatus(stream);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(void) openCompleted
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -35,32 +35,34 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
@class DataSource;
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class STKDataSource;
|
||||
|
||||
@protocol STKDataSourceDelegate<NSObject>
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSource;
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSource;
|
||||
-(void) dataSourceEof:(DataSource*)dataSource;
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource;
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource;
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource;
|
||||
@end
|
||||
|
||||
@protocol AudioDataSource<NSObject>
|
||||
@property (readwrite) double averageBitRate;
|
||||
@property (readwrite) long long audioDataOffset;
|
||||
@end
|
||||
@interface STKDataSource : NSObject
|
||||
|
||||
@interface DataSource : NSObject
|
||||
|
||||
@property (readonly) long long position;
|
||||
@property (readonly) long long length;
|
||||
@property (readonly) BOOL supportsSeek;
|
||||
@property (readonly) SInt64 position;
|
||||
@property (readonly) SInt64 length;
|
||||
@property (readonly) BOOL hasBytesAvailable;
|
||||
@property (readwrite, unsafe_unretained) id<STKDataSourceDelegate> delegate;
|
||||
@property (nonatomic, readwrite, assign) double durationHint;
|
||||
@property (readwrite, unsafe_unretained, nullable) id<STKDataSourceDelegate> delegate;
|
||||
@property (nonatomic, strong, nullable) NSURL *recordToFileUrl;
|
||||
|
||||
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
|
||||
-(void) unregisterForEvents;
|
||||
-(void) close;
|
||||
|
||||
-(void) seekToOffset:(long long)offset;
|
||||
-(void) seekToOffset:(SInt64)offset;
|
||||
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size;
|
||||
-(AudioFileTypeID) audioFileTypeHint;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
|
|
@ -34,15 +34,15 @@
|
|||
|
||||
#import "STKDataSource.h"
|
||||
|
||||
@implementation DataSource
|
||||
@implementation STKDataSource
|
||||
@synthesize delegate;
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
return -1;
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@
|
|||
}
|
||||
|
||||
-(void) close
|
||||
{
|
||||
{
|
||||
}
|
||||
|
||||
-(BOOL) hasBytesAvailable
|
||||
|
|
@ -79,4 +79,9 @@
|
|||
return 0;
|
||||
}
|
||||
|
||||
-(BOOL) supportsSeek
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -34,10 +34,14 @@
|
|||
|
||||
#import "STKDataSource.h"
|
||||
|
||||
@interface DataSourceWrapper : DataSource<STKDataSourceDelegate>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)innerDataSource;
|
||||
@interface STKDataSourceWrapper : STKDataSource<STKDataSourceDelegate>
|
||||
|
||||
@property (readonly) DataSource* innerDataSource;
|
||||
-(instancetype) initWithDataSource:(STKDataSource*)innerDataSource;
|
||||
|
||||
@property (readonly) STKDataSource* innerDataSource;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@
|
|||
|
||||
#import "STKDataSourceWrapper.h"
|
||||
|
||||
@interface DataSourceWrapper()
|
||||
@property (readwrite) DataSource* innerDataSource;
|
||||
@interface STKDataSourceWrapper()
|
||||
@property (readwrite) STKDataSource* innerDataSource;
|
||||
@end
|
||||
|
||||
@implementation DataSourceWrapper
|
||||
@implementation STKDataSourceWrapper
|
||||
|
||||
-(id) initWithDataSource:(DataSource*)innerDataSourceIn
|
||||
-(instancetype) initWithDataSource:(STKDataSource*)innerDataSourceIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
|
|
@ -62,12 +62,12 @@
|
|||
self.innerDataSource.delegate = nil;
|
||||
}
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return self.innerDataSource.length;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
return [self.innerDataSource seekToOffset:offset];
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
return [self.innerDataSource readIntoBuffer:buffer withSize:size];
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return self.innerDataSource.position;
|
||||
}
|
||||
|
|
@ -102,17 +102,17 @@
|
|||
return self.innerDataSource.hasBytesAvailable;
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(DataSource*)dataSource
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource
|
||||
{
|
||||
[self.delegate dataSourceDataAvailable:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceErrorOccured:(DataSource*)dataSource
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource
|
||||
{
|
||||
[self.delegate dataSourceErrorOccured:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(DataSource*)dataSource
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSource
|
||||
{
|
||||
[self.delegate dataSourceEof:self];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// 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
|
||||
|
|
@ -0,0 +1,211 @@
|
|||
//
|
||||
// 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,21 +34,27 @@
|
|||
|
||||
#import "STKCoreFoundationDataSource.h"
|
||||
|
||||
@interface HttpDataSource : STKCoreFoundationDataSource
|
||||
{
|
||||
@private
|
||||
int seekStart;
|
||||
int relativePosition;
|
||||
long long fileLength;
|
||||
int discontinuous;
|
||||
NSDictionary* httpHeaders;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@property (readwrite, retain) NSURL* url;
|
||||
@property (readwrite) UInt32 httpStatusCode;
|
||||
@class STKHTTPDataSource;
|
||||
|
||||
typedef void(^STKURLBlock)(NSURL* url);
|
||||
typedef NSURL* _Nonnull (^STKURLProvider)();
|
||||
typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock callback);
|
||||
|
||||
@interface STKHTTPDataSource : STKCoreFoundationDataSource
|
||||
|
||||
@property (readonly, retain) NSURL* url;
|
||||
@property (readonly) UInt32 httpStatusCode;
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
|
||||
-(id) initWithURL:(NSURL*)url;
|
||||
-(instancetype) initWithURL:(NSURL*)url;
|
||||
-(instancetype) initWithURL:(NSURL*)url httpRequestHeaders:(NSDictionary*)httpRequestHeaders;
|
||||
-(instancetype) initWithURLProvider:(STKURLProvider)urlProvider;
|
||||
-(instancetype) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider;
|
||||
-(nullable NSRunLoop*) eventsRunLoop;
|
||||
-(void) reconnect;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,608 @@
|
|||
/**********************************************************************************
|
||||
AudioPlayer.m
|
||||
|
||||
Created by Thong Nguyen on 14/05/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
|
||||
4. Neither the name of Thong Nguyen nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Thong Nguyen ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THONG NGUYEN BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**********************************************************************************/
|
||||
|
||||
#import "STKHTTPDataSource.h"
|
||||
#import "STKLocalFileDataSource.h"
|
||||
|
||||
@interface STKHTTPDataSource()
|
||||
{
|
||||
@private
|
||||
BOOL supportsSeek;
|
||||
UInt32 httpStatusCode;
|
||||
SInt64 seekStart;
|
||||
SInt64 relativePosition;
|
||||
SInt64 fileLength;
|
||||
int discontinuous;
|
||||
int requestSerialNumber;
|
||||
int prefixBytesRead;
|
||||
NSData* prefixBytes;
|
||||
NSMutableData* iceHeaderData;
|
||||
BOOL iceHeaderSearchComplete;
|
||||
BOOL iceHeaderAvailable;
|
||||
BOOL httpHeaderNotAvailable;
|
||||
|
||||
NSURL* currentUrl;
|
||||
STKAsyncURLProvider asyncUrlProvider;
|
||||
NSDictionary* httpHeaders;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
NSDictionary* requestHeaders;
|
||||
}
|
||||
-(void) open;
|
||||
|
||||
@end
|
||||
|
||||
@implementation STKHTTPDataSource
|
||||
|
||||
-(instancetype) initWithURL:(NSURL*)urlIn
|
||||
{
|
||||
return [self initWithURLProvider:^NSURL* { return urlIn; }];
|
||||
}
|
||||
|
||||
-(instancetype) initWithURL:(NSURL *)urlIn httpRequestHeaders:(NSDictionary *)httpRequestHeaders
|
||||
{
|
||||
self = [self initWithURLProvider:^NSURL* { return urlIn; }];
|
||||
self->requestHeaders = httpRequestHeaders;
|
||||
return self;
|
||||
}
|
||||
|
||||
-(instancetype) initWithURLProvider:(STKURLProvider)urlProviderIn
|
||||
{
|
||||
urlProviderIn = [urlProviderIn copy];
|
||||
|
||||
return [self initWithAsyncURLProvider:^(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock block)
|
||||
{
|
||||
block(urlProviderIn());
|
||||
}];
|
||||
}
|
||||
|
||||
-(instancetype) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProviderIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
seekStart = 0;
|
||||
relativePosition = 0;
|
||||
fileLength = -1;
|
||||
|
||||
self->asyncUrlProvider = [asyncUrlProviderIn copy];
|
||||
|
||||
audioFileTypeHint = [STKLocalFileDataSource audioFileTypeHintFromFileExtension:self->currentUrl.pathExtension];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
NSLog(@"STKHTTPDataSource dealloc");
|
||||
}
|
||||
|
||||
-(NSURL*) url
|
||||
{
|
||||
return self->currentUrl;
|
||||
}
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)mimeType
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static NSDictionary* fileTypesByMimeType;
|
||||
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
fileTypesByMimeType =
|
||||
@{
|
||||
@"audio/mp3": @(kAudioFileMP3Type),
|
||||
@"audio/mpg": @(kAudioFileMP3Type),
|
||||
@"audio/mpeg": @(kAudioFileMP3Type),
|
||||
@"audio/wav": @(kAudioFileWAVEType),
|
||||
@"audio/x-wav": @(kAudioFileWAVEType),
|
||||
@"audio/vnd.wav": @(kAudioFileWAVEType),
|
||||
@"audio/aifc": @(kAudioFileAIFCType),
|
||||
@"audio/aiff": @(kAudioFileAIFFType),
|
||||
@"audio/x-m4a": @(kAudioFileM4AType),
|
||||
@"audio/x-mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/aacp": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/m4a": @(kAudioFileM4AType),
|
||||
@"audio/mp4": @(kAudioFileMPEG4Type),
|
||||
@"video/mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/caf": @(kAudioFileCAFType),
|
||||
@"audio/x-caf": @(kAudioFileCAFType),
|
||||
@"audio/aac": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/aacp": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/ac3": @(kAudioFileAC3Type),
|
||||
@"audio/3gp": @(kAudioFile3GPType),
|
||||
@"video/3gp": @(kAudioFile3GPType),
|
||||
@"audio/3gpp": @(kAudioFile3GPType),
|
||||
@"video/3gpp": @(kAudioFile3GPType),
|
||||
@"audio/3gp2": @(kAudioFile3GP2Type),
|
||||
@"video/3gp2": @(kAudioFile3GP2Type)
|
||||
};
|
||||
});
|
||||
|
||||
NSNumber* number = [fileTypesByMimeType objectForKey:mimeType];
|
||||
|
||||
if (!number)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (AudioFileTypeID)number.intValue;
|
||||
}
|
||||
|
||||
-(AudioFileTypeID) 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
|
||||
{
|
||||
if (stream == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.httpStatusCode == 0)
|
||||
{
|
||||
if ([self parseHttpHeader])
|
||||
{
|
||||
if ([self hasBytesAvailable])
|
||||
{
|
||||
[super dataAvailable];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[super dataAvailable];
|
||||
}
|
||||
}
|
||||
|
||||
-(SInt64) position
|
||||
{
|
||||
return seekStart + relativePosition;
|
||||
}
|
||||
|
||||
-(SInt64) length
|
||||
{
|
||||
return fileLength >= 0 ? fileLength : 0;
|
||||
}
|
||||
|
||||
-(void) reconnect
|
||||
{
|
||||
NSRunLoop* savedEventsRunLoop = eventsRunLoop;
|
||||
|
||||
[self close];
|
||||
|
||||
eventsRunLoop = savedEventsRunLoop;
|
||||
|
||||
[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");
|
||||
|
||||
stream = 0;
|
||||
relativePosition = 0;
|
||||
seekStart = offset;
|
||||
|
||||
self->isInErrorState = NO;
|
||||
|
||||
if (!self->supportsSeek && offset != self->relativePosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self openForSeek:YES];
|
||||
}
|
||||
|
||||
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
|
||||
{
|
||||
return [self privateReadIntoBuffer:buffer withSize:size];
|
||||
}
|
||||
|
||||
-(int) privateReadIntoBuffer:(UInt8*)buffer withSize:(int)size
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (prefixBytes != nil)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return read;
|
||||
}
|
||||
|
||||
relativePosition += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
-(void) open
|
||||
{
|
||||
return [self openForSeek:NO];
|
||||
}
|
||||
|
||||
-(void) openForSeek:(BOOL)forSeek
|
||||
{
|
||||
int localRequestSerialNumber;
|
||||
|
||||
requestSerialNumber++;
|
||||
localRequestSerialNumber = requestSerialNumber;
|
||||
|
||||
asyncUrlProvider(self, forSeek, ^(NSURL* url)
|
||||
{
|
||||
if (localRequestSerialNumber != self->requestSerialNumber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->currentUrl = url;
|
||||
|
||||
if (url == nil)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1);
|
||||
|
||||
if (seekStart > 0 && supportsSeek)
|
||||
{
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%lld-", seekStart]);
|
||||
|
||||
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);
|
||||
|
||||
if (stream == nil)
|
||||
{
|
||||
CFRelease(message);
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CFReadStreamSetProperty(stream, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground);
|
||||
|
||||
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
|
||||
{
|
||||
CFRelease(message);
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Proxy support
|
||||
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
|
||||
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
|
||||
CFRelease(proxySettings);
|
||||
|
||||
// SSL support
|
||||
if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
||||
{
|
||||
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
|
||||
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
|
||||
[NSNull null], kCFStreamSSLPeerName,
|
||||
nil];
|
||||
|
||||
CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, (__bridge CFTypeRef)sslSettings);
|
||||
}
|
||||
|
||||
[self reregisterForEvents];
|
||||
|
||||
self->httpStatusCode = 0;
|
||||
|
||||
// Open
|
||||
if (!CFReadStreamOpen(stream))
|
||||
{
|
||||
CFRelease(stream);
|
||||
CFRelease(message);
|
||||
|
||||
stream = 0;
|
||||
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self->isInErrorState = NO;
|
||||
|
||||
CFRelease(message);
|
||||
});
|
||||
}
|
||||
|
||||
-(UInt32) httpStatusCode
|
||||
{
|
||||
return self->httpStatusCode;
|
||||
}
|
||||
|
||||
-(NSRunLoop*) eventsRunLoop
|
||||
{
|
||||
return self->eventsRunLoop;
|
||||
}
|
||||
|
||||
-(NSString*) description
|
||||
{
|
||||
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
|
||||
}
|
||||
|
||||
-(BOOL) supportsSeek
|
||||
{
|
||||
return self->supportsSeek;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -1,259 +0,0 @@
|
|||
/**********************************************************************************
|
||||
AudioPlayer.m
|
||||
|
||||
Created by Thong Nguyen on 14/05/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
|
||||
4. Neither the name of Thong Nguyen nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Thong Nguyen ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THONG NGUYEN BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**********************************************************************************/
|
||||
|
||||
#import "STKHttpDataSource.h"
|
||||
#import "STKLocalFileDataSource.h"
|
||||
|
||||
@interface HttpDataSource()
|
||||
-(void) open;
|
||||
@end
|
||||
|
||||
@implementation HttpDataSource
|
||||
@synthesize url;
|
||||
|
||||
-(id) initWithURL:(NSURL*)urlIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
seekStart = 0;
|
||||
relativePosition = 0;
|
||||
fileLength = -1;
|
||||
|
||||
self.url = urlIn;
|
||||
|
||||
[self open];
|
||||
|
||||
audioFileTypeHint = [LocalFileDataSource audioFileTypeHintFromFileExtension:urlIn.pathExtension];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)mimeType
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
static NSDictionary* fileTypesByMimeType;
|
||||
|
||||
dispatch_once(&onceToken, ^
|
||||
{
|
||||
fileTypesByMimeType =
|
||||
@{
|
||||
@"audio/mp3": @(kAudioFileMP3Type),
|
||||
@"audio/mpg": @(kAudioFileMP3Type),
|
||||
@"audio/mpeg": @(kAudioFileMP3Type),
|
||||
@"audio/wav": @(kAudioFileWAVEType),
|
||||
@"audio/aifc": @(kAudioFileAIFCType),
|
||||
@"audio/aiff": @(kAudioFileAIFFType),
|
||||
@"audio/x-m4a": @(kAudioFileM4AType),
|
||||
@"audio/x-mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/m4a": @(kAudioFileM4AType),
|
||||
@"audio/mp4": @(kAudioFileMPEG4Type),
|
||||
@"audio/caf": @(kAudioFileCAFType),
|
||||
@"audio/aac": @(kAudioFileAAC_ADTSType),
|
||||
@"audio/ac3": @(kAudioFileAC3Type),
|
||||
@"audio/3gp": @(kAudioFile3GPType)
|
||||
};
|
||||
});
|
||||
|
||||
NSNumber* number = [fileTypesByMimeType objectForKey:mimeType];
|
||||
|
||||
if (!number)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (AudioFileTypeID)number.intValue;
|
||||
}
|
||||
|
||||
-(AudioFileTypeID) audioFileTypeHint
|
||||
{
|
||||
return audioFileTypeHint;
|
||||
}
|
||||
|
||||
-(void) dataAvailable
|
||||
{
|
||||
if (fileLength < 0)
|
||||
{
|
||||
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
||||
|
||||
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
||||
|
||||
self.httpStatusCode = CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
||||
|
||||
CFRelease(response);
|
||||
|
||||
if (self.httpStatusCode == 200)
|
||||
{
|
||||
if (seekStart == 0)
|
||||
{
|
||||
fileLength = (long long)[[httpHeaders objectForKey:@"Content-Length"] integerValue];
|
||||
}
|
||||
|
||||
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"];
|
||||
AudioFileTypeID typeIdFromMimeType = [HttpDataSource audioFileTypeHintFromMimeType:contentType];
|
||||
|
||||
if (typeIdFromMimeType != 0)
|
||||
{
|
||||
audioFileTypeHint = typeIdFromMimeType;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self errorOccured];
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
[super dataAvailable];
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
{
|
||||
return seekStart + relativePosition;
|
||||
}
|
||||
|
||||
-(long long) length
|
||||
{
|
||||
return fileLength >= 0 ? fileLength : 0;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
{
|
||||
if (eventsRunLoop)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
}
|
||||
|
||||
if (stream)
|
||||
{
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(stream);
|
||||
}
|
||||
|
||||
stream = 0;
|
||||
relativePosition = 0;
|
||||
seekStart = (int)offset;
|
||||
|
||||
[self open];
|
||||
[self reregisterForEvents];
|
||||
}
|
||||
|
||||
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int read = (int)CFReadStreamRead(stream, buffer, size);
|
||||
|
||||
if (read < 0)
|
||||
{
|
||||
return read;
|
||||
}
|
||||
|
||||
relativePosition += read;
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
-(void) open
|
||||
{
|
||||
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self.url, kCFHTTPVersion1_1);
|
||||
|
||||
if (seekStart > 0)
|
||||
{
|
||||
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%d-", seekStart]);
|
||||
|
||||
discontinuous = YES;
|
||||
}
|
||||
|
||||
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
|
||||
|
||||
if (stream == nil)
|
||||
{
|
||||
CFRelease(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
|
||||
{
|
||||
CFRelease(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Proxy support
|
||||
|
||||
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
|
||||
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
|
||||
CFRelease(proxySettings);
|
||||
|
||||
// SSL support
|
||||
|
||||
if ([url.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
|
||||
{
|
||||
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
|
||||
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
|
||||
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
|
||||
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
|
||||
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
|
||||
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
|
||||
[NSNull null], kCFStreamSSLPeerName,
|
||||
nil];
|
||||
|
||||
CFReadStreamSetProperty(stream, kCFStreamPropertySSLSettings, (__bridge CFTypeRef)sslSettings);
|
||||
}
|
||||
|
||||
// Open
|
||||
|
||||
if (!CFReadStreamOpen(stream))
|
||||
{
|
||||
CFRelease(stream);
|
||||
CFRelease(message);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CFRelease(message);
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -34,18 +34,14 @@
|
|||
|
||||
#import "STKCoreFoundationDataSource.h"
|
||||
|
||||
@interface LocalFileDataSource : STKCoreFoundationDataSource
|
||||
{
|
||||
@private
|
||||
long long position;
|
||||
long long length;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface STKLocalFileDataSource : STKCoreFoundationDataSource
|
||||
|
||||
+(AudioFileTypeID) audioFileTypeHintFromFileExtension:(NSString*)fileExtension;
|
||||
|
||||
@property (readonly, copy) NSString* filePath;
|
||||
|
||||
-(id) initWithFilePath:(NSString*)filePath;
|
||||
-(instancetype) initWithFilePath:(NSString*)filePath;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
|||
|
|
@ -34,24 +34,26 @@
|
|||
|
||||
#import "STKLocalFileDataSource.h"
|
||||
|
||||
@interface LocalFileDataSource()
|
||||
@interface STKLocalFileDataSource()
|
||||
{
|
||||
SInt64 position;
|
||||
SInt64 length;
|
||||
AudioFileTypeID audioFileTypeHint;
|
||||
}
|
||||
@property (readwrite, copy) NSString* filePath;
|
||||
|
||||
-(void) open;
|
||||
@end
|
||||
|
||||
@implementation LocalFileDataSource
|
||||
@implementation STKLocalFileDataSource
|
||||
@synthesize filePath;
|
||||
|
||||
-(id) initWithFilePath:(NSString*)filePathIn
|
||||
-(instancetype) initWithFilePath:(NSString*)filePathIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self.filePath = filePathIn;
|
||||
|
||||
[self open];
|
||||
|
||||
audioFileTypeHint = [LocalFileDataSource audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
|
||||
audioFileTypeHint = [[self class] audioFileTypeHintFromFileExtension:filePathIn.pathExtension];
|
||||
}
|
||||
|
||||
return self;
|
||||
|
|
@ -103,6 +105,8 @@
|
|||
{
|
||||
if (stream)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
|
||||
CFReadStreamClose(stream);
|
||||
|
||||
stream = 0;
|
||||
|
|
@ -111,26 +115,23 @@
|
|||
|
||||
-(void) open
|
||||
{
|
||||
NSURL* url = [[NSURL alloc] initFileURLWithPath:self.filePath];
|
||||
|
||||
if (stream)
|
||||
{
|
||||
[self unregisterForEvents];
|
||||
|
||||
CFReadStreamClose(stream);
|
||||
CFRelease(stream);
|
||||
|
||||
stream = 0;
|
||||
}
|
||||
|
||||
NSURL* url = [[NSURL alloc] initFileURLWithPath:self.filePath];
|
||||
|
||||
stream = CFReadStreamCreateWithFile(NULL, (__bridge CFURLRef)url);
|
||||
|
||||
NSError *fileError;
|
||||
|
||||
NSFileManager *manager = [[NSFileManager alloc] init];
|
||||
|
||||
NSString *path = [NSString stringWithUTF8String:[url fileSystemRepresentation]];
|
||||
|
||||
NSDictionary *attributes = [manager attributesOfItemAtPath:path
|
||||
error:&fileError];
|
||||
NSError* fileError;
|
||||
NSFileManager* manager = [[NSFileManager alloc] init];
|
||||
NSDictionary* attributes = [manager attributesOfItemAtPath:filePath error:&fileError];
|
||||
|
||||
if (fileError)
|
||||
{
|
||||
|
|
@ -141,22 +142,23 @@
|
|||
}
|
||||
|
||||
NSNumber* number = [attributes objectForKey:@"NSFileSize"];
|
||||
|
||||
|
||||
if (number)
|
||||
{
|
||||
length = number.longLongValue;
|
||||
}
|
||||
|
||||
[self reregisterForEvents];
|
||||
|
||||
CFReadStreamOpen(stream);
|
||||
}
|
||||
|
||||
-(long long) position
|
||||
-(SInt64) position
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
-(long long) length
|
||||
-(SInt64) length
|
||||
{
|
||||
return length;
|
||||
}
|
||||
|
|
@ -179,7 +181,7 @@
|
|||
return retval;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(long long)offset
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
CFStreamStatus status = kCFStreamStatusClosed;
|
||||
|
||||
|
|
@ -196,7 +198,18 @@
|
|||
|
||||
[self close];
|
||||
[self open];
|
||||
[self reregisterForEvents];
|
||||
}
|
||||
|
||||
if (stream == 0)
|
||||
{
|
||||
CFRunLoopPerformBlock(eventsRunLoop.getCFRunLoop, NSRunLoopCommonModes, ^
|
||||
{
|
||||
[self errorOccured];
|
||||
});
|
||||
|
||||
CFRunLoopWakeUp(eventsRunLoop.getCFRunLoop);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, (__bridge CFTypeRef)[NSNumber numberWithLongLong:offset]) != TRUE)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
//
|
||||
// STKQueueEntry.h
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 30/01/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import "STKDataSource.h"
|
||||
#import "libkern/OSAtomic.h"
|
||||
#import "AudioToolbox/AudioToolbox.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface STKQueueEntry : NSObject
|
||||
{
|
||||
@public
|
||||
OSSpinLock spinLock;
|
||||
|
||||
BOOL parsedHeader;
|
||||
Float64 sampleRate;
|
||||
double packetDuration;
|
||||
UInt64 audioDataOffset;
|
||||
UInt64 audioDataByteCount;
|
||||
UInt32 packetBufferSize;
|
||||
volatile Float64 seekTime;
|
||||
volatile SInt64 framesQueued;
|
||||
volatile SInt64 framesPlayed;
|
||||
volatile SInt64 lastFrameQueued;
|
||||
volatile int processedPacketsCount;
|
||||
volatile int processedPacketsSizeTotal;
|
||||
AudioStreamBasicDescription audioStreamBasicDescription;
|
||||
double durationHint;
|
||||
}
|
||||
|
||||
@property (readonly) UInt64 audioDataLengthInBytes;
|
||||
@property (readwrite, retain) NSObject* queueItemId;
|
||||
@property (readwrite, retain) STKDataSource* dataSource;
|
||||
|
||||
-(instancetype) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
|
||||
|
||||
-(void) reset;
|
||||
-(double) duration;
|
||||
-(Float64) progressInFrames;
|
||||
-(double) calculatedBitRate;
|
||||
-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
//
|
||||
// STKQueueEntry.m
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 30/01/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import "STKQueueEntry.h"
|
||||
#import "STKDataSource.h"
|
||||
|
||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN (2)
|
||||
#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED (64)
|
||||
|
||||
@implementation STKQueueEntry
|
||||
|
||||
-(instancetype) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self->spinLock = OS_SPINLOCK_INIT;
|
||||
|
||||
self.dataSource = dataSourceIn;
|
||||
self.queueItemId = queueItemIdIn;
|
||||
self->lastFrameQueued = -1;
|
||||
self->durationHint = dataSourceIn.durationHint;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) reset
|
||||
{
|
||||
OSSpinLockLock(&self->spinLock);
|
||||
self->framesQueued = 0;
|
||||
self->framesPlayed = 0;
|
||||
self->lastFrameQueued = -1;
|
||||
OSSpinLockUnlock(&self->spinLock);
|
||||
}
|
||||
|
||||
-(double) calculatedBitRate
|
||||
{
|
||||
double retval;
|
||||
|
||||
if (packetDuration > 0)
|
||||
{
|
||||
if (processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED || (audioStreamBasicDescription.mBytesPerFrame == 0 && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN))
|
||||
{
|
||||
double averagePacketByteSize = (double)processedPacketsSizeTotal / (double)processedPacketsCount;
|
||||
|
||||
retval = averagePacketByteSize / packetDuration * 8;
|
||||
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
-(double) duration
|
||||
{
|
||||
if (durationHint > 0.0) return durationHint;
|
||||
|
||||
if (self->sampleRate <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
UInt64 audioDataLengthInBytes = [self audioDataLengthInBytes];
|
||||
|
||||
double calculatedBitRate = [self calculatedBitRate];
|
||||
|
||||
if (calculatedBitRate < 1.0 || self.dataSource.length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return audioDataLengthInBytes / (calculatedBitRate / 8);
|
||||
}
|
||||
|
||||
-(UInt64) audioDataLengthInBytes
|
||||
{
|
||||
if (audioDataByteCount)
|
||||
{
|
||||
return audioDataByteCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!self.dataSource.length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return self.dataSource.length - audioDataOffset;
|
||||
}
|
||||
}
|
||||
|
||||
-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription
|
||||
{
|
||||
if (self->audioStreamBasicDescription.mSampleRate == 0)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) == 0);
|
||||
}
|
||||
|
||||
-(Float64) progressInFrames
|
||||
{
|
||||
OSSpinLockLock(&self->spinLock);
|
||||
Float64 retval = (self->seekTime * self->audioStreamBasicDescription.mSampleRate) + self->framesPlayed;
|
||||
OSSpinLockUnlock(&self->spinLock);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
-(NSString*) description
|
||||
{
|
||||
return [[self queueItemId] description];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
//
|
||||
// Prefix header
|
||||
//
|
||||
// The contents of this file are implicitly included at the beginning of every source file.
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#endif
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// StreamingKitMac.h
|
||||
// StreamingKitMac
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface StreamingKitMac : NSObject
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// StreamingKitMac.m
|
||||
// StreamingKitMac
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import "StreamingKitMac.h"
|
||||
|
||||
@implementation StreamingKitMac
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// StreamingKitMacTests.m
|
||||
// StreamingKitMacTests
|
||||
//
|
||||
// Created by Thong Nguyen on 02/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
@interface StreamingKitMacTests : XCTestCase
|
||||
|
||||
@end
|
||||
|
||||
@implementation StreamingKitMacTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testExample
|
||||
{
|
||||
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
/* Localized versions of Info.plist keys */
|
||||
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
|
|
|
|||
Loading…
Reference in New Issue