ref: bffdc94734f0ab972cfdb0b874ebbac9c694d824
dir: /src/flac.c/
#if defined(HAVE_CONFIG_H)
# include <config.h>
#endif
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "flac.h"
#include "opus_header.h"
#if defined(HAVE_LIBFLAC)
/*Callback to read more data for the FLAC decoder.*/
static FLAC__StreamDecoderReadStatus read_callback(
const FLAC__StreamDecoder *decoder,FLAC__byte buffer[],size_t *bytes,
void *client_data){
flacfile *flac;
(void)decoder;
flac=(flacfile *)client_data;
if(*bytes>0){
int bufpos;
int buflen;
bufpos=flac->bufpos;
buflen=flac->buflen;
if(bufpos<buflen){
size_t bytes_to_copy;
/*If we haven't consumed all the data we used for file ID yet, consume
some more.*/
bytes_to_copy=buflen-bufpos;
bytes_to_copy=*bytes<bytes_to_copy?*bytes:bytes_to_copy;
memcpy(buffer,flac->oldbuf,bytes_to_copy);
flac->bufpos+=bytes_to_copy;
*bytes=bytes_to_copy;
}else{
/*Otherwise just read from the file.*/
*bytes=fread(buffer,sizeof(*buffer),*bytes,flac->f);
}
/*This pretty much comes from the FLAC documentation, except that we only
check ferror() if we didn't read any bytes at all.*/
return *bytes==0?ferror(flac->f)?
FLAC__STREAM_DECODER_READ_STATUS_ABORT:
FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM:
FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
/*Callback to test the stream for EOF.*/
static FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder,
void *client_data){
flacfile *flac;
(void)decoder;
flac=(flacfile *)client_data;
return feof(flac->f)?true:false;
}
/*A version of strncasecmp() that is guaranteed to only ignore the case of
ASCII characters.*/
int flac_strncasecmp(const char *_a,const char *_b,int _n){
int i;
for(i=0;i<_n;i++){
int a;
int b;
int d;
a=_a[i];
b=_b[i];
if(a>='a'&&a<='z')a-='a'-'A';
if(b>='a'&&b<='z')b-='a'-'A';
d=a-b;
if(d)return d;
}
return 0;
}
/*Callback to process a metadata packet.*/
static void metadata_callback(const FLAC__StreamDecoder *decoder,
const FLAC__StreamMetadata *metadata,void *client_data){
flacfile *flac;
oe_enc_opt *inopt;
(void)decoder;
flac=(flacfile *)client_data;
inopt=flac->inopt;
switch(metadata->type){
case FLAC__METADATA_TYPE_STREAMINFO:
flac->max_blocksize=metadata->data.stream_info.max_blocksize;
inopt->rate=metadata->data.stream_info.sample_rate;
inopt->channels=flac->channels=metadata->data.stream_info.channels;
inopt->samplesize=metadata->data.stream_info.bits_per_sample;
inopt->total_samples_per_channel=
metadata->data.stream_info.total_samples;
flac->block_buf=malloc(
flac->max_blocksize*flac->channels*sizeof(*flac->block_buf));
flac->block_buf_pos=0;
flac->block_buf_len=0;
break;
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
{
FLAC__StreamMetadata_VorbisComment_Entry *comments;
FLAC__uint32 num_comments;
FLAC__uint32 i;
double reference_loudness;
double album_gain;
double track_gain;
double gain;
int saw_album_gain;
int saw_track_gain;
if(!inopt->copy_comments)break;
num_comments=metadata->data.vorbis_comment.num_comments;
comments=metadata->data.vorbis_comment.comments;
saw_album_gain=saw_track_gain=0;
album_gain=track_gain=0;
/*The default reference loudness for ReplayGain is 89.0 dB*/
reference_loudness=89;
for(i=0;i<num_comments;i++){
char *entry;
char *end;
entry=(char *)comments[i].entry;
/*Check for ReplayGain tags.
Parse the ones we have R128 equivalents for, and skip the others.*/
if(flac_strncasecmp(entry,"REPLAYGAIN_REFERENCE_LOUDNESS=",30)==0){
gain=strtod(entry+30,&end);
if(end<=entry+30){
fprintf(stderr,_("WARNING: Invalid ReplayGain tag: %s\n"),entry);
}
else reference_loudness=gain;
continue;
}
if(flac_strncasecmp(entry,"REPLAYGAIN_ALBUM_GAIN=",22)==0){
gain=strtod(entry+22,&end);
if(end<=entry+22){
fprintf(stderr,_("WARNING: Invalid ReplayGain tag: %s\n"),entry);
}
else{
album_gain=gain;
saw_album_gain=1;
}
continue;
}
if(flac_strncasecmp(entry,"REPLAYGAIN_TRACK_GAIN=",22)==0){
gain=strtod(entry+22,&end);
if(end<entry+22){
fprintf(stderr,_("WARNING: Invalid ReplayGain tag: %s\n"),entry);
}
else{
track_gain=gain;
saw_track_gain=1;
}
continue;
}
if(flac_strncasecmp(entry,"REPLAYGAIN_ALBUM_PEAK=",22)==0
||flac_strncasecmp(entry,"REPLAYGAIN_TRACK_PEAK=",22)==0){
continue;
}
if(!strchr(entry,'=')){
fprintf(stderr,_("WARNING: Invalid comment: %s\n"),entry);
fprintf(stderr,
_("Discarding comment not in the form name=value\n"));
continue;
}
comment_add(&inopt->comments,&inopt->comments_length,NULL,entry);
}
/*Set the header gain to the album gain after converting to the R128
reference level.*/
if(saw_album_gain){
gain=256*(album_gain+(84-reference_loudness))+0.5;
inopt->gain=gain<-32768?-32768:gain<32767?(int)floor(gain):32767;
}
/*If there was a track gain, then add an equivalent R128 tag for that.*/
if(saw_track_gain){
char track_gain_buf[7];
int track_gain_val;
gain=256*(track_gain-album_gain)+0.5;
track_gain_val=gain<-32768?-32768:gain<32767?(int)floor(gain):32767;
sprintf(track_gain_buf,"%i",track_gain_val);
comment_add(&inopt->comments,&inopt->comments_length,
"R128_TRACK_GAIN=",track_gain_buf);
}
}
break;
default:
break;
}
}
/*Callback to process an audio frame.*/
static FLAC__StreamDecoderWriteStatus write_callback(
const FLAC__StreamDecoder *decoder,const FLAC__Frame *frame,
const FLAC__int32 *const buffer[],void *client_data){
flacfile *flac;
int channels;
opus_int32 blocksize;
int bits_per_sample;
float scale;
const int *channel_permute;
float *block_buf;
int ci;
opus_int32 si;
(void)decoder;
flac=(flacfile *)client_data;
/*We do not allow the number of channels to change.*/
channels=frame->header.channels;
if(channels!=flac->channels){
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
/*We do not allow block sizes larger than the declared maximum.*/
blocksize=frame->header.blocksize;
if(blocksize>flac->max_blocksize){
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
/*We do allow the bits per sample to change, though this will confound Opus's
silence detection.*/
bits_per_sample=frame->header.bits_per_sample;
speex_assert(bits_per_sample>0&&bits_per_sample<=32);
scale=(0x80000000U>>(bits_per_sample-1))*(1.0F/0x80000000U);
channel_permute=flac->channel_permute;
block_buf=flac->block_buf;
for(ci=0;ci<channels;ci++){
const FLAC__int32 *channel_buf;
channel_buf=buffer[channel_permute[ci]];
for(si=0;si<blocksize;si++){
/*There's a loss of precision here for 32-bit samples, but libFLAC
doesn't currently support more than 24.*/
block_buf[si*channels+ci]=scale*(float)channel_buf[si];
}
}
flac->block_buf_pos=0;
flac->block_buf_len=blocksize;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
/*Dummy error callback (required by libFLAC).*/
void error_callback(const FLAC__StreamDecoder *decoder,
FLAC__StreamDecoderErrorStatus status,void *client_data){
(void)decoder;
(void)status;
(void)client_data;
}
int flac_id(unsigned char *buf,int len){
/*Something screwed up.*/
if(len<4)return 0;
/*Not FLAC.*/
if(memcmp(buf,"fLaC",4))return 0;
/*Looks like FLAC.*/
return 1;
}
int oggflac_id(unsigned char *buf,int len){
/*Something screwed up.*/
if(len<33)return 0;
/*Not Ogg.*/
if(memcmp(buf,"OggS",4))return 0;
/*Not FLAC.*/
if(memcmp(buf+28,"\177FLAC",5))return 0;
/*Looks like OggFLAC.*/
return 1;
}
/*Read more data for the encoder.*/
long flac_read(void *client_data,float *buffer,int samples){
flacfile *flac;
int channels;
float *block_buf;
long ret;
flac=(flacfile *)client_data;
channels=flac->channels;
block_buf=flac->block_buf;
ret=0;
/*Keep reading until we get all the samples or hit an error/EOF.
Short reads are not allowed.*/
while(samples>0){
opus_int32 block_buf_pos;
opus_int32 block_buf_len;
size_t samples_to_copy;
block_buf_pos=flac->block_buf_pos;
block_buf_len=flac->block_buf_len;
if(block_buf_pos>=block_buf_len){
/*Read the next frame from the stream.*/
if(!FLAC__stream_decoder_process_single(flac->decoder))return ret;
block_buf_pos=flac->block_buf_pos;
block_buf_len=flac->block_buf_len;
/*If we didn't get another block, we hit EOF.
FLAC__stream_decoder_process_single still returns successfully in this
case.*/
if(block_buf_pos>=block_buf_len)return ret;
}
block_buf_len-=block_buf_pos;
samples_to_copy=samples<block_buf_len?samples:block_buf_len;
memcpy(buffer,block_buf+block_buf_pos*channels,
samples_to_copy*channels*sizeof(*buffer));
flac->block_buf_pos+=samples_to_copy;
ret+=samples_to_copy;
buffer+=samples_to_copy*channels;
samples-=samples_to_copy;
}
return ret;
}
int flac_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen){
flacfile *flac;
/*Ok. At this point, we know we have a FLAC or an OggFLAC file.
Set up the FLAC decoder.*/
flac=malloc(sizeof(*flac));
flac->decoder=FLAC__stream_decoder_new();
FLAC__stream_decoder_set_md5_checking(flac->decoder,false);
/*We get STREAMINFO packets by default, but not VORBIS_COMMENT.*/
FLAC__stream_decoder_set_metadata_respond(flac->decoder,
FLAC__METADATA_TYPE_VORBIS_COMMENT);
flac->inopt=opt;
flac->f=in;
flac->oldbuf=malloc(buflen*sizeof(*flac->oldbuf));
memcpy(flac->oldbuf,oldbuf,buflen*sizeof(*flac->oldbuf));
flac->bufpos=0;
flac->buflen=buflen;
flac->block_buf=NULL;
if((*(flac_id(oldbuf,buflen)?
FLAC__stream_decoder_init_stream:FLAC__stream_decoder_init_ogg_stream))(
flac->decoder,read_callback,NULL,NULL,NULL,eof_callback,
write_callback,metadata_callback,error_callback,flac)==
FLAC__STREAM_DECODER_INIT_STATUS_OK){
/*Decode until we get the file length, sample rate, the number of channels,
and the Vorbis comments (if any).*/
if(FLAC__stream_decoder_process_until_end_of_metadata(flac->decoder)){
opt->read_samples=flac_read;
opt->readdata=flac;
/*FLAC supports 1 to 8 channels only.*/
speex_assert(flac->channels>0&&flac->channels<=8);
/*It uses the same channel mappings as WAV.*/
flac->channel_permute=wav_permute_matrix[flac->channels-1];
return 1;
}
}
flac_close(flac);
fprintf(stderr,_("ERROR: Could not open FLAC stream.\n"));
return 0;
}
void flac_close(void *client_data){
flacfile *flac;
flac=(flacfile *)client_data;
free(flac->block_buf);
free(flac->oldbuf);
FLAC__stream_decoder_delete(flac->decoder);
free(flac);
}
#else
/*FLAC support is disabled.*/
int flac_id(unsigned char *buf,int len){
(void)buf;
(void)len;
return 0;
}
int oggflac_id(unsigned char *buf,int len){
(void)buf;
(void)len;
return 0;
}
int flac_open(FILE *in,oe_enc_opt *opt,unsigned char *oldbuf,int buflen){
(void)in;
(void)opt;
(void)oldbuf;
(void)buflen;
return 0;
}
void flac_close(void *client_data){
(void)client_data;
}
#endif