2005-06-02 18:16:43 +00:00
|
|
|
//
|
2007-02-24 20:36:27 +00:00
|
|
|
// FlacDecoder.m
|
2005-06-02 18:16:43 +00:00
|
|
|
// zyVorbis
|
|
|
|
//
|
|
|
|
// Created by Vincent Spader on 1/25/05.
|
2005-07-02 21:02:06 +00:00
|
|
|
// Copyright 2005 Vincent Spader All rights reserved.
|
2005-06-02 18:16:43 +00:00
|
|
|
//
|
|
|
|
|
2007-02-24 20:36:27 +00:00
|
|
|
#import "FlacDecoder.h"
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
#import "HTTPSource.h"
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-12 15:16:59 +00:00
|
|
|
extern void grabbag__cuesheet_emit(NSString **out, const FLAC__StreamMetadata *cuesheet, const char *file_reference);
|
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
@implementation FlacDecoder
|
2022-02-07 10:06:51 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
FLAC__StreamDecoderReadStatus ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte blockBuffer[], size_t *bytes, void *client_data) {
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2022-02-07 05:49:27 +00:00
|
|
|
long bytesRead = [[flacDecoder source] read:blockBuffer amount:*bytes];
|
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
if(bytesRead < 0) {
|
2022-02-07 05:49:27 +00:00
|
|
|
*bytes = 0;
|
2007-03-04 21:32:03 +00:00
|
|
|
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
2022-02-07 05:49:27 +00:00
|
|
|
} else if(bytesRead == 0) {
|
|
|
|
*bytes = 0;
|
2007-03-04 21:32:03 +00:00
|
|
|
[flacDecoder setEndOfStream:YES];
|
|
|
|
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
|
2022-02-07 05:49:27 +00:00
|
|
|
} else {
|
|
|
|
*bytes = bytesRead;
|
2007-03-04 21:32:03 +00:00
|
|
|
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
FLAC__StreamDecoderSeekStatus SeekCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) {
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
if(![[flacDecoder source] seek:absolute_byte_offset whence:SEEK_SET])
|
|
|
|
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
|
|
|
else
|
|
|
|
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
FLAC__StreamDecoderTellStatus TellCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) {
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2007-03-04 21:32:03 +00:00
|
|
|
|
|
|
|
off_t pos;
|
|
|
|
if((pos = [[flacDecoder source] tell]) < 0)
|
|
|
|
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
|
|
|
|
else {
|
|
|
|
*absolute_byte_offset = (FLAC__uint64)pos;
|
|
|
|
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
FLAC__bool EOFCallback(const FLAC__StreamDecoder *decoder, void *client_data) {
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
return (FLAC__bool)[flacDecoder endOfStream];
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
FLAC__StreamDecoderLengthStatus LengthCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) {
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
if([[flacDecoder source] seekable]) {
|
2007-03-04 21:32:03 +00:00
|
|
|
long currentPos = [[flacDecoder source] tell];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
[[flacDecoder source] seek:0 whence:SEEK_END];
|
|
|
|
*stream_length = [[flacDecoder source] tell];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
[[flacDecoder source] seek:currentPos whence:SEEK_SET];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
2022-02-07 05:49:27 +00:00
|
|
|
} else {
|
2007-03-04 21:32:03 +00:00
|
|
|
*stream_length = 0;
|
|
|
|
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const sampleblockBuffer[], void *client_data) {
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
if(flacDecoder->abortFlag)
|
|
|
|
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
|
|
|
|
|
2022-02-08 03:18:45 +00:00
|
|
|
uint32_t channels = frame->header.channels;
|
|
|
|
uint32_t bitsPerSample = frame->header.bits_per_sample;
|
|
|
|
uint32_t frequency = frame->header.sample_rate;
|
|
|
|
|
|
|
|
if(channels != flacDecoder->channels ||
|
|
|
|
bitsPerSample != flacDecoder->bitsPerSample ||
|
|
|
|
frequency != flacDecoder->frequency) {
|
|
|
|
if(channels != flacDecoder->channels) {
|
|
|
|
flacDecoder->channelConfig = 0;
|
|
|
|
}
|
|
|
|
flacDecoder->channels = channels;
|
|
|
|
flacDecoder->bitsPerSample = bitsPerSample;
|
|
|
|
flacDecoder->frequency = frequency;
|
|
|
|
[flacDecoder willChangeValueForKey:@"properties"];
|
|
|
|
[flacDecoder didChangeValueForKey:@"properties"];
|
|
|
|
}
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
void *blockBuffer = [flacDecoder blockBuffer];
|
2007-02-27 23:56:52 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
int8_t *alias8;
|
2007-02-27 23:56:52 +00:00
|
|
|
int16_t *alias16;
|
|
|
|
int32_t *alias32;
|
|
|
|
int sample, channel;
|
2022-02-07 05:49:27 +00:00
|
|
|
int32_t audioSample;
|
|
|
|
|
|
|
|
switch(frame->header.bits_per_sample) {
|
|
|
|
case 8:
|
|
|
|
// Interleave the audio (no need for byte swapping)
|
|
|
|
alias8 = blockBuffer;
|
|
|
|
for(sample = 0; sample < frame->header.blocksize; ++sample) {
|
|
|
|
for(channel = 0; channel < frame->header.channels; ++channel) {
|
|
|
|
*alias8++ = (int8_t)sampleblockBuffer[channel][sample];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 16:
|
|
|
|
// Interleave the audio, converting to big endian byte order
|
|
|
|
alias16 = blockBuffer;
|
|
|
|
for(sample = 0; sample < frame->header.blocksize; ++sample) {
|
|
|
|
for(channel = 0; channel < frame->header.channels; ++channel) {
|
|
|
|
*alias16++ = (int16_t)OSSwapHostToBigInt16((int16_t)sampleblockBuffer[channel][sample]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 24:
|
|
|
|
// Interleave the audio (no need for byte swapping)
|
|
|
|
alias8 = blockBuffer;
|
|
|
|
for(sample = 0; sample < frame->header.blocksize; ++sample) {
|
|
|
|
for(channel = 0; channel < frame->header.channels; ++channel) {
|
|
|
|
audioSample = sampleblockBuffer[channel][sample];
|
|
|
|
*alias8++ = (int8_t)(audioSample >> 16);
|
|
|
|
*alias8++ = (int8_t)(audioSample >> 8);
|
|
|
|
*alias8++ = (int8_t)audioSample;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 32:
|
|
|
|
// Interleave the audio, converting to big endian byte order
|
|
|
|
alias32 = blockBuffer;
|
|
|
|
for(sample = 0; sample < frame->header.blocksize; ++sample) {
|
|
|
|
for(channel = 0; channel < frame->header.channels; ++channel) {
|
|
|
|
*alias32++ = OSSwapHostToBigInt32(sampleblockBuffer[channel][sample]);
|
|
|
|
}
|
|
|
|
}
|
2007-02-27 23:56:52 +00:00
|
|
|
default:
|
2022-02-07 05:49:27 +00:00
|
|
|
// Time for some nearest byte padding up to 32
|
|
|
|
alias8 = blockBuffer;
|
|
|
|
int sampleSize = frame->header.bits_per_sample;
|
|
|
|
int sampleBit;
|
|
|
|
for(sample = 0; sample < frame->header.blocksize; ++sample) {
|
|
|
|
for(channel = 0; channel < frame->header.channels; ++channel) {
|
|
|
|
int32_t sampleExtended = sampleblockBuffer[channel][sample];
|
|
|
|
for(sampleBit = sampleSize - 8; sampleBit >= -8; sampleBit -= 8) {
|
|
|
|
if(sampleBit >= 0)
|
|
|
|
*alias8++ = (uint8_t)((sampleExtended >> sampleBit) & 0xFF);
|
|
|
|
else
|
|
|
|
*alias8++ = (uint8_t)((sampleExtended << -sampleBit) & 0xFF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2007-02-27 23:56:52 +00:00
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
[flacDecoder setBlockBufferFrames:frame->header.blocksize];
|
2005-06-02 18:16:43 +00:00
|
|
|
|
|
|
|
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2013-10-11 14:25:41 +00:00
|
|
|
// This callback is only called for STREAMINFO blocks
|
2022-02-07 05:49:27 +00:00
|
|
|
void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
|
|
|
|
// Some flacs observed in the wild have multiple STREAMINFO metadata blocks,
|
|
|
|
// of which only first one has sane values, so only use values from the first STREAMINFO
|
|
|
|
// to determine stream format (this seems to be consistent with flac spec: http://flac.sourceforge.net/format.html)
|
2016-05-05 20:05:39 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
if(!flacDecoder->hasStreamInfo && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
|
2022-02-07 05:49:27 +00:00
|
|
|
flacDecoder->channels = metadata->data.stream_info.channels;
|
2022-02-08 03:18:45 +00:00
|
|
|
flacDecoder->channelConfig = 0;
|
2022-02-07 05:49:27 +00:00
|
|
|
flacDecoder->frequency = metadata->data.stream_info.sample_rate;
|
|
|
|
flacDecoder->bitsPerSample = metadata->data.stream_info.bits_per_sample;
|
|
|
|
|
|
|
|
flacDecoder->totalFrames = metadata->data.stream_info.total_samples;
|
|
|
|
|
|
|
|
flacDecoder->hasStreamInfo = YES;
|
|
|
|
}
|
2022-02-07 10:06:51 +00:00
|
|
|
|
2022-02-15 03:54:08 +00:00
|
|
|
if(metadata->type == FLAC__METADATA_TYPE_CUESHEET && !flacDecoder->cuesheetFound) {
|
|
|
|
flacDecoder->cuesheetFound = YES;
|
|
|
|
|
2022-02-12 15:16:59 +00:00
|
|
|
NSString *_cuesheet;
|
|
|
|
grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]);
|
|
|
|
|
|
|
|
if(![_cuesheet isEqual:flacDecoder->cuesheet]) {
|
|
|
|
flacDecoder->cuesheet = _cuesheet;
|
|
|
|
if(![flacDecoder->source seekable]) {
|
|
|
|
[flacDecoder willChangeValueForKey:@"metadata"];
|
|
|
|
[flacDecoder didChangeValueForKey:@"metadata"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(metadata->type == FLAC__METADATA_TYPE_PICTURE) {
|
|
|
|
NSData *_albumArt = [NSData dataWithBytes:metadata->data.picture.data length:metadata->data.picture.data_length];
|
|
|
|
if(![_albumArt isEqual:flacDecoder->albumArt]) {
|
|
|
|
flacDecoder->albumArt = _albumArt;
|
|
|
|
if(![flacDecoder->source seekable]) {
|
|
|
|
[flacDecoder willChangeValueForKey:@"metadata"];
|
|
|
|
[flacDecoder didChangeValueForKey:@"metadata"];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-07 10:06:51 +00:00
|
|
|
if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
|
2022-02-09 21:44:50 +00:00
|
|
|
NSString *_artist = flacDecoder->artist;
|
2022-02-12 15:16:59 +00:00
|
|
|
NSString *_albumartist = flacDecoder->albumartist;
|
|
|
|
NSString *_album = flacDecoder->album;
|
2022-02-09 21:44:50 +00:00
|
|
|
NSString *_title = flacDecoder->title;
|
2022-02-12 15:16:59 +00:00
|
|
|
NSString *_genre = flacDecoder->genre;
|
|
|
|
NSNumber *_year = flacDecoder->year;
|
|
|
|
NSNumber *_track = flacDecoder->track;
|
|
|
|
NSNumber *_disc = flacDecoder->disc;
|
|
|
|
float _replayGainAlbumGain = flacDecoder->replayGainAlbumGain;
|
|
|
|
float _replayGainAlbumPeak = flacDecoder->replayGainAlbumPeak;
|
|
|
|
float _replayGainTrackGain = flacDecoder->replayGainTrackGain;
|
|
|
|
float _replayGainTrackPeak = flacDecoder->replayGainTrackPeak;
|
|
|
|
NSString *_cuesheet = flacDecoder->cuesheet;
|
2022-02-09 21:44:50 +00:00
|
|
|
const FLAC__StreamMetadata_VorbisComment *vorbis_comment = &metadata->data.vorbis_comment;
|
|
|
|
for(int i = 0; i < vorbis_comment->num_comments; ++i) {
|
2022-02-12 15:16:59 +00:00
|
|
|
char *_name;
|
|
|
|
char *_value;
|
|
|
|
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(vorbis_comment->comments[i], &_name, &_value)) {
|
|
|
|
NSString *name = [NSString stringWithUTF8String:_name];
|
|
|
|
NSString *value = [NSString stringWithUTF8String:_value];
|
|
|
|
free(_name);
|
|
|
|
free(_value);
|
2022-02-09 21:44:50 +00:00
|
|
|
name = [name lowercaseString];
|
2022-02-12 15:16:59 +00:00
|
|
|
if([name isEqualToString:@"artist"]) {
|
|
|
|
_artist = value;
|
|
|
|
} else if([name isEqualToString:@"albumartist"]) {
|
|
|
|
_albumartist = value;
|
2022-02-09 21:44:50 +00:00
|
|
|
} else if([name isEqualToString:@"album"]) {
|
|
|
|
_album = value;
|
|
|
|
} else if([name isEqualToString:@"title"]) {
|
|
|
|
_title = value;
|
2022-02-12 15:16:59 +00:00
|
|
|
} else if([name isEqualToString:@"genre"]) {
|
|
|
|
_genre = value;
|
|
|
|
} else if([name isEqualToString:@"cuesheet"]) {
|
|
|
|
_cuesheet = value;
|
2022-02-15 03:54:08 +00:00
|
|
|
flacDecoder->cuesheetFound = YES;
|
2022-02-12 15:16:59 +00:00
|
|
|
} else if([name isEqualToString:@"date"] ||
|
|
|
|
[name isEqualToString:@"year"]) {
|
2022-06-17 13:39:02 +00:00
|
|
|
_year = @([value intValue]);
|
2022-02-12 15:16:59 +00:00
|
|
|
} else if([name isEqualToString:@"tracknumber"] ||
|
|
|
|
[name isEqualToString:@"tracknum"] ||
|
|
|
|
[name isEqualToString:@"track"]) {
|
2022-06-17 13:39:02 +00:00
|
|
|
_track = @([value intValue]);
|
2022-02-12 15:16:59 +00:00
|
|
|
} else if([name isEqualToString:@"discnumber"] ||
|
|
|
|
[name isEqualToString:@"discnum"] ||
|
|
|
|
[name isEqualToString:@"disc"]) {
|
2022-06-17 13:39:02 +00:00
|
|
|
_disc = @([value intValue]);
|
2022-02-12 15:16:59 +00:00
|
|
|
} else if([name isEqualToString:@"replaygain_album_gain"]) {
|
|
|
|
_replayGainAlbumGain = [value floatValue];
|
|
|
|
} else if([name isEqualToString:@"replaygain_album_peak"]) {
|
|
|
|
_replayGainAlbumPeak = [value floatValue];
|
|
|
|
} else if([name isEqualToString:@"replaygain_track_gain"]) {
|
|
|
|
_replayGainTrackGain = [value floatValue];
|
|
|
|
} else if([name isEqualToString:@"replaygain_track_peak"]) {
|
|
|
|
_replayGainTrackPeak = [value floatValue];
|
2022-02-09 21:44:50 +00:00
|
|
|
} else if([name isEqualToString:@"waveformatextensible_channel_mask"]) {
|
|
|
|
if([value hasPrefix:@"0x"]) {
|
|
|
|
char *end;
|
|
|
|
const char *_value = [value UTF8String] + 2;
|
|
|
|
flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-12 15:16:59 +00:00
|
|
|
if(![_artist isEqual:flacDecoder->artist] ||
|
|
|
|
![_albumartist isEqual:flacDecoder->albumartist] ||
|
|
|
|
![_album isEqual:flacDecoder->album] ||
|
|
|
|
![_title isEqual:flacDecoder->title] ||
|
|
|
|
![_genre isEqual:flacDecoder->genre] ||
|
|
|
|
![_cuesheet isEqual:flacDecoder->cuesheet] ||
|
|
|
|
![_year isEqual:flacDecoder->year] ||
|
|
|
|
![_track isEqual:flacDecoder->track] ||
|
|
|
|
![_disc isEqual:flacDecoder->disc] ||
|
|
|
|
_replayGainAlbumGain != flacDecoder->replayGainAlbumGain ||
|
|
|
|
_replayGainAlbumPeak != flacDecoder->replayGainAlbumPeak ||
|
|
|
|
_replayGainTrackGain != flacDecoder->replayGainTrackGain ||
|
|
|
|
_replayGainTrackPeak != flacDecoder->replayGainTrackPeak) {
|
2022-02-09 21:44:50 +00:00
|
|
|
flacDecoder->artist = _artist;
|
2022-02-12 15:16:59 +00:00
|
|
|
flacDecoder->albumartist = _albumartist;
|
|
|
|
flacDecoder->album = _album;
|
2022-02-09 21:44:50 +00:00
|
|
|
flacDecoder->title = _title;
|
2022-02-12 15:16:59 +00:00
|
|
|
flacDecoder->genre = _genre;
|
|
|
|
flacDecoder->cuesheet = _cuesheet;
|
|
|
|
flacDecoder->year = _year;
|
|
|
|
flacDecoder->track = _track;
|
|
|
|
flacDecoder->disc = _disc;
|
|
|
|
flacDecoder->replayGainAlbumGain = _replayGainAlbumGain;
|
|
|
|
flacDecoder->replayGainAlbumPeak = _replayGainAlbumPeak;
|
|
|
|
flacDecoder->replayGainTrackGain = _replayGainTrackGain;
|
|
|
|
flacDecoder->replayGainTrackPeak = _replayGainTrackPeak;
|
|
|
|
|
|
|
|
if(![flacDecoder->source seekable]) {
|
|
|
|
[flacDecoder willChangeValueForKey:@"metadata"];
|
|
|
|
[flacDecoder didChangeValueForKey:@"metadata"];
|
|
|
|
}
|
2022-02-09 21:44:50 +00:00
|
|
|
}
|
2022-02-07 10:06:51 +00:00
|
|
|
}
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
|
2022-02-09 21:44:50 +00:00
|
|
|
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
|
|
|
|
if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC)
|
|
|
|
flacDecoder->abortFlag = YES;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)open:(id<CogSource>)s {
|
2007-03-04 21:32:03 +00:00
|
|
|
[self setSource:s];
|
2022-02-07 05:49:27 +00:00
|
|
|
[self setSize:0];
|
|
|
|
|
|
|
|
if([s seekable]) {
|
|
|
|
[s seek:0 whence:SEEK_END];
|
|
|
|
[self setSize:[s tell]];
|
|
|
|
[s seek:0 whence:SEEK_SET];
|
|
|
|
}
|
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
// Must peek at stream! HTTP reader supports seeking within its buffer
|
|
|
|
BOOL isOggFlac = NO;
|
|
|
|
uint8_t buffer[4];
|
|
|
|
[s read:buffer amount:4];
|
|
|
|
[s seek:0 whence:SEEK_SET];
|
|
|
|
if(memcmp(buffer, "OggS", 4) == 0) {
|
|
|
|
isOggFlac = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
artist = @"";
|
2022-02-12 15:16:59 +00:00
|
|
|
albumartist = @"";
|
|
|
|
album = @"";
|
2022-02-09 21:44:50 +00:00
|
|
|
title = @"";
|
2022-02-12 15:16:59 +00:00
|
|
|
genre = @"";
|
|
|
|
year = @(0);
|
|
|
|
track = @(0);
|
|
|
|
disc = @(0);
|
|
|
|
replayGainAlbumGain = 0.0;
|
|
|
|
replayGainAlbumPeak = 0.0;
|
|
|
|
replayGainTrackGain = 0.0;
|
|
|
|
replayGainTrackPeak = 0.0;
|
|
|
|
albumArt = [NSData data];
|
2022-02-15 03:54:08 +00:00
|
|
|
cuesheetFound = NO;
|
2022-02-12 15:16:59 +00:00
|
|
|
cuesheet = @"";
|
2022-02-09 21:44:50 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
decoder = FLAC__stream_decoder_new();
|
2022-02-07 05:49:27 +00:00
|
|
|
if(decoder == NULL)
|
2005-06-02 18:16:43 +00:00
|
|
|
return NO;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
if(![source seekable]) {
|
|
|
|
FLAC__stream_decoder_set_md5_checking(decoder, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
FLAC__stream_decoder_set_metadata_ignore_all(decoder);
|
|
|
|
FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
|
|
|
|
FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);
|
2022-02-12 15:16:59 +00:00
|
|
|
FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_PICTURE);
|
|
|
|
FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_CUESHEET);
|
2022-02-09 21:44:50 +00:00
|
|
|
|
|
|
|
abortFlag = NO;
|
|
|
|
|
|
|
|
FLAC__StreamDecoderInitStatus ret;
|
|
|
|
|
|
|
|
if(isOggFlac) {
|
|
|
|
ret = FLAC__stream_decoder_init_ogg_stream(decoder,
|
|
|
|
ReadCallback,
|
|
|
|
([source seekable] ? SeekCallback : NULL),
|
|
|
|
([source seekable] ? TellCallback : NULL),
|
|
|
|
([source seekable] ? LengthCallback : NULL),
|
|
|
|
([source seekable] ? EOFCallback : NULL),
|
|
|
|
WriteCallback,
|
|
|
|
MetadataCallback,
|
|
|
|
ErrorCallback,
|
|
|
|
(__bridge void *)(self));
|
|
|
|
} else {
|
|
|
|
ret = FLAC__stream_decoder_init_stream(decoder,
|
|
|
|
ReadCallback,
|
|
|
|
([source seekable] ? SeekCallback : NULL),
|
|
|
|
([source seekable] ? TellCallback : NULL),
|
|
|
|
([source seekable] ? LengthCallback : NULL),
|
|
|
|
([source seekable] ? EOFCallback : NULL),
|
|
|
|
WriteCallback,
|
|
|
|
MetadataCallback,
|
|
|
|
ErrorCallback,
|
|
|
|
(__bridge void *)(self));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(ret != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
|
2005-06-02 18:16:43 +00:00
|
|
|
return NO;
|
2007-03-04 21:32:03 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-03-04 21:32:03 +00:00
|
|
|
FLAC__stream_decoder_process_until_end_of_metadata(decoder);
|
2005-06-02 18:16:43 +00:00
|
|
|
|
2022-02-07 10:06:51 +00:00
|
|
|
if(hasStreamInfo) {
|
|
|
|
[self willChangeValueForKey:@"properties"];
|
|
|
|
[self didChangeValueForKey:@"properties"];
|
|
|
|
}
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
blockBuffer = malloc(SAMPLE_blockBuffer_SIZE);
|
2007-02-27 23:56:52 +00:00
|
|
|
|
2005-06-02 18:16:43 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (int)readAudio:(void *)buffer frames:(UInt32)frames {
|
2007-11-24 20:16:27 +00:00
|
|
|
int framesRead = 0;
|
2022-02-07 05:49:27 +00:00
|
|
|
while(framesRead < frames) {
|
|
|
|
if(blockBufferFrames == 0) {
|
2022-02-08 03:18:45 +00:00
|
|
|
if(framesRead) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) {
|
2007-11-24 20:16:27 +00:00
|
|
|
break;
|
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2022-02-09 21:44:50 +00:00
|
|
|
if(!FLAC__stream_decoder_process_single(decoder)) {
|
|
|
|
break;
|
|
|
|
}
|
2007-11-24 20:16:27 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2022-02-08 03:18:45 +00:00
|
|
|
int bytesPerFrame = ((bitsPerSample + 7) / 8) * channels;
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
int framesToRead = blockBufferFrames;
|
2022-02-07 05:49:27 +00:00
|
|
|
if(blockBufferFrames > frames) {
|
2007-11-24 20:16:27 +00:00
|
|
|
framesToRead = frames;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
memcpy(((uint8_t *)buffer) + (framesRead * bytesPerFrame), (uint8_t *)blockBuffer, framesToRead * bytesPerFrame);
|
|
|
|
|
2008-01-23 02:45:57 +00:00
|
|
|
frames -= framesToRead;
|
2007-11-24 20:16:27 +00:00
|
|
|
framesRead += framesToRead;
|
|
|
|
blockBufferFrames -= framesToRead;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
if(blockBufferFrames > 0) {
|
2007-11-24 20:16:27 +00:00
|
|
|
memmove((uint8_t *)blockBuffer, ((uint8_t *)blockBuffer) + (framesToRead * bytesPerFrame), blockBufferFrames * bytesPerFrame);
|
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-09 23:04:49 +00:00
|
|
|
if(![source seekable]) {
|
|
|
|
Class sourceClass = [source class];
|
|
|
|
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
|
|
|
HTTPSource *httpSource = (HTTPSource *)source;
|
|
|
|
if([httpSource hasMetadata]) {
|
|
|
|
NSDictionary *metadata = [httpSource metadata];
|
|
|
|
NSString *_genre = [metadata valueForKey:@"genre"];
|
|
|
|
NSString *_album = [metadata valueForKey:@"album"];
|
|
|
|
NSString *_artist = [metadata valueForKey:@"artist"];
|
|
|
|
NSString *_title = [metadata valueForKey:@"title"];
|
|
|
|
if(![_genre isEqualToString:genre] ||
|
|
|
|
![_album isEqualToString:album] ||
|
|
|
|
![_artist isEqualToString:artist] ||
|
|
|
|
![_title isEqualToString:title]) {
|
|
|
|
genre = _genre;
|
|
|
|
album = _album;
|
|
|
|
artist = _artist;
|
|
|
|
title = _title;
|
|
|
|
[self willChangeValueForKey:@"metadata"];
|
|
|
|
[self didChangeValueForKey:@"metadata"];
|
|
|
|
}
|
2022-02-09 21:44:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
return framesRead;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)close {
|
|
|
|
if(decoder) {
|
2007-03-04 21:32:03 +00:00
|
|
|
FLAC__stream_decoder_finish(decoder);
|
|
|
|
FLAC__stream_decoder_delete(decoder);
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
if(blockBuffer) {
|
2007-11-24 20:16:27 +00:00
|
|
|
free(blockBuffer);
|
2007-02-27 23:56:52 +00:00
|
|
|
}
|
|
|
|
|
2005-06-02 18:16:43 +00:00
|
|
|
decoder = NULL;
|
2007-11-24 20:16:27 +00:00
|
|
|
blockBuffer = NULL;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)dealloc {
|
|
|
|
[self close];
|
2016-06-19 19:57:18 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (long)seek:(long)sample {
|
|
|
|
if(!FLAC__stream_decoder_seek_absolute(decoder, sample))
|
|
|
|
return -1;
|
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
return sample;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
// bs methods
|
|
|
|
- (char *)blockBuffer {
|
2007-11-24 20:16:27 +00:00
|
|
|
return blockBuffer;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
- (int)blockBufferFrames {
|
2007-11-24 20:16:27 +00:00
|
|
|
return blockBufferFrames;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setBlockBufferFrames:(int)frames {
|
2007-11-24 20:16:27 +00:00
|
|
|
blockBufferFrames = frames;
|
2005-06-02 18:16:43 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (FLAC__StreamDecoder *)decoder {
|
2005-06-02 18:16:43 +00:00
|
|
|
return decoder;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setSource:(id<CogSource>)s {
|
2007-03-04 21:32:03 +00:00
|
|
|
source = s;
|
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
- (id<CogSource>)source {
|
2007-03-04 21:32:03 +00:00
|
|
|
return source;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setEndOfStream:(BOOL)eos {
|
2007-03-04 21:32:03 +00:00
|
|
|
endOfStream = eos;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)endOfStream {
|
2007-03-04 21:32:03 +00:00
|
|
|
return endOfStream;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setSize:(long)size {
|
|
|
|
fileSize = size;
|
2021-12-11 08:22:19 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (NSDictionary *)properties {
|
2022-06-17 13:39:02 +00:00
|
|
|
return @{ @"channels": @(channels),
|
|
|
|
@"channelConfig": @(channelConfig),
|
|
|
|
@"bitsPerSample": @(bitsPerSample),
|
|
|
|
@"sampleRate": @(frequency),
|
|
|
|
@"totalFrames": @(totalFrames),
|
|
|
|
@"seekable": @([source seekable]),
|
|
|
|
@"bitrate": @(fileSize ? (fileSize * 8 / ((totalFrames + (frequency / 2)) / frequency)) / 1000 : 0),
|
|
|
|
@"codec": @"FLAC",
|
|
|
|
@"endian": @"big",
|
|
|
|
@"encoding": @"lossless" };
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-09 03:56:39 +00:00
|
|
|
- (NSDictionary *)metadata {
|
2022-02-12 15:16:59 +00:00
|
|
|
return @{ @"artist": artist, @"albumartist": albumartist, @"album": album, @"title": title, @"genre": genre, @"year": year, @"track": track, @"disc": disc, @"replayGainAlbumGain": @(replayGainAlbumGain), @"replayGainAlbumPeak": @(replayGainAlbumPeak), @"replayGainTrackGain": @(replayGainTrackGain), @"replayGainTrackPeak": @(replayGainTrackPeak), @"cuesheet": cuesheet, @"albumArt": albumArt };
|
2022-02-09 03:56:39 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (NSArray *)fileTypes {
|
2022-01-19 02:12:57 +00:00
|
|
|
return @[@"flac"];
|
2007-10-14 18:39:58 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (NSArray *)mimeTypes {
|
2022-02-09 21:44:50 +00:00
|
|
|
return @[@"audio/x-flac", @"application/ogg", @"audio/ogg"];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (float)priority {
|
|
|
|
return 2.0;
|
Implemented support for multiple decoders per file name extension, with a floating point priority control per interface. In the event that more than one input is registered to a given extension, and we match that extension, it will be passed off to an instance of the multi-decoder wrapper, which will try opening the file with all of the decoders in order of priority, until either one of them accepts it, or all of them have failed. This paves the way for adding a VGMSTREAM input, so I can give it a very low priority, since it has several formats that are verified by file name extension only. All current inputs have been given a priority of 1.0, except for CoreAudio, which was given a priority of 0.5, because it contains an MP3 and AC3 decoders that I'd rather not use if I don't have to.
2013-10-21 17:54:11 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
+ (NSArray *)fileTypeAssociations {
|
|
|
|
return @[
|
|
|
|
@[@"FLAC Audio File", @"flac.icns", @"flac"]
|
|
|
|
];
|
2022-01-18 11:06:03 +00:00
|
|
|
}
|
|
|
|
|
2005-06-02 18:16:43 +00:00
|
|
|
@end
|