Changeset 1010

Show
Ignore:
Timestamp:
10/17/07 09:46:17 (15 months ago)
Author:
stephen_booth
Message:

Added an option for sample-accurate seeking

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/Audio/Decoders/MPEGDecoder.m

    r1009 r1010  
    2727 
    2828#define INPUT_BUFFER_SIZE       (5 * 8192) 
     29#define SEEK_BUFFER_SIZE        1024 
    2930#define LAME_HEADER_SIZE        ((8 * 5) + 4 + 4 + 8 + 32 + 16 + 16 + 4 + 4 + 8 + 12 + 12 + 8 + 8 + 2 + 3 + 11 + 32 + 32 + 32) 
    3031 
     
    8283@interface MPEGDecoder (Private) 
    8384- (BOOL) scanFile; 
     85- (SInt64) seekToFrameApproximately:(SInt64)frame; 
     86- (SInt64) seekToFrameAccurately:(SInt64)frame; 
    8487@end 
    8588 
     
    175178- (BOOL)                        supportsSeeking                                 { return YES; } 
    176179 
    177         // FIXME: Seeking breaks gapless playback for the stream 
    178180- (SInt64) seekToFrame:(SInt64)frame 
    179181{ 
    180         double  fraction        = (double)frame / [self totalFrames]; 
    181         long    seekPoint       = 0; 
    182          
    183         // If a Xing header was found, interpolate in TOC 
    184         if(_foundXingHeader) { 
    185                 double          percent         = 100 * fraction; 
    186                 unsigned        firstIndex      = percent; 
    187                  
    188                 if(99 < firstIndex) 
    189                         firstIndex = 99; 
    190                  
    191                 double firstOffset      = _xingTOC[firstIndex]; 
    192                 double secondOffset     = 256; 
    193                  
    194                 if(99 > firstIndex) 
    195                         secondOffset = _xingTOC[firstIndex + 1];; 
    196                          
    197                         double x = firstOffset + (secondOffset - firstOffset) * (percent - firstIndex); 
    198                         seekPoint = (long)((1.0 / 256.0) * x * _fileBytes);  
    199         } 
     182        if([[NSUserDefaults standardUserDefaults] boolForKey:@"accurateMP3Seeking"]) 
     183                return [self seekToFrameAccurately:frame]; 
    200184        else 
    201                 seekPoint = (long)_fileBytes * fraction; 
    202          
    203         int result = fseek(_file, seekPoint, SEEK_SET); 
    204         if(0 == result) { 
    205                 mad_stream_buffer(&_mad_stream, NULL, 0); 
    206                  
    207                 // Reset frame count to prevent early termination of playback 
    208                 _mpegFramesDecoded                      = 0; 
    209                 _samplesDecoded                         = 0; 
    210                 _samplesToSkipInNextFrame       = 0; 
    211                  
    212                 _currentFrame                           = frame; 
    213         } 
    214          
    215         // Right now it's only possible to return an approximation of the audio frame 
    216         return (-1 == result ? -1 : frame); 
     185                return [self seekToFrameApproximately:frame]; 
    217186} 
    218187 
     
    686655} 
    687656 
     657- (SInt64) seekToFrameApproximately:(SInt64)frame 
     658{ 
     659        double  fraction        = (double)frame / [self totalFrames]; 
     660        long    seekPoint       = 0; 
     661         
     662        // If a Xing header was found, interpolate in TOC 
     663        if(_foundXingHeader) { 
     664                double          percent         = 100 * fraction; 
     665                unsigned        firstIndex      = percent; 
     666                 
     667                if(99 < firstIndex) 
     668                        firstIndex = 99; 
     669                 
     670                double firstOffset      = _xingTOC[firstIndex]; 
     671                double secondOffset     = 256; 
     672                 
     673                if(99 > firstIndex) 
     674                        secondOffset = _xingTOC[firstIndex + 1];; 
     675                         
     676                        double x = firstOffset + (secondOffset - firstOffset) * (percent - firstIndex); 
     677                        seekPoint = (long)((1.0 / 256.0) * x * _fileBytes);  
     678        } 
     679        else 
     680                seekPoint = (long)_fileBytes * fraction; 
     681         
     682        int result = fseek(_file, seekPoint, SEEK_SET); 
     683        if(0 == result) { 
     684                mad_stream_buffer(&_mad_stream, NULL, 0); 
     685                 
     686                // Reset frame count to prevent early termination of playback 
     687                _mpegFramesDecoded                      = 0; 
     688                _samplesDecoded                         = 0; 
     689                _samplesToSkipInNextFrame       = 0; 
     690                 
     691                _currentFrame                           = frame; 
     692        } 
     693         
     694        // Right now it's only possible to return an approximation of the audio frame 
     695        return (-1 == result ? -1 : frame); 
     696} 
     697 
     698- (SInt64) seekToFrameAccurately:(SInt64)frame 
     699{ 
     700        NSParameterAssert(0 <= frame && frame < [self totalFrames]); 
     701         
     702        // Brute-force seeking is necessary since frame-accurate seeking is required 
     703         
     704        // To seek to a frame earlier in the file, rewind to the beginning 
     705        if([self currentFrame] > frame) { 
     706                if(-1 == fseek(_file, 0, SEEK_SET)) 
     707                        return -1; 
     708                 
     709                // Reset decoder parameters 
     710                _mpegFramesDecoded                      = 0; 
     711                _currentFrame                           = 0; 
     712                _samplesToSkipInNextFrame       = 0; 
     713                _samplesDecoded                         = 0; 
     714                 
     715                // Zero the buffers 
     716                unsigned i; 
     717                for(i = 0; i < _bufferList->mNumberBuffers; ++i) 
     718                        _bufferList->mBuffers[i].mDataByteSize = 0; 
     719                 
     720                mad_stream_buffer(&_mad_stream, NULL, 0); 
     721        } 
     722         
     723        // Allocate a buffer list to use for seeking 
     724        AudioBufferList *bufferList = calloc(sizeof(AudioBufferList) + (sizeof(AudioBuffer) * (_format.mChannelsPerFrame - 1)), 1); 
     725        NSAssert(NULL != bufferList, @"Unable to allocate memory"); 
     726         
     727        bufferList->mNumberBuffers = _format.mChannelsPerFrame; 
     728         
     729        unsigned i; 
     730        for(i = 0; i < bufferList->mNumberBuffers; ++i) { 
     731                bufferList->mBuffers[i].mData = calloc(SEEK_BUFFER_SIZE, sizeof(float)); 
     732                NSAssert(NULL != bufferList->mBuffers[i].mData, @"Unable to allocate memory"); 
     733                 
     734                bufferList->mBuffers[i].mNumberChannels = 1; 
     735        } 
     736         
     737        // Decode until the desired frame is reached             
     738        for(;;) { 
     739                UInt32 framesRemaining  = frame - [self currentFrame]; 
     740                UInt32 framesToRead             = (SEEK_BUFFER_SIZE > framesRemaining ? framesRemaining : SEEK_BUFFER_SIZE);; 
     741                 
     742                if(0 == framesRemaining) 
     743                        break; 
     744                 
     745                UInt32 framesRead = [self readAudio:bufferList frameCount:framesToRead]; 
     746                if(0 == framesRead) 
     747                        break; 
     748        } 
     749         
     750        for(i = 0; i < bufferList->mNumberBuffers; ++i) 
     751                free(bufferList->mBuffers[i].mData), bufferList->mBuffers[i].mData = NULL;       
     752        free(bufferList), bufferList = NULL; 
     753         
     754        return [self currentFrame]; 
     755} 
     756 
    688757@end