Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e805e4740c | |||
| 484285df84 | |||
| a9dfb2eddf | |||
| 3fcf054a23 | |||
| a615419404 | |||
| e43a4613f8 | |||
| ca928dfe1e |
@@ -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 */,
|
||||
@@ -365,7 +369,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
@@ -403,7 +407,7 @@
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../StreamingKit/StreamingKit",
|
||||
);
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
VALIDATE_PRODUCT = YES;
|
||||
|
||||
@@ -40,6 +40,9 @@
|
||||
///
|
||||
|
||||
@interface AudioPlayerView()
|
||||
{
|
||||
NSMutableArray* meters;
|
||||
}
|
||||
-(void) setupTimer;
|
||||
-(void) updateControls;
|
||||
@end
|
||||
@@ -55,6 +58,8 @@
|
||||
{
|
||||
self.audioPlayer = audioPlayerIn;
|
||||
|
||||
self.audioPlayer.spectrumAnalyzerEnabled = YES;
|
||||
|
||||
CGSize size = CGSizeMake(220, 50);
|
||||
|
||||
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
|
||||
@@ -114,9 +119,23 @@
|
||||
|
||||
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];
|
||||
@@ -193,9 +212,18 @@
|
||||
|
||||
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
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */; };
|
||||
A168C6F118BB67DC003D170D /* STKBufferChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = A168C6F018BB67DC003D170D /* STKBufferChunk.m */; };
|
||||
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; };
|
||||
A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; };
|
||||
A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; };
|
||||
@@ -39,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 */
|
||||
@@ -85,10 +85,6 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferingDataSource.h; sourceTree = "<group>"; };
|
||||
A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferingDataSource.m; sourceTree = "<group>"; };
|
||||
A168C6EF18BB67DC003D170D /* STKBufferChunk.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferChunk.h; sourceTree = "<group>"; };
|
||||
A168C6F018BB67DC003D170D /* STKBufferChunk.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferChunk.m; sourceTree = "<group>"; };
|
||||
A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKitMac.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A1A4996A189E744400E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; };
|
||||
A1A4996D189E744500E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
|
||||
@@ -131,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 */
|
||||
@@ -138,6 +136,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1F341081908183A00CA7755 /* Accelerate.framework in Frameworks */,
|
||||
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -156,6 +155,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
A1F341041908183300CA7755 /* Accelerate.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -254,6 +254,8 @@
|
||||
A1E7C4CA188D57F50010896F /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A1F341071908183A00CA7755 /* Accelerate.framework */,
|
||||
A1F341031908183300CA7755 /* Accelerate.framework */,
|
||||
A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */,
|
||||
A1C9767618981BFE0057F881 /* AudioUnit.framework */,
|
||||
A1E7C507188D62D20010896F /* UIKit.framework */,
|
||||
@@ -272,10 +274,6 @@
|
||||
A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */,
|
||||
A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */,
|
||||
A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */,
|
||||
A168C6EF18BB67DC003D170D /* STKBufferChunk.h */,
|
||||
A168C6F018BB67DC003D170D /* STKBufferChunk.m */,
|
||||
A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */,
|
||||
A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */,
|
||||
A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */,
|
||||
A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */,
|
||||
A1E7C4F7188D5E550010896F /* STKDataSource.h */,
|
||||
@@ -512,9 +510,7 @@
|
||||
A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */,
|
||||
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */,
|
||||
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */,
|
||||
A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */,
|
||||
A1E7C502188D5E550010896F /* STKDataSource.m in Sources */,
|
||||
A168C6F118BB67DC003D170D /* STKBufferChunk.m in Sources */,
|
||||
A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */,
|
||||
A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */,
|
||||
);
|
||||
@@ -674,7 +670,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
@@ -705,7 +701,7 @@
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
|
||||
@@ -146,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)
|
||||
@@ -263,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
|
||||
|
||||
@@ -40,8 +40,13 @@
|
||||
#import "STKQueueEntry.h"
|
||||
#import "NSMutableArray+STKAudioPlayer.h"
|
||||
#import "libkern/OSAtomic.h"
|
||||
#include <Accelerate/Accelerate.h>
|
||||
#import <float.h>
|
||||
|
||||
#ifndef DBL_MAX
|
||||
#define DBL_MAX 1.7976931348623157e+308
|
||||
#endif
|
||||
|
||||
#pragma mark Defines
|
||||
|
||||
#define kOutputBus 0
|
||||
@@ -185,6 +190,7 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
|
||||
BOOL meteringEnabled;
|
||||
BOOL equalizerOn;
|
||||
BOOL equalizerEnabled;
|
||||
BOOL spectrumAnalyzerEnabled;
|
||||
STKAudioPlayerOptions options;
|
||||
|
||||
NSMutableArray* converterNodes;
|
||||
@@ -249,6 +255,14 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
|
||||
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;
|
||||
@@ -466,6 +480,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
|
||||
self->volume = 1.0;
|
||||
self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0;
|
||||
self->spectrumAnalyzerEnabled = NO;
|
||||
|
||||
PopulateOptionsWithDefault(&options);
|
||||
|
||||
@@ -526,7 +541,12 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
{
|
||||
currentlyPlayingEntry.dataSource.delegate = nil;
|
||||
[currentlyReadingEntry.dataSource unregisterForEvents];
|
||||
|
||||
OSSpinLockLock(¤tEntryReferencesLock);
|
||||
|
||||
currentlyPlayingEntry = nil;
|
||||
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
}
|
||||
|
||||
[self stopAudioUnitWithReason:STKAudioPlayerStopReasonDisposed];
|
||||
@@ -569,6 +589,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
pthread_cond_destroy(&mainThreadSyncCallReadyCondition);
|
||||
|
||||
free(readBuffer);
|
||||
free(originalReal);
|
||||
free(obtainedReal);
|
||||
free(window);
|
||||
}
|
||||
|
||||
-(void) startSystemBackgroundTask
|
||||
@@ -924,20 +947,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
}
|
||||
|
||||
OSSpinLockLock(¤tEntryReferencesLock);
|
||||
|
||||
STKQueueEntry* entry = currentlyPlayingEntry;
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
|
||||
if (entry == nil)
|
||||
{
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
double retval = [entry duration];
|
||||
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
|
||||
double progress = [self progress];
|
||||
|
||||
if (retval < progress && retval > 0)
|
||||
@@ -960,7 +978,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
return 0;
|
||||
}
|
||||
|
||||
OSSpinLockLock(¤tEntryReferencesLock);
|
||||
STKQueueEntry* entry = currentlyPlayingEntry;
|
||||
OSSpinLockUnlock(¤tEntryReferencesLock);
|
||||
|
||||
if (entry == nil)
|
||||
{
|
||||
@@ -1347,7 +1367,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&playerMutex);
|
||||
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -1398,6 +1417,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
|
||||
|
||||
playbackThreadRunLoop = nil;
|
||||
|
||||
[self destroyAudioResources];
|
||||
|
||||
[threadFinishedCondLock lock];
|
||||
[threadFinishedCondLock unlockWithCondition:1];
|
||||
}
|
||||
@@ -2530,11 +2551,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
||||
{
|
||||
STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon;
|
||||
|
||||
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
|
||||
STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry;
|
||||
STKQueueEntry* currentlyReadingEntry = audioPlayer->currentlyReadingEntry;
|
||||
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
|
||||
|
||||
OSSpinLockLock(&audioPlayer->pcmBufferSpinLock);
|
||||
|
||||
BOOL waitForBuffer = NO;
|
||||
BOOL muted = audioPlayer->muted;
|
||||
STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry;
|
||||
AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer;
|
||||
UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes;
|
||||
UInt32 used = audioPlayer->pcmBufferUsedFrameCount;
|
||||
@@ -2552,10 +2577,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
||||
|
||||
if (entry->lastFrameQueued >= 0)
|
||||
{
|
||||
framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued);
|
||||
framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued);
|
||||
}
|
||||
|
||||
if (entry && audioPlayer->currentlyReadingEntry == entry
|
||||
if (entry && currentlyReadingEntry == entry
|
||||
&& entry->framesQueued < framesRequiredToStartPlaying)
|
||||
{
|
||||
waitForBuffer = YES;
|
||||
@@ -2765,13 +2790,19 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
||||
{
|
||||
pthread_mutex_lock(&audioPlayer->playerMutex);
|
||||
|
||||
if (lastFramePlayed && entry == audioPlayer->currentlyPlayingEntry)
|
||||
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
|
||||
STKQueueEntry* currentlyPlayingEntry = audioPlayer->currentlyPlayingEntry;
|
||||
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
|
||||
|
||||
if (lastFramePlayed && entry == currentlyPlayingEntry)
|
||||
{
|
||||
[audioPlayer audioQueueFinishedPlaying:entry];
|
||||
|
||||
while (extraFramesPlayedNotAssigned > 0)
|
||||
{
|
||||
OSSpinLockLock(&audioPlayer->currentEntryReferencesLock);
|
||||
STKQueueEntry* newEntry = audioPlayer->currentlyPlayingEntry;
|
||||
OSSpinLockUnlock(&audioPlayer->currentEntryReferencesLock);
|
||||
|
||||
if (newEntry != nil)
|
||||
{
|
||||
@@ -2895,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;
|
||||
@@ -2924,11 +3064,12 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self->meteringEnabled = value;
|
||||
|
||||
if (!value)
|
||||
{
|
||||
[self removeFrameFilterWithName:@"STKMeteringFilter"];
|
||||
self->meteringEnabled = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// STKBufferChunk.h
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 24/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface STKBufferChunk : NSObject
|
||||
{
|
||||
@public
|
||||
UInt32 index;
|
||||
UInt32 size;
|
||||
UInt32 position;
|
||||
UInt8* buffer;
|
||||
}
|
||||
|
||||
@property (readonly) UInt32 absoluteStart;
|
||||
@property (readonly) UInt32 absolutePosition;
|
||||
|
||||
-(id) initWithBufferSize:(UInt32)sizeIn;
|
||||
|
||||
@end
|
||||
@@ -1,40 +0,0 @@
|
||||
//
|
||||
// STKBufferChunk.m
|
||||
// StreamingKit
|
||||
//
|
||||
// Created by Thong Nguyen on 24/02/2014.
|
||||
// Copyright (c) 2014 Thong Nguyen. All rights reserved.
|
||||
//
|
||||
|
||||
#import "STKBufferChunk.h"
|
||||
|
||||
@implementation STKBufferChunk
|
||||
|
||||
-(id) initWithBufferSize:(UInt32)sizeIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self->size = sizeIn;
|
||||
|
||||
self->buffer = calloc(sizeof(UInt8), sizeIn);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
free(self->buffer);
|
||||
}
|
||||
|
||||
-(UInt32) absoluteStart
|
||||
{
|
||||
return self->index * self->size;
|
||||
}
|
||||
|
||||
-(UInt32) absolutePosition
|
||||
{
|
||||
return self.absoluteStart + self->position;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,44 +0,0 @@
|
||||
/**********************************************************************************
|
||||
STKBufferingDataSource.h
|
||||
|
||||
Created by Thong Nguyen on 16/10/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
|
||||
4. Neither the name of Thong Nguyen nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Thong Nguyen ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THONG NGUYEN BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**********************************************************************************/
|
||||
|
||||
#import "STKDataSource.h"
|
||||
|
||||
@interface STKBufferingDataSource : STKDataSource
|
||||
|
||||
@property (readonly) SInt64 position;
|
||||
@property (readonly) SInt64 length;
|
||||
|
||||
-(id) initWithDataSource:(STKDataSource*)dataSourceIn withMaxSize:(int)maxSizeIn;
|
||||
|
||||
@end
|
||||
@@ -1,319 +0,0 @@
|
||||
/**********************************************************************************
|
||||
STKBufferingDataSource.m
|
||||
|
||||
Created by Thong Nguyen on 16/10/2012.
|
||||
https://github.com/tumtumtum/audjustable
|
||||
|
||||
Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. All advertising materials mentioning features or use of this software
|
||||
must display the following acknowledgement:
|
||||
This product includes software developed by Thong Nguyen (tumtumtum@gmail.com)
|
||||
4. Neither the name of Thong Nguyen nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Thong Nguyen ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THONG NGUYEN BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**********************************************************************************/
|
||||
|
||||
#import "STKBufferingDataSource.h"
|
||||
#import "STKBufferChunk.h"
|
||||
#import <pthread.h>
|
||||
|
||||
#define STK_BUFFER_CHUNK_SIZE (128 * 1024)
|
||||
|
||||
@interface STKBufferingDataSource()
|
||||
{
|
||||
@private
|
||||
NSRunLoop* runLoop;
|
||||
SInt32 maxSize;
|
||||
UInt32 chunkSize;
|
||||
UInt32 chunkCount;
|
||||
SInt64 position;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t condition;
|
||||
STKBufferChunk* __strong * bufferChunks;
|
||||
STKDataSource* dataSource;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface STKBufferingDataSourceThread : NSThread
|
||||
{
|
||||
@private
|
||||
NSRunLoop* runLoop;
|
||||
NSConditionLock* threadStartedLock;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation STKBufferingDataSourceThread
|
||||
|
||||
-(id) init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
threadStartedLock = [[NSConditionLock alloc] initWithCondition:0];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(NSRunLoop*) runLoop
|
||||
{
|
||||
[threadStartedLock lockWhenCondition:1];
|
||||
[threadStartedLock unlockWithCondition:0];
|
||||
|
||||
return self->runLoop;
|
||||
}
|
||||
|
||||
-(void) main
|
||||
{
|
||||
runLoop = [NSRunLoop currentRunLoop];
|
||||
|
||||
[threadStartedLock lockWhenCondition:0];
|
||||
[threadStartedLock unlockWithCondition:1];
|
||||
|
||||
[runLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
|
||||
|
||||
while (true)
|
||||
{
|
||||
NSDate* date = [[NSDate alloc] initWithTimeIntervalSinceNow:10];
|
||||
|
||||
[runLoop runMode:NSDefaultRunLoopMode beforeDate:date];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
static STKBufferingDataSourceThread* thread;
|
||||
|
||||
@implementation STKBufferingDataSource
|
||||
|
||||
+(void) initialize
|
||||
{
|
||||
thread = [[STKBufferingDataSourceThread alloc] init];
|
||||
|
||||
[thread start];
|
||||
}
|
||||
|
||||
-(id) initWithDataSource:(STKDataSource*)dataSourceIn withMaxSize:(int)maxSizeIn
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self->maxSize = maxSizeIn;
|
||||
self->dataSource = dataSourceIn;
|
||||
self->chunkSize = STK_BUFFER_CHUNK_SIZE;
|
||||
|
||||
self->dataSource.delegate = self.delegate;
|
||||
|
||||
[self->dataSource registerForEvents:[thread runLoop]];
|
||||
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
||||
pthread_mutex_init(&self->mutex, &attr);
|
||||
pthread_cond_init(&self->condition, NULL);
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
self->dataSource.delegate = nil;
|
||||
|
||||
for (int i = 0; i < self->chunkCount; i++)
|
||||
{
|
||||
self->bufferChunks[i] = nil;
|
||||
}
|
||||
|
||||
free(self->bufferChunks);
|
||||
|
||||
pthread_mutex_destroy(&self->mutex);
|
||||
pthread_cond_destroy(&self->condition);
|
||||
}
|
||||
|
||||
-(void) createBuffer
|
||||
{
|
||||
if (self->bufferChunks == nil)
|
||||
{
|
||||
int length = (int)MIN(self.length == 0? 1024 * 1024 : self.length, self->maxSize);
|
||||
|
||||
self->chunkCount = (int)((length / self->chunkSize) + 1);
|
||||
self->bufferChunks = (__strong STKBufferChunk**)calloc(sizeof(STKBufferChunk*), self->chunkCount);
|
||||
}
|
||||
}
|
||||
|
||||
-(STKBufferChunk*) chunkForPosition:(SInt64)positionIn createIfNotExist:(BOOL)createIfNotExist
|
||||
{
|
||||
int chunkIndex = (int)(positionIn / chunkCount);
|
||||
|
||||
if (self->bufferChunks[chunkIndex] == nil && createIfNotExist)
|
||||
{
|
||||
self->bufferChunks[chunkIndex] = [[STKBufferChunk alloc] initWithBufferSize:STK_BUFFER_CHUNK_SIZE];
|
||||
}
|
||||
|
||||
return self->bufferChunks[chunkIndex];
|
||||
}
|
||||
|
||||
-(SInt64) length
|
||||
{
|
||||
return self->dataSource.length;
|
||||
}
|
||||
|
||||
-(void) seekToOffset:(SInt64)offset
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
[self seekToNextGap];
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
-(BOOL) hasBytesAvailable
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(int) readIntoBuffer:(UInt8*)bufferIn withSize:(int)size
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
-(void) invokeBlockOnEventsRunLoop:(void(^)())block
|
||||
{
|
||||
if (!runLoop)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
block = [block copy];
|
||||
|
||||
CFRunLoopPerformBlock(runLoop.getCFRunLoop, NSRunLoopCommonModes, ^
|
||||
{
|
||||
if ([self hasBytesAvailable])
|
||||
{
|
||||
block();
|
||||
}
|
||||
});
|
||||
|
||||
CFRunLoopWakeUp(runLoop.getCFRunLoop);
|
||||
}
|
||||
|
||||
-(BOOL) registerForEvents:(NSRunLoop*)runLoopIn
|
||||
{
|
||||
runLoop = runLoopIn;
|
||||
|
||||
[dataSource registerForEvents:[thread runLoop]];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void) unregisterForEvents
|
||||
{
|
||||
runLoop = nil;
|
||||
|
||||
[dataSource unregisterForEvents];
|
||||
}
|
||||
|
||||
-(void) close
|
||||
{
|
||||
[dataSource unregisterForEvents];
|
||||
[dataSource close];
|
||||
}
|
||||
|
||||
-(void) seekToNextGap
|
||||
{
|
||||
int startChunkIndex = (int)(self->position / chunkCount);
|
||||
|
||||
for (int i = 0; i < self->chunkCount; i++)
|
||||
{
|
||||
int chunkIndex = (i + startChunkIndex) % self->chunkCount;
|
||||
|
||||
STKBufferChunk* chunk = self->bufferChunks[chunkIndex];
|
||||
|
||||
if (chunk == nil)
|
||||
{
|
||||
chunk = [[STKBufferChunk alloc] initWithBufferSize:STK_BUFFER_CHUNK_SIZE];
|
||||
|
||||
chunk->index = chunkIndex;
|
||||
|
||||
self->bufferChunks[chunkIndex] = chunk;
|
||||
}
|
||||
|
||||
if (chunk->position < chunk->size)
|
||||
{
|
||||
[dataSource seekToOffset:(self->chunkSize * chunk->index) + chunk->position];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn
|
||||
{
|
||||
if (![dataSourceIn hasBytesAvailable])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
if (self->bufferChunks == nil)
|
||||
{
|
||||
[self createBuffer];
|
||||
}
|
||||
|
||||
SInt64 sourcePosition = dataSourceIn.position;
|
||||
|
||||
STKBufferChunk* chunk = [self chunkForPosition:sourcePosition createIfNotExist:YES];
|
||||
|
||||
if (chunk->position >= chunk->size)
|
||||
{
|
||||
[self seekToNextGap];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int offset = dataSourceIn.position % self->chunkSize;
|
||||
|
||||
if (offset >= chunk->position)
|
||||
{
|
||||
[self seekToNextGap];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int bytesToRead = self->chunkSize - offset;
|
||||
int bytesRead = [dataSourceIn readIntoBuffer:(chunk->buffer + offset) withSize:bytesToRead];
|
||||
|
||||
chunk->position = offset + bytesRead;
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
-(void) dataSourceErrorOccured:(STKDataSource*)dataSourceIn
|
||||
{
|
||||
[self.delegate dataSourceErrorOccured:self];
|
||||
}
|
||||
|
||||
-(void) dataSourceEof:(STKDataSource*)dataSourceIn
|
||||
{
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -200,6 +200,18 @@
|
||||
[self open];
|
||||
}
|
||||
|
||||
if (stream == 0)
|
||||
{
|
||||
CFRunLoopPerformBlock(eventsRunLoop.getCFRunLoop, NSRunLoopCommonModes, ^
|
||||
{
|
||||
[self errorOccured];
|
||||
});
|
||||
|
||||
CFRunLoopWakeUp(eventsRunLoop.getCFRunLoop);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (CFReadStreamSetProperty(stream, kCFStreamPropertyFileCurrentOffset, (__bridge CFTypeRef)[NSNumber numberWithLongLong:offset]) != TRUE)
|
||||
{
|
||||
position = 0;
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
self->spinLock = OS_SPINLOCK_INIT;
|
||||
|
||||
self.dataSource = dataSourceIn;
|
||||
self.queueItemId = queueItemIdIn;
|
||||
self->lastFrameQueued = -1;
|
||||
|
||||
Reference in New Issue
Block a user