Preliminary gapless playback.

CQTexperiment
vspader 2007-10-18 02:33:12 +00:00
parent 0e2dcf5014
commit 97ff03f184
3 changed files with 421 additions and 423 deletions

View File

@ -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;
};

View File

@ -16,14 +16,12 @@
#define INPUT_BUFFER_SIZE 5*8192
@interface MADDecoder : NSObject <CogDecoder>
{
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<CogSource> _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;

View File

@ -8,282 +8,289 @@
#import "MADDecoder.h"
#undef HAVE_CONFIG_H
#import <ID3Tag/id3tag.h>
@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