Changeset 1043

Show
Ignore:
Timestamp:
10/30/07 18:54:13 (15 months ago)
Author:
stephen_booth
Message:

Factored LoopableRegionDecoder? from ScheduledAudioRegion?

Location:
trunk
Files:
3 added
11 modified

Legend:

Unmodified
Added
Removed
  • trunk/Audio/AudioPlayer.m

    r1039 r1043  
    2424#import "AudioLibrary.h" 
    2525#import "AudioStream.h" 
    26 #import "AudioDecoder.h" 
    2726 
    2827#include <CoreServices/CoreServices.h> 
     
    244243                NSLog(@"AudioPlayer error: Unable to reset AUGraph AudioUnits: %i", err); 
    245244         
    246         AudioDecoder *decoder = [AudioDecoder audioDecoderForURL:[stream valueForKey:StreamURLKey] error:error]; 
     245        id <AudioDecoderMethods> decoder = [stream decoder:error]; 
    247246        if(nil == decoder) 
    248247                return NO; 
     
    300299        } 
    301300         
    302         // Determine the subrange of the stream to play, if any 
    303         NSNumber                                *streamStartingFrame    = [stream valueForKey:StreamStartingFrameKey]; 
    304         NSNumber                                *streamFrameCount               = [stream valueForKey:StreamFrameCountKey]; 
    305         ScheduledAudioRegion    *region                                 = nil; 
    306          
    307         // For reasons related to SQLite (see http://sqlite.org/nulls.html), -1 is used instead of NULL 
    308         if(-1 == [streamStartingFrame intValue] && -1 == [streamFrameCount intValue]) 
    309                 region = [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder]; 
    310         else if(-1 == [streamFrameCount intValue]) 
    311                 region = [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startingFrame:[streamStartingFrame longLongValue]]; 
    312         else 
    313                 region = [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startingFrame:[streamStartingFrame longLongValue] frameCount:[streamFrameCount unsignedIntValue]]; 
    314  
    315301        // Schedule the region for playback, and start scheduling audio slices 
    316         [[self scheduler] scheduleAudioRegion:region]; 
     302        [[self scheduler] scheduleAudioRegion:[ScheduledAudioRegion scheduledAudioRegionWithDecoder:decoder]]; 
    317303        [[self scheduler] startScheduling]; 
    318304 
     
    329315                return NO; 
    330316 
    331         AudioDecoder *decoder = [AudioDecoder audioDecoderForURL:[stream valueForKey:StreamURLKey] error:error]; 
     317        id <AudioDecoderMethods> decoder = [stream decoder:error]; 
    332318        if(nil == decoder) 
    333319                return NO; 
     
    345331        if(NO == formatsMatch || NO == channelLayoutsMatch) 
    346332                return NO; 
    347          
    348         // Determine the subrange of the stream to play, if any 
    349         NSNumber                                *streamStartingFrame    = [stream valueForKey:StreamStartingFrameKey]; 
    350         NSNumber                                *streamFrameCount               = [stream valueForKey:StreamFrameCountKey]; 
    351         ScheduledAudioRegion    *region                                 = nil; 
    352          
    353         // For reasons related to SQLite (see http://sqlite.org/nulls.html), -1 is used instead of NULL 
    354         if(-1 == [streamStartingFrame intValue] && -1 == [streamFrameCount intValue]) 
    355                 region = [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder]; 
    356         else if(-1 == [streamFrameCount intValue]) 
    357                 region = [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startingFrame:[streamStartingFrame longLongValue]]; 
    358         else 
    359                 region = [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startingFrame:[streamStartingFrame longLongValue] frameCount:[streamFrameCount unsignedIntValue]]; 
    360          
     333 
    361334        // The formats and channel layouts match, so schedule the region for playback 
    362         [[self scheduler] scheduleAudioRegion:region]; 
     335        [[self scheduler] scheduleAudioRegion:[ScheduledAudioRegion scheduledAudioRegionWithDecoder:decoder]]; 
    363336 
    364337        return YES; 
     
    393366- (BOOL) streamSupportsSeeking 
    394367{ 
    395         return ([self hasValidStream] && [[[self scheduler] regionBeingRendered] supportsSeeking]); 
     368        return ([self hasValidStream] && [[[[self scheduler] regionBeingRendered] decoder] supportsSeeking]); 
    396369} 
    397370 
     
    449422        if(kAudioTimeStampSampleTimeValid & timeStamp.mFlags) { 
    450423                SInt64 lastRenderedFrame = [self startingFrame] + timeStamp.mSampleTime - _regionStartingFrame; 
    451                 [self setStartingFrame:[[[self scheduler] regionBeingScheduled] seekToFrame:lastRenderedFrame]]; 
     424                [self setStartingFrame:[[[[self scheduler] regionBeingScheduled] decoder] seekToFrame:lastRenderedFrame]]; 
    452425                [self setPlayingFrame:0]; 
    453426        } 
     
    476449         
    477450        if(nil != currentRegion && [[currentRegion decoder] supportsSeeking]) { 
    478                 SInt64 totalFrames              = [currentRegion totalFrames]; 
    479                 SInt64 currentFrame             = [currentRegion currentFrame]; 
     451                SInt64 totalFrames              = [[currentRegion decoder] totalFrames]; 
     452                SInt64 currentFrame             = [[currentRegion decoder] currentFrame]; 
    480453                SInt64 desiredFrame             = currentFrame + (SInt64)(seconds * [[currentRegion decoder] format].mSampleRate); 
    481454                 
     
    492465         
    493466        if(nil != currentRegion && [[currentRegion decoder] supportsSeeking]) { 
    494                 SInt64 currentFrame             = [currentRegion currentFrame]; 
     467                SInt64 currentFrame             = [[currentRegion decoder] currentFrame]; 
    495468                SInt64 desiredFrame             = currentFrame - (SInt64)(seconds * [[currentRegion decoder] format].mSampleRate); 
    496469                 
     
    507480         
    508481        if(nil != currentRegion && [[currentRegion decoder] supportsSeeking]) { 
    509                 SInt64 totalFrames = [currentRegion totalFrames];                
     482                SInt64 totalFrames = [[currentRegion decoder] totalFrames];              
    510483                [self setCurrentFrame:totalFrames - 1]; 
    511484        } 
     
    627600                currentFrame = [self totalFrames ] - 1; 
    628601         
    629         [self setStartingFrame:[[[self scheduler] regionBeingScheduled] seekToFrame:currentFrame + _regionStartingFrame]]; 
     602        [self setStartingFrame:[[[[self scheduler] regionBeingScheduled] decoder] seekToFrame:currentFrame + _regionStartingFrame]]; 
    630603        [self setPlayingFrame:0]; 
    631604 
     
    691664#endif 
    692665         
    693         [self setTotalFrames:[region totalFrames]]; 
     666        [self setTotalFrames:[[region decoder] totalFrames]]; 
    694667         
    695668        [self willChangeValueForKey:@"hasValidStream"]; 
  • trunk/Audio/AudioScheduler.m

    r1017 r1043  
    2121#import "AudioScheduler.h" 
    2222#import "ScheduledAudioRegion.h" 
    23 #import "AudioDecoder.h" 
    2423 
    2524// ======================================== 
     
    9493 
    9594        // Determine if region rendering is complete 
    96         if([[scheduler regionBeingRendered] atEnd] && [[scheduler regionBeingRendered] framesRendered] == [[scheduler regionBeingRendered] framesScheduled]) { 
     95        if([[[scheduler regionBeingRendered] decoder] currentFrame] == [[[scheduler regionBeingRendered] decoder] totalFrames]  
     96           && [[scheduler regionBeingRendered] framesRendered] == [[scheduler regionBeingRendered] framesScheduled]) { 
    9797 
    9898                // Notify the delegate 
  • trunk/Audio/Decoders/AudioDecoder.h

    r884 r1043  
    2222#include <CoreAudio/CoreAudioTypes.h> 
    2323 
     24#import "AudioDecoderMethods.h" 
     25 
    2426// ======================================== 
    2527// Error Codes 
     
    3436}; 
    3537 
    36 // A decoder reads audio data in some format and provides it as 32-bit float non-interleaved PCM 
    37 @interface AudioDecoder : NSObject 
     38// Superclass for an audio decoder covering an entire file 
     39@interface AudioDecoder : NSObject <AudioDecoderMethods> 
    3840{ 
    3941        NSURL                                                   *_url;                          // The location of the stream to be decoded 
     
    4547} 
    4648 
    47 + (AudioDecoder *) audioDecoderForURL:(NSURL *)url error:(NSError **)error; 
     49// Return an AudioDecoder of the appropriate class 
     50+ (AudioDecoder *) decoderWithURL:(NSURL *)url error:(NSError **)error; 
    4851 
    4952// Designated initializer 
     
    5255// The stream this decoder will process 
    5356- (NSURL *) URL; 
    54  
    55 // The type of PCM data provided by this AudioDecoder 
    56 - (AudioStreamBasicDescription) format; 
    57 - (NSString *) formatDescription; 
    58  
    59 // The layout of the channels this AudioDecoder provides 
    60 - (AudioChannelLayout) channelLayout; 
    61 - (NSString *) channelLayoutDescription; 
    62  
    63 // Attempt to read frameCount frames of audio, returning the actual number of frames read 
    64 - (UInt32) readAudio:(AudioBufferList *)bufferList frameCount:(UInt32)frameCount; 
    65  
    66 // The native (PCM) format of the source file 
    67 - (AudioStreamBasicDescription) sourceFormat; 
    68 - (NSString *) sourceFormatDescription; 
    69  
    70 // ======================================== 
    71 // Input audio information 
    72 // ======================================== 
    73 - (SInt64) totalFrames; 
    74 - (SInt64) currentFrame; 
    75 - (SInt64) framesRemaining; 
    76  
    77 - (BOOL) supportsSeeking; 
    78 - (SInt64) seekToFrame:(SInt64)frame; 
    79  
    8057@end 
  • trunk/Audio/Decoders/AudioDecoder.m

    r884 r1043  
    4747} 
    4848 
    49 + (AudioDecoder *) audioDecoderForURL:(NSURL *)url error:(NSError **)error 
     49+ (AudioDecoder *) decoderWithURL:(NSURL *)url error:(NSError **)error 
    5050{ 
    5151        NSParameterAssert(nil != url); 
     
    166166         
    167167        if((self = [super init])) { 
    168                 _url = [url retain]; 
     168                _url = [url copy]; 
    169169 
    170170                // Canonical Core Audio format 
     
    257257} 
    258258 
    259 - (SInt64)                      totalFrames                                                             { return -1; } 
    260 - (SInt64)                      currentFrame                                                    { return -1; } 
     259- (SInt64)                      totalFrames                                                             { return 0; } 
     260- (SInt64)                      currentFrame                                                    { return 0; } 
    261261- (SInt64)                      framesRemaining                                                 { return ([self totalFrames] - [self currentFrame]); } 
    262262 
  • trunk/Audio/ScheduledAudioRegion.h

    r1025 r1043  
    2020 
    2121#import <Cocoa/Cocoa.h> 
    22  
    2322#include <AudioToolbox/AudioToolbox.h> 
    2423 
    25 @class AudioDecoder; 
     24#import "AudioDecoderMethods.h" 
    2625 
    27 // This class is a bit of an amalgam- it contains all the logic for accessing a subrange of  
    28 // audio from a Decoder, but also the buffers and associated internal state that  
    29 // AudioScheduler needs to use an object of this class. 
     26// A class encapsulating an AudioDecoder and the buffers and associated internal state that  
     27// AudioScheduler needs to use a decoder 
    3028@interface ScheduledAudioRegion : NSObject 
    3129{ 
    32         AudioDecoder                    *_decoder; 
    33         AudioTimeStamp                  _startTime; 
    34         SInt64                                  _startingFrame; 
    35         UInt32                                  _frameCount; 
    36         unsigned                                _loopCount; 
     30        id <AudioDecoderMethods>        _decoder; 
    3731         
    38         UInt32                                  _framesReadInCurrentLoop; 
    39         SInt64                                  _totalFramesRead; 
    40         unsigned                                _completedLoops; 
     32        AudioTimeStamp                          _startTime; 
     33         
     34        ScheduledAudioSlice                     *_sliceBuffer; 
    4135 
    42         BOOL                                    _atEnd; 
    43          
    44         // ======================================== 
    45         // AudioScheduler members 
    46         ScheduledAudioSlice             *_sliceBuffer; 
     36        unsigned                                        _numberSlices; 
     37        unsigned                                        _framesPerSlice; 
    4738 
    48         unsigned                                _numberSlices; 
    49         unsigned                                _framesPerSlice; 
    50  
    51         SInt64                                  _framesScheduled; 
    52         SInt64                                  _framesRendered; 
     39        SInt64                                          _framesScheduled; 
     40        SInt64                                          _framesRendered; 
    5341}        
    5442 
    55 // ======================================== 
    56 // Creation 
    57 // ======================================== 
    58 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder; 
     43+ (ScheduledAudioRegion *) scheduledAudioRegionWithDecoder:(id <AudioDecoderMethods>)decoder; 
     44+ (ScheduledAudioRegion *) scheduledAudioRegionWithDecoder:(id <AudioDecoderMethods>)decoder startTime:(AudioTimeStamp)startTime; 
    5945 
    60 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startingFrame:(SInt64)startingFrame; 
    61 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount; 
    62 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount loopCount:(unsigned)loopCount; 
     46- (id) initWithDecoder:(id <AudioDecoderMethods>)decoder; 
     47- (id) initWithDecoder:(id <AudioDecoderMethods>)decoder startTime:(AudioTimeStamp)startTime; 
    6348 
    64 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime; 
    65 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime startingFrame:(SInt64)startingFrame; 
    66 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount; 
    67 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount loopCount:(unsigned)loopCount; 
    68  
    69 // ======================================== 
    70 // Properties 
    71 // ======================================== 
    72 - (AudioDecoder *) decoder; 
    73 - (void) setDecoder:(AudioDecoder *)decoder; 
     49- (id <AudioDecoderMethods>) decoder; 
     50- (void) setDecoder:(id <AudioDecoderMethods>)decoder; 
    7451 
    7552- (AudioTimeStamp) startTime; 
    7653- (void) setStartTime:(AudioTimeStamp)startTime; 
    7754 
    78 - (SInt64) startingFrame; 
    79 - (void) setStartingFrame:(SInt64)startingFrame; 
    80  
    81 - (UInt32) frameCount; 
    82 - (void) setFrameCount:(UInt32)frameCount; 
    83  
    84 - (unsigned) loopCount; 
    85 - (void) setLoopCount:(unsigned)loopCount; 
    86  
    87 // ======================================== 
    88 // Audio access 
    89 // ======================================== 
    90 - (unsigned) completedLoops; 
    91  
    92 - (SInt64) totalFrames; 
    93 - (SInt64) currentFrame; 
    94 - (SInt64) framesRemaining; 
    95  
    96 - (BOOL) supportsSeeking; 
    97 - (SInt64) seekToFrame:(SInt64)frame; 
    98  
    9955- (SInt64) framesScheduled; 
    10056- (SInt64) framesRendered; 
    10157 
    102 @end 
    103  
    104 // ======================================== 
    105 // AudioScheduler methods 
    106 // ======================================== 
    107 @interface ScheduledAudioRegion (AudioSchedulerMethods) 
    10858- (unsigned) numberOfSlicesInBuffer; 
    10959- (unsigned) numberOfFramesPerSlice; 
     
    11363- (void) clearSlice:(unsigned)sliceIndex; 
    11464 
    115 - (void) reset; 
    116  
    11765- (void) clearFramesScheduled; 
    11866- (void) clearFramesRendered; 
    11967 
    120 - (UInt32) readAudio:(AudioBufferList *)bufferList frameCount:(UInt32)frameCount; 
    12168- (UInt32) readAudioInSlice:(unsigned)sliceIndex; 
    12269 
     
    12673- (void) scheduledAdditionalFrames:(UInt32)frameCount; 
    12774- (void) renderedAdditionalFrames:(UInt32)frameCount; 
    128  
    129 - (BOOL) atEnd; 
    13075@end 
  • trunk/Audio/ScheduledAudioRegion.m

    r1025 r1043  
    130130#pragma mark Creation 
    131131 
    132 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder 
    133 { 
    134         AudioTimeStamp startTime = { 0 }; 
    135          
    136         startTime.mFlags                = kAudioTimeStampSampleTimeValid; 
    137         startTime.mSampleTime   = 0; 
    138          
    139         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:0 frameCount:[decoder totalFrames] loopCount:0]; 
    140 } 
    141  
    142 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startingFrame:(SInt64)startingFrame 
    143 { 
    144         AudioTimeStamp startTime = { 0 }; 
    145          
    146         startTime.mFlags                = kAudioTimeStampSampleTimeValid; 
    147         startTime.mSampleTime   = 0; 
    148          
    149         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:startingFrame frameCount:([decoder totalFrames] - startingFrame) loopCount:0]; 
    150 } 
    151  
    152 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount 
    153 { 
    154         AudioTimeStamp startTime = { 0 }; 
    155          
    156         startTime.mFlags                = kAudioTimeStampSampleTimeValid; 
    157         startTime.mSampleTime   = 0; 
    158          
    159         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:startingFrame frameCount:frameCount loopCount:0]; 
    160 } 
    161  
    162 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount loopCount:(unsigned)loopCount 
    163 { 
    164         AudioTimeStamp startTime = { 0 }; 
    165          
    166         startTime.mFlags                = kAudioTimeStampSampleTimeValid; 
    167         startTime.mSampleTime   = 0; 
    168          
    169         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:startingFrame frameCount:frameCount loopCount:loopCount]; 
    170 } 
    171  
    172 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime 
    173 { 
    174         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:0 frameCount:[decoder totalFrames] loopCount:0]; 
    175 } 
    176  
    177 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime startingFrame:(SInt64)startingFrame 
    178 { 
    179         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:startingFrame frameCount:([decoder totalFrames] - startingFrame) loopCount:0]; 
    180 } 
    181  
    182 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount 
    183 { 
    184         return [ScheduledAudioRegion scheduledAudioRegionForDecoder:decoder startTime:startTime startingFrame:startingFrame frameCount:frameCount loopCount:0]; 
    185 } 
    186  
    187 + (ScheduledAudioRegion *) scheduledAudioRegionForDecoder:(AudioDecoder *)decoder startTime:(AudioTimeStamp)startTime startingFrame:(SInt64)startingFrame frameCount:(unsigned)frameCount loopCount:(unsigned)loopCount 
    188 { 
    189         ScheduledAudioRegion *result = [[ScheduledAudioRegion alloc] init]; 
    190          
    191         [result setDecoder:decoder]; 
    192         [result setStartTime:startTime]; 
    193         [result setStartingFrame:startingFrame]; 
    194         [result setFrameCount:frameCount]; 
    195         [result setLoopCount:loopCount]; 
    196          
    197         if(0 != [result startingFrame]) 
    198                 [result reset]; 
    199          
    200         return [result autorelease]; 
     132+ (ScheduledAudioRegion *) scheduledAudioRegionWithDecoder:(id <AudioDecoderMethods>)decoder 
     133{ 
     134        return [[[ScheduledAudioRegion alloc] initWithDecoder:decoder] autorelease]; 
     135} 
     136 
     137+ (ScheduledAudioRegion *) scheduledAudioRegionWithDecoder:(id <AudioDecoderMethods>)decoder startTime:(AudioTimeStamp)startTime 
     138{ 
     139        return [[[ScheduledAudioRegion alloc] initWithDecoder:decoder startTime:startTime] autorelease]; 
     140} 
     141 
     142- (id) initWithDecoder:(id <AudioDecoderMethods>)decoder 
     143{ 
     144        if((self = [super init])) { 
     145                AudioTimeStamp startTime = { 0 }; 
     146                 
     147                startTime.mFlags                = kAudioTimeStampSampleTimeValid; 
     148                startTime.mSampleTime   = 0; 
     149                 
     150                [self setDecoder:decoder]; 
     151                [self setStartTime:startTime]; 
     152        } 
     153        return self; 
     154} 
     155 
     156- (id) initWithDecoder:(id <AudioDecoderMethods>)decoder startTime:(AudioTimeStamp)startTime 
     157{ 
     158        if((self = [super init])) { 
     159                [self setDecoder:decoder]; 
     160                [self setStartTime:startTime]; 
     161        } 
     162        return self; 
    201163} 
    202164 
     
    221183} 
    222184 
    223 - (AudioDecoder *)      decoder                                                                 { return [[_decoder retain] autorelease]; } 
    224  
    225 - (void) setDecoder:(AudioDecoder *)decoder 
     185- (id <AudioDecoderMethods>)    decoder                                         { return [[_decoder retain] autorelease]; } 
     186 
     187- (void) setDecoder:(id <AudioDecoderMethods>)decoder 
    226188{ 
    227189        NSParameterAssert(nil != decoder); 
     
    233195} 
    234196 
    235 - (unsigned)            loopCount                                                               { return _loopCount; } 
    236 - (void)                        setLoopCount:(unsigned)loopCount                { _loopCount = loopCount; } 
    237  
    238 - (SInt64)                      startingFrame                                                   { return _startingFrame; } 
    239  
    240 - (void) setStartingFrame:(SInt64)startingFrame 
    241 { 
    242         NSParameterAssert(0 <= startingFrame); 
    243          
    244         _startingFrame = startingFrame; 
    245 } 
    246  
    247 - (UInt32)                      frameCount                                                      { return _frameCount; } 
    248  
    249 - (void) setFrameCount:(UInt32)frameCount 
    250 { 
    251         NSParameterAssert(0 < frameCount); 
    252  
    253         _frameCount = frameCount; 
    254 } 
    255  
    256 #pragma mark Playback 
    257  
    258 - (unsigned)            completedLoops                                                  { return _completedLoops; } 
    259  
    260 - (SInt64)                      totalFrames                                                             { return (([self loopCount] + 1) * [self frameCount]); } 
    261 - (SInt64)                      currentFrame                                                    { return _totalFramesRead; } 
    262 - (SInt64)                      framesRemaining                                                 { return ([self totalFrames] - [self currentFrame]); } 
    263  
    264 - (BOOL)                        supportsSeeking                                                 { return [[self decoder] supportsSeeking]; } 
    265  
    266 - (SInt64) seekToFrame:(SInt64)frame 
    267 { 
    268         NSParameterAssert(0 <= frame && frame < [self totalFrames]); 
    269          
    270         _completedLoops                         = frame / [self frameCount]; 
    271         _framesReadInCurrentLoop        = frame % [self frameCount]; 
    272         _totalFramesRead                        = frame; 
    273         _atEnd                                          = NO; 
    274  
    275         [[self decoder] seekToFrame:[self startingFrame] + _framesReadInCurrentLoop]; 
    276          
    277         return [self currentFrame]; 
    278 } 
    279  
    280197- (SInt64)                      framesScheduled                                                 { return _framesScheduled; } 
    281198- (SInt64)                      framesRendered                                                  { return _framesRendered; } 
    282  
    283 - (NSString *) description 
    284 { 
    285         return [NSString stringWithFormat:@"%@ (%qi, %qi)", [self decoder], [self framesRendered], [self framesScheduled]]; 
    286 } 
    287  
    288 @end 
    289  
    290 @implementation ScheduledAudioRegion (AudioSchedulerMethods) 
    291199 
    292200- (unsigned) numberOfSlicesInBuffer 
     
    326234} 
    327235 
    328 - (void) reset 
    329 { 
    330         [[self decoder] seekToFrame:[self startingFrame]]; 
    331          
    332         _framesReadInCurrentLoop        = 0; 
    333         _totalFramesRead                        = 0; 
    334         _completedLoops                         = 0; 
    335         _atEnd                                          = NO; 
    336 } 
    337  
    338236- (void)                        clearFramesScheduled                                    { _framesScheduled = 0; } 
    339237- (void)                        clearFramesRendered                                             { _framesRendered = 0; } 
    340238 
    341 - (UInt32) readAudio:(AudioBufferList *)bufferList frameCount:(UInt32)frameCount 
    342 { 
    343         NSParameterAssert(NULL != bufferList); 
    344         NSParameterAssert(0 < frameCount); 
    345          
    346         if([self loopCount] < [self completedLoops]) 
    347                 return 0; 
    348  
    349         UInt32  framesRemaining         = [self startingFrame] + [self frameCount] - [[self decoder] currentFrame]; 
    350         UInt32  framesToRead            = (frameCount < framesRemaining ? frameCount : framesRemaining); 
    351         UInt32  framesRead                      = 0; 
    352          
    353         if(0 < framesToRead) 
    354                 framesRead = [[self decoder] readAudio:bufferList frameCount:framesToRead]; 
    355          
    356         _framesReadInCurrentLoop        += framesRead; 
    357         _totalFramesRead                        += framesRead; 
    358          
    359         if([self frameCount] == _framesReadInCurrentLoop || (0 == framesRead && 0 != framesToRead)) { 
    360                 ++_completedLoops; 
    361                 _framesReadInCurrentLoop = 0; 
    362  
    363                 if([self loopCount] < [self completedLoops]) 
    364                         _atEnd = YES; 
    365                 else 
    366                         [[self decoder] seekToFrame:[self startingFrame]]; 
    367         } 
    368          
    369         return framesRead;       
    370 } 
    371  
    372239- (UInt32) readAudioInSlice:(unsigned)sliceIndex 
    373240{ 
    374241        NSParameterAssert(sliceIndex < [self numberOf