Compare commits

...

59 Commits

Author SHA1 Message Date
Thong Nguyen 8e592b6741 Merge pull request #467 from dylancom/patch-1
fix: weird bleeps when playing a shoutcast stream
2022-05-31 18:34:41 +01:00
Dylan 68236d76ea fix: weird bleeps when playing a shoutcast stream
Fixes #460
2022-05-31 19:24:43 +02:00
Diego Stamigni eb9268e83e Merge pull request #423 from diegostamigni/master
Replace OSSpinLock with os_unfair_lock v2
2019-12-16 11:30:29 +00:00
Diego Stamigni 4252c1b45d Update README.md 2019-12-16 10:19:42 +00:00
Diego Stamigni dbf61906fd Fixes re SpinLock back port compatibility. Minor cleanup and upgrades on .proj and sample apps. 2019-03-27 17:39:33 +00:00
Diego Stamigni 67b81d190b Merge branch 'master' of github.com:diegostamigni/StreamingKit into diegostamigni 2019-03-27 16:26:40 +00:00
Diego Stamigni 07451cb2e6 Merge pull request #397 from tepmnthar/master
Modify podspec line orders to fix 'Cannot synthesize weak property because the current deployment target does not support weak references'
2019-03-21 15:55:24 +00:00
Diego Stamigni 9153fda04e StreamingKit.podspec reverted 2019-03-21 15:54:32 +00:00
Diego Stamigni 168cd85284 Small macOS sample app UI improvements 2019-03-21 10:04:53 +00:00
Diego Stamigni 9cfa0c368e gitignore updated 2019-03-21 08:58:08 +00:00
Diego Stamigni c53433afa2 pragma warning for deprecation ignored for OSSpinLockLock on fallback 2019-03-20 10:06:40 +00:00
Diego Stamigni d8aca1ec43 STKSpinLock helper around OSSpinLock / os_unfair_lock added
OSSpinLock replaced with custom back compatible version of os_unfair_lock
2019-03-20 09:57:30 +00:00
Diego Stamigni d224c48cb9 gitignore updated 2019-03-20 09:07:47 +00:00
Diego Stamigni bc0c0d0cf2 Merge pull request #276 from pcbeard/CanonicalAudioDescription
Add +canonicalAudioStreamBasicDescription to access format of PCM data
2019-03-19 11:21:35 +00:00
Diego Stamigni 568b3a5699 Merge pull request #277 from pcbeard/PlaybackRate
Playback rate
2019-03-19 11:18:54 +00:00
Thong Nguyen 6c10b3c374 Merge pull request #313 from g3ntleman/master
Added IPv6 support. Improved rechability check.
2019-03-15 19:42:42 +00:00
Thong Nguyen 027ee073b4 Merge pull request #421 from ssyzh/grammar
Fixed setting kAudioConverterDecompressionMagicCookie property with incorrect cookie pointer.
2019-03-15 19:18:45 +00:00
sunhao 0bd6246973 a wrong grammar 2019-01-25 14:23:13 +08:00
tepmnthar a2d5924aa1 change back 2018-04-12 11:30:07 +08:00
tepmnthar 0b8d4c77fc Modify podspec line orders to fix 'Cannot synthesize weak property because the current deployment target does not support weak references' 2018-04-12 11:24:26 +08:00
derpoliuk a9c7005b3d Bump pod version to 0.1.30 in podspec 2018-03-11 07:43:33 +02:00
derpoliuk 37ef63d8f7 Delete StreamingKit-head.podspec 2018-03-11 07:43:32 +02:00
Stanislav Derpoliuk 06678acdcc Merge pull request #391 from haritowa/master
Fix STKAudioPlayer leak with enabled metering
2018-03-11 07:32:30 +02:00
haritowa 1eec503a8b Fix STKAudioPlayer leak with enabled metering 2018-03-10 19:49:54 +03:00
Stanislav Derpoliuk 3b071cb5ff Merge pull request #179 from abuharsky/master
Added support for Icecast stream metadata
2018-03-05 11:48:21 +02:00
derpoliuk 12e7218848 Update project's version in StreamingKit.podspec 2018-01-17 15:37:48 +02:00
Stanislav Derpoliuk 230ed02f31 Merge pull request #384 from derpoliuk/fix-seek-progress
Fix wrong -[STKAudioPlayer progress] after seek
2018-01-17 15:32:45 +02:00
derpoliuk b189ca63a0 Fix wrong -[STKAudioPlayer progress] after seek 2018-01-12 19:19:59 +02:00
Stanislav Derpoliuk 859976339f Merge pull request #354 from docterd/master
Fixed a SSL Handshaking issue
2018-01-05 17:11:56 +02:00
Stanislav Derpoliuk 8823bbdc15 Merge pull request #383 from derpoliuk/weak-delegate
Make STKAudioPlayer's delegate weak
2017-12-16 07:31:27 +02:00
Stanislav Derpoliuk a6e4ec93bd Merge pull request #377 from derpoliuk/revert-329-master
Revert "Fix playback after seek"
2017-12-16 07:31:15 +02:00
derpoliuk 8e47ee2924 Make STKAudioPlayer's delegate weak 2017-11-09 11:20:27 +02:00
Stanislav Derpoliuk 46a3e67ad0 Revert "Fix playback after seek" 2017-11-09 11:18:19 +02:00
Dennis Oberhoff 06fe1615ed Fixed a SSL Handshaking issue 2017-07-16 21:34:15 +02:00
Thong Nguyen a32ba73a88 Merge pull request #329 from derpoliuk/master
Fix playback after seek
2017-05-10 11:27:12 +01:00
derpoliuk 6d00aa0dff Fix playback after seek (#273)
Use estimated byte offset (if available) instead
of calculated byte offset
2017-03-29 11:44:48 +03:00
Dirk Theisen cd13e7e9d4 Added IPv6 support. Improved rechability check. 2016-12-23 13:29:52 +01:00
Patrick Beard 0a927032e4 Use @property for rate, add overlap 2016-03-16 22:47:25 -07:00
Patrick Beard 785780f8b3 Cleaned up version of https://github.com/tumtumtum/StreamingKit/pull/262 2016-03-16 20:32:09 -07:00
Patrick Beard b71fc0efca Add +canonicalAudioStreamBasicDescription to access format of PCM data 2016-03-16 19:17:34 -07:00
Thong Nguyen 2d251d5150 Updated podspec 2016-01-04 11:06:47 +00:00
Thong Nguyen 2043330287 Removed commented out bad code 2016-01-04 11:05:20 +00:00
Thong Nguyen 3cb6349c97 Merge pull request #209 from corprew/patch-1
spelling fix
2015-12-19 23:14:54 +00:00
Thong Nguyen b51035a267 Merge branch 'richardgroves-patch-1' 2015-12-19 23:14:13 +00:00
Thong Nguyen 0d0280b631 Fix conflicts with richardgroves-patch-1 2015-12-19 23:14:02 +00:00
Thong Nguyen f9f8199015 Merge branch 'NOUSguide-master' 2015-12-19 23:11:33 +00:00
Thong Nguyen d9a6ba7248 Fixed conflicts 2015-12-19 23:11:25 +00:00
Thong Nguyen 6264442f58 Merge pull request #247 from kwillick/master
Swift compatibilty improvements
2015-12-19 23:09:30 +00:00
Kipp Hickman 830ed0f3db Added nullablity tags for better Swift compatibility. 2015-12-18 14:04:31 -08:00
Kipp Hickman 6ef69bacf0 Changed init methods to return instancetype instead of id.
Using instancetype allows the compiler to perform more error
checking. This article provides an in-depth explanation:
http://nshipster.com/instancetype/.
2015-12-18 14:04:24 -08:00
Thong Nguyen d8b77ae214 Fixed bug in progressInFrames 2015-12-11 01:06:53 +00:00
Thong Nguyen 499e54731d Merge pull request #175 from sergiou87/master
Paused Audio Player doesn't transit to Stopped state on stop
2015-12-11 01:02:14 +00:00
kampfgnu f84f1ef0bd use brackets to test for formatID 2015-11-26 11:43:41 +01:00
kampfgnu 3bc3a85df3 fix mono streams
by removing a line that is already removed in v 0.1.25, but not in master...
2015-11-26 11:41:07 +01:00
kampfgnu 520f98a6b3 fix reconnect to live streams
seeking to a non-zero offset works only if seek is supported (accept-ranges header)
2015-11-10 11:02:57 +01:00
Richard Groves 726ec86d77 Update STKAudioPlayer.m
The original line throws this warning in XCode - 
...../External Libraries/StreamingKit/StreamingKit/StreamingKit/STKAudioPlayer.m:880:61: Comparison of constant 'kAudioFormatLinearPCM' (1819304813) with boolean expression is always false

I'm guessing the precedence of ! vs == causes it to be 'mis-interpreted'.

Not quite sure of the logic where audio format != linear PCM implies a discontinuous stream, but...
2015-06-26 11:45:29 +01:00
Corprew Reed 15b0242305 spelling fix 2015-06-18 14:14:47 -07:00
Alexander Buharsky ece165a5d0 Added support for Icecast stream metadata 2015-01-23 15:55:21 +03:00
Sergio Padrino de99ec9d7a Make sure the audio player changes to stopped state when it's stopped from paused state 2015-01-14 16:56:06 +01:00
32 changed files with 1238 additions and 436 deletions
+2
View File
@@ -24,3 +24,5 @@ Icon?
ehthumbs.db
Thumbs.db
xcuserdata
xcshareddata
*.idea
+122 -5
View File
@@ -30,6 +30,41 @@
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
1D5086651A711D060030B19C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = A1E7C4C8188D57F50010896F;
remoteInfo = StreamingKit;
};
1D5086671A711D060030B19C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = A1E7C4D8188D57F60010896F;
remoteInfo = StreamingKitTests;
};
1D5086691A711D060030B19C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = A1A49969189E744400E2A2E2;
remoteInfo = StreamingKitMac;
};
1D50866B1A711D060030B19C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = A1A4997A189E744500E2A2E2;
remoteInfo = StreamingKitMacTests;
};
1D50866D1A711D160030B19C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = A1E7C4C7188D57F50010896F;
remoteInfo = StreamingKit;
};
A1115951188D686000641365 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = A1115929188D686000641365 /* Project object */;
@@ -40,6 +75,7 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = StreamingKit.xcodeproj; path = ../StreamingKit/StreamingKit.xcodeproj; sourceTree = "<group>"; };
A1115931188D686000641365 /* ExampleApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
A1115934188D686000641365 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
A1115936188D686000641365 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
@@ -97,6 +133,17 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
1D50865E1A711D050030B19C /* Products */ = {
isa = PBXGroup;
children = (
1D5086661A711D060030B19C /* libStreamingKit.a */,
1D5086681A711D060030B19C /* StreamingKitTests.xctest */,
1D50866A1A711D060030B19C /* libStreamingKitMac.a */,
1D50866C1A711D060030B19C /* StreamingKitMacTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
A1115928188D686000641365 = {
isa = PBXGroup;
children = (
@@ -119,6 +166,7 @@
A1115933188D686000641365 /* Frameworks */ = {
isa = PBXGroup;
children = (
1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */,
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */,
A1F5E491189EB3F20070B03F /* AVFoundation.framework */,
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */,
@@ -200,6 +248,7 @@
buildRules = (
);
dependencies = (
1D50866E1A711D160030B19C /* PBXTargetDependency */,
);
name = ExampleApp;
productName = ExampleApp;
@@ -230,7 +279,7 @@
A1115929188D686000641365 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0710;
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Thong Nguyen";
TargetAttributes = {
A111594B188D686000641365 = {
@@ -248,6 +297,12 @@
mainGroup = A1115928188D686000641365;
productRefGroup = A1115932188D686000641365 /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = 1D50865E1A711D050030B19C /* Products */;
ProjectRef = 1D50865D1A711D050030B19C /* StreamingKit.xcodeproj */;
},
);
projectRoot = "";
targets = (
A1115930188D686000641365 /* ExampleApp */,
@@ -256,6 +311,37 @@
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
1D5086661A711D060030B19C /* libStreamingKit.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libStreamingKit.a;
remoteRef = 1D5086651A711D060030B19C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
1D5086681A711D060030B19C /* StreamingKitTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = StreamingKitTests.xctest;
remoteRef = 1D5086671A711D060030B19C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
1D50866A1A711D060030B19C /* libStreamingKitMac.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = libStreamingKitMac.a;
remoteRef = 1D5086691A711D060030B19C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
1D50866C1A711D060030B19C /* StreamingKitMacTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = StreamingKitMacTests.xctest;
remoteRef = 1D50866B1A711D060030B19C /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
A111592F188D686000641365 /* Resources */ = {
isa = PBXResourcesBuildPhase;
@@ -301,6 +387,11 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
1D50866E1A711D160030B19C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = StreamingKit;
targetProxy = 1D50866D1A711D160030B19C /* PBXContainerItemProxy */;
};
A1115952188D686000641365 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = A1115930188D686000641365 /* ExampleApp */;
@@ -336,19 +427,32 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -366,7 +470,7 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../StreamingKit/StreamingKit",
);
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
@@ -381,18 +485,31 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@@ -404,7 +521,7 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../StreamingKit/StreamingKit",
);
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@@ -420,7 +537,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LLVM_LTO = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
@@ -437,7 +554,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LLVM_LTO = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
+3 -4
View File
@@ -24,13 +24,12 @@
-(BOOL) application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
NSError* error;
Float32 bufferLength = 0.1;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
[[AVAudioSession sharedInstance] setPreferredIOBufferDuration:bufferLength 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];
@@ -70,7 +69,7 @@
-(void) audioPlayerViewPlayFromIcecastSelected:(AudioPlayerView *)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"http://shoutmedia.abc.net.au:10326"];
NSURL* url = [NSURL URLWithString:@"http://nashe.streamr.ru/jazz-128.mp3"];
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
+1
View File
@@ -50,6 +50,7 @@
@private
NSTimer* timer;
UILabel* label;
UILabel* metadataLabel;
UILabel* statusLabel;
UISlider* slider;
UISwitch* enableEqSwitch;
+18
View File
@@ -111,6 +111,11 @@
[enableEqSwitch addTarget:self action:@selector(onEnableEqSwitch) forControlEvents:UIControlEventAllTouchEvents];
metadataLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 10, frame.size.width, 25)];
metadataLabel.textAlignment = NSTextAlignmentCenter;
metadataLabel.font = [UIFont boldSystemFontOfSize:17.0f];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 40, frame.size.width, 25)];
label.textAlignment = NSTextAlignmentCenter;
@@ -131,6 +136,7 @@
[self addSubview:queueShortFileButton];
[self addSubview:queuePcmWaveFileFromHTTPButton];
[self addSubview:repeatSwitch];
[self addSubview:metadataLabel];
[self addSubview:label];
[self addSubview:statusLabel];
[self addSubview:stopButton];
@@ -218,26 +224,31 @@
-(void) playFromHTTPButtonTouched
{
[self.delegate audioPlayerViewPlayFromHTTPSelected:self];
metadataLabel.text = nil;
}
-(void) playFromIcecasButtonTouched
{
[self.delegate audioPlayerViewPlayFromIcecastSelected:self];
metadataLabel.text = nil;
}
-(void) playFromLocalFileButtonTouched
{
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
metadataLabel.text = nil;
}
-(void) queueShortFileButtonTouched
{
[self.delegate audioPlayerViewQueueShortFileSelected:self];
metadataLabel.text = nil;
}
-(void) queuePcmWaveFileButtonTouched
{
[self.delegate audioPlayerViewQueuePcmWaveFileSelected:self];
metadataLabel.text = nil;
}
-(void) muteButtonPressed
@@ -375,4 +386,11 @@
NSLog(@"%@", line);
}
- (void)audioPlayer:(STKAudioPlayer *)audioPlayer didReadStreamMetadata:(NSDictionary *)dictionary
{
dispatch_async(dispatch_get_main_queue(), ^{
self->metadataLabel.text = dictionary[@"StreamTitle"];
});
}
@end
@@ -238,7 +238,7 @@
A1A49999189E765800E2A2E2 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0500;
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Thong Nguyen";
TargetAttributes = {
A1A499C1189E765800E2A2E2 = {
@@ -358,18 +358,32 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -382,7 +396,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.9;
MACOSX_DEPLOYMENT_TARGET = 10.11;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
@@ -395,26 +409,39 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_NO_COMMON_BLOCKS = 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;
MACOSX_DEPLOYMENT_TARGET = 10.11;
SDKROOT = macosx;
};
name = Release;
@@ -432,8 +459,8 @@
"$(SRCROOT)/../StreamingKit/StreamingKit",
);
INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist";
MACOSX_DEPLOYMENT_TARGET = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -452,8 +479,8 @@
"$(SRCROOT)/../StreamingKit/StreamingKit",
);
INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist";
MACOSX_DEPLOYMENT_TARGET = "";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -476,6 +503,7 @@
);
INFOPLIST_FILE = "ExampleAppMacTests/ExampleAppMacTests-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -495,6 +523,7 @@
GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch";
INFOPLIST_FILE = "ExampleAppMacTests/ExampleAppMacTests-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -10,29 +10,29 @@
<string>ExampleAppMac</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
<string>https://github.com/tumtumtum/StreamingKit.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace</string>
<string>ExampleAppMac/ExampleAppMac.xcodeproj</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
<string>../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/tumtumtum/StreamingKit.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>110</integer>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
<key>IDESourceControlWCCName</key>
<string>StreamingKit</string>
</dict>
+61 -13
View File
@@ -14,6 +14,7 @@
NSView* meter;
NSSlider* slider;
STKAudioPlayer* audioPlayer;
NSTextField* textField;
}
@end
@@ -21,26 +22,67 @@
-(void) applicationDidFinishLaunching:(NSNotification *)aNotification
{
CGRect frame = [self.window.contentView frame];
NSButton* playFromHTTPButton = [[NSButton alloc] initWithFrame:CGRectMake(10, 10, frame.size.width - 20, 100)];
NSButton* playFromHTTPButton = [[NSButton alloc] init];
[playFromHTTPButton setTitle:@"Play from HTTP"];
[playFromHTTPButton setAction:@selector(playFromHTTP)];
NSButton* stopButton = [[NSButton alloc] init];
[stopButton setTitle:@"Stop"];
[stopButton setAction:@selector(stopPlaying)];
NSStackView *buttonStackView = [[NSStackView alloc] initWithFrame:self.window.contentView.frame];
[buttonStackView setDistribution:NSStackViewDistributionFillEqually];
[buttonStackView setSpacing:8];
[buttonStackView setOrientation:NSUserInterfaceLayoutOrientationHorizontal];
[buttonStackView addArrangedSubview:playFromHTTPButton];
[buttonStackView addArrangedSubview:stopButton];
slider = [[NSSlider alloc] initWithFrame:CGRectMake(10, 140, frame.size.width - 20, 20)];
slider = [[NSSlider alloc] init];
[slider setTranslatesAutoresizingMaskIntoConstraints:false];
[slider setAction:@selector(sliderChanged:)];
meter = [[NSView alloc] initWithFrame:CGRectMake(10, 200, 0, 20)];
meter = [[NSView alloc] init];
[meter setTranslatesAutoresizingMaskIntoConstraints:false];
[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} } ];
NSView *meterWrapper = [[NSView alloc] init];
[meterWrapper setTranslatesAutoresizingMaskIntoConstraints:false];
[meterWrapper addSubview:meter];
textField = [[NSTextField alloc] init];
[textField setTranslatesAutoresizingMaskIntoConstraints:false];
textField.stringValue = @"http://www.abstractpath.com/files/audiosamples/sample.mp3";
NSStackView *stackView = [[NSStackView alloc] initWithFrame:self.window.contentView.frame];
[stackView setTranslatesAutoresizingMaskIntoConstraints:false];
[stackView setDistribution:NSStackViewDistributionEqualSpacing];
[stackView setSpacing:8];
[stackView setOrientation:NSUserInterfaceLayoutOrientationVertical];
[stackView addArrangedSubview:textField];
[stackView addArrangedSubview:meterWrapper];
[stackView addArrangedSubview:slider];
[stackView addArrangedSubview:buttonStackView];
[[self.window contentView] addSubview:stackView];
[stackView.topAnchor constraintEqualToAnchor:self.window.contentView.topAnchor constant:16].active = true;
[stackView.bottomAnchor constraintEqualToAnchor:self.window.contentView.bottomAnchor constant:-16].active = true;
[stackView.rightAnchor constraintEqualToAnchor:self.window.contentView.rightAnchor constant:-16].active = true;
[stackView.leftAnchor constraintEqualToAnchor:self.window.contentView.leftAnchor constant:16].active = true;
[meter.topAnchor constraintEqualToAnchor:meterWrapper.topAnchor].active = true;
[meter.bottomAnchor constraintEqualToAnchor:meterWrapper.bottomAnchor].active = true;
[meter.leadingAnchor constraintEqualToAnchor:meterWrapper.leadingAnchor].active = true;
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;
@@ -58,7 +100,13 @@
-(void) playFromHTTP
{
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer play:textField.stringValue];
audioPlayer.rate = 2;
}
- (void) stopPlaying
{
[audioPlayer stop];
}
-(void) tick:(NSTimer*)timer
@@ -1,100 +1,17 @@
<?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">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4439"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</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>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29">
<items>
<menuItem title="ExampleAppMac" id="56">
@@ -102,6 +19,9 @@
<items>
<menuItem title="About ExampleAppMac" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
@@ -116,95 +36,211 @@
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide ExampleAppMac" keyEquivalent="h" id="134"/>
<menuItem title="Hide ExampleAppMac" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
</connections>
</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"/>
<menuItem title="Quit ExampleAppMac" keyEquivalent="q" id="136">
<connections>
<action selector="terminate:" target="-3" id="449"/>
</connections>
</menuItem>
</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="New" keyEquivalent="n" id="82">
<connections>
<action selector="newDocument:" target="-1" id="373"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="72">
<connections>
<action selector="openDocument:" target="-1" id="374"/>
</connections>
</menuItem>
<menuItem title="Open Recent" id="124">
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
<items>
<menuItem title="Clear Menu" id="126"/>
<menuItem title="Clear Menu" id="126">
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
</connections>
</menuItem>
</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="Close" keyEquivalent="w" id="73">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="75">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="112">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
</connections>
</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"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</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="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="redo:" target="-1" id="231"/>
</connections>
</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="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
</connections>
</menuItem>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="485">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="486"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
</connections>
</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…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="534">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="535"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208">
<connections>
<action selector="performFindPanelAction:" target="-1" id="487"/>
</connections>
</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"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="488"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221">
<connections>
<action selector="performFindPanelAction:" target="-1" id="489"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
</connections>
</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 title="Show Spelling and Grammar" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="453"/>
<menuItem title="Check Spelling While Typing" id="219"/>
<menuItem title="Check Grammar With Spelling" id="346"/>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
</connections>
</menuItem>
<menuItem title="Correct Spelling Automatically" id="454">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="456"/>
</connections>
</menuItem>
</items>
</menu>
@@ -214,18 +250,38 @@
<items>
<menuItem title="Show Substitutions" id="457">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="458"/>
</connections>
</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 Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
</connections>
</menuItem>
<menuItem title="Smart Dashes" id="460">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="461"/>
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="462">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="463"/>
</connections>
</menuItem>
</items>
</menu>
@@ -236,12 +292,21 @@
<items>
<menuItem title="Make Upper Case" id="452">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="464"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="465">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="468"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="466">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="467"/>
</connections>
</menuItem>
</items>
</menu>
@@ -249,8 +314,16 @@
<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"/>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
@@ -265,13 +338,37 @@
<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 title="Show Fonts" keyEquivalent="t" id="389">
<connections>
<action selector="orderFrontFontPanel:" target="420" id="424"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390">
<connections>
<action selector="addFontTrait:" target="420" id="421"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391">
<connections>
<action selector="addFontTrait:" target="420" id="422"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="432"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394"/>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394">
<connections>
<action selector="modifyFont:" target="420" id="425"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395">
<connections>
<action selector="modifyFont:" target="420" id="423"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -279,15 +376,27 @@
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="438"/>
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="441"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="431"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="435"/>
</connections>
</menuItem>
</items>
</menu>
@@ -298,12 +407,21 @@
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="439"/>
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="440"/>
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="434"/>
</connections>
</menuItem>
</items>
</menu>
@@ -314,30 +432,55 @@
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="437"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="430"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="429"/>
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="426"/>
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="427"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="433"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="428"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="436"/>
</connections>
</menuItem>
</items>
</menu>
@@ -346,12 +489,27 @@
<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="Align Left" keyEquivalent="{" id="498">
<connections>
<action selector="alignLeft:" target="-1" id="524"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="499">
<connections>
<action selector="alignCenter:" target="-1" id="518"/>
</connections>
</menuItem>
<menuItem title="Justify" id="500">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="523"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="501">
<connections>
<action selector="alignRight:" target="-1" id="521"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="501"/>
<menuItem isSeparatorItem="YES" id="502"/>
<menuItem title="Writing Direction" id="503">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -363,14 +521,23 @@
<menuItem id="510">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="525"/>
</connections>
</menuItem>
<menuItem id="511">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="526"/>
</connections>
</menuItem>
<menuItem id="512">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="527"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="513"/>
<menuItem title="Selection" enabled="NO" id="514">
@@ -379,14 +546,23 @@
<menuItem id="515">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="528"/>
</connections>
</menuItem>
<menuItem id="516">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="529"/>
</connections>
</menuItem>
<menuItem id="517">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="530"/>
</connections>
</menuItem>
</items>
</menu>
@@ -394,12 +570,21 @@
<menuItem isSeparatorItem="YES" id="504"/>
<menuItem title="Show Ruler" id="505">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="520"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="506">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="522"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="507">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="519"/>
</connections>
</menuItem>
</items>
</menu>
@@ -412,20 +597,39 @@
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</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 title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Bring All to Front" id="5"/>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
@@ -433,17 +637,21 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="491">
<items>
<menuItem title="ExampleAppMac Help" keyEquivalent="?" id="492"/>
<menuItem title="ExampleAppMac Help" keyEquivalent="?" id="492">
<connections>
<action selector="showHelp:" target="-1" id="493"/>
</connections>
</menuItem>
</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"/>
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="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"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
@@ -454,14 +662,6 @@
<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>
<customObject id="420" customClass="NSFontManager"/>
</objects>
</document>
@@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
+2 -2
View File
@@ -11,7 +11,7 @@ The primary motivation of this project was to decouple the input data sources fr
* 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, AutoRecoveryingHTTP DataSources are provided).
* 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 (0% - 1% CPU usage when streaming).
* Optimised for linear data sources. Random access sources are required only for seeking.
@@ -63,4 +63,4 @@ STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
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](http://www.twitter.com/tumtumtum))
Copyright (c) 2012-2019, Thong Nguyen ([@tumtumtum](http://www.twitter.com/tumtumtum))
-16
View File
@@ -1,16 +0,0 @@
Pod::Spec.new do |s|
s.name = "StreamingKit"
s.version = "0.0.0"
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'
s.author = { "Thong Nguyen" => "tumtumtum@gmail.com" }
s.source = { :git => "https://github.com/tumtumtum/StreamingKit.git" }
s.platform = :ios
s.requires_arc = true
s.source_files = 'StreamingKit/StreamingKit/*.{h,m}'
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 -4
View File
@@ -1,16 +1,16 @@
Pod::Spec.new do |s|
s.name = "StreamingKit"
s.version = "0.1.27"
s.version = "0.1.31"
s.platform = :ios
s.ios.deployment_target = '5.0'
s.osx.deployment_target = '10.7'
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'
s.author = { "Thong Nguyen" => "tumtumtum@gmail.com" }
s.source = { :git => "https://github.com/tumtumtum/StreamingKit.git", :tag => s.version.to_s}
s.platform = :ios
s.requires_arc = true
s.source_files = 'StreamingKit/StreamingKit/*.{h,m}'
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
@@ -7,6 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
40B6239322423B1E005D725D /* STKMacro.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B6239222423B1E005D725D /* STKMacro.h */; };
40B6239422423B1F005D725D /* STKMacro.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B6239222423B1E005D725D /* STKMacro.h */; };
40B6239822423F28005D725D /* STKSpinLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B6239722423F28005D725D /* STKSpinLock.h */; };
40B6239922423F28005D725D /* STKSpinLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 40B6239722423F28005D725D /* STKSpinLock.h */; };
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, ); }; };
@@ -91,6 +95,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
40B6239222423B1E005D725D /* STKMacro.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STKMacro.h; sourceTree = "<group>"; };
40B6239722423F28005D725D /* STKSpinLock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = STKSpinLock.h; 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; };
@@ -270,6 +276,8 @@
A1E7C4CD188D57F50010896F /* StreamingKit */ = {
isa = PBXGroup;
children = (
A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */,
A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */,
A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */,
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */,
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */,
@@ -284,10 +292,10 @@
A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */,
A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */,
A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */,
40B6239222423B1E005D725D /* STKMacro.h */,
A1BF65D0189A6582004DD08C /* STKQueueEntry.h */,
A1BF65D1189A6582004DD08C /* STKQueueEntry.m */,
A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */,
A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */,
40B6239722423F28005D725D /* STKSpinLock.h */,
A1E7C4CE188D57F50010896F /* Supporting Files */,
);
path = StreamingKit;
@@ -326,9 +334,11 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
40B6239822423F28005D725D /* STKSpinLock.h in Headers */,
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */,
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */,
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */,
40B6239322423B1E005D725D /* STKMacro.h in Headers */,
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */,
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */,
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */,
@@ -341,6 +351,8 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
40B6239922423F28005D725D /* STKSpinLock.h in Headers */,
40B6239422423B1F005D725D /* STKMacro.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -425,7 +437,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = STK;
LastUpgradeCheck = 0710;
LastUpgradeCheck = 1010;
ORGANIZATIONNAME = "Thong Nguyen";
};
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
@@ -669,18 +681,31 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -693,7 +718,8 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.9;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
};
@@ -707,24 +733,38 @@
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = 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;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MACOSX_DEPLOYMENT_TARGET = 10.9;
SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES;
};
@@ -8,10 +8,14 @@
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSMutableArray (STKAudioPlayer)
-(void) enqueue:(id)obj;
-(void) skipQueue:(id)obj;
-(void) skipQueueWithQueue:(NSMutableArray*)queue;
-(id) dequeue;
-(id) peek;
-(nullable id) dequeue;
-(nullable id) peek;
@end
NS_ASSUME_NONNULL_END
+20 -6
View File
@@ -44,6 +44,8 @@
#include "UIKit/UIApplication.h"
#endif
NS_ASSUME_NONNULL_BEGIN
typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
{
STKAudioPlayerStateReady,
@@ -133,6 +135,9 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
/// Raised when items queued items are cleared (usually because of a call to play, setDataSource or stop)
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems;
/// Raised when datasource read stream metadata. Called from the non-main thread.
-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didReadStreamMetadata:(NSDictionary*)dictionary;
@end
@interface STKAudioPlayer : NSObject<STKDataSourceDelegate>
@@ -146,18 +151,22 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
@property (readonly) double duration;
/// Gets the current item progress in seconds
@property (readonly) double progress;
/// Gets or sets the playback rate (default is 1.0)
@property(readwrite) float rate;
// Gets or sets the playback overlap (default is 8.0)
@property(readwrite) float overlap;
/// 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) NSArray* frameFilters;
@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) NSObject* mostRecentlyQueuedStillPendingItem;
@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
@@ -165,7 +174,7 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
/// 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;
@property (readwrite, weak) id<STKAudioPlayerDelegate> delegate;
/// Creates a datasource from a given URL.
/// URLs with FILE schemes will return an STKLocalFileDataSource.
@@ -173,11 +182,14 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
/// URLs with unrecognised schemes will return nil.
+(STKDataSource*) dataSourceFromURL:(NSURL*)url;
/// Returns canonical audio format used by STKFrameFilter blocks.
+(AudioStreamBasicDescription)canonicalAudioStreamBasicDescription;
/// Initializes a new STKAudioPlayer with the default options
-(id) init;
-(instancetype) init;
/// Initializes a new STKAudioPlayer with the given options
-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn;
-(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
@@ -254,7 +266,7 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
/// Appends a frame filter with the given name and filter block just after the filter with the given name.
/// If the given name is nil, the filter will be inserted at the beginning of the filter change
-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block;
-(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).
@@ -268,3 +280,5 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
@end
NS_ASSUME_NONNULL_END
+243 -146
View File
@@ -173,7 +173,7 @@ STKAudioPlayerInternalState;
@end
@implementation STKFrameFilterEntry
-(id) initWithFilter:(STKFrameFilter)filterIn andName:(NSString*)nameIn
-(instancetype) initWithFilter:(STKFrameFilter)filterIn andName:(NSString*)nameIn
{
if (self = [super init])
{
@@ -203,6 +203,7 @@ static AudioComponentDescription mixerDescription;
static AudioComponentDescription nbandUnitDescription;
static AudioComponentDescription outputUnitDescription;
static AudioComponentDescription convertUnitDescription;
static AudioComponentDescription playbackRateUnitDescription;
static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
static AudioStreamBasicDescription recordAudioStreamBasicDescription;
@@ -234,9 +235,11 @@ static AudioStreamBasicDescription recordAudioStreamBasicDescription;
AUNode eqOutputNode;
AUNode mixerInputNode;
AUNode mixerOutputNode;
AUNode playbackRateNode;
AudioComponentInstance eqUnit;
AudioComponentInstance mixerUnit;
AudioComponentInstance playbackRateUnit;
AudioComponentInstance outputUnit;
UInt32 eqBandCount;
@@ -252,8 +255,8 @@ static AudioStreamBasicDescription recordAudioStreamBasicDescription;
NSMutableArray* upcomingQueue;
NSMutableArray* bufferingQueue;
OSSpinLock pcmBufferSpinLock;
OSSpinLock internalStateLock;
os_unfair_lock pcmBufferSpinLock;
os_unfair_lock internalStateLock;
volatile UInt32 pcmBufferTotalFrameCount;
volatile UInt32 pcmBufferFrameStartIndex;
volatile UInt32 pcmBufferUsedFrameCount;
@@ -283,11 +286,11 @@ static AudioStreamBasicDescription recordAudioStreamBasicDescription;
UInt32 recordPacketSize;
AudioStreamPacketDescription *recordPacketDescriptions;
void(^stopBackBackgroundTaskBlock)();
void(^stopBackBackgroundTaskBlock)(void);
int32_t seekVersion;
OSSpinLock seekLock;
OSSpinLock currentEntryReferencesLock;
os_unfair_lock seekLock;
os_unfair_lock currentEntryReferencesLock;
pthread_mutex_t playerMutex;
pthread_cond_t playerThreadReadyCondition;
@@ -342,7 +345,16 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
#else
const int bytesPerSample = sizeof(AudioSampleType);
#endif
playbackRateUnitDescription = (AudioComponentDescription)
{
.componentManufacturer = kAudioUnitManufacturer_Apple,
.componentType = kAudioUnitType_FormatConverter,
.componentSubType = kAudioUnitSubType_AUiPodTimeOther,
.componentFlags = 0,
.componentFlagsMask = 0,
};
canonicalAudioStreamBasicDescription = (AudioStreamBasicDescription)
{
.mSampleRate = 44100.00,
@@ -439,13 +451,13 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
break;
}
OSSpinLockLock(&internalStateLock);
setLock(&internalStateLock);
waitingForDataAfterSeekFrameCount = 0;
if (value == internalState)
{
OSSpinLockUnlock(&internalStateLock);
lockUnlock(&internalStateLock);
return;
}
@@ -454,7 +466,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
{
if (!ifInState(self->internalState))
{
OSSpinLockUnlock(&internalStateLock);
lockUnlock(&internalStateLock);
return;
}
@@ -468,7 +480,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
{
self.state = newState;
OSSpinLockUnlock(&internalStateLock);
lockUnlock(&internalStateLock);
dispatch_async(dispatch_get_main_queue(), ^
{
@@ -477,7 +489,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
else
{
OSSpinLockUnlock(&internalStateLock);
lockUnlock(&internalStateLock);
}
}
@@ -504,15 +516,20 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
}
-(id) init
-(instancetype) init
{
return [self initWithOptions:(STKAudioPlayerOptions){}];
}
-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn
-(instancetype) initWithOptions:(STKAudioPlayerOptions)optionsIn
{
if (self = [super init])
{
pcmBufferSpinLock = OS_UNFAIR_LOCK_INIT;
internalStateLock = OS_UNFAIR_LOCK_INIT;
seekLock = OS_UNFAIR_LOCK_INIT;
currentEntryReferencesLock = OS_UNFAIR_LOCK_INIT;
options = optionsIn;
self->volume = 1.0;
@@ -579,11 +596,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
currentlyPlayingEntry.dataSource.delegate = nil;
[currentlyReadingEntry.dataSource unregisterForEvents];
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
}
[self closeRecordAudioFile];
@@ -680,6 +695,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return retval;
}
+ (AudioStreamBasicDescription)canonicalAudioStreamBasicDescription
{
return canonicalAudioStreamBasicDescription;
}
-(void) clearQueue
{
pthread_mutex_lock(&playerMutex);
@@ -982,9 +1002,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return 0;
}
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
STKQueueEntry* entry = currentlyPlayingEntry;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
if (entry == nil)
{
@@ -1014,23 +1034,47 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return 0;
}
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
STKQueueEntry* entry = currentlyPlayingEntry;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
if (entry == nil)
{
return 0;
}
OSSpinLockLock(&entry->spinLock);
setLock(&entry->spinLock);
double retval = entry->seekTime + (entry->framesPlayed / canonicalAudioStreamBasicDescription.mSampleRate);
OSSpinLockUnlock(&entry->spinLock);
lockUnlock(&entry->spinLock);
return retval;
}
-(BOOL) invokeOnPlaybackThread:(void(^)())block
-(float) rate
{
AudioUnitParameterValue rateValue;
AudioUnitGetParameter(playbackRateUnit, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, &rateValue);
return rateValue;
}
-(void) setRate:(float)rate
{
AudioUnitSetParameter(playbackRateUnit, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, rate, 0);
}
-(float) overlap
{
AudioUnitParameterValue overlapValue;
AudioUnitGetParameter(playbackRateUnit, kNewTimePitchParam_Overlap, kAudioUnitScope_Global, 0, &overlapValue);
return overlapValue;
}
-(void) setOverlap:(float)overlap
{
AudioUnitSetParameter(playbackRateUnit, kNewTimePitchParam_Overlap, kAudioUnitScope_Global, 0, overlap, 0);
}
-(BOOL) invokeOnPlaybackThread:(void(^)(void))block
{
NSRunLoop* runLoop = playbackThreadRunLoop;
@@ -1069,7 +1113,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return;
}
OSSpinLockLock(&seekLock);
setLock(&seekLock);
BOOL seekAlreadyRequested = seekToTimeWasRequested;
@@ -1080,14 +1124,14 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
{
OSAtomicIncrement32(&seekVersion);
OSSpinLockUnlock(&seekLock);
lockUnlock(&seekLock);
[self wakeupPlaybackThread];
return;
}
OSSpinLockUnlock(&seekLock);
lockUnlock(&seekLock);
}
-(void) createPlaybackThread
@@ -1138,9 +1182,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[currentlyReadingEntry.dataSource close];
}
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
currentlyReadingEntry = entry;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
currentlyReadingEntry.dataSource.delegate = self;
[currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]];
@@ -1184,25 +1228,25 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
{
if (!isPlayingSameItemProbablySeek)
{
OSSpinLockLock(&next->spinLock);
setLock(&next->spinLock);
next->seekTime = 0;
OSSpinLockUnlock(&next->spinLock);
lockUnlock(&next->spinLock);
OSSpinLockLock(&seekLock);
setLock(&seekLock);
seekToTimeWasRequested = NO;
OSSpinLockUnlock(&seekLock);
lockUnlock(&seekLock);
}
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
currentlyPlayingEntry = next;
NSObject* playingQueueItemId = playingQueueItemId = currentlyPlayingEntry.queueItemId;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
if (!isPlayingSameItemProbablySeek && entry)
{
[self playbackThreadQueueMainThreadSyncBlock:^
{
[self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration];
[self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:self->stopReason andProgress:progress andDuration:duration];
}];
}
@@ -1218,15 +1262,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
else
{
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
if (!isPlayingSameItemProbablySeek && entry)
{
[self playbackThreadQueueMainThreadSyncBlock:^
{
[self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration];
[self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:self->stopReason andProgress:progress andDuration:duration];
}];
}
}
@@ -1234,7 +1278,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[self wakeupPlaybackThread];
}
-(void) dispatchSyncOnMainThread:(void(^)())block
-(void) dispatchSyncOnMainThread:(void(^)(void))block
{
__block BOOL finished = NO;
@@ -1245,15 +1289,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
dispatch_async(dispatch_get_main_queue(), ^
{
if (!disposeWasRequested)
if (!self->disposeWasRequested)
{
block();
}
pthread_mutex_lock(&mainThreadSyncCallMutex);
pthread_mutex_lock(&self->mainThreadSyncCallMutex);
finished = YES;
pthread_cond_signal(&mainThreadSyncCallReadyCondition);
pthread_mutex_unlock(&mainThreadSyncCallMutex);
pthread_cond_signal(&self->mainThreadSyncCallReadyCondition);
pthread_mutex_unlock(&self->mainThreadSyncCallMutex);
});
pthread_mutex_lock(&mainThreadSyncCallMutex);
@@ -1276,13 +1320,13 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
pthread_mutex_unlock(&mainThreadSyncCallMutex);
}
-(void) playbackThreadQueueMainThreadSyncBlock:(void(^)())block
-(void) playbackThreadQueueMainThreadSyncBlock:(void(^)(void))block
{
block = [block copy];
[self invokeOnPlaybackThread:^
{
if (disposeWasRequested)
if (self->disposeWasRequested)
{
return;
}
@@ -1382,21 +1426,21 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
int32_t originalSeekVersion;
BOOL originalSeekToTimeRequested;
OSSpinLockLock(&seekLock);
setLock(&seekLock);
originalSeekVersion = seekVersion;
originalSeekToTimeRequested = seekToTimeWasRequested;
OSSpinLockUnlock(&seekLock);
lockUnlock(&seekLock);
if (originalSeekToTimeRequested && currentlyReadingEntry == currentlyPlayingEntry)
{
[self processSeekToTime];
OSSpinLockLock(&seekLock);
setLock(&seekLock);
if (originalSeekVersion == seekVersion)
{
seekToTimeWasRequested = NO;
}
OSSpinLockUnlock(&seekLock);
lockUnlock(&seekLock);
}
}
else if (currentlyPlayingEntry == nil && seekToTimeWasRequested)
@@ -1445,10 +1489,10 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
currentlyPlayingEntry.dataSource.delegate = nil;
pthread_mutex_lock(&playerMutex);
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
currentlyPlayingEntry = nil;
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
pthread_mutex_unlock(&playerMutex);
[self closeRecordAudioFile];
@@ -1483,9 +1527,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
seekByteOffset = currentEntry.dataSource.length - 2 * currentEntry->packetBufferSize;
}
OSSpinLockLock(&currentEntry->spinLock);
setLock(&currentEntry->spinLock);
currentEntry->seekTime = requestedSeekTime;
OSSpinLockUnlock(&currentEntry->spinLock);
lockUnlock(&currentEntry->spinLock);
double calculatedBitRate = [currentEntry calculatedBitRate];
@@ -1497,15 +1541,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
error = AudioFileStreamSeek(audioFileStream, seekPacket, &packetAlignedByteOffset, &ioFlags);
if (!error && !(ioFlags & kAudioFileStreamSeekFlag_OffsetIsEstimated))
{
double delta = ((seekByteOffset - (SInt64)currentEntry->audioDataOffset) - packetAlignedByteOffset) / calculatedBitRate * 8;
OSSpinLockLock(&currentEntry->spinLock);
currentEntry->seekTime -= delta;
OSSpinLockUnlock(&currentEntry->spinLock);
if (!error) {
seekByteOffset = packetAlignedByteOffset + currentEntry->audioDataOffset;
if (!(ioFlags & kAudioFileStreamSeekFlag_OffsetIsEstimated)) {
double delta = ((seekByteOffset - (SInt64)currentEntry->audioDataOffset) - packetAlignedByteOffset) / calculatedBitRate * 8;
setLock(&currentEntry->spinLock);
currentEntry->seekTime -= delta;
lockUnlock(&currentEntry->spinLock);
}
}
}
@@ -1598,7 +1642,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return;
}
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
if (currentlyReadingEntry == nil)
{
@@ -1606,7 +1650,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[dataSourceIn close];
}
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
}
}
@@ -1656,23 +1700,29 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return;
}
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->lastFrameQueued = currentlyReadingEntry->framesQueued;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry.dataSource.delegate = nil;
[currentlyReadingEntry.dataSource unregisterForEvents];
[currentlyReadingEntry.dataSource close];
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
pthread_mutex_unlock(&playerMutex);
[self processRunloop];
}
-(void)dataSource:(STKDataSource *)dataSource didReadStreamMetadata:(NSDictionary *)metadata
{
if([self.delegate respondsToSelector:@selector(audioPlayer:didReadStreamMetadata:)])
[self.delegate audioPlayer:self didReadStreamMetadata:metadata];
}
-(void) pause
{
pthread_mutex_lock(&playerMutex);
@@ -1741,7 +1791,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
-(void) resetPcmBuffers
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
self->pcmBufferFrameStartIndex = 0;
self->pcmBufferUsedFrameCount = 0;
@@ -1750,7 +1800,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self->averagePowerDb[0] = STK_DBMIN;
self->averagePowerDb[1] = STK_DBMIN;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
}
-(void) stop
@@ -1772,26 +1822,26 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[self invokeOnPlaybackThread:^
{
pthread_mutex_lock(&playerMutex);
pthread_mutex_lock(&self->playerMutex);
{
currentlyReadingEntry.dataSource.delegate = nil;
[currentlyReadingEntry.dataSource unregisterForEvents];
[currentlyReadingEntry.dataSource close];
self->currentlyReadingEntry.dataSource.delegate = nil;
[self->currentlyReadingEntry.dataSource unregisterForEvents];
[self->currentlyReadingEntry.dataSource close];
if (currentlyPlayingEntry)
if (self->currentlyPlayingEntry)
{
[self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil];
[self processFinishPlayingIfAnyAndPlayingNext:self->currentlyPlayingEntry withNext:nil];
}
[self clearQueue];
OSSpinLockLock(&currentEntryReferencesLock);
currentlyPlayingEntry = nil;
currentlyReadingEntry = nil;
seekToTimeWasRequested = NO;
OSSpinLockUnlock(&currentEntryReferencesLock);
setLock(&self->currentEntryReferencesLock);
self->currentlyPlayingEntry = nil;
self->currentlyReadingEntry = nil;
self->seekToTimeWasRequested = NO;
lockUnlock(&self->currentEntryReferencesLock);
}
pthread_mutex_unlock(&playerMutex);
pthread_mutex_unlock(&self->playerMutex);
}];
[self wakeupPlaybackThread];
@@ -1811,7 +1861,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[self invokeOnPlaybackThread:^
{
disposeWasRequested = YES;
self->disposeWasRequested = YES;
CFRunLoopStop(CFRunLoopGetCurrent());
}];
@@ -1897,20 +1947,20 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
-(NSObject*) currentlyPlayingQueueItemId
{
OSSpinLockLock(&currentEntryReferencesLock);
setLock(&currentEntryReferencesLock);
STKQueueEntry* entry = currentlyPlayingEntry;
if (entry == nil)
{
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
return nil;
}
NSObject* retval = entry.queueItemId;
OSSpinLockUnlock(&currentEntryReferencesLock);
lockUnlock(&currentEntryReferencesLock);
return retval;
}
@@ -1977,9 +2027,8 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
[self destroyAudioConverter];
canonicalAudioStreamBasicDescription.mChannelsPerFrame = asbd->mChannelsPerFrame;
BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil;
if (isRecording)
{
recordAudioStreamBasicDescription = (AudioStreamBasicDescription)
@@ -2047,7 +2096,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
return;
}
status = AudioConverterSetProperty(audioConverterRef, kAudioConverterDecompressionMagicCookie, cookieSize, &cookieData);
status = AudioConverterSetProperty(audioConverterRef, kAudioConverterDecompressionMagicCookie, cookieSize, cookieData);
if (status)
{
@@ -2134,6 +2183,22 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
}
}
-(void) createPlaybackRateUnit
{
OSStatus status;
CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &playbackRateUnitDescription, &playbackRateNode));
CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, playbackRateNode, &playbackRateUnitDescription, &playbackRateUnit));
//maxframes changed here
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(playbackRateUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice)));
#if TARGET_OS_IPHONE
CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(playbackRateUnit, kNewTimePitchParam_Rate, kAudioUnitScope_Global, 0, 1, 0));
#endif
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(playbackRateUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)));
}
-(void) createOutputUnit
{
OSStatus status;
@@ -2312,6 +2377,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
[self createEqUnit];
[self createMixerUnit];
[self createPlaybackRateUnit];
[self createOutputUnit];
[self connectGraph];
@@ -2360,7 +2426,13 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
[nodes addObject:@(mixerNode)];
[units addObject:[NSValue valueWithPointer:mixerUnit]];
}
if (playbackRateNode)
{
[nodes addObject:@(playbackRateNode)];
[units addObject:[NSValue valueWithPointer:playbackRateUnit]];
}
if (outputNode)
{
[nodes addObject:@(outputNode)];
@@ -2442,6 +2514,9 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
}
else if (!isRunning)
{
stopReason = stopReasonIn;
self.internalState = STKAudioPlayerInternalStateStopped;
return;
}
@@ -2551,12 +2626,12 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
while (true)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
UInt32 used = pcmBufferUsedFrameCount;
UInt32 start = pcmBufferFrameStartIndex;
UInt32 end = (pcmBufferFrameStartIndex + pcmBufferUsedFrameCount) % pcmBufferTotalFrameCount;
UInt32 framesLeftInsideBuffer = pcmBufferTotalFrameCount - used;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
if (framesLeftInsideBuffer == 0)
{
@@ -2564,12 +2639,12 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
while (true)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
used = pcmBufferUsedFrameCount;
start = pcmBufferFrameStartIndex;
end = (pcmBufferFrameStartIndex + pcmBufferUsedFrameCount) % pcmBufferTotalFrameCount;
framesLeftInsideBuffer = pcmBufferTotalFrameCount - used;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
if (framesLeftInsideBuffer > 0)
{
@@ -2631,13 +2706,13 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
pcmBufferUsedFrameCount += framesAdded;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->framesQueued += framesAdded;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
return;
}
@@ -2652,13 +2727,13 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
if (framesToDecode == 0)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
pcmBufferUsedFrameCount += framesAdded;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->framesQueued += framesAdded;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
continue;
}
@@ -2678,25 +2753,25 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
pcmBufferUsedFrameCount += framesAdded;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->framesQueued += framesAdded;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
return;
}
else if (status == 0)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
pcmBufferUsedFrameCount += framesAdded;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->framesQueued += framesAdded;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
continue;
}
@@ -2727,25 +2802,25 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
pcmBufferUsedFrameCount += framesAdded;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->framesQueued += framesAdded;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
return;
}
else if (status == 0)
{
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
pcmBufferUsedFrameCount += framesAdded;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
OSSpinLockLock(&currentlyReadingEntry->spinLock);
setLock(&currentlyReadingEntry->spinLock);
currentlyReadingEntry->framesQueued += framesAdded;
OSSpinLockUnlock(&currentlyReadingEntry->spinLock);
lockUnlock(&currentlyReadingEntry->spinLock);
continue;
}
@@ -2823,12 +2898,12 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
{
STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon;
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
setLock(&audioPlayer->currentEntryReferencesLock);
STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry;
STKQueueEntry* currentlyReadingEntry = audioPlayer->currentlyReadingEntry;
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
lockUnlock(&audioPlayer->currentEntryReferencesLock);
OSSpinLockLock(&audioPlayer->pcmBufferSpinLock);
setLock(&audioPlayer->pcmBufferSpinLock);
BOOL waitForBuffer = NO;
BOOL muted = audioPlayer->muted;
@@ -2888,7 +2963,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
}
}
OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock);
lockUnlock(&audioPlayer->pcmBufferSpinLock);
UInt32 totalFramesCopied = 0;
@@ -2921,10 +2996,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
totalFramesCopied = framesToCopy;
OSSpinLockLock(&audioPlayer->pcmBufferSpinLock);
setLock(&audioPlayer->pcmBufferSpinLock);
audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount;
audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied;
OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock);
lockUnlock(&audioPlayer->pcmBufferSpinLock);
}
else
{
@@ -2964,10 +3039,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
totalFramesCopied = framesToCopy + moreFramesToCopy;
OSSpinLockLock(&audioPlayer->pcmBufferSpinLock);
setLock(&audioPlayer->pcmBufferSpinLock);
audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount;
audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied;
OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock);
lockUnlock(&audioPlayer->pcmBufferSpinLock);
}
[audioPlayer setInternalState:STKAudioPlayerInternalStatePlaying ifInState:^BOOL(STKAudioPlayerInternalState state)
@@ -3041,7 +3116,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return 0;
}
OSSpinLockLock(&entry->spinLock);
setLock(&entry->spinLock);
SInt64 extraFramesPlayedNotAssigned = 0;
SInt64 framesPlayedForCurrent = totalFramesCopied;
@@ -3056,15 +3131,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
BOOL lastFramePlayed = entry->framesPlayed == entry->lastFrameQueued;
OSSpinLockUnlock(&entry->spinLock);
lockUnlock(&entry->spinLock);
if (signal || lastFramePlayed)
{
pthread_mutex_lock(&audioPlayer->playerMutex);
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
setLock(&audioPlayer->currentEntryReferencesLock);
STKQueueEntry* currentlyPlayingEntry = audioPlayer->currentlyPlayingEntry;
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
lockUnlock(&audioPlayer->currentEntryReferencesLock);
if (lastFramePlayed && entry == currentlyPlayingEntry)
{
@@ -3072,15 +3147,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
while (extraFramesPlayedNotAssigned > 0)
{
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
setLock(&audioPlayer->currentEntryReferencesLock);
STKQueueEntry* newEntry = audioPlayer->currentlyPlayingEntry;
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
lockUnlock(&audioPlayer->currentEntryReferencesLock);
if (newEntry != nil)
{
SInt64 framesPlayedForCurrent = extraFramesPlayedNotAssigned;
OSSpinLockLock(&newEntry->spinLock);
setLock(&newEntry->spinLock);
if (newEntry->lastFrameQueued > 0)
{
@@ -3091,13 +3166,13 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
if (newEntry->framesPlayed == newEntry->lastFrameQueued)
{
OSSpinLockUnlock(&newEntry->spinLock);
lockUnlock(&newEntry->spinLock);
[audioPlayer audioQueueFinishedPlaying:newEntry];
}
else
{
OSSpinLockUnlock(&newEntry->spinLock);
lockUnlock(&newEntry->spinLock);
}
extraFramesPlayedNotAssigned -= framesPlayedForCurrent;
@@ -3188,6 +3263,16 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return peakPowerDb[channelNumber];
}
-(void) setPeakPowerInDecibelsForChannel:(NSUInteger)channelNumber andPower: (Float32)power
{
if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame)
{
return;
}
peakPowerDb[channelNumber] = power;
}
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber
{
if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame)
@@ -3198,6 +3283,16 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return averagePowerDb[channelNumber];
}
-(void) setAveragePowerInDecibelsForChannel:(NSUInteger)channelNumber andPower: (Float32)power
{
if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame)
{
return;
}
averagePowerDb[channelNumber] = power;
}
-(BOOL) meteringEnabled
{
return self->meteringEnabled;
@@ -3235,6 +3330,8 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
}
else
{
__weak STKAudioPlayer* weakSelf = self;
[self appendFrameFilterWithName:@"STKMeteringFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames)
{
SInt16* samples16 = (SInt16*)frames;
@@ -3277,17 +3374,17 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return;
}
peakPowerDb[0] = MIN(MAX(decibelsLeft, -60), 0);
peakPowerDb[1] = MIN(MAX(decibelsRight, -60), 0);
[weakSelf setPeakPowerInDecibelsForChannel:0 andPower:MIN(MAX(decibelsLeft, -60), 0)];
[weakSelf setPeakPowerInDecibelsForChannel:1 andPower:MIN(MAX(decibelsRight, -60), 0)];
if (countLeft > 0)
{
averagePowerDb[0] = MIN(MAX(totalValueLeft / frameCount, -60), 0);
[weakSelf setAveragePowerInDecibelsForChannel:0 andPower:MIN(MAX(totalValueLeft / frameCount, -60), 0)];
}
if (countRight != 0)
{
averagePowerDb[1] = MIN(MAX(totalValueRight / frameCount, -60), 0);
[weakSelf setAveragePowerInDecibelsForChannel:1 andPower:MIN(MAX(totalValueRight / frameCount, -60), 0)];
}
}];
}
@@ -3321,7 +3418,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
NSArray* replacement = [NSArray arrayWithArray:newFrameFilters];
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
if (newFrameFilters.count > 0)
{
frameFilters = replacement;
@@ -3330,7 +3427,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
{
frameFilters = nil;
}
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
pthread_mutex_unlock(&self->playerMutex);
}
@@ -3361,9 +3458,9 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
NSArray* replacement = [NSArray arrayWithArray:newFrameFilters];
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
frameFilters = replacement;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
pthread_mutex_unlock(&self->playerMutex);
}
@@ -3394,9 +3491,9 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
NSArray* replacement = [NSArray arrayWithArray:newFrameFilters];
OSSpinLockLock(&pcmBufferSpinLock);
setLock(&pcmBufferSpinLock);
frameFilters = replacement;
OSSpinLockUnlock(&pcmBufferSpinLock);
lockUnlock(&pcmBufferSpinLock);
pthread_mutex_unlock(&self->playerMutex);
}
@@ -36,6 +36,8 @@
#import "STKHTTPDataSource.h"
#import "STKDataSourceWrapper.h"
NS_ASSUME_NONNULL_BEGIN
typedef struct
{
int watchdogPeriodSeconds;
@@ -45,8 +47,10 @@ STKAutoRecoveringHTTPDataSourceOptions;
@interface STKAutoRecoveringHTTPDataSource : STKDataSourceWrapper
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource;
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource;
@property (readonly) STKHTTPDataSource* innerDataSource;
@end
NS_ASSUME_NONNULL_END
@@ -108,33 +108,26 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
return (STKHTTPDataSource*)self.innerDataSource;
}
-(id) initWithDataSource:(STKDataSource *)innerDataSource
-(instancetype) initWithDataSource:(STKDataSource *)innerDataSource
{
return [self initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSource];
}
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn
{
return [self initWithHTTPDataSource:innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions){}];
}
-(id) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions)optionsIn
-(instancetype) initWithHTTPDataSource:(STKHTTPDataSource*)innerDataSourceIn andOptions:(STKAutoRecoveringHTTPDataSourceOptions)optionsIn
{
if (self = [super initWithDataSource: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;
PopulateOptionsWithDefault(&optionsIn);
self->options = optionsIn;
reachabilityRef = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)&zeroAddress);
NSString* hostname = innerDataSourceIn.url.host;
if (hostname.length) {
reachabilityRef = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
}
}
return self;
@@ -142,18 +135,16 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
-(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;
if (reachabilityRef) {
SCNetworkReachabilityContext context = { 0, (__bridge void*)self, NULL, NULL, NULL };
if (SCNetworkReachabilitySetCallback(reachabilityRef, ReachabilityCallback, &context)) {
if(SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, runLoop.getCFRunLoop, kCFRunLoopDefaultMode))
{
return YES;
}
}
}
return retVal;
return NO;
}
-(BOOL) registerForEvents:(NSRunLoop*)runLoop
@@ -240,6 +231,8 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
{
SCNetworkReachabilityFlags flags;
if (! reachabilityRef) return YES; // Assume reachability, if unknown
if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags))
{
return ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
@@ -34,6 +34,8 @@
#import "STKDataSource.h"
NS_ASSUME_NONNULL_BEGIN
@class STKCoreFoundationDataSource;
@interface CoreFoundationDataSourceClientInfo : NSObject
@@ -62,3 +64,5 @@
-(CFStreamStatus) status;
@end
NS_ASSUME_NONNULL_END
+7 -2
View File
@@ -35,12 +35,15 @@
#import <Foundation/Foundation.h>
#include <AudioToolbox/AudioToolbox.h>
NS_ASSUME_NONNULL_BEGIN
@class STKDataSource;
@protocol STKDataSourceDelegate<NSObject>
-(void) dataSourceDataAvailable:(STKDataSource*)dataSource;
-(void) dataSourceErrorOccured:(STKDataSource*)dataSource;
-(void) dataSourceEof:(STKDataSource*)dataSource;
-(void) dataSource:(STKDataSource*)dataSource didReadStreamMetadata:(NSDictionary*)metadata;
@end
@interface STKDataSource : NSObject
@@ -50,8 +53,8 @@
@property (readonly) SInt64 length;
@property (readonly) BOOL hasBytesAvailable;
@property (nonatomic, readwrite, assign) double durationHint;
@property (readwrite, unsafe_unretained) id<STKDataSourceDelegate> delegate;
@property (nonatomic, strong) NSURL *recordToFileUrl;
@property (readwrite, unsafe_unretained, nullable) id<STKDataSourceDelegate> delegate;
@property (nonatomic, strong, nullable) NSURL *recordToFileUrl;
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
-(void) unregisterForEvents;
@@ -62,3 +65,5 @@
-(AudioFileTypeID) audioFileTypeHint;
@end
NS_ASSUME_NONNULL_END
@@ -34,10 +34,14 @@
#import "STKDataSource.h"
NS_ASSUME_NONNULL_BEGIN
@interface STKDataSourceWrapper : STKDataSource<STKDataSourceDelegate>
-(id) initWithDataSource:(STKDataSource*)innerDataSource;
-(instancetype) initWithDataSource:(STKDataSource*)innerDataSource;
@property (readonly) STKDataSource* innerDataSource;
@end
NS_ASSUME_NONNULL_END
@@ -40,7 +40,7 @@
@implementation STKDataSourceWrapper
-(id) initWithDataSource:(STKDataSource*)innerDataSourceIn
-(instancetype) initWithDataSource:(STKDataSource*)innerDataSourceIn
{
if (self = [super init])
{
@@ -117,4 +117,9 @@
[self.delegate dataSourceEof:self];
}
- (void)dataSource:(STKDataSource *)dataSource didReadStreamMetadata:(NSDictionary *)metadata
{
[self.delegate dataSource:self didReadStreamMetadata:metadata];
}
@end
+10 -6
View File
@@ -34,10 +34,12 @@
#import "STKCoreFoundationDataSource.h"
NS_ASSUME_NONNULL_BEGIN
@class STKHTTPDataSource;
typedef void(^STKURLBlock)(NSURL* url);
typedef NSURL*(^STKURLProvider)();
typedef NSURL* _Nonnull (^STKURLProvider)(void);
typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek, STKURLBlock callback);
@interface STKHTTPDataSource : STKCoreFoundationDataSource
@@ -46,11 +48,13 @@ typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek,
@property (readonly) UInt32 httpStatusCode;
+(AudioFileTypeID) audioFileTypeHintFromMimeType:(NSString*)fileExtension;
-(id) initWithURL:(NSURL*)url;
-(id) initWithURL:(NSURL *)url httpRequestHeaders:(NSDictionary *)httpRequestHeaders;
-(id) initWithURLProvider:(STKURLProvider)urlProvider;
-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProvider;
-(NSRunLoop*) eventsRunLoop;
-(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
+144 -28
View File
@@ -52,6 +52,12 @@
BOOL iceHeaderAvailable;
BOOL httpHeaderNotAvailable;
NSMutableData *_metadataData;
int _metadataOffset;
int _metadataBytesRead;
int _metadataStep;
int _metadataLength;
NSURL* currentUrl;
STKAsyncURLProvider asyncUrlProvider;
NSDictionary* httpHeaders;
@@ -64,19 +70,20 @@
@implementation STKHTTPDataSource
-(id) initWithURL:(NSURL*)urlIn
-(instancetype) initWithURL:(NSURL*)urlIn
{
currentUrl = urlIn;
return [self initWithURLProvider:^NSURL* { return urlIn; }];
}
-(id) initWithURL:(NSURL *)urlIn httpRequestHeaders:(NSDictionary *)httpRequestHeaders
-(instancetype) initWithURL:(NSURL *)urlIn httpRequestHeaders:(NSDictionary *)httpRequestHeaders
{
self = [self initWithURLProvider:^NSURL* { return urlIn; }];
self->requestHeaders = httpRequestHeaders;
return self;
}
-(id) initWithURLProvider:(STKURLProvider)urlProviderIn
-(instancetype) initWithURLProvider:(STKURLProvider)urlProviderIn
{
urlProviderIn = [urlProviderIn copy];
@@ -86,7 +93,7 @@
}];
}
-(id) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProviderIn
-(instancetype) initWithAsyncURLProvider:(STKAsyncURLProvider)asyncUrlProviderIn
{
if (self = [super init])
{
@@ -311,9 +318,18 @@
self->iceHeaderData = nil;
}
// check ICY headers
NSString* icyHeaders = [httpHeaders objectForKey:@"Icy-Metaint"] ?: [httpHeaders objectForKey:@"icy-metaint"];
if (icyHeaders != nil)
{
_metadataBytesRead = 0;
_metadataStep = [icyHeaders intValue];
_metadataOffset = _metadataStep;
}
if (([httpHeaders objectForKey:@"Accept-Ranges"] ?: [httpHeaders objectForKey:@"accept-ranges"]) != nil)
{
self->supportsSeek = YES;
self->supportsSeek = ![[httpHeaders objectForKey:@"Accept-Ranges"] isEqualToString:@"none"];
}
if (self.httpStatusCode == 200)
@@ -411,7 +427,7 @@
eventsRunLoop = savedEventsRunLoop;
[self seekToOffset:self.position];
[self seekToOffset:self->supportsSeek ? self.position : 0];
}
-(void) seekToOffset:(SInt64)offset
@@ -443,6 +459,7 @@
return [self privateReadIntoBuffer:buffer withSize:size];
}
#pragma mark - Custom buffer reading
-(int) privateReadIntoBuffer:(UInt8*)buffer withSize:(int)size
{
if (size == 0)
@@ -466,12 +483,90 @@
return count;
}
int read = [super readIntoBuffer:buffer withSize:size];
int read;
// read ICY stream metadata
// http://www.smackfu.com/stuff/programming/shoutcast.html
//
if(_metadataStep > 0)
{
// read audio stream before next metadata chunk
if(_metadataOffset > 0)
{
read = [super readIntoBuffer:buffer withSize:MIN(_metadataOffset, size)];
if(read > 0)
_metadataOffset -= read;
}
// read metadata
else
{
// first we need to read one byte with length
if(_metadataLength == 0)
{
// read only 1 byte
UInt8 metadataLengthByte;
read = [super readIntoBuffer:&metadataLengthByte withSize:1];
if(read > 0)
{
_metadataLength = metadataLengthByte * 16;
// prepare
if(_metadataLength > 0)
{
_metadataData = [NSMutableData dataWithLength:_metadataLength];
_metadataBytesRead = 0;
}
// reset
else
{
_metadataOffset = _metadataStep;
_metadataData = nil;
_metadataLength = 0;
}
// return 0, because no audio bytes read
relativePosition += read;
read = 0;
}
}
// read metadata bytes
else
{
read = [super readIntoBuffer:(_metadataData.mutableBytes + _metadataBytesRead)
withSize:_metadataLength - _metadataBytesRead];
if(read > 0)
{
_metadataBytesRead += read;
// done reading, so process it
if(_metadataBytesRead == _metadataLength)
{
if([self.delegate respondsToSelector:@selector(dataSource:didReadStreamMetadata:)])
[self.delegate dataSource:self didReadStreamMetadata:[self _processIcyMetadata:_metadataData]];
// reset
_metadataData = nil;
_metadataOffset = _metadataStep;
_metadataLength = 0;
_metadataBytesRead = 0;
}
// return 0, because no audio bytes read
relativePosition += read;
read = 0;
}
}
}
}
else
{
read = [super readIntoBuffer:buffer withSize:size];
}
if (read < 0)
{
return read;
}
relativePosition += read;
@@ -506,11 +601,11 @@
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1);
if (seekStart > 0 && supportsSeek)
if (self->seekStart > 0 && self->supportsSeek)
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%lld-", seekStart]);
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%lld-", self->seekStart]);
discontinuous = YES;
self->discontinuous = YES;
}
for (NSString* key in self->requestHeaders)
@@ -521,11 +616,11 @@
}
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Ice-MetaData"), CFSTR("0"));
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Icy-MetaData"), CFSTR("1"));
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
if (stream == nil)
self->stream = CFReadStreamCreateForHTTPRequest(NULL, message);
if (self->stream == nil)
{
CFRelease(message);
@@ -534,9 +629,9 @@
return;
}
CFReadStreamSetProperty(stream, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground);
CFReadStreamSetProperty(self->stream, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground);
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
if (!CFReadStreamSetProperty(self->stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
{
CFRelease(message);
@@ -547,19 +642,17 @@
// Proxy support
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
CFReadStreamSetProperty(self->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);
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
nil];
CFReadStreamSetProperty(self->stream, kCFStreamPropertySSLSettings, (__bridge CFTypeRef)sslSettings);
}
[self reregisterForEvents];
@@ -567,12 +660,12 @@
self->httpStatusCode = 0;
// Open
if (!CFReadStreamOpen(stream))
if (!CFReadStreamOpen(self->stream))
{
CFRelease(stream);
CFRelease(self->stream);
CFRelease(message);
stream = 0;
self->stream = NULL;
[self errorOccured];
@@ -605,4 +698,27 @@
return self->supportsSeek;
}
#pragma mark - Private
- (NSDictionary*)_processIcyMetadata:(NSData*)data
{
NSMutableDictionary *metadata = [NSMutableDictionary new];
NSString *metadataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *pairs = [metadataString componentsSeparatedByString:@";"];
for(NSString *pair in pairs)
{
NSArray *components = [pair componentsSeparatedByString:@"="];
if(components.count < 2)
continue;
NSString *key = components[0];
NSString *value = [pair substringWithRange:NSMakeRange(key.length + 2, pair.length - (key.length + 2) - 1)];
[metadata setValue:value forKey:key];
}
return metadata;
}
@end
@@ -34,10 +34,14 @@
#import "STKCoreFoundationDataSource.h"
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
@@ -47,7 +47,7 @@
@implementation STKLocalFileDataSource
@synthesize filePath;
-(id) initWithFilePath:(NSString*)filePathIn
-(instancetype) initWithFilePath:(NSString*)filePathIn
{
if (self = [super init])
{
+29
View File
@@ -0,0 +1,29 @@
//
// STKMacro.h
// StreamingKit
//
// Created by Diego Stamigni on 20/03/2019.
// Copyright © 2019 Thong Nguyen. All rights reserved.
//
#pragma once
#import <objc/runtime.h>
#if TARGET_OS_IPHONE
#import <UIKit/UIKit.h>
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) \
([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#endif
#define DEPLOYMENT_TARGET_HIGHER_THAN_10 TARGET_OS_WATCH || \
TARGET_OS_TV || \
(TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0) || \
(!TARGET_OS_IOS && __MAC_OS_X_VERSION_MIN_ALLOWED >= __MAC_10_12)
#define BASE_SDK_HIGHER_THAN_10 (TARGET_OS_WATCH || \
TARGET_OS_TV || \
(TARGET_OS_IOS && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0) || \
(!TARGET_OS_IOS && __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12))
#define DEVICE_HIGHER_THAN_10 objc_getClass("NSDimension")
+8 -4
View File
@@ -7,13 +7,15 @@
//
#import "STKDataSource.h"
#import "libkern/OSAtomic.h"
#import "STKSpinLock.h"
#import "AudioToolbox/AudioToolbox.h"
NS_ASSUME_NONNULL_BEGIN
@interface STKQueueEntry : NSObject
{
@public
OSSpinLock spinLock;
os_unfair_lock spinLock;
BOOL parsedHeader;
Float64 sampleRate;
@@ -35,7 +37,7 @@
@property (readwrite, retain) NSObject* queueItemId;
@property (readwrite, retain) STKDataSource* dataSource;
-(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
-(instancetype) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId;
-(void) reset;
-(double) duration;
@@ -43,4 +45,6 @@
-(double) calculatedBitRate;
-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription;
@end
@end
NS_ASSUME_NONNULL_END
+8 -8
View File
@@ -14,11 +14,11 @@
@implementation STKQueueEntry
-(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
-(instancetype) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn
{
if (self = [super init])
{
self->spinLock = OS_SPINLOCK_INIT;
self->spinLock = OS_UNFAIR_LOCK_INIT;
self.dataSource = dataSourceIn;
self.queueItemId = queueItemIdIn;
@@ -31,11 +31,11 @@
-(void) reset
{
OSSpinLockLock(&self->spinLock);
setLock(&self->spinLock);
self->framesQueued = 0;
self->framesPlayed = 0;
self->lastFrameQueued = -1;
OSSpinLockUnlock(&self->spinLock);
lockUnlock(&self->spinLock);
}
-(double) calculatedBitRate
@@ -109,9 +109,9 @@
-(Float64) progressInFrames
{
OSSpinLockLock(&self->spinLock);
Float64 retval = self->seekTime + self->framesPlayed;
OSSpinLockUnlock(&self->spinLock);
setLock(&self->spinLock);
Float64 retval = (self->seekTime + self->audioStreamBasicDescription.mSampleRate) + self->framesPlayed;
lockUnlock(&self->spinLock);
return retval;
}
@@ -121,4 +121,4 @@
return [[self queueItemId] description];
}
@end
@end
+77
View File
@@ -0,0 +1,77 @@
//
// STKSpinLock.h
// StreamingKit
//
// Created by Diego Stamigni on 20/03/2019.
// Copyright © 2019 Thong Nguyen. All rights reserved.
//
#pragma once
#import "STKMacro.h"
#include <dlfcn.h>
#if BASE_SDK_HIGHER_THAN_10
#import <os/lock.h>
#else
#define OS_UNFAIR_LOCK_INIT ((os_unfair_lock){0})
typedef struct _os_unfair_lock_s {
uint32_t _os_unfair_lock_opaque;
} os_unfair_lock, *os_unfair_lock_t;
#endif
#if !DEPLOYMENT_TARGET_HIGHER_THAN_10
#import <libkern/OSAtomic.h>
static void (*os_unfair_lock_lock_ptr)(void *lock) = NULL;
static void (*os_unfair_lock_unlock_ptr)(void *lock) = NULL;
#endif
static void setLock(os_unfair_lock *lock)
{
#if DEPLOYMENT_TARGET_HIGHER_THAN_10
os_unfair_lock_lock(lock);
#else
if (DEVICE_HIGHER_THAN_10)
{
if (os_unfair_lock_lock_ptr == NULL)
{
os_unfair_lock_lock_ptr = dlsym(dlopen(NULL, RTLD_NOW | RTLD_GLOBAL), "os_unfair_lock_lock");
}
if (os_unfair_lock_lock_ptr != NULL)
{
os_unfair_lock_lock_ptr(lock);
return;
}
}
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
OSSpinLockLock((void *)lock);
#endif
}
static void lockUnlock(os_unfair_lock *lock)
{
#if DEPLOYMENT_TARGET_HIGHER_THAN_10
os_unfair_lock_unlock(lock);
#else
if (DEVICE_HIGHER_THAN_10)
{
if (os_unfair_lock_unlock_ptr == NULL)
{
os_unfair_lock_unlock_ptr = dlsym(dlopen(NULL, RTLD_NOW | RTLD_GLOBAL), "os_unfair_lock_unlock");
}
if (os_unfair_lock_unlock_ptr != NULL)
{
os_unfair_lock_unlock_ptr(lock);
return;
}
}
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
OSSpinLockUnlock((void *)lock);
#endif
}