| 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]; |
| | 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 | |