// // FlacDecoder.m // zyVorbis // // Created by Vincent Spader on 1/25/05. // Copyright 2005 Vincent Spader All rights reserved. // #import "FlacDecoder.h" #import "Logging.h" #import "HTTPSource.h" extern void grabbag__cuesheet_emit(NSString **out, const FLAC__StreamMetadata *cuesheet, const char *file_reference); @implementation FlacDecoder FLAC__StreamDecoderReadStatus ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte blockBuffer[], size_t *bytes, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; long bytesRead = [[flacDecoder source] read:blockBuffer amount:*bytes]; if(bytesRead < 0) { *bytes = 0; return FLAC__STREAM_DECODER_READ_STATUS_ABORT; } else if(bytesRead == 0) { *bytes = 0; [flacDecoder setEndOfStream:YES]; return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; } else { *bytes = bytesRead; return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } } FLAC__StreamDecoderSeekStatus SeekCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; 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; } FLAC__StreamDecoderTellStatus TellCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; 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; } } FLAC__bool EOFCallback(const FLAC__StreamDecoder *decoder, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; return (FLAC__bool)[flacDecoder endOfStream]; } FLAC__StreamDecoderLengthStatus LengthCallback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; if([[flacDecoder source] seekable]) { long currentPos = [[flacDecoder source] tell]; [[flacDecoder source] seek:0 whence:SEEK_END]; *stream_length = [[flacDecoder source] tell]; [[flacDecoder source] seek:currentPos whence:SEEK_SET]; return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } else { *stream_length = 0; return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR; } } FLAC__StreamDecoderWriteStatus WriteCallback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const sampleblockBuffer[], void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; if(flacDecoder->abortFlag) return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; 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"]; } void *blockBuffer = [flacDecoder blockBuffer]; int8_t *alias8; int16_t *alias16; int32_t *alias32; int sample, channel; 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]); } } default: // 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; } [flacDecoder setBlockBufferFrames:frame->header.blocksize]; return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } // This callback is only called for STREAMINFO blocks 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) FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; if(!flacDecoder->hasStreamInfo && metadata->type == FLAC__METADATA_TYPE_STREAMINFO) { flacDecoder->channels = metadata->data.stream_info.channels; flacDecoder->channelConfig = 0; 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; } if(metadata->type == FLAC__METADATA_TYPE_CUESHEET && !flacDecoder->cuesheetFound) { flacDecoder->cuesheetFound = YES; 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"]; } } } if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { NSString *_artist = flacDecoder->artist; NSString *_albumartist = flacDecoder->albumartist; NSString *_album = flacDecoder->album; NSString *_title = flacDecoder->title; 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; const FLAC__StreamMetadata_VorbisComment *vorbis_comment = &metadata->data.vorbis_comment; for(int i = 0; i < vorbis_comment->num_comments; ++i) { 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); name = [name lowercaseString]; if([name isEqualToString:@"artist"]) { _artist = value; } else if([name isEqualToString:@"albumartist"]) { _albumartist = value; } else if([name isEqualToString:@"album"]) { _album = value; } else if([name isEqualToString:@"title"]) { _title = value; } else if([name isEqualToString:@"genre"]) { _genre = value; } else if([name isEqualToString:@"cuesheet"]) { _cuesheet = value; flacDecoder->cuesheetFound = YES; } else if([name isEqualToString:@"date"] || [name isEqualToString:@"year"]) { _year = @([value intValue]); } else if([name isEqualToString:@"tracknumber"] || [name isEqualToString:@"tracknum"] || [name isEqualToString:@"track"]) { _track = @([value intValue]); } else if([name isEqualToString:@"discnumber"] || [name isEqualToString:@"discnum"] || [name isEqualToString:@"disc"]) { _disc = @([value intValue]); } 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]; } 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); } } } } 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) { flacDecoder->artist = _artist; flacDecoder->albumartist = _albumartist; flacDecoder->album = _album; flacDecoder->title = _title; 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"]; } } } } void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; if(status != FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC) flacDecoder->abortFlag = YES; } - (BOOL)open:(id)s { [self setSource:s]; [self setSize:0]; if([s seekable]) { [s seek:0 whence:SEEK_END]; [self setSize:[s tell]]; [s seek:0 whence:SEEK_SET]; } // 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 = @""; albumartist = @""; album = @""; title = @""; genre = @""; year = @(0); track = @(0); disc = @(0); replayGainAlbumGain = 0.0; replayGainAlbumPeak = 0.0; replayGainTrackGain = 0.0; replayGainTrackPeak = 0.0; albumArt = [NSData data]; cuesheetFound = NO; cuesheet = @""; decoder = FLAC__stream_decoder_new(); if(decoder == NULL) return NO; 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); FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_PICTURE); FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_CUESHEET); 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) { return NO; } FLAC__stream_decoder_process_until_end_of_metadata(decoder); if(hasStreamInfo) { [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; } blockBuffer = malloc(SAMPLE_blockBuffer_SIZE); return YES; } - (int)readAudio:(void *)buffer frames:(UInt32)frames { int framesRead = 0; while(framesRead < frames) { if(blockBufferFrames == 0) { if(framesRead) { break; } if(FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_STREAM) { break; } if(!FLAC__stream_decoder_process_single(decoder)) { break; } } int bytesPerFrame = ((bitsPerSample + 7) / 8) * channels; int framesToRead = blockBufferFrames; if(blockBufferFrames > frames) { framesToRead = frames; } memcpy(((uint8_t *)buffer) + (framesRead * bytesPerFrame), (uint8_t *)blockBuffer, framesToRead * bytesPerFrame); frames -= framesToRead; framesRead += framesToRead; blockBufferFrames -= framesToRead; if(blockBufferFrames > 0) { memmove((uint8_t *)blockBuffer, ((uint8_t *)blockBuffer) + (framesToRead * bytesPerFrame), blockBufferFrames * bytesPerFrame); } } 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"]; } } } } return framesRead; } - (void)close { if(decoder) { FLAC__stream_decoder_finish(decoder); FLAC__stream_decoder_delete(decoder); } if(blockBuffer) { free(blockBuffer); } decoder = NULL; blockBuffer = NULL; } - (void)dealloc { [self close]; } - (long)seek:(long)sample { if(!FLAC__stream_decoder_seek_absolute(decoder, sample)) return -1; return sample; } // bs methods - (char *)blockBuffer { return blockBuffer; } - (int)blockBufferFrames { return blockBufferFrames; } - (void)setBlockBufferFrames:(int)frames { blockBufferFrames = frames; } - (FLAC__StreamDecoder *)decoder { return decoder; } - (void)setSource:(id)s { source = s; } - (id)source { return source; } - (void)setEndOfStream:(BOOL)eos { endOfStream = eos; } - (BOOL)endOfStream { return endOfStream; } - (void)setSize:(long)size { fileSize = size; } - (NSDictionary *)properties { 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" }; } - (NSDictionary *)metadata { 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 }; } + (NSArray *)fileTypes { return @[@"flac"]; } + (NSArray *)mimeTypes { return @[@"audio/x-flac", @"application/ogg", @"audio/ogg"]; } + (float)priority { return 2.0; } + (NSArray *)fileTypeAssociations { return @[ @[@"FLAC Audio File", @"flac.icns", @"flac"] ]; } @end