Compare commits

..

2 Commits

Author SHA1 Message Date
Thong Nguyen e805e4740c More Spectrum Analyzer work 2014-04-24 18:22:40 +01:00
Thong Nguyen 484285df84 Started work on Spectrum Analyzer 2014-04-23 18:03:23 +01:00
33 changed files with 304 additions and 816 deletions
@@ -27,6 +27,7 @@
A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */; };
A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F5E491189EB3F20070B03F /* AVFoundation.framework */; };
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; };
A1F3410A1908185900CA7755 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F341091908185900CA7755 /* Accelerate.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -65,6 +66,7 @@
A142571C18907861005F0129 /* airplane.aac */ = {isa = PBXFileReference; lastKnownFileType = file; path = airplane.aac; sourceTree = "<group>"; };
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
A1F341091908185900CA7755 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
A1F5E491189EB3F20070B03F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */
@@ -74,6 +76,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A1F3410A1908185900CA7755 /* Accelerate.framework in Frameworks */,
A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */,
A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */,
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */,
@@ -119,6 +122,7 @@
A1115933188D686000641365 /* Frameworks */ = {
isa = PBXGroup;
children = (
A1F341091908185900CA7755 /* Accelerate.framework */,
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */,
A1F5E491189EB3F20070B03F /* AVFoundation.framework */,
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */,
@@ -230,7 +234,7 @@
A1115929188D686000641365 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0710;
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = "Thong Nguyen";
TargetAttributes = {
A111594B188D686000641365 = {
@@ -346,7 +350,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -423,7 +426,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LLVM_LTO = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -440,7 +442,6 @@
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
LLVM_LTO = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -462,7 +463,6 @@
"$(inherited)",
);
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
@@ -481,7 +481,6 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleAppTests/ExampleAppTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
+5 -15
View File
@@ -32,14 +32,13 @@
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(bufferLength), &bufferLength);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.rootViewController = [[UIViewController alloc] init];
self.window.backgroundColor = [UIColor whiteColor];
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
audioPlayer.meteringEnabled = YES;
audioPlayer.volume = 1;
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
audioPlayerView.delegate = self;
@@ -47,9 +46,9 @@
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];
[self.window addSubview:audioPlayerView];
[self.window makeKeyAndVisible];
[self.window.rootViewController.view addSubview:audioPlayerView];
return YES;
}
@@ -61,22 +60,13 @@
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
-(void) audioPlayerViewPlayFromIcecastSelected:(AudioPlayerView *)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"http://shoutmedia.abc.net.au:10326"];
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
[audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]];
}
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView
{
NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"];
@@ -99,7 +89,7 @@
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView
{
NSURL* url = [NSURL URLWithString:@"http://www.abstractpath.com/files/audiosamples/perfectly.wav"];
NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/perfectly.wav"];
STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url];
-2
View File
@@ -39,7 +39,6 @@
@protocol AudioPlayerViewDelegate<NSObject>
-(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewPlayFromIcecastSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView;
-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView;
@@ -58,7 +57,6 @@
UIButton* playButton;
UIButton* stopButton;
UIButton* playFromHTTPButton;
UIButton* playFromIcecastButton;
UIButton* queueShortFileButton;
UIButton* queuePcmWaveFileFromHTTPButton;
UIButton* playFromLocalFileButton;
+41 -35
View File
@@ -40,6 +40,9 @@
///
@interface AudioPlayerView()
{
NSMutableArray* meters;
}
-(void) setupTimer;
-(void) updateControls;
@end
@@ -55,50 +58,47 @@
{
self.audioPlayer = audioPlayerIn;
self.audioPlayer.spectrumAnalyzerEnabled = YES;
CGSize size = CGSizeMake(220, 50);
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
playFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10, size.width, size.height);
[playFromHTTPButton addTarget:self action:@selector(playFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromHTTPButton setTitle:@"Play from HTTP" forState:UIControlStateNormal];
playFromIcecastButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playFromIcecastButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 35, size.width, size.height);
[playFromIcecastButton addTarget:self action:@selector(playFromIcecasButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromIcecastButton setTitle:@"Play from Icecast" forState:UIControlStateNormal];
playFromLocalFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playFromLocalFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 70, size.width, size.height);
playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 50, size.width, size.height);
[playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal];
queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
queueShortFileButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 105, size.width, size.height);
queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 100, size.width, size.height);
[queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal];
queuePcmWaveFileFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((frame.size.width - size.width) / 2, frame.size.height * 0.10 + 140, size.width, size.height);
queuePcmWaveFileFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 150, size.width, size.height);
[queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside];
[queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal];
size = CGSizeMake(90, 40);
playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
playButton.frame = CGRectMake(30, 400, size.width, size.height);
playButton.frame = CGRectMake(30, 380, size.width, size.height);
[playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside];
stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
stopButton.frame = CGRectMake((frame.size.width - size.width) - 30, 400, size.width, size.height);
stopButton.frame = CGRectMake((320 - size.width) - 30, 380, size.width, size.height);
[stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[stopButton setTitle:@"Stop" forState:UIControlStateNormal];
muteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
muteButton.frame = CGRectMake((frame.size.width - size.width) - 30, 430, size.width, size.height);
muteButton.frame = CGRectMake((320 - size.width) - 30, 410, size.width, size.height);
[muteButton addTarget:self action:@selector(muteButtonPressed) forControlEvents:UIControlEventTouchUpInside];
[muteButton setTitle:@"Mute" forState:UIControlStateNormal];
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, queuePcmWaveFileFromHTTPButton.frame.origin.y + queuePcmWaveFileFromHTTPButton.frame.size.height + 20, 20)];
slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)];
slider.continuous = YES;
[slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged];
@@ -106,27 +106,40 @@
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(30, frame.size.height * 0.15 + 180, size.width, size.height)];
enableEqSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(frame.size.width - size.width - 30, frame.size.height * 0.15 + 180, size.width, size.height)];
enableEqSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(320 - size.width - 30, frame.size.height * 0.15 + 180, size.width, size.height)];
enableEqSwitch.on = audioPlayer.equalizerEnabled;
[enableEqSwitch addTarget:self action:@selector(onEnableEqSwitch) forControlEvents:UIControlEventAllTouchEvents];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 40, frame.size.width, 25)];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 10, frame.size.width, 25)];
label.textAlignment = NSTextAlignmentCenter;
statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 50, frame.size.width, 50)];
statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 8, frame.size.width, 50)];
statusLabel.textAlignment = NSTextAlignmentCenter;
meters = [[NSMutableArray alloc] init];
meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)];
meter.backgroundColor = [UIColor greenColor];
for (int i = 0; i < 256; i++)
{
UIView* freqMeter = [[UIView alloc] initWithFrame:CGRectMake(i, self.bounds.size.height, 1, 0)];
freqMeter.backgroundColor = [UIColor blueColor];
[self addSubview:freqMeter];
[meters addObject:freqMeter];
[self sendSubviewToBack:freqMeter];
}
[self addSubview:slider];
[self addSubview:playButton];
[self addSubview:playFromHTTPButton];
[self addSubview:playFromIcecastButton];
[self addSubview:playFromLocalFileButton];
[self addSubview:queueShortFileButton];
[self addSubview:queuePcmWaveFileFromHTTPButton];
@@ -180,17 +193,6 @@
return;
}
if (audioPlayer.currentlyPlayingQueueItemId == nil)
{
slider.value = 0;
slider.minimumValue = 0;
slider.maximumValue = 0;
label.text = @"";
return;
}
if (audioPlayer.duration != 0)
{
slider.minimumValue = 0;
@@ -205,14 +207,23 @@
slider.minimumValue = 0;
slider.maximumValue = 0;
label.text = [NSString stringWithFormat:@"Live stream %@", [self formatTimeFromSeconds:audioPlayer.progress]];
label.text = @"";
}
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
CGFloat newWidth = 320 * (([audioPlayer averagePowerInDecibelsForChannel:1] + 60) / 60);
CGFloat newWidth = 320 * (([audioPlayer testPowerWithIndex:100] + 96) / 96);
meter.frame = CGRectMake(0, 460, newWidth, 20);
for (int i = 0; i < 256; i++)
{
UIView* freqMeter = [meters objectAtIndex:i];
CGFloat height = 200 * (([audioPlayer testPowerWithIndex:i] + 96) / 96);
freqMeter.frame = CGRectMake(freqMeter.frame.origin.x, self.bounds.size.height - height, 1, height);
}
}
-(void) playFromHTTPButtonTouched
@@ -220,11 +231,6 @@
[self.delegate audioPlayerViewPlayFromHTTPSelected:self];
}
-(void) playFromIcecasButtonTouched
{
[self.delegate audioPlayerViewPlayFromIcecastSelected:self];
}
-(void) playFromLocalFileButtonTouched
{
[self.delegate audioPlayerViewPlayFromLocalFileSelected:self];
+1 -1
View File
@@ -9,7 +9,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
@@ -5,31 +5,16 @@
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
@@ -1,6 +0,0 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
@@ -1,31 +1,5 @@
{
"images" : [
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "736h",
"filename" : "TX6sV.png",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "3x"
},
{
"orientation" : "landscape",
"idiom" : "iphone",
"extent" : "full-screen",
"minimum-system-version" : "8.0",
"subtype" : "736h",
"scale" : "3x"
},
{
"extent" : "full-screen",
"idiom" : "iphone",
"subtype" : "667h",
"filename" : "dBEHd.png",
"minimum-system-version" : "8.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
@@ -34,12 +8,11 @@
"scale" : "2x"
},
{
"extent" : "full-screen",
"orientation" : "portrait",
"idiom" : "iphone",
"subtype" : "retina4",
"filename" : "TX6sV-2.png",
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"orientation" : "portrait",
"scale" : "2x"
},
{
@@ -69,26 +42,6 @@
"extent" : "full-screen",
"minimum-system-version" : "7.0",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"scale" : "1x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"extent" : "full-screen",
"scale" : "2x"
},
{
"orientation" : "portrait",
"idiom" : "iphone",
"filename" : "TX6sV-1.png",
"extent" : "full-screen",
"subtype" : "retina4",
"scale" : "2x"
}
],
"info" : {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
+2 -2
View File
@@ -58,7 +58,7 @@
-(void) playFromHTTP
{
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
}
-(void) tick:(NSTimer*)timer
@@ -72,7 +72,7 @@
CGFloat meterWidth = 0;
if (audioPlayer.currentlyPlayingQueueItemId != nil)
if (audioPlayer.duration != 0)
{
slider.minValue = 0;
slider.maxValue = audioPlayer.duration;
+3 -3
View File
@@ -4,7 +4,7 @@
Inspired by Matt Gallagher's AudioStreamer:
https://github.com/mattgallagher/AudioStreamer
Copyright (c) 2015 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -15,12 +15,12 @@
documentation and/or other materials provided with the distribution.
3. All advertising materials mentioning features or use of this software
must display the following acknowledgement:
This product includes software developed by Thong Nguyen.
This product includes software developed by the <organization>.
4. Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THONG NGUYEN ''AS IS'' AND ANY
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+4 -4
View File
@@ -2,7 +2,7 @@
StreamingKit (formally Audjustable) is an audio playback and streaming library for iOS and Mac OSX. StreamingKit uses CoreAudio to decompress and playback audio (using hardware or software codecs) whilst providing a clean and simple object-oriented API.
The primary motivation of this project was to decouple the input data sources from the actual player logic in order to allow advanced customizable input handling such as HTTP progressive download based streaming, encryption/decryption, auto-recovery, dynamic-buffering. StreamingKit is the only streaming and playback library that supports dead-easy [gapless playback](https://github.com/tumtumtum/StreamingKit/wiki/Gapless-playback) between audio files of differing formats.
The primary motivation of this project was to decouple the input data sources from the actual player logic in order to allow advanced customizable input handling such as HTTP streaming, encryption/decryption, auto-recovery, dynamic-buffering. StreamingKit is the only streaming and playback library that supports dead-easy [gapless playback](https://github.com/tumtumtum/StreamingKit/wiki/Gapless-playback) between audio files of differing formats.
## Main Features
@@ -34,7 +34,7 @@ There are two main classes. The `STKDataSource` class which is the abstract bas
```objective-c
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer play:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
```
### Gapless playback
@@ -42,8 +42,8 @@ STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
```objective-c
STKAudioPlayer* audioPlayer = [[STKAudioPlayer alloc] init];
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/sample.mp3"];
[audioPlayer queue:@"http://www.abstractpath.com/files/audiosamples/airplane.aac"];
[audioPlayer queue:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];
[audioPlayer queue:@"http://fs.bloom.fm/oss/audiosamples/airplane.aac"];
```
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "StreamingKit"
s.version = "0.1.27"
s.version = "0.1.19"
s.summary = "A fast and extensible audio streamer for iOS and OSX with support for gapless playback and custom (non-HTTP) sources."
s.homepage = "https://github.com/tumtumtum/StreamingKit/"
s.license = 'MIT'
@@ -10,29 +10,29 @@
<string>StreamingKit</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
<string>https://github.com/tumtumtum/StreamingKit.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>StreamingKit.xcworkspace</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>3E9414865BAE5433092B9D136FFC1F054EA505C2</key>
<key>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</key>
<string>..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/tumtumtum/StreamingKit.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<integer>110</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>3E9414865BAE5433092B9D136FFC1F054EA505C2</string>
<string>DD310C30-B3D0-4BD7-9565-9F29F09CC4F8</string>
<key>IDESourceControlWCCName</key>
<string>StreamingKit</string>
</dict>
@@ -7,14 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F1188D5E550010896F /* STKAudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F7188D5E550010896F /* STKDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4F9188D5E550010896F /* STKDataSourceWrapper.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4FB188D5E550010896F /* STKHTTPDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD81A1140E4005675A0 /* STKLocalFileDataSource.h in Headers */ = {isa = PBXBuildFile; fileRef = A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */; settings = {ATTRIBUTES = (Public, ); }; };
5B949CD91A1140E4005675A0 /* STKQueueEntry.h in Headers */ = {isa = PBXBuildFile; fileRef = A1BF65D0189A6582004DD08C /* STKQueueEntry.h */; settings = {ATTRIBUTES = (Public, ); }; };
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; };
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; };
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
@@ -45,6 +37,8 @@
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; };
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; };
A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; };
A1F341041908183300CA7755 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F341031908183300CA7755 /* Accelerate.framework */; };
A1F341081908183A00CA7755 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F341071908183A00CA7755 /* Accelerate.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -133,6 +127,8 @@
A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKLocalFileDataSource.h; sourceTree = "<group>"; };
A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKLocalFileDataSource.m; sourceTree = "<group>"; };
A1E7C507188D62D20010896F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
A1F341031908183300CA7755 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
A1F341071908183A00CA7755 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Accelerate.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -140,6 +136,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A1F341081908183A00CA7755 /* Accelerate.framework in Frameworks */,
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -158,6 +155,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
A1F341041908183300CA7755 /* Accelerate.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -256,6 +254,8 @@
A1E7C4CA188D57F50010896F /* Frameworks */ = {
isa = PBXGroup;
children = (
A1F341071908183A00CA7755 /* Accelerate.framework */,
A1F341031908183300CA7755 /* Accelerate.framework */,
A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */,
A1C9767618981BFE0057F881 /* AudioUnit.framework */,
A1E7C507188D62D20010896F /* UIKit.framework */,
@@ -322,21 +322,6 @@
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
5B949CD11A1140CF005675A0 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
5B949CD21A1140E4005675A0 /* STKAudioPlayer.h in Headers */,
5B949CD31A1140E4005675A0 /* STKAutoRecoveringHTTPDataSource.h in Headers */,
5B949CD41A1140E4005675A0 /* STKCoreFoundationDataSource.h in Headers */,
5B949CD51A1140E4005675A0 /* STKDataSource.h in Headers */,
5B949CD61A1140E4005675A0 /* STKDataSourceWrapper.h in Headers */,
5B949CD71A1140E4005675A0 /* STKHTTPDataSource.h in Headers */,
5B949CD81A1140E4005675A0 /* STKLocalFileDataSource.h in Headers */,
5B949CD91A1140E4005675A0 /* STKQueueEntry.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
A1A49967189E744400E2A2E2 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
@@ -389,7 +374,6 @@
A1E7C4C4188D57F50010896F /* Sources */,
A1E7C4C5188D57F50010896F /* Frameworks */,
A1E7C4C6188D57F50010896F /* CopyFiles */,
5B949CD11A1140CF005675A0 /* Headers */,
);
buildRules = (
);
@@ -425,7 +409,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = STK;
LastUpgradeCheck = 0710;
LastUpgradeCheck = 0510;
ORGANIZATIONNAME = "Thong Nguyen";
};
buildConfigurationList = A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */;
@@ -578,7 +562,6 @@
A1A49988189E744500E2A2E2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(DEVELOPER_FRAMEWORKS_DIR)",
@@ -592,7 +575,6 @@
);
MACOSX_DEPLOYMENT_TARGET = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SDKROOT = macosx;
};
name = Debug;
@@ -600,7 +582,6 @@
A1A49989189E744500E2A2E2 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -611,7 +592,6 @@
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
MACOSX_DEPLOYMENT_TARGET = "";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SDKROOT = macosx;
};
name = Release;
@@ -633,7 +613,6 @@
);
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
WRAPPER_EXTENSION = xctest;
@@ -654,7 +633,6 @@
GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch";
INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist";
MACOSX_DEPLOYMENT_TARGET = 10.8;
PRODUCT_BUNDLE_IDENTIFIER = "com.abstractpath.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = macosx;
WRAPPER_EXTENSION = xctest;
@@ -678,7 +656,6 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
@@ -742,7 +719,6 @@
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SKIP_INSTALL = YES;
};
name = Debug;
@@ -759,7 +735,6 @@
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
PUBLIC_HEADERS_FOLDER_PATH = include/StreamingKit;
SKIP_INSTALL = YES;
};
name = Release;
@@ -779,7 +754,6 @@
"$(inherited)",
);
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xctest;
};
@@ -796,7 +770,6 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "StreamingKit/StreamingKit-Prefix.pch";
INFOPLIST_FILE = "StreamingKitTests/StreamingKitTests-Info.plist";
PRODUCT_BUNDLE_IDENTIFIER = "abstractpath.com.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = xctest;
};
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
+14 -14
View File
@@ -44,7 +44,7 @@
#include "UIKit/UIApplication.h"
#endif
typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
typedef enum
{
STKAudioPlayerStateReady,
STKAudioPlayerStateRunning = 1,
@@ -54,9 +54,10 @@ typedef NS_OPTIONS(NSInteger, STKAudioPlayerState)
STKAudioPlayerStateStopped = (1 << 4),
STKAudioPlayerStateError = (1 << 5),
STKAudioPlayerStateDisposed = (1 << 6)
};
}
STKAudioPlayerState;
typedef NS_ENUM(NSInteger, STKAudioPlayerStopReason)
typedef enum
{
STKAudioPlayerStopReasonNone = 0,
STKAudioPlayerStopReasonEof,
@@ -64,9 +65,10 @@ typedef NS_ENUM(NSInteger, STKAudioPlayerStopReason)
STKAudioPlayerStopReasonPendingNext,
STKAudioPlayerStopReasonDisposed,
STKAudioPlayerStopReasonError = 0xffff
};
}
STKAudioPlayerStopReason;
typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode)
typedef enum
{
STKAudioPlayerErrorNone = 0,
STKAudioPlayerErrorDataSource,
@@ -75,13 +77,9 @@ typedef NS_ENUM(NSInteger, STKAudioPlayerErrorCode)
STKAudioPlayerErrorCodecError,
STKAudioPlayerErrorDataNotFound,
STKAudioPlayerErrorOther = 0xffff
};
}
STKAudioPlayerErrorCode;
///
/// Options to initiailise the Audioplayer with.
/// By default if you set buffer size or seconds to 0, the non-zero default will be used
/// If you would like to disable the buffer option completely set to STK_DISABLE_BUFFER
///
typedef struct
{
/// If YES then seeking a track will cause all pending items to be flushed from the queue
@@ -93,7 +91,7 @@ typedef struct
/// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger.
UInt32 readBufferSize;
/// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM)
Float32 bufferSizeInSeconds;
UInt32 bufferSizeInSeconds;
/// Number of seconds of decompressed audio is required before playback first starts for each item (Default is 0.5 seconds. Must be larger than bufferSizeInSeconds)
Float32 secondsRequiredToStartPlaying;
/// Seconds after a seek is performed before data needs to come in (after which the state will change to playing/buffering)
@@ -103,8 +101,6 @@ typedef struct
}
STKAudioPlayerOptions;
#define STK_DISABLE_BUFFER (0xffffffff)
typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames);
@interface STKFrameFilterEntry : NSObject
@@ -150,6 +146,8 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
@property (readwrite) BOOL meteringEnabled;
/// Enables or disables the EQ
@property (readwrite) BOOL equalizerEnabled;
/// Enables or disables the spectrum analyzer (fft)
@property (readwrite) BOOL spectrumAnalyzerEnabled;
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
@property (readonly) NSArray* frameFilters;
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
@@ -267,4 +265,6 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
-(float) testPowerWithIndex:(int)index;
@end
+142 -304
View File
@@ -40,6 +40,7 @@
#import "STKQueueEntry.h"
#import "NSMutableArray+STKAudioPlayer.h"
#import "libkern/OSAtomic.h"
#include <Accelerate/Accelerate.h>
#import <float.h>
#ifndef DBL_MAX
@@ -63,9 +64,6 @@
#define STK_DEFAULT_PACKET_BUFFER_SIZE (2048)
#define STK_DEFAULT_GRACE_PERIOD_AFTER_SEEK_SECONDS (0.5)
#define OSSTATUS_PRINTF_PLACEHOLDER @"%c%c%c%c"
#define OSSTATUS_PRINTF_VALUE(status) (char)(((status) >> 24) & 0xFF), (char)(((status) >> 16) & 0xFF), (char)(((status) >> 8) & 0xFF), (char)((status) & 0xFF)
#define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]];
static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options)
@@ -96,34 +94,6 @@ static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options)
}
}
static void NormalizeDisabledBuffers(STKAudioPlayerOptions* options)
{
if (options->bufferSizeInSeconds == STK_DISABLE_BUFFER)
{
options->bufferSizeInSeconds = 0;
}
if (options->readBufferSize == STK_DISABLE_BUFFER)
{
options->readBufferSize = 0;
}
if (options->secondsRequiredToStartPlaying == STK_DISABLE_BUFFER)
{
options->secondsRequiredToStartPlaying = 0;
}
if (options->secondsRequiredToStartPlayingAfterBufferUnderun == STK_DISABLE_BUFFER)
{
options->secondsRequiredToStartPlayingAfterBufferUnderun = 0;
}
if (options->gracePeriodAfterSeekInSeconds == STK_DISABLE_BUFFER)
{
options->gracePeriodAfterSeekInSeconds = 0;
}
}
#define CHECK_STATUS_AND_REPORT(call) \
if ((status = (call))) \
{ \
@@ -204,7 +174,6 @@ static AudioComponentDescription nbandUnitDescription;
static AudioComponentDescription outputUnitDescription;
static AudioComponentDescription convertUnitDescription;
static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
static AudioStreamBasicDescription recordAudioStreamBasicDescription;
@interface STKAudioPlayer()
{
@@ -221,6 +190,7 @@ static AudioStreamBasicDescription recordAudioStreamBasicDescription;
BOOL meteringEnabled;
BOOL equalizerOn;
BOOL equalizerEnabled;
BOOL spectrumAnalyzerEnabled;
STKAudioPlayerOptions options;
NSMutableArray* converterNodes;
@@ -273,16 +243,7 @@ static AudioStreamBasicDescription recordAudioStreamBasicDescription;
AudioFileStreamID audioFileStream;
NSConditionLock* threadStartedLock;
NSConditionLock* threadFinishedCondLock;
AudioFileID recordAudioFileId;
UInt32 recordFilePacketPosition;
AudioConverterRef recordAudioConverterRef;
UInt32 recordOutputBufferSize;
UInt8 *recordOutputBuffer;
UInt32 recordPacketsPerBuffer;
UInt32 recordPacketSize;
AudioStreamPacketDescription *recordPacketDescriptions;
void(^stopBackBackgroundTaskBlock)();
int32_t seekVersion;
@@ -294,6 +255,14 @@ static AudioStreamBasicDescription recordAudioStreamBasicDescription;
pthread_mutex_t mainThreadSyncCallMutex;
pthread_cond_t mainThreadSyncCallReadyCondition;
float* window;
float* obtainedReal;
float* originalReal;
int fftStride;
FFTSetup setupReal;
DSPSplitComplex fftInput;
volatile BOOL waiting;
volatile double requestedSeekTime;
volatile BOOL disposeWasRequested;
@@ -333,15 +302,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
.componentSubType = kAudioUnitSubType_AUConverter,
.componentFlags = 0,
.componentFlagsMask = 0
};
};
#ifdef CA_CANONICAL_DEPRECATED
const int bytesPerSample = sizeof(SInt16);
#elif __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000
const int bytesPerSample = sizeof(SInt16);
#else
const int bytesPerSample = sizeof(AudioSampleType);
#endif
canonicalAudioStreamBasicDescription = (AudioStreamBasicDescription)
{
@@ -354,7 +317,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
.mBitsPerChannel = 8 * bytesPerSample,
.mBytesPerPacket = (bytesPerSample * 2)
};
outputUnitDescription = (AudioComponentDescription)
{
.componentType = kAudioUnitType_Output,
@@ -517,9 +480,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self->volume = 1.0;
self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0;
self->spectrumAnalyzerEnabled = NO;
PopulateOptionsWithDefault(&options);
NormalizeDisabledBuffers(&options);
framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlaying;
framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlayingAfterBufferUnderun;
@@ -586,8 +549,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
OSSpinLockUnlock(&currentEntryReferencesLock);
}
[self closeRecordAudioFile];
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonDisposed];
[self clearQueue];
@@ -628,7 +589,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
pthread_cond_destroy(&mainThreadSyncCallReadyCondition);
free(readBuffer);
free(pcmAudioBufferList.mBuffers[0].mData);
free(originalReal);
free(obtainedReal);
free(window);
}
-(void) startSystemBackgroundTask
@@ -856,11 +819,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
pthread_mutex_lock(&playerMutex);
if (entryToUpdate->audioStreamBasicDescription.mFormatID == 0)
{
entryToUpdate->audioStreamBasicDescription = newBasicDescription;
}
entryToUpdate->audioStreamBasicDescription = newBasicDescription;
entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate;
entryToUpdate->packetDuration = entryToUpdate->audioStreamBasicDescription.mFramesPerPacket / entryToUpdate->sampleRate;
@@ -907,7 +866,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
case kAudioFileStreamProperty_ReadyToProducePackets:
{
if (audioConverterAudioStreamBasicDescription.mFormatID != kAudioFormatLinearPCM)
if (!audioConverterAudioStreamBasicDescription.mFormatID == kAudioFormatLinearPCM)
{
discontinuous = YES;
}
@@ -941,8 +900,13 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
if (pasbd.mFormatID == kAudioFormatMPEG4AAC_HE || pasbd.mFormatID == kAudioFormatMPEG4AAC_HE_V2)
{
//
// We've found HE-AAC, remember this to tell the audio queue
// when we construct it.
//
#if !TARGET_IPHONE_SIMULATOR
currentlyReadingEntry->audioStreamBasicDescription = pasbd;
#endif
break;
}
}
@@ -1146,8 +1110,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
[currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]];
[currentlyReadingEntry.dataSource seekToOffset:0];
[self closeRecordAudioFile];
if (startPlaying)
{
if (clearQueue)
@@ -1450,8 +1412,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
currentlyReadingEntry = nil;
OSSpinLockUnlock(&currentEntryReferencesLock);
pthread_mutex_unlock(&playerMutex);
[self closeRecordAudioFile];
self.internalState = STKAudioPlayerInternalStateDisposed;
@@ -1514,11 +1474,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
AudioConverterReset(audioConverterRef);
}
if (recordAudioConverterRef)
{
AudioConverterReset(recordAudioConverterRef);
}
[currentEntry reset];
[currentEntry.dataSource seekToOffset:seekByteOffset];
@@ -1637,9 +1592,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
}
NSObject* queueItemId = currentlyReadingEntry.queueItemId;
[self closeRecordAudioFile];
[self dispatchSyncOnMainThread:^
{
[self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId];
@@ -1764,8 +1717,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
return;
}
[self closeRecordAudioFile];
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonUserAction];
[self resetPcmBuffers];
@@ -1860,35 +1811,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self.muted = NO;
}
-(void) closeRecordAudioFile
{
if (recordAudioFileId)
{
AudioFileClose(recordAudioFileId);
recordAudioFileId = NULL;
}
if (recordAudioConverterRef)
{
AudioConverterDispose(recordAudioConverterRef);
recordAudioConverterRef = nil;
}
if (recordOutputBuffer)
{
free(recordOutputBuffer);
recordOutputBuffer = NULL;
}
if (recordPacketDescriptions)
{
free(recordPacketDescriptions);
recordPacketDescriptions = NULL;
}
recordFilePacketPosition = 0;
}
-(void) dispose
{
[self stop];
@@ -1961,43 +1883,17 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
{
OSStatus status;
Boolean writable;
UInt32 cookieSize = 0;
UInt32 cookieSize;
if (memcmp(asbd, &audioConverterAudioStreamBasicDescription, sizeof(AudioStreamBasicDescription)) == 0)
{
AudioConverterReset(audioConverterRef);
if (recordAudioConverterRef)
{
AudioConverterReset(recordAudioConverterRef);
}
return;
}
[self destroyAudioConverter];
canonicalAudioStreamBasicDescription.mChannelsPerFrame = asbd->mChannelsPerFrame;
BOOL isRecording = currentlyReadingEntry.dataSource.recordToFileUrl != nil;
if (isRecording)
{
recordAudioStreamBasicDescription = (AudioStreamBasicDescription)
{
.mFormatID = kAudioFormatMPEG4AAC,
.mFormatFlags = kMPEG4Object_AAC_LC,
.mChannelsPerFrame = canonicalAudioStreamBasicDescription.mChannelsPerFrame,
.mSampleRate = canonicalAudioStreamBasicDescription.mSampleRate,
};
UInt32 dataSize = sizeof(recordAudioStreamBasicDescription);
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&dataSize,
&recordAudioStreamBasicDescription);
}
AudioClassDescription classDesc;
if (GetHardwareCodecClassDesc(asbd->mFormatID, &classDesc))
@@ -2016,29 +1912,14 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
return;
}
}
if (isRecording && !recordAudioConverterRef)
{
status = AudioConverterNew(&canonicalAudioStreamBasicDescription, &recordAudioStreamBasicDescription, &recordAudioConverterRef);
if (status)
{
NSLog(@"STKAudioPlayer failed to create a recording audio converter");
}
}
audioConverterAudioStreamBasicDescription = *asbd;
if (self->currentlyReadingEntry.dataSource.audioFileTypeHint != kAudioFileAAC_ADTSType)
{
status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable);
status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable);
if (status)
{
return;
}
void* cookieData = alloca(cookieSize);
if (!status)
{
void* cookieData = alloca(cookieSize);
status = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData);
@@ -2051,87 +1932,9 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
if (status)
{
[self unexpectedError:STKAudioPlayerErrorAudioSystemError];
return;
}
}
if (recordAudioConverterRef)
{
if (recordAudioFileId)
{
AudioFileClose(recordAudioFileId);
recordAudioFileId = NULL;
}
if (recordOutputBuffer)
{
free(recordOutputBuffer);
recordOutputBuffer = NULL;
}
if (recordPacketDescriptions)
{
free(recordPacketDescriptions);
recordPacketDescriptions = NULL;
}
recordOutputBufferSize = 32 * 1024;
recordPacketSize = canonicalAudioStreamBasicDescription.mBytesPerPacket;
if (recordPacketSize == 0)
{
UInt32 size = sizeof(recordPacketSize);
if (0 == AudioConverterGetProperty(recordAudioConverterRef, kAudioConverterPropertyMaximumOutputPacketSize, &size, &recordPacketSize))
{
if (recordPacketSize > recordOutputBufferSize)
{
recordOutputBufferSize = recordPacketSize;
}
recordPacketsPerBuffer = recordOutputBufferSize / recordPacketSize;
}
else
{
AudioConverterDispose(recordAudioConverterRef);
recordAudioConverterRef = NULL;
NSLog(@"STKAudioPlayer: Can't support this output format for recording");
}
}
else
{
recordPacketsPerBuffer = recordOutputBufferSize / recordPacketSize;
}
UInt32 propertySize = sizeof(UInt32);
UInt32 externallyFramed = 0;
OSStatus error = AudioFormatGetProperty(kAudioFormatProperty_FormatIsExternallyFramed, sizeof(recordAudioStreamBasicDescription), &recordAudioStreamBasicDescription, &propertySize, &externallyFramed);
if (externallyFramed)
{
recordPacketDescriptions = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * recordPacketsPerBuffer);
}
recordOutputBuffer = (UInt8 *)malloc(sizeof(UInt8) * recordOutputBufferSize);
error = AudioFileCreateWithURL(
(__bridge CFURLRef)(currentlyReadingEntry.dataSource.recordToFileUrl),
kAudioFileCAFType,
&recordAudioStreamBasicDescription,
kAudioFileFlags_EraseFile,
&recordAudioFileId);
recordFilePacketPosition = 0;
if (error)
{
NSLog(@"STKAudioPlayer failed to create a recording audio file at %@", currentlyReadingEntry.dataSource.recordToFileUrl);
[self closeRecordAudioFile];
}
}
}
-(void) createOutputUnit
@@ -2623,11 +2426,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL);
framesAdded = framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
@@ -2671,11 +2469,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
framesAdded += framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
@@ -2720,11 +2513,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
framesAdded = framesToDecode;
if ((status == 100 || status == 0) && recordAudioFileId && recordAudioConverterRef)
{
[self handleRecordingOfAudioPackets:framesToDecode audioBuffer:&localPcmBufferList.mBuffers[0]];
}
if (status == 100)
{
OSSpinLockLock(&pcmBufferSpinLock);
@@ -2759,66 +2547,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu
}
}
- (void)handleRecordingOfAudioPackets:(UInt32)numberOfPackets audioBuffer:(AudioBuffer *)audioBuffer
{
if (recordAudioFileId && recordAudioConverterRef)
{
AudioConvertInfo recordConvertInfo;
recordConvertInfo.done = NO;
recordConvertInfo.numberOfPackets = numberOfPackets;
recordConvertInfo.packetDescriptions = NULL;
recordConvertInfo.audioBuffer = *audioBuffer;
AudioBufferList convertedData;
convertedData.mNumberBuffers = 1;
convertedData.mBuffers[0].mNumberChannels = recordAudioStreamBasicDescription.mChannelsPerFrame;
convertedData.mBuffers[0].mDataByteSize = recordOutputBufferSize;
convertedData.mBuffers[0].mData = recordOutputBuffer;
UInt32 ioOutputDataPackets;
OSStatus status;
while (1)
{
ioOutputDataPackets = recordPacketsPerBuffer;
status = AudioConverterFillComplexBuffer(recordAudioConverterRef, AudioConverterCallback, (void*)&recordConvertInfo, &ioOutputDataPackets, &convertedData, recordPacketDescriptions);
if (status == 100 || status == 0)
{
if (ioOutputDataPackets > 0)
{
OSStatus writeError = AudioFileWritePackets(recordAudioFileId,
NO,
convertedData.mBuffers[0].mDataByteSize,
recordPacketDescriptions,
recordFilePacketPosition,
&ioOutputDataPackets,
convertedData.mBuffers[0].mData);
if (writeError)
{
NSLog(@"STKAudioPlayer:handleRecordingOfAudioPackets failed on AudioFileWritePackets with error \"" OSSTATUS_PRINTF_PLACEHOLDER "\"", OSSTATUS_PRINTF_VALUE(writeError));
}
else
{
recordFilePacketPosition += ioOutputDataPackets;
}
}
}
else
{
NSLog(@"STKAudioPlayer: Unexpected error during recording audio file conversion");
}
if (status == 100)
{
break;
}
}
}
}
static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData)
{
STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon;
@@ -3198,6 +2926,115 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return averagePowerDb[channelNumber];
}
-(BOOL) spectrumAnalyzerEnabled
{
return self->spectrumAnalyzerEnabled;
}
-(void) setSpectrumAnalyzerEnabled:(BOOL)value
{
if (self->spectrumAnalyzerEnabled == value)
{
return;
}
self->spectrumAnalyzerEnabled = value;
if (!value)
{
[self removeFrameFilterWithName:@"STKSpectrumAnalyzerFilter"];
}
else
{
if (!obtainedReal)
{
int maxSamples = 4096;
int log2n = log2f(maxSamples);
int n = 1 << log2n;
fftStride = 1;
int nOver2 = maxSamples / 2;
fftInput.realp = (float*)calloc(nOver2, sizeof(float));
fftInput.imagp =(float*)calloc(nOver2, sizeof(float));
obtainedReal = (float*)calloc(n, sizeof(float));
originalReal = (float*)calloc(n, sizeof(float));
window = (float*)calloc(maxSamples, sizeof(float));
vDSP_blkman_window(window, maxSamples, 0);
setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);
}
[self appendFrameFilterWithName:@"STKSpectrumAnalyzerFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames)
{
int log2n = log2f(frameCount);
int n = 1 << log2n;
int nOver2 = frameCount / 2;
SInt16* samples16 = (SInt16*)frames;
SInt32* samples32 = (SInt32*)frames;
if (bytesPerFrame / channelsPerFrame == 2)
{
for (int i = 0, j = 0; i < frameCount * channelsPerFrame; i+= channelsPerFrame, j += 2)
{
originalReal[j] = samples16[i] / 32768.0;
}
}
else if (bytesPerFrame / channelsPerFrame == 4)
{
for (int i = 0, j = 0; i < frameCount * channelsPerFrame; i+= channelsPerFrame, j+= 2)
{
originalReal[j] = samples32[i] / 32768.0;
}
}
vDSP_hann_window(window, n, 0);
vDSP_vmul(originalReal, 2, window, 1, originalReal, 2, n);
vDSP_ctoz((COMPLEX*)originalReal, 2, &fftInput, 1, nOver2);
vDSP_fft_zrip(setupReal, &fftInput, fftStride, log2n, FFT_FORWARD);
float one = 1;
float scale = (float)1.0 / (2 * n);
vDSP_vsmul(fftInput.realp, 1, &scale, fftInput.realp, 1, nOver2);
vDSP_vsmul(fftInput.imagp, 1, &scale, fftInput.imagp, 1, nOver2);
pthread_mutex_lock(&self->playerMutex);
vDSP_zvmags(&fftInput, 1, obtainedReal, 1, nOver2);
vDSP_vdbcon(obtainedReal, 1, &one, obtainedReal, 1, nOver2, 0);
float vmin = -96;
float vmax = 0;
vDSP_vclip(obtainedReal, 1, &vmin, &vmax, obtainedReal, 1, nOver2);
pthread_mutex_unlock(&self->playerMutex);
}];
}
}
-(float) testPowerWithIndex:(int)index
{
pthread_mutex_lock(&self->playerMutex);
if (!obtainedReal)
{
pthread_mutex_unlock(&self->playerMutex);
return 0;
}
float retval = obtainedReal[index];
pthread_mutex_unlock(&self->playerMutex);
return retval;
}
-(BOOL) meteringEnabled
{
return self->meteringEnabled;
@@ -3227,11 +3064,12 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
{
return;
}
self->meteringEnabled = value;
if (!value)
{
[self removeFrameFilterWithName:@"STKMeteringFilter"];
self->meteringEnabled = NO;
}
else
{
@@ -101,8 +101,6 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o
@implementation STKAutoRecoveringHTTPDataSource
@dynamic innerDataSource;
-(STKHTTPDataSource*) innerHTTPDataSource
{
return (STKHTTPDataSource*)self.innerDataSource;
@@ -43,10 +43,9 @@
@interface STKCoreFoundationDataSource : STKDataSource
{
@public
CFReadStreamRef stream;
@protected
BOOL isInErrorState;
CFReadStreamRef stream;
NSRunLoop* eventsRunLoop;
}
@@ -41,10 +41,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
switch (eventType)
{
case kCFStreamEventErrorOccurred:
{
[datasource errorOccured];
break;
}
case kCFStreamEventEndEncountered:
[datasource eof];
break;
@@ -137,6 +135,8 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve
{
CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered, NULL, NULL);
CFReadStreamUnscheduleFromRunLoop(stream, [eventsRunLoop getCFRunLoop], kCFRunLoopCommonModes);
eventsRunLoop = nil;
}
}
-3
View File
@@ -45,13 +45,10 @@
@interface STKDataSource : NSObject
@property (readonly) BOOL supportsSeek;
@property (readonly) SInt64 position;
@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;
-(BOOL) registerForEvents:(NSRunLoop*)runLoop;
-(void) unregisterForEvents;
@@ -79,9 +79,4 @@
return 0;
}
-(BOOL) supportsSeek
{
return YES;
}
@end
@@ -47,7 +47,6 @@ typedef void(^STKAsyncURLProvider)(STKHTTPDataSource* dataSource, BOOL forSeek,
+(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;
+63 -288
View File
@@ -38,25 +38,17 @@
@interface STKHTTPDataSource()
{
@private
BOOL supportsSeek;
UInt32 httpStatusCode;
SInt64 seekStart;
SInt64 relativePosition;
SInt64 fileLength;
int discontinuous;
int requestSerialNumber;
int prefixBytesRead;
NSData* prefixBytes;
NSMutableData* iceHeaderData;
BOOL iceHeaderSearchComplete;
BOOL iceHeaderAvailable;
BOOL httpHeaderNotAvailable;
NSURL* currentUrl;
STKAsyncURLProvider asyncUrlProvider;
NSDictionary* httpHeaders;
AudioFileTypeID audioFileTypeHint;
NSDictionary* requestHeaders;
}
-(void) open;
@@ -69,13 +61,6 @@
return [self initWithURLProvider:^NSURL* { return urlIn; }];
}
-(id) initWithURL:(NSURL *)urlIn httpRequestHeaders:(NSDictionary *)httpRequestHeaders
{
self = [self initWithURLProvider:^NSURL* { return urlIn; }];
self->requestHeaders = httpRequestHeaders;
return self;
}
-(id) initWithURLProvider:(STKURLProvider)urlProviderIn
{
urlProviderIn = [urlProviderIn copy];
@@ -125,8 +110,6 @@
@"audio/mpg": @(kAudioFileMP3Type),
@"audio/mpeg": @(kAudioFileMP3Type),
@"audio/wav": @(kAudioFileWAVEType),
@"audio/x-wav": @(kAudioFileWAVEType),
@"audio/vnd.wav": @(kAudioFileWAVEType),
@"audio/aifc": @(kAudioFileAIFCType),
@"audio/aiff": @(kAudioFileAIFFType),
@"audio/x-m4a": @(kAudioFileM4AType),
@@ -134,18 +117,10 @@
@"audio/aacp": @(kAudioFileAAC_ADTSType),
@"audio/m4a": @(kAudioFileM4AType),
@"audio/mp4": @(kAudioFileMPEG4Type),
@"video/mp4": @(kAudioFileMPEG4Type),
@"audio/caf": @(kAudioFileCAFType),
@"audio/x-caf": @(kAudioFileCAFType),
@"audio/aac": @(kAudioFileAAC_ADTSType),
@"audio/aacp": @(kAudioFileAAC_ADTSType),
@"audio/ac3": @(kAudioFileAC3Type),
@"audio/3gp": @(kAudioFile3GPType),
@"video/3gp": @(kAudioFile3GPType),
@"audio/3gpp": @(kAudioFile3GPType),
@"video/3gpp": @(kAudioFile3GPType),
@"audio/3gp2": @(kAudioFile3GP2Type),
@"video/3gp2": @(kAudioFile3GP2Type)
@"audio/3gp": @(kAudioFile3GPType)
};
});
@@ -164,233 +139,70 @@
return audioFileTypeHint;
}
-(NSDictionary*) parseIceHeader:(NSData*)headerData
{
NSMutableDictionary* retval = [[NSMutableDictionary alloc] init];
NSCharacterSet* characterSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
NSString* fullString = [[NSString alloc] initWithData:headerData encoding:NSUTF8StringEncoding];
NSArray* strings = [fullString componentsSeparatedByCharactersInSet:characterSet];
httpHeaders = [NSMutableDictionary dictionary];
for (NSString* s in strings)
{
if (s.length == 0)
{
continue;
}
if ([s hasPrefix:@"ICY "])
{
NSArray* parts = [s componentsSeparatedByString:@" "];
if (parts.count >= 2)
{
self->httpStatusCode = [parts[1] intValue];
}
continue;
}
NSRange range = [s rangeOfString:@":"];
if (range.location == NSNotFound)
{
continue;
}
NSString* key = [s substringWithRange: (NSRange){.location = 0, .length = range.location}];
NSString* value = [s substringFromIndex:range.location + 1];
[retval setValue:value forKey:key];
}
return retval;
}
-(BOOL) parseHttpHeader
{
if (!httpHeaderNotAvailable)
{
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
if (response)
{
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
if (httpHeaders.count == 0)
{
httpHeaderNotAvailable = YES;
}
else
{
self->httpStatusCode = (UInt32)CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
}
CFRelease(response);
}
}
if (httpHeaderNotAvailable)
{
if (self->iceHeaderSearchComplete && !self->iceHeaderAvailable)
{
return YES;
}
if (!self->iceHeaderSearchComplete)
{
UInt8 byte;
UInt8 terminal1[] = { '\n', '\n' };
UInt8 terminal2[] = { '\r', '\n', '\r', '\n' };
if (iceHeaderData == nil)
{
iceHeaderData = [NSMutableData dataWithCapacity:1024];
}
while (true)
{
if (![self hasBytesAvailable])
{
break;
}
int read = [super readIntoBuffer:&byte withSize:1];
if (read <= 0)
{
break;
}
[iceHeaderData appendBytes:&byte length:read];
if (iceHeaderData.length >= sizeof(terminal1))
{
if (memcmp(&terminal1[0], [self->iceHeaderData bytes] + iceHeaderData.length - sizeof(terminal1), sizeof(terminal1)) == 0)
{
self->iceHeaderAvailable = YES;
self->iceHeaderSearchComplete = YES;
break;
}
}
if (iceHeaderData.length >= sizeof(terminal2))
{
if (memcmp(&terminal2[0], [self->iceHeaderData bytes] + iceHeaderData.length - sizeof(terminal2), sizeof(terminal2)) == 0)
{
self->iceHeaderAvailable = YES;
self->iceHeaderSearchComplete = YES;
break;
}
}
if (iceHeaderData.length >= 4)
{
if (memcmp([self->iceHeaderData bytes], "ICY ", 4) != 0 && memcmp([self->iceHeaderData bytes], "HTTP", 4) != 0)
{
self->iceHeaderAvailable = NO;
self->iceHeaderSearchComplete = YES;
prefixBytes = iceHeaderData;
return YES;
}
}
}
if (!self->iceHeaderSearchComplete)
{
return NO;
}
}
httpHeaders = [self parseIceHeader:self->iceHeaderData];
self->iceHeaderData = nil;
}
if (([httpHeaders objectForKey:@"Accept-Ranges"] ?: [httpHeaders objectForKey:@"accept-ranges"]) != nil)
{
self->supportsSeek = YES;
}
if (self.httpStatusCode == 200)
{
if (seekStart == 0)
{
id value = [httpHeaders objectForKey:@"Content-Length"] ?: [httpHeaders objectForKey:@"content-length"];
fileLength = (SInt64)[value longLongValue];
}
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"] ?: [httpHeaders objectForKey:@"content-type"] ;
AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType];
if (typeIdFromMimeType != 0)
{
audioFileTypeHint = typeIdFromMimeType;
}
}
else if (self.httpStatusCode == 206)
{
NSString* contentRange = [httpHeaders objectForKey:@"Content-Range"] ?: [httpHeaders objectForKey:@"content-range"];
NSArray* components = [contentRange componentsSeparatedByString:@"/"];
if (components.count == 2)
{
fileLength = [[components objectAtIndex:1] integerValue];
}
}
else if (self.httpStatusCode == 416)
{
if (self.length >= 0)
{
seekStart = self.length;
}
[self eof];
return NO;
}
else if (self.httpStatusCode >= 300)
{
[self errorOccured];
return NO;
}
return YES;
}
-(void) dataAvailable
{
if (stream == NULL)
{
if (stream == NULL) {
return;
}
if (self.httpStatusCode == 0)
{
if ([self parseHttpHeader])
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
if (response)
{
if ([self hasBytesAvailable])
{
[super dataAvailable];
}
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
return;
}
else
{
return;
self->httpStatusCode = (UInt32)CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
CFRelease(response);
}
if (self.httpStatusCode == 200)
{
if (seekStart == 0)
{
fileLength = (SInt64)[[httpHeaders objectForKey:@"Content-Length"] longLongValue];
}
NSString* contentType = [httpHeaders objectForKey:@"Content-Type"];
AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType];
if (typeIdFromMimeType != 0)
{
audioFileTypeHint = typeIdFromMimeType;
}
}
else if (self.httpStatusCode == 206)
{
NSString* contentRange = [httpHeaders objectForKey:@"Content-Range"];
NSArray* components = [contentRange componentsSeparatedByString:@"/"];
if (components.count == 2)
{
fileLength = [[components objectAtIndex:1] integerValue];
}
}
else if (self.httpStatusCode == 416)
{
if (self.length >= 0)
{
seekStart = self.length;
}
[self eof];
return;
}
else if (self.httpStatusCode >= 300)
{
[self errorOccured];
return;
}
}
else
{
[super dataAvailable];
}
[super dataAvailable];
}
-(SInt64) position
@@ -430,43 +242,17 @@
self->isInErrorState = NO;
if (!self->supportsSeek && offset != self->relativePosition)
{
return;
}
[self openForSeek:YES];
}
-(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size
{
return [self privateReadIntoBuffer:buffer withSize:size];
}
-(int) privateReadIntoBuffer:(UInt8*)buffer withSize:(int)size
{
if (size == 0)
{
return 0;
}
if (prefixBytes != nil)
{
int count = MIN(size, (int)prefixBytes.length - prefixBytesRead);
[prefixBytes getBytes:buffer length:count];
prefixBytesRead += count;
if (prefixBytesRead >= prefixBytes.length)
{
prefixBytes = nil;
}
return count;
}
int read = [super readIntoBuffer:buffer withSize:size];
int read = (int)CFReadStreamRead(stream, buffer, size);
if (read < 0)
{
@@ -496,7 +282,7 @@
{
return;
}
self->currentUrl = url;
if (url == nil)
@@ -506,23 +292,13 @@
CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (__bridge CFURLRef)self->currentUrl, kCFHTTPVersion1_1);
if (seekStart > 0 && supportsSeek)
if (seekStart > 0)
{
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Range"), (__bridge CFStringRef)[NSString stringWithFormat:@"bytes=%lld-", seekStart]);
discontinuous = YES;
}
for (NSString* key in self->requestHeaders)
{
NSString* value = [self->requestHeaders objectForKey:key];
CFHTTPMessageSetHeaderFieldValue(message, (__bridge CFStringRef)key, (__bridge CFStringRef)value);
}
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Accept"), CFSTR("*/*"));
CFHTTPMessageSetHeaderFieldValue(message, CFSTR("Ice-MetaData"), CFSTR("0"));
stream = CFReadStreamCreateForHTTPRequest(NULL, message);
if (stream == nil)
@@ -533,8 +309,6 @@
return;
}
CFReadStreamSetProperty(stream, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground, (__bridge CFStringRef)NSStreamNetworkServiceTypeBackground);
if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
{
@@ -546,15 +320,20 @@
}
// Proxy support
CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
CFRelease(proxySettings);
// SSL support
if ([self->currentUrl.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)
{
NSDictionary* sslSettings = [NSDictionary dictionaryWithObjectsAndKeys:
(NSString*)kCFStreamSocketSecurityLevelNegotiatedSSL, kCFStreamSSLLevel,
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredRoots,
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
[NSNull null], kCFStreamSSLPeerName,
nil];
@@ -567,6 +346,7 @@
self->httpStatusCode = 0;
// Open
if (!CFReadStreamOpen(stream))
{
CFRelease(stream);
@@ -600,9 +380,4 @@
return [NSString stringWithFormat:@"HTTP data source with file length: %lld and position: %lld", self.length, self.position];
}
-(BOOL) supportsSeek
{
return self->supportsSeek;
}
@end
-1
View File
@@ -28,7 +28,6 @@
volatile int processedPacketsCount;
volatile int processedPacketsSizeTotal;
AudioStreamBasicDescription audioStreamBasicDescription;
double durationHint;
}
@property (readonly) UInt64 audioDataLengthInBytes;
+1 -4
View File
@@ -23,7 +23,6 @@
self.dataSource = dataSourceIn;
self.queueItemId = queueItemIdIn;
self->lastFrameQueued = -1;
self->durationHint = dataSourceIn.durationHint;
}
return self;
@@ -46,7 +45,7 @@
{
if (processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_PREFERRED || (audioStreamBasicDescription.mBytesPerFrame == 0 && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS_MIN))
{
double averagePacketByteSize = (double)processedPacketsSizeTotal / (double)processedPacketsCount;
double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount;
retval = averagePacketByteSize / packetDuration * 8;
@@ -61,8 +60,6 @@
-(double) duration
{
if (durationHint > 0.0) return durationHint;
if (self->sampleRate <= 0)
{
return 0;
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>com.abstractpath.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
@@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<string>abstractpath.com.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>