From e3ed6c6deebc3d202f9e84dba2d4ead7ea42927e Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Tue, 18 Feb 2014 13:38:03 +0000 Subject: [PATCH] Removed AudioDataSource (for now). Changed all int64_t and long long types to SInt64. Started adding Buffered data source --- .../StreamingKit.xcodeproj/project.pbxproj | 6 + StreamingKit/StreamingKit/STKAudioPlayer.m | 24 ++-- .../STKAutoRecoveringHTTPDataSource.m | 2 +- .../StreamingKit/STKBufferingDataSource.h | 43 ++++++++ .../StreamingKit/STKBufferingDataSource.m | 103 ++++++++++++++++++ .../STKCoreFoundationDataSource.m | 2 +- StreamingKit/StreamingKit/STKDataSource.h | 11 +- StreamingKit/StreamingKit/STKDataSource.m | 6 +- .../StreamingKit/STKDataSourceWrapper.m | 6 +- StreamingKit/StreamingKit/STKHTTPDataSource.m | 14 +-- .../StreamingKit/STKLocalFileDataSource.m | 10 +- StreamingKit/StreamingKit/STKQueueEntry.h | 7 +- StreamingKit/StreamingKit/STKQueueEntry.m | 13 --- 13 files changed, 187 insertions(+), 60 deletions(-) create mode 100644 StreamingKit/StreamingKit/STKBufferingDataSource.h create mode 100644 StreamingKit/StreamingKit/STKBufferingDataSource.m diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index fc79b52..354fa9d 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + A1682FA318B3903900F29FEC /* STKBufferingDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1682FA218B3903900F29FEC /* STKBufferingDataSource.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 */; }; @@ -83,6 +84,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKBufferingDataSource.h; sourceTree = ""; }; + A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKBufferingDataSource.m; sourceTree = ""; }; 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; }; @@ -266,6 +269,8 @@ A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */, A1E7C4F3188D5E550010896F /* STKAutoRecoveringHTTPDataSource.h */, A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */, + A1682FA118B3903900F29FEC /* STKBufferingDataSource.h */, + A1682FA218B3903900F29FEC /* STKBufferingDataSource.m */, A1E7C4F5188D5E550010896F /* STKCoreFoundationDataSource.h */, A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */, A1E7C4F7188D5E550010896F /* STKDataSource.h */, @@ -502,6 +507,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 */, A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */, A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */, diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index bfa1e66..fcce371 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -772,8 +772,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyReadingEntry->parsedHeader = YES; currentlyReadingEntry->audioDataOffset = offset; - [currentlyReadingEntry updateAudioDataSource]; - break; } case kAudioFileStreamProperty_FileFormat: @@ -825,8 +823,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn entryToUpdate->packetBufferSize = packetBufferSize; } - [entryToUpdate updateAudioDataSource]; - [self createAudioConverter:¤tlyReadingEntry->audioStreamBasicDescription]; pthread_mutex_unlock(&playerMutex); @@ -843,8 +839,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyReadingEntry->audioDataByteCount = audioDataByteCount; - [currentlyReadingEntry updateAudioDataSource]; - break; } case kAudioFileStreamProperty_ReadyToProducePackets: @@ -1421,7 +1415,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return; } - long long seekByteOffset = currentEntry->audioDataOffset + (requestedSeekTime / self.duration) * (currentlyReadingEntry.audioDataLengthInBytes); + SInt64 seekByteOffset = currentEntry->audioDataOffset + (requestedSeekTime / self.duration) * (currentlyReadingEntry.audioDataLengthInBytes); if (seekByteOffset > currentEntry.dataSource.length - (2 * currentEntry->packetBufferSize)) { @@ -1459,7 +1453,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn AudioConverterReset(audioConverterRef); } - [currentEntry updateAudioDataSource]; [currentEntry reset]; [currentEntry.dataSource seekToOffset:seekByteOffset]; @@ -1511,7 +1504,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn // iOS will shutdown network connections if the app is backgrounded (i.e. device is locked when player is paused) // We try to reopen -- should probably add a back-off protocol in the future - long long position = currentlyReadingEntry.dataSource.position; + SInt64 position = currentlyReadingEntry.dataSource.position; + [currentlyReadingEntry.dataSource seekToOffset:position]; return; @@ -2554,7 +2548,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* { if (state == STKAudioPlayerInternalStateWaitingForData) { - int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; + SInt64 framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; if (entry->lastFrameQueued >= 0) { @@ -2569,7 +2563,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* } else if (state == STKAudioPlayerInternalStateRebuffering) { - int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToPlayAfterRebuffering; + SInt64 framesRequiredToStartPlaying = audioPlayer->framesRequiredToPlayAfterRebuffering; if (entry->lastFrameQueued >= 0) { @@ -2583,7 +2577,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* } else if (state == STKAudioPlayerInternalStateWaitingForDataAfterSeek) { - int64_t framesRequiredToStartPlaying = 1024; + SInt64 framesRequiredToStartPlaying = 1024; if (entry->lastFrameQueued >= 0) { @@ -2752,8 +2746,8 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* OSSpinLockLock(&entry->spinLock); - int64_t extraFramesPlayedNotAssigned = 0; - int64_t framesPlayedForCurrent = totalFramesCopied; + SInt64 extraFramesPlayedNotAssigned = 0; + SInt64 framesPlayedForCurrent = totalFramesCopied; if (entry->lastFrameQueued >= 0) { @@ -2781,7 +2775,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* if (newEntry != nil) { - int64_t framesPlayedForCurrent = extraFramesPlayedNotAssigned; + SInt64 framesPlayedForCurrent = extraFramesPlayedNotAssigned; OSSpinLockLock(&newEntry->spinLock); diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index cdee441..9248554 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -246,7 +246,7 @@ static void PopulateOptionsWithDefault(STKAutoRecoveringHTTPDataSourceOptions* o return NO; } --(void) seekToOffset:(long long)offset +-(void) seekToOffset:(int64_t)offset { ticksWhenLastDataReceived = GetTickCount(); diff --git a/StreamingKit/StreamingKit/STKBufferingDataSource.h b/StreamingKit/StreamingKit/STKBufferingDataSource.h new file mode 100644 index 0000000..c5cdefd --- /dev/null +++ b/StreamingKit/StreamingKit/STKBufferingDataSource.h @@ -0,0 +1,43 @@ +/********************************************************************************** + 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 withBufferSize:(int)bufferSize; +@end diff --git a/StreamingKit/StreamingKit/STKBufferingDataSource.m b/StreamingKit/StreamingKit/STKBufferingDataSource.m new file mode 100644 index 0000000..ec4b9c2 --- /dev/null +++ b/StreamingKit/StreamingKit/STKBufferingDataSource.m @@ -0,0 +1,103 @@ +/********************************************************************************** + 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" + +@interface STKBufferingDataSource() +{ +@private + int bufferSize; + uint8_t* buffer; + STKDataSource* dataSource; +} +@end + +@implementation STKBufferingDataSource + +-(id) initWithDataSource:(STKDataSource*)dataSourceIn withBufferSize:(int)bufferSizeIn; +{ + if (self = [super init]) + { + self->bufferSize = bufferSizeIn; + self->dataSource = dataSourceIn; + + self->dataSource.delegate = self.delegate; + } + + return self; +} + +-(void) dealloc +{ + self->dataSource.delegate = nil; + + free(self->buffer); +} + +-(void) createBuffer +{ + if (self->buffer == nil) + { + if (self->bufferSize == 0) + { + } + } +} + +-(SInt64) length +{ + return self->dataSource.length; +} + +-(void) seekToOffset:(SInt64)offset +{ +} + +-(void) dataSourceDataAvailable:(STKDataSource*)dataSource +{ + [self.delegate dataSourceDataAvailable:self]; +} + +-(void) dataSourceErrorOccured:(STKDataSource*)dataSource +{ + [self.delegate dataSourceErrorOccured:self]; +} + +-(void) dataSourceEof:(STKDataSource*)dataSource +{ + [self.delegate dataSourceEof:self]; +} + + +@end diff --git a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m index 4b9c7b3..1763acd 100644 --- a/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m +++ b/StreamingKit/StreamingKit/STKCoreFoundationDataSource.m @@ -120,7 +120,7 @@ static void ReadStreamCallbackProc(CFReadStreamRef stream, CFStreamEventType eve { } --(void) seekToOffset:(long long)offset +-(void) seekToOffset:(SInt64)offset { } diff --git a/StreamingKit/StreamingKit/STKDataSource.h b/StreamingKit/StreamingKit/STKDataSource.h index ee0904a..69c2961 100644 --- a/StreamingKit/StreamingKit/STKDataSource.h +++ b/StreamingKit/StreamingKit/STKDataSource.h @@ -43,15 +43,10 @@ -(void) dataSourceEof:(STKDataSource*)dataSource; @end -@protocol AudioDataSource -@property (readwrite) double averageBitRate; -@property (readwrite) long long audioDataOffset; -@end - @interface STKDataSource : NSObject -@property (readonly) long long position; -@property (readonly) long long length; +@property (readonly) SInt64 position; +@property (readonly) SInt64 length; @property (readonly) BOOL hasBytesAvailable; @property (readwrite, unsafe_unretained) id delegate; @@ -59,7 +54,7 @@ -(void) unregisterForEvents; -(void) close; --(void) seekToOffset:(long long)offset; +-(void) seekToOffset:(SInt64)offset; -(int) readIntoBuffer:(UInt8*)buffer withSize:(int)size; -(AudioFileTypeID) audioFileTypeHint; diff --git a/StreamingKit/StreamingKit/STKDataSource.m b/StreamingKit/StreamingKit/STKDataSource.m index 81c7c82..a6f4911 100644 --- a/StreamingKit/StreamingKit/STKDataSource.m +++ b/StreamingKit/StreamingKit/STKDataSource.m @@ -37,12 +37,12 @@ @implementation STKDataSource @synthesize delegate; --(long long) length +-(SInt64) length { return 0; } --(void) seekToOffset:(long long)offset +-(void) seekToOffset:(SInt64)offset { } @@ -51,7 +51,7 @@ return -1; } --(long long) position +-(SInt64) position { return 0; } diff --git a/StreamingKit/StreamingKit/STKDataSourceWrapper.m b/StreamingKit/StreamingKit/STKDataSourceWrapper.m index b0c3b81..76e1634 100644 --- a/StreamingKit/StreamingKit/STKDataSourceWrapper.m +++ b/StreamingKit/StreamingKit/STKDataSourceWrapper.m @@ -62,12 +62,12 @@ self.innerDataSource.delegate = nil; } --(long long) length +-(SInt64) length { return self.innerDataSource.length; } --(void) seekToOffset:(long long)offset +-(void) seekToOffset:(SInt64)offset { return [self.innerDataSource seekToOffset:offset]; } @@ -77,7 +77,7 @@ return [self.innerDataSource readIntoBuffer:buffer withSize:size]; } --(long long) position +-(SInt64) position { return self.innerDataSource.position; } diff --git a/StreamingKit/StreamingKit/STKHTTPDataSource.m b/StreamingKit/StreamingKit/STKHTTPDataSource.m index 367d7ac..2046b44 100644 --- a/StreamingKit/StreamingKit/STKHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.m @@ -38,9 +38,9 @@ @interface STKHTTPDataSource() { @private - long long seekStart; - long long relativePosition; - long long fileLength; + SInt64 seekStart; + SInt64 relativePosition; + SInt64 fileLength; int discontinuous; int requestSerialNumber; @@ -161,7 +161,7 @@ { if (seekStart == 0) { - fileLength = (long long)[[httpHeaders objectForKey:@"Content-Length"] integerValue]; + fileLength = (SInt64)[[httpHeaders objectForKey:@"Content-Length"] longLongValue]; } NSString* contentType = [httpHeaders objectForKey:@"Content-Type"]; @@ -204,12 +204,12 @@ [super dataAvailable]; } --(long long) position +-(SInt64) position { return seekStart + relativePosition; } --(long long) length +-(SInt64) length { return fileLength >= 0 ? fileLength : 0; } @@ -225,7 +225,7 @@ [self seekToOffset:self.position]; } --(void) seekToOffset:(long long)offset +-(void) seekToOffset:(SInt64)offset { NSRunLoop* savedEventsRunLoop = eventsRunLoop; diff --git a/StreamingKit/StreamingKit/STKLocalFileDataSource.m b/StreamingKit/StreamingKit/STKLocalFileDataSource.m index 031b4d1..aa2ed12 100644 --- a/StreamingKit/StreamingKit/STKLocalFileDataSource.m +++ b/StreamingKit/StreamingKit/STKLocalFileDataSource.m @@ -36,8 +36,8 @@ @interface STKLocalFileDataSource() { - long long position; - long long length; + SInt64 position; + SInt64 length; AudioFileTypeID audioFileTypeHint; } @property (readwrite, copy) NSString* filePath; @@ -153,12 +153,12 @@ CFReadStreamOpen(stream); } --(long long) position +-(SInt64) position { return position; } --(long long) length +-(SInt64) length { return length; } @@ -181,7 +181,7 @@ return retval; } --(void) seekToOffset:(long long)offset +-(void) seekToOffset:(SInt64)offset { CFStreamStatus status = kCFStreamStatusClosed; diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index 9033fec..78e3784 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -22,9 +22,9 @@ UInt64 audioDataByteCount; UInt32 packetBufferSize; volatile Float64 seekTime; - volatile int64_t framesQueued; - volatile int64_t framesPlayed; - volatile int64_t lastFrameQueued; + volatile SInt64 framesQueued; + volatile SInt64 framesPlayed; + volatile SInt64 lastFrameQueued; volatile int processedPacketsCount; volatile int processedPacketsSizeTotal; AudioStreamBasicDescription audioStreamBasicDescription; @@ -40,7 +40,6 @@ -(double) duration; -(Float64) progressInFrames; -(double) calculatedBitRate; --(void) updateAudioDataSource; -(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription; @end \ No newline at end of file diff --git a/StreamingKit/StreamingKit/STKQueueEntry.m b/StreamingKit/StreamingKit/STKQueueEntry.m index d4e4cbd..1e27973 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.m +++ b/StreamingKit/StreamingKit/STKQueueEntry.m @@ -56,19 +56,6 @@ return retval; } --(void) updateAudioDataSource -{ - if ([self.dataSource conformsToProtocol:@protocol(AudioDataSource)]) - { - double calculatedBitrate = [self calculatedBitRate]; - - id audioDataSource = (id)self.dataSource; - - audioDataSource.averageBitRate = calculatedBitrate; - audioDataSource.audioDataOffset = audioDataOffset; - } -} - -(double) duration { if (self->sampleRate <= 0)