From 97ff03f184f817b3a52182d81f566a26b9e48709 Mon Sep 17 00:00:00 2001 From: vspader Date: Thu, 18 Oct 2007 02:33:12 +0000 Subject: [PATCH] Preliminary gapless playback. --- Plugins/MAD/MAD.xcodeproj/project.pbxproj | 2 + Plugins/MAD/MADDecoder.h | 17 +- Plugins/MAD/MADDecoder.m | 825 +++++++++++----------- 3 files changed, 421 insertions(+), 423 deletions(-) diff --git a/Plugins/MAD/MAD.xcodeproj/project.pbxproj b/Plugins/MAD/MAD.xcodeproj/project.pbxproj index 0792a7e85..0eb130dae 100644 --- a/Plugins/MAD/MAD.xcodeproj/project.pbxproj +++ b/Plugins/MAD/MAD.xcodeproj/project.pbxproj @@ -312,6 +312,7 @@ GCC_PREFIX_HEADER = MAD_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; + OTHER_CFLAGS = "-Wall"; PRODUCT_NAME = MAD; WRAPPER_EXTENSION = bundle; ZERO_LINK = YES; @@ -340,6 +341,7 @@ GCC_PREFIX_HEADER = MAD_Prefix.pch; INFOPLIST_FILE = Info.plist; INSTALL_PATH = "$(HOME)/Library/Bundles"; + OTHER_CFLAGS = "-Wall"; PRODUCT_NAME = MAD; WRAPPER_EXTENSION = bundle; }; diff --git a/Plugins/MAD/MADDecoder.h b/Plugins/MAD/MADDecoder.h index da4f13ff5..bdcb7ccf6 100644 --- a/Plugins/MAD/MADDecoder.h +++ b/Plugins/MAD/MADDecoder.h @@ -16,14 +16,12 @@ #define INPUT_BUFFER_SIZE 5*8192 - @interface MADDecoder : NSObject { struct mad_stream _stream; struct mad_frame _frame; struct mad_synth _synth; - mad_timer_t _timer; - mad_timer_t _duration; + unsigned char _inputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD]; unsigned char *_outputBuffer; int _outputAvailable; @@ -31,15 +29,16 @@ id _source; - BOOL _seekSkip; - BOOL _firstFrame; //For gapless playback of mp3s - BOOL _gapless; - long _currentFrame; - int _startPadding; - int _endPadding; + BOOL _foundXingHeader; + BOOL _foundLAMEHeader; + + long _samplesDecoded; + long _totalSamples; + uint16_t _startPadding; + uint16_t _endPadding; int channels; int bitsPerSample; diff --git a/Plugins/MAD/MADDecoder.m b/Plugins/MAD/MADDecoder.m index d1a56d6b7..b1a365607 100644 --- a/Plugins/MAD/MADDecoder.m +++ b/Plugins/MAD/MADDecoder.m @@ -8,282 +8,289 @@ #import "MADDecoder.h" #undef HAVE_CONFIG_H -#import @implementation MADDecoder -/*XING FUN*/ +#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) -#define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g') -#define INFO_MAGIC (('I' << 24) | ('n' << 16) | ('f' << 8) | 'o') -#define LAME_MAGIC (('L' << 24) | ('A' << 16) | ('M' << 8) | 'E') +// From vbrheadersdk: +// ======================================== +// A Xing header may be present in the ancillary +// data field of the first frame of an mp3 bitstream +// The Xing header (optionally) contains +// frames total number of audio frames in the bitstream +// bytes total number of bytes in the bitstream +// toc table of contents -struct xing +// toc (table of contents) gives seek points +// for random access +// the ith entry determines the seek point for +// i-percent duration +// seek point in bytes = (toc[i]/256.0) * total_bitstream_bytes +// e.g. half duration seek point = (toc[50]/256.0) * total_bitstream_bytes + +#define FRAMES_FLAG 0x0001 +#define BYTES_FLAG 0x0002 +#define TOC_FLAG 0x0004 +#define VBR_SCALE_FLAG 0x0008 + + +//Scan file quickly +- (BOOL)scanFile { - long flags; /* valid fields (see below) */ - unsigned long frames; /* total number of frames */ - unsigned long bytes; /* total number of bytes */ - unsigned char toc[100]; /* 100-point seek table */ - long scale; /* ?? */ -}; - -struct lame -{ - long flags; - int start_padding; - int end_padding; -}; - -enum -{ - XING_FRAMES = 0x00000001L, - XING_BYTES = 0x00000002L, - XING_TOC = 0x00000004L, - XING_SCALE = 0x00000008L -}; - -int lame_parse(struct lame *lame, struct mad_bitptr *ptr, unsigned int bitlen) -{ - unsigned long magic; - unsigned long garbage; + BOOL inputEOF = NO; - magic = mad_bit_read(ptr, 32); //4 bytes - - if (magic != LAME_MAGIC) - return 0; - - mad_bit_skip(ptr, 17*8); //17 bytes skipped - garbage = mad_bit_read(ptr, 24); //3 bytes - - lame->start_padding = (garbage >> 12) & 0x000FFF; - lame->end_padding = garbage & 0x000FFF; - - NSLog(@"Padding; %i, %i", lame->start_padding, lame->end_padding); - - return 1; -} - -int xing_parse(struct xing *xing, struct mad_bitptr *ptr, unsigned int bitlen) -{ - xing->flags = 0; - unsigned long magic; - - if (bitlen < 64) - return 0; - - magic = mad_bit_read(ptr, 32); - if (magic != INFO_MAGIC && magic != XING_MAGIC) - return 0; - - xing->flags = mad_bit_read(ptr, 32); - bitlen -= 64; - - if (xing->flags & XING_FRAMES) { - if (bitlen < 32) - return 0; - - xing->frames = mad_bit_read(ptr, 32); - bitlen -= 32; - } - - if (xing->flags & XING_BYTES) { - if (bitlen < 32) - return 0; - - xing->bytes = mad_bit_read(ptr, 32); - bitlen -= 32; - } - - if (xing->flags & XING_TOC) { - int i; - - if (bitlen < 800) - return 0; - - for (i = 0; i < 100; ++i) - xing->toc[i] = mad_bit_read(ptr, 8); - - bitlen -= 800; - } - - if (xing->flags & XING_SCALE) { - if (bitlen < 32) - return 0; - - xing->scale = mad_bit_read(ptr, 32); - bitlen -= 32; - } - - return 1; -} - -int parse_headers(struct xing *xing, struct lame *lame, struct mad_bitptr ptr, unsigned int bitlen) -{ - xing->flags = 0; - lame->flags = 0; - - if (xing_parse(xing, &ptr, bitlen)) - { - lame_parse(lame, &ptr, bitlen); - - return 1; - } - - return 0; -} - - -- (BOOL)scanFileFast:(BOOL)fast useXing:(BOOL)use_xing -{ - const int BUFFER_SIZE = 16*1024; - const int N_AVERAGE_FRAMES = 10; - struct mad_stream stream; - struct mad_header header; - struct mad_frame frame; /* to read xing data */ - struct xing xing; - struct lame lame; - int remainder = 0; - int data_used = 0; - int len = 0; - int tagsize = 0; - int frames = 0; - unsigned char buffer[BUFFER_SIZE]; - BOOL has_xing = NO; - BOOL vbr = NO; + struct mad_frame frame; + + int framesDecoded = 0; + int bytesToRead, bytesRemaining; + int samplesPerMPEGFrame = 0; + + int id3_length = 0; mad_stream_init (&stream); - mad_header_init (&header); mad_frame_init (&frame); - bitrate = 0; - _duration = mad_timer_zero; - - frames = 0; - [_source seek:0 whence:SEEK_END]; _fileSize = [_source tell]; [_source seek:0 whence:SEEK_SET]; - BOOL done = NO; - - while (!done) - { - remainder = stream.bufend - stream.next_frame; + for (;;) { + if(NULL == stream.buffer || MAD_ERROR_BUFLEN == stream.error) { + if(stream.next_frame) { + bytesRemaining = stream.bufend - stream.next_frame; - memcpy (buffer, stream.this_frame, remainder); - len = [_source read:buffer + remainder amount:BUFFER_SIZE - remainder]; - - if (len <= 0) - break; - - mad_stream_buffer (&stream, buffer, len + remainder); - - while (1) - { - if (mad_header_decode (&header, &stream) == -1) + memmove(_inputBuffer, stream.next_frame, bytesRemaining); + + bytesToRead = INPUT_BUFFER_SIZE - bytesRemaining; + } + else { + bytesToRead = INPUT_BUFFER_SIZE, + bytesRemaining = 0; + } + + // Read raw bytes from the MP3 file + int bytesRead = [_source read:_inputBuffer + bytesRemaining amount: bytesToRead]; + + if (bytesRead == 0) { - if (stream.error == MAD_ERROR_BUFLEN) - { - break; + memset(_inputBuffer + bytesRemaining + bytesRead, 0, MAD_BUFFER_GUARD); + bytesRead += MAD_BUFFER_GUARD; + inputEOF = YES; + } + + mad_stream_buffer(&stream, _inputBuffer, bytesRead + bytesRemaining); + stream.error = MAD_ERROR_NONE; + } + + if (mad_frame_decode(&frame, &stream) == -1) + { + if (MAD_RECOVERABLE(stream.error)) + { + // Prevent ID3 tags from reporting recoverable frame errors + const uint8_t *buffer = stream.this_frame; + unsigned buflen = stream.bufend - stream.this_frame; + + if(10 <= buflen && 0x49 == buffer[0] && 0x44 == buffer[1] && 0x33 == buffer[2]) { + id3_length = (((buffer[6] & 0x7F) << (3 * 7)) | ((buffer[7] & 0x7F) << (2 * 7)) | + ((buffer[8] & 0x7F) << (1 * 7)) | ((buffer[9] & 0x7F) << (0 * 7))); + + // Add 10 bytes for ID3 header + id3_length += 10; + + mad_stream_skip(&stream, id3_length); } - if (!MAD_RECOVERABLE (stream.error)) - { - break; - } - if (stream.error == MAD_ERROR_LOSTSYNC) - { - /* ignore LOSTSYNC due to ID3 tags */ - tagsize = id3_tag_query (stream.this_frame, - stream.bufend - - stream.this_frame); - if (tagsize > 0) - { - mad_stream_skip (&stream, tagsize); - continue; - } - - } - + continue; } - frames++; - - mad_timer_add (&_duration, header.duration); - data_used += stream.next_frame - stream.this_frame; - if (frames == 1) + else if (stream.error == MAD_ERROR_BUFLEN && inputEOF) { - /* most of these *should* remain constant */ - bitrate = header.bitrate; - frequency = header.samplerate; - channels = MAD_NCHANNELS(&header); - - if (use_xing) - { - frame.header = header; - if (mad_frame_decode(&frame, &stream) == -1) - continue; - - if (parse_headers(&xing, &lame, stream.anc_ptr, stream.anc_bitlen)) - { - has_xing = YES; - vbr = YES; - - frames = xing.frames; - mad_timer_multiply (&_duration, frames); - - bitrate = 8.0 * xing.bytes / mad_timer_count(_duration, MAD_UNITS_SECONDS); - done = YES; - break; - } - } - + break; + } + else if (stream.error == MAD_ERROR_BUFLEN) + { + continue; } else { - /* perhaps we have a VBR file */ - if (bitrate != header.bitrate) - vbr = YES; - if (vbr) - bitrate += header.bitrate; - } - - if ((!vbr || (vbr && !has_xing)) && fast && frames >= N_AVERAGE_FRAMES) - { - float frame_size = ((double)data_used) / N_AVERAGE_FRAMES; - frames = (_fileSize - tagsize) / frame_size; - - _duration.seconds /= N_AVERAGE_FRAMES; - _duration.fraction /= N_AVERAGE_FRAMES; - mad_timer_multiply (&_duration, frames); - - done = YES; + //NSLog(@"Unrecoverable error: %s", mad_stream_errorstr(&stream)); break; } } - if (stream.error != MAD_ERROR_BUFLEN) + framesDecoded++; + + if (framesDecoded == 1) + { + frequency = frame.header.samplerate; + channels = MAD_NCHANNELS(&frame.header); + + if(MAD_FLAG_LSF_EXT & frame.header.flags || MAD_FLAG_MPEG_2_5_EXT & frame.header.flags) { + switch(frame.header.layer) { + case MAD_LAYER_I: samplesPerMPEGFrame = 384; break; + case MAD_LAYER_II: samplesPerMPEGFrame = 1152; break; + case MAD_LAYER_III: samplesPerMPEGFrame = 576; break; + } + } + else { + switch(frame.header.layer) { + case MAD_LAYER_I: samplesPerMPEGFrame = 384; break; + case MAD_LAYER_II: samplesPerMPEGFrame = 1152; break; + case MAD_LAYER_III: samplesPerMPEGFrame = 1152; break; + } + } + + unsigned ancillaryBitsRemaining = stream.anc_bitlen; + + if(32 > ancillaryBitsRemaining) + continue; + + uint32_t magic = mad_bit_read(&stream.anc_ptr, 32); + ancillaryBitsRemaining -= 32; + + if('Xing' == magic || 'Info' == magic) { + unsigned i; + uint32_t flags = 0, frames = 0, bytes = 0, vbrScale = 0; + + if(32 > ancillaryBitsRemaining) + continue; + + flags = mad_bit_read(&stream.anc_ptr, 32); + ancillaryBitsRemaining -= 32; + + // 4 byte value containing total frames + if(FRAMES_FLAG & flags) { + if(32 > ancillaryBitsRemaining) + continue; + + frames = mad_bit_read(&stream.anc_ptr, 32); + ancillaryBitsRemaining -= 32; + + // Determine number of samples, discounting encoder delay and padding + // Our concept of a frame is the same as CoreAudio's- one sample across all channels + _totalSamples = frames * samplesPerMPEGFrame; + //NSLog(@"TOTAL READ FROM XING"); + } + + // 4 byte value containing total bytes + if(BYTES_FLAG & flags) { + if(32 > ancillaryBitsRemaining) + continue; + + bytes = mad_bit_read(&stream.anc_ptr, 32); + ancillaryBitsRemaining -= 32; + } + + // 100 bytes containing TOC information + if(TOC_FLAG & flags) { + if(8 * 100 > ancillaryBitsRemaining) + continue; + + for(i = 0; i < 100; ++i) + /*_xingTOC[i] = */ mad_bit_read(&stream.anc_ptr, 8); + + ancillaryBitsRemaining -= (8* 100); + } + + // 4 byte value indicating encoded vbr scale + if(VBR_SCALE_FLAG & flags) { + if(32 > ancillaryBitsRemaining) + continue; + + vbrScale = mad_bit_read(&stream.anc_ptr, 32); + ancillaryBitsRemaining -= 32; + } + + framesDecoded = frames; + + _foundXingHeader = YES; + + // Loook for the LAME header next + // http://gabriel.mp3-tech.org/mp3infotag.html + if(32 > ancillaryBitsRemaining) + continue; + magic = mad_bit_read(&stream.anc_ptr, 32); + + ancillaryBitsRemaining -= 32; + + if('LAME' == magic) { + + if(LAME_HEADER_SIZE > ancillaryBitsRemaining) + continue; + + /*unsigned char versionString [5 + 1]; + memset(versionString, 0, 6);*/ + + for(i = 0; i < 5; ++i) + /*versionString[i] =*/ mad_bit_read(&stream.anc_ptr, 8); + + /*uint8_t infoTagRevision =*/ mad_bit_read(&stream.anc_ptr, 4); + /*uint8_t vbrMethod =*/ mad_bit_read(&stream.anc_ptr, 4); + + /*uint8_t lowpassFilterValue =*/ mad_bit_read(&stream.anc_ptr, 8); + + /*float peakSignalAmplitude =*/ mad_bit_read(&stream.anc_ptr, 32); + /*uint16_t radioReplayGain =*/ mad_bit_read(&stream.anc_ptr, 16); + /*uint16_t audiophileReplayGain =*/ mad_bit_read(&stream.anc_ptr, 16); + + /*uint8_t encodingFlags =*/ mad_bit_read(&stream.anc_ptr, 4); + /*uint8_t athType =*/ mad_bit_read(&stream.anc_ptr, 4); + + /*uint8_t lameBitrate =*/ mad_bit_read(&stream.anc_ptr, 8); + + _startPadding = mad_bit_read(&stream.anc_ptr, 12); + _endPadding = mad_bit_read(&stream.anc_ptr, 12); + + _startPadding += 528 + 1; //MDCT/filterbank delay + _totalSamples += 528 + 1; + + /*uint8_t misc =*/ mad_bit_read(&stream.anc_ptr, 8); + + /*uint8_t mp3Gain =*/ mad_bit_read(&stream.anc_ptr, 8); + + /*uint8_t unused =*/mad_bit_read(&stream.anc_ptr, 2); + /*uint8_t surroundInfo =*/ mad_bit_read(&stream.anc_ptr, 3); + /*uint16_t presetInfo =*/ mad_bit_read(&stream.anc_ptr, 11); + + /*uint32_t musicGain =*/ mad_bit_read(&stream.anc_ptr, 32); + + /*uint32_t musicCRC =*/ mad_bit_read(&stream.anc_ptr, 32); + + /*uint32_t tagCRC =*/ mad_bit_read(&stream.anc_ptr, 32); + + ancillaryBitsRemaining -= LAME_HEADER_SIZE; + + _foundLAMEHeader = YES; + break; + } + } + } + else + { + _totalSamples = (double)frame.header.samplerate * ((_fileSize - id3_length) / (frame.header.bitrate / 8.0)); + //NSLog(@"Guestimating total samples"); + break; - } + } + } - if (vbr && !has_xing) - bitrate = bitrate / frames; + //Need to make sure this is correct + bitrate = (_fileSize - id3_length) / (length * 1000.0); mad_frame_finish (&frame); - mad_header_finish (&header); mad_stream_finish (&stream); - length = mad_timer_count(_duration, MAD_UNITS_MILLISECONDS); - - bitrate /= 1000; + //Need to fix this too. + length = 1000.0 * (_totalSamples / frequency); bitsPerSample = 16; - + [_source seek:0 whence:SEEK_SET]; - + [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; - - return frames != 0; + + return YES; } @@ -297,14 +304,14 @@ int parse_headers(struct xing *xing, struct lame *lame, struct mad_bitptr ptr, u mad_stream_init(&_stream); mad_frame_init(&_frame); mad_synth_init(&_synth); - mad_timer_reset(&_timer); _firstFrame = YES; + //NSLog(@"OPEN: %i", _firstFrame); if ([_source seekable]) { - return [self scanFileFast:YES useXing:YES]; + return [self scanFile]; } - + return YES; } @@ -315,26 +322,8 @@ int parse_headers(struct xing *xing, struct lame *lame, struct mad_bitptr ptr, u static inline signed int scale (mad_fixed_t sample) { BOOL hard_limit = YES; -// BOOL replaygain = NO; - /* replayGain by SamKR */ - double scale = -1; -/* if (replaygain) - { - if (file_info->has_replaygain) - { - scale = file_info->replaygain_track_scale; - if (file_info->replaygain_album_scale != -1 - && (scale==-1 || ! xmmsmad_config.replaygain.track_mode)) - { - scale = file_info->replaygain_album_scale; - } - } - if (scale == -1) - scale = xmmsmad_config.replaygain.default_scale; - } -*/ - if (scale == -1) - scale = 1.0; + + double scale = 1.0; /* hard-limit (clipping-prevention) */ if (hard_limit) @@ -360,7 +349,7 @@ static inline signed int scale (mad_fixed_t sample) /* round */ /* add half of the bits_to_loose range to round */ sample += (1L << (n_bits_to_loose - 1)); - + /* clip */ /* make sure we are between -1 and 1 */ if (sample >= MAD_F_ONE) @@ -380,175 +369,194 @@ static inline signed int scale (mad_fixed_t sample) * to get the sign bit. */ sample >>= n_bits_to_loose; - + return sample; } - (void)writeOutput { - unsigned int nsamples; - mad_fixed_t const *left_ch, *right_ch; + unsigned int startingSample = 0; + unsigned int sampleCount = _synth.pcm.length; + + //NSLog(@"Position: %li/%li", _samplesDecoded, _totalSamples); + //NSLog(@"<%i, %i>", _startPadding, _endPadding); -// if (_outputAvailable) { -// NSLog(@"Losing Output: %i", _outputAvailable); -// } - nsamples = _synth.pcm.length; - left_ch = _synth.pcm.samples[0]; - right_ch = _synth.pcm.samples[1]; - _outputAvailable = nsamples * channels * (bitsPerSample/8); + //NSLog(@"Counts: %i, %i", startingSample, sampleCount); + if (_foundLAMEHeader) { + if (_samplesDecoded < _startPadding) { + //NSLog(@"Skipping start."); + startingSample = _startPadding - _samplesDecoded; + } + + if (_samplesDecoded > _totalSamples - _endPadding + startingSample) { + //NSLog(@"End of file. Not writing."); + return; + } + if (_samplesDecoded + (sampleCount - startingSample) > _totalSamples - _endPadding) { + sampleCount = _totalSamples - _endPadding - _samplesDecoded + startingSample; + //NSLog(@"End of file. %li", _totalSamples - _endPadding - _samplesDecoded); + } + + if (startingSample > sampleCount) { + _samplesDecoded += _synth.pcm.length; + //NSLog(@"Skipping entire sample"); + return; + } + + } + + //NSLog(@"Revised: %i, %i", startingSample, sampleCount); + + _samplesDecoded += _synth.pcm.length; + + _outputAvailable = (sampleCount - startingSample) * channels * (bitsPerSample/8); + if (_outputBuffer) free(_outputBuffer); - + _outputBuffer = (unsigned char *) malloc (_outputAvailable * sizeof (char)); - unsigned char *outputPtr = _outputBuffer; - - int i; - for (i=0; i < nsamples; i++) - { - signed short sample; - /* output sample(s) in 16-bit signed little-endian PCM */ - sample = scale(left_ch[i]); - *(outputPtr++) = sample>>8; - *(outputPtr++) = sample & 0xff; - - if (channels == 2) - { - sample = scale(right_ch[i]); - *(outputPtr++) = sample>>8; - *(outputPtr++) = sample & 0xff; - } - } + unsigned int i, j; + unsigned int stride = channels * 2; + for (j = 0; j < channels; j++) + { + /* output sample(s) in 16-bit signed little-endian PCM */ + mad_fixed_t const *channel = _synth.pcm.samples[j]; + unsigned char *outputPtr = _outputBuffer + (j * 2); + + for (i = startingSample; i < sampleCount; i++) + { + signed short sample = scale(channel[i]); + + outputPtr[0] = sample>>8; + outputPtr[1] = sample & 0xff; + outputPtr += stride; + } + } } - (int)fillBuffer:(void *)buf ofSize:(UInt32)size { - int remainder; - int len; - BOOL eof = NO; - int amountToCopy = size; - int amountRemaining = size; + int inputToRead; + int inputRemaining; + BOOL inputEOF = NO; - if (amountToCopy > _outputAvailable) - amountToCopy = _outputAvailable; - - if (amountToCopy) { - memcpy(buf, _outputBuffer, amountToCopy); - memmove(_outputBuffer, _outputBuffer + amountToCopy, _outputAvailable - amountToCopy); - - amountRemaining -= amountToCopy; - _outputAvailable -= amountToCopy; - } - - while (amountRemaining > 0 && !eof) { + int bytesRead = 0; + + for (;;) + { + int bytesRemaining = size - bytesRead; + int bytesToCopy = (_outputAvailable > bytesRemaining ? bytesRemaining : _outputAvailable); + + memcpy(buf + bytesRead, _outputBuffer, bytesToCopy); + bytesRead += bytesToCopy; + + if (bytesToCopy != _outputAvailable) { + memmove(_outputBuffer, _outputBuffer + bytesToCopy, _outputAvailable - bytesToCopy); + } + + _outputAvailable -= bytesToCopy; + + if (bytesRead == size) + break; + if (_stream.buffer == NULL || _stream.error == MAD_ERROR_BUFLEN) { - if (!_seekSkip) + if (_stream.next_frame != NULL) { - remainder = _stream.bufend - _stream.next_frame; - if (remainder) - memmove(_inputBuffer, _stream.this_frame, remainder); + inputRemaining = _stream.bufend - _stream.next_frame; + + memmove(_inputBuffer, _stream.next_frame, inputRemaining); + + inputToRead = INPUT_BUFFER_SIZE - inputRemaining; } else { - remainder = 0; + inputToRead = INPUT_BUFFER_SIZE; + inputRemaining = 0; } - - len = [_source read:_inputBuffer+remainder amount:INPUT_BUFFER_SIZE-remainder]; - if (len <= 0) + + int inputRead = [_source read:_inputBuffer + inputRemaining amount:INPUT_BUFFER_SIZE - inputRemaining]; + if (inputRead == 0) { - eof = YES; + memset(_inputBuffer + inputRemaining + inputRead, 0, MAD_BUFFER_GUARD); + inputRead += MAD_BUFFER_GUARD; + inputEOF = YES; + } + + mad_stream_buffer(&_stream, _inputBuffer, inputRead + inputRemaining); + _stream.error = MAD_ERROR_NONE; + //NSLog(@"Read stream."); + } + + if (mad_frame_decode(&_frame, &_stream) == -1) { + if (MAD_RECOVERABLE (_stream.error)) + { + const uint8_t *buffer = _stream.this_frame; + unsigned buflen = _stream.bufend - _stream.this_frame; + uint32_t id3_length = 0; + + //No longer need ID3Tag framework + if(10 <= buflen && 0x49 == buffer[0] && 0x44 == buffer[1] && 0x33 == buffer[2]) { + id3_length = (((buffer[6] & 0x7F) << (3 * 7)) | ((buffer[7] & 0x7F) << (2 * 7)) | + ((buffer[8] & 0x7F) << (1 * 7)) | ((buffer[9] & 0x7F) << (0 * 7))); + + // Add 10 bytes for ID3 header + id3_length += 10; + + mad_stream_skip(&_stream, id3_length); + } + + //NSLog(@"recoverable error"); + continue; + } + else if (MAD_ERROR_BUFLEN == _stream.error && inputEOF) + { + //NSLog(@"EOF"); break; } - - len += remainder; - if (len < MAD_BUFFER_GUARD) { - int i; - for (i = len; i < MAD_BUFFER_GUARD; i++) - _inputBuffer[i] = 0; - len = MAD_BUFFER_GUARD; - } - - mad_stream_buffer(&_stream, _inputBuffer, len); - _stream.error = 0; - - if (_seekSkip) + else if (MAD_ERROR_BUFLEN == _stream.error) { - int skip = 2; - do - { - if (mad_frame_decode (&_frame, &_stream) == 0) - { - mad_timer_add (&_timer, _frame.header.duration); - if (--skip == 0) - mad_synth_frame (&_synth, &_frame); - } - else if (!MAD_RECOVERABLE (_stream.error)) - break; - } while (skip); - - _seekSkip = NO; + //NSLog(@"Bufferlen"); + continue; } - - } - if (mad_frame_decode(&_frame, &_stream) == -1) { - if (!MAD_RECOVERABLE (_stream.error)) + else { - if(_stream.error==MAD_ERROR_BUFLEN) { - continue; - } - - eof = YES; + //NSLog(@"Unrecoverable stream error: %s", mad_stream_errorstr(&_stream)); + break; } - - if (_stream.error == MAD_ERROR_LOSTSYNC) - { - // ignore LOSTSYNC due to ID3 tags - int tagsize = id3_tag_query (_stream.this_frame, - _stream.bufend - - _stream.this_frame); - if (tagsize > 0) - { - mad_stream_skip (&_stream, tagsize); - } - } - - continue; } - - if (_firstFrame && ![_source seekable]) { - frequency = _frame.header.samplerate; - channels = MAD_NCHANNELS(&_frame.header); - bitsPerSample = 16; - - [self willChangeValueForKey:@"properties"]; - [self didChangeValueForKey:@"properties"]; - - _firstFrame = NO; - } - - mad_timer_add (&_timer, _frame.header.duration); + //NSLog(@"Decoded buffer."); mad_synth_frame (&_synth, &_frame); + //NSLog(@"first frame: %i", _firstFrame); + if (_firstFrame) + { + _firstFrame = NO; + + if (![_source seekable]) { + frequency = _frame.header.samplerate; + channels = MAD_NCHANNELS(&_frame.header); + bitsPerSample = 16; + + [self willChangeValueForKey:@"properties"]; + [self didChangeValueForKey:@"properties"]; + } + //NSLog(@"FIRST FRAME!!! %i %i", _foundXingHeader, _foundLAMEHeader); + if (_foundXingHeader) { + //NSLog(@"Skipping xing header."); + continue; + } + } [self writeOutput]; - amountToCopy = amountRemaining; - if (amountToCopy > _outputAvailable) { - amountToCopy = _outputAvailable; - } - if (amountRemaining < amountToCopy) { - amountToCopy = amountRemaining; - } - - memcpy(((char *)buf)+(size-amountRemaining), _outputBuffer, amountToCopy); - memmove(_outputBuffer, _outputBuffer + amountToCopy, _outputAvailable - amountToCopy); - amountRemaining -= amountToCopy; - _outputAvailable -= amountToCopy; + //NSLog(@"Wrote output"); } - - return (size - amountRemaining); + + //NSLog(@"Read: %i/%i", bytesRead, size); + return bytesRead; } - (void)close @@ -559,7 +567,7 @@ static inline signed int scale (mad_fixed_t sample) [_source release]; _source = nil; } - + if (_outputBuffer) { free(_outputBuffer); @@ -573,29 +581,18 @@ static inline signed int scale (mad_fixed_t sample) - (double)seekToTime:(double)milliseconds { - unsigned long new_position; - unsigned long seconds = milliseconds/1000.0; - unsigned long total_seconds = mad_timer_count(_duration, MAD_UNITS_SECONDS); - - if (seconds > total_seconds) - seconds = total_seconds; + if (milliseconds > length) + milliseconds = length; - mad_timer_set(&_timer, seconds, 0, 0); - new_position = ((double) seconds / (double) total_seconds) * _fileSize; - + unsigned long new_position = (milliseconds / length) * _fileSize; [_source seek:new_position whence:SEEK_SET]; - - //mad_stream_sync(&_stream); - _stream.error = MAD_ERROR_BUFLEN; - _stream.sync = 0; - _outputAvailable = 0; - - mad_frame_mute(&_frame); - mad_synth_mute(&_synth); - - _seekSkip = YES; - return seconds*1000.0; + mad_stream_buffer(&_stream, NULL, 0); + + //Gapless busted after seek. Mp3 just doesn't have sample-accurate seeking. Maybe xing toc? + _samplesDecoded = (milliseconds / length) * _totalSamples; + + return milliseconds; } - (NSDictionary *)properties