diff --git a/src/Makefile.am b/src/Makefile.am index 33919516f..08924e63c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -6,14 +6,14 @@ mpd_headers = buffer2array.h interface.h command.h playlist.h ls.h \ audio.h playerData.h stats.h myfprintf.h sig_handlers.h decode.h log.h \ audiofile_decode.h charConv.h permission.h mpd_types.h pcm_utils.h \ mp4_decode.h aac_decode.h signal_check.h utf8.h inputStream.h \ - outputBuffer.h + outputBuffer.h replayGain.h mpd_SOURCES = main.c buffer2array.c interface.c command.c playlist.c ls.c \ song.c list.c directory.c tables.c utils.c path.c mp3_decode.c \ tag.c player.c listen.c conf.c ogg_decode.c volume.c flac_decode.c \ audio.c playerData.c stats.c myfprintf.c sig_handlers.c decode.c log.c \ audiofile_decode.c charConv.c permission.c pcm_utils.c mp4_decode.c \ aac_decode.c signal_check.c utf8.c inputStream.c outputBuffer.c \ - $(mpd_headers) + replayGain.c $(mpd_headers) mpd_CFLAGS = $(MPD_CFLAGS) mpd_LDADD = $(MPD_LIBS) $(ID3_LIB) $(MAD_LIB) $(MP4FF_LIB) diff --git a/src/conf.c b/src/conf.c index 53287ea29..2027ae872 100644 --- a/src/conf.c +++ b/src/conf.c @@ -37,7 +37,7 @@ #define CONF_COMMENT '#' -#define CONF_NUMBER_OF_PARAMS 27 +#define CONF_NUMBER_OF_PARAMS 28 #define CONF_NUMBER_OF_PATHS 6 #define CONF_NUMBER_OF_REQUIRED 5 #define CONF_NUMBER_OF_ALLOW_CATS 1 @@ -123,7 +123,8 @@ char ** readConf(char * file) { "filesystem_charset", "password", "default_permissions", - "buffer_size" + "buffer_size", + "replaygain" }; int conf_absolutePaths[CONF_NUMBER_OF_PATHS] = { diff --git a/src/conf.h b/src/conf.h index d4d798b9c..3999f7f1f 100644 --- a/src/conf.h +++ b/src/conf.h @@ -48,6 +48,7 @@ #define CONF_PASSWORD 24 #define CONF_DEFAULT_PERMISSIONS 25 #define CONF_BUFFER_SIZE 26 +#define CONF_REPLAYGAIN 27 #define CONF_CAT_CHAR "\n" diff --git a/src/flac_decode.c b/src/flac_decode.c index b8d768428..7865d700a 100644 --- a/src/flac_decode.c +++ b/src/flac_decode.c @@ -25,6 +25,7 @@ #include "pcm_utils.h" #include "inputStream.h" #include "outputBuffer.h" +#include "replayGain.h" #include #include @@ -41,8 +42,8 @@ typedef struct { OutputBuffer * cb; AudioFormat * af; DecoderControl * dc; - char * file; InputStream inStream; + float replayGainScale; } FlacData; /* this code is based on flac123, from flac-tools */ @@ -66,9 +67,7 @@ FLAC__SeekableStreamDecoderLengthStatus flacLength( const FLAC__SeekableStreamDecoder *, FLAC__uint64 *, void *); FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder *, void *); -void flacPlayFile(char *file, OutputBuffer * cb, AudioFormat * af, - DecoderControl *dc) -{ +int flac_decode(OutputBuffer * cb, AudioFormat * af, DecoderControl *dc) { FLAC__SeekableStreamDecoder * flacDec; FlacData data; int status = 1; @@ -80,14 +79,14 @@ void flacPlayFile(char *file, OutputBuffer * cb, AudioFormat * af, data.cb = cb; data.af = af; data.dc = dc; - data.file = file; + data.replayGainScale = 1.0; - if(openInputStreamFromFile(&(data.inStream),file)<0) { - ERROR("unable to open flac: %s\n",file); - return; + if(openInputStreamFromFile(&(data.inStream),dc->file)<0) { + ERROR("unable to open flac: %s\n",dc->file); + return -1; } - if(!(flacDec = FLAC__seekable_stream_decoder_new())) return; + if(!(flacDec = FLAC__seekable_stream_decoder_new())) return -1; /*status&=FLAC__file_decoder_set_md5_checking(flacDec,1);*/ status&=FLAC__seekable_stream_decoder_set_read_callback(flacDec, flacRead); @@ -107,28 +106,37 @@ void flacPlayFile(char *file, OutputBuffer * cb, AudioFormat * af, status&=FLAC__seekable_stream_decoder_set_client_data(flacDec, (void *)&data); if(!status) { - ERROR("flac problem before init(): %s\n",file); + ERROR("flac problem before init(): %s\n",dc->file); flacPrintErroredState( - FLAC__seekable_stream_decoder_get_state(flacDec),file); + FLAC__seekable_stream_decoder_get_state(flacDec), + dc->file); FLAC__seekable_stream_decoder_delete(flacDec); - return; + return -1; } + if(FLAC__seekable_stream_decoder_init(flacDec)!= FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) { - ERROR("flac problem doing init(): %s\n",file); + ERROR("flac problem doing init(): %s\n",dc->file); flacPrintErroredState( - FLAC__seekable_stream_decoder_get_state(flacDec),file); + FLAC__seekable_stream_decoder_get_state(flacDec), + dc->file); FLAC__seekable_stream_decoder_delete(flacDec); - return; + return -1; } + if(!FLAC__seekable_stream_decoder_process_until_end_of_metadata(flacDec)) { - ERROR("flac problem reading metadata: %s\n",file); + ERROR("flac problem reading metadata: %s\n", dc->file); flacPrintErroredState( - FLAC__seekable_stream_decoder_get_state(flacDec),file); + FLAC__seekable_stream_decoder_get_state(flacDec), + dc->file); FLAC__seekable_stream_decoder_delete(flacDec); - return; + return -1; } + + dc->state = DECODE_STATE_DECODE; + dc->start = 0; + while(1) { FLAC__seekable_stream_decoder_process_single(flacDec); if(FLAC__seekable_stream_decoder_get_state(flacDec)!= @@ -155,7 +163,8 @@ void flacPlayFile(char *file, OutputBuffer * cb, AudioFormat * af, /*FLAC__file_decoder_process_until_end_of_file(flacDec);*/ if(!dc->stop) { flacPrintErroredState( - FLAC__seekable_stream_decoder_get_state(flacDec),file); + FLAC__seekable_stream_decoder_get_state(flacDec), + dc->file); FLAC__seekable_stream_decoder_finish(flacDec); } FLAC__seekable_stream_decoder_delete(flacDec); @@ -164,6 +173,16 @@ void flacPlayFile(char *file, OutputBuffer * cb, AudioFormat * af, flacSendChunk(&data); flushOutputBuffer(data.cb); } + + if(dc->seek) dc->seek = 0; + + if(dc->stop) { + dc->state = DECODE_STATE_STOP; + dc->stop = 0; + } + else dc->state = DECODE_STATE_STOP; + + return 0; } FLAC__SeekableStreamDecoderReadStatus flacRead( @@ -232,16 +251,16 @@ void flacError(const FLAC__SeekableStreamDecoder *dec, switch(status) { case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: - ERROR("flac lost sync: %s\n",data->file); + ERROR("flac lost sync: %s\n",data->dc->file); break; case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: - ERROR("bad header %s\n",data->file); + ERROR("bad header %s\n",data->dc->file); break; case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: - ERROR("crc mismatch %s\n",data->file); + ERROR("crc mismatch %s\n",data->dc->file); break; default: - ERROR("unknow flac error %s\n",data->file); + ERROR("unknow flac error %s\n",data->dc->file); } } @@ -277,12 +296,83 @@ void flacPrintErroredState(FLAC__SeekableStreamDecoderState state, } } -void flacMetadata(const FLAC__SeekableStreamDecoder *dec, - const FLAC__StreamMetadata *meta, void *data) +int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block, char * cmnt, + float * fl) { + int offset = FLAC__metadata_object_vorbiscomment_find_entry_from( + block,0,cmnt); + + if(offset >= 0) { + int pos = strlen(cmnt)+1; /* 1 is for '=' */ + int len = block->data.vorbis_comment.comments[offset].length + -pos; + if(len > 0) { + char * dup = malloc(len+1); + memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len); + dup[len] = '\0'; + *fl = atof(dup); + free(dup); + return 1; + } + } + + return 0; +} + +/* replaygain stuff by AliasMrJones */ +void flacParseReplayGain(const FLAC__StreamMetadata *block, FlacData * data) { + int found; + float gain = 0.0; + float peak = 0.0; + int state = getReplayGainState(); + + if(state == REPLAYGAIN_OFF) return; + + found = flacFindVorbisCommentFloat(block,"replaygain_album_gain",&gain); + if(found) { + flacFindVorbisCommentFloat(block,"replaygain_album_peak", + &peak); + } + + if(!found || state == REPLAYGAIN_TRACK) { + if(flacFindVorbisCommentFloat(block,"replaygain_track_gain", + &gain)) + { + peak = 0.0; + flacFindVorbisCommentFloat(block, + "replaygain_track_peak",&peak); + } + } + + data->replayGainScale = computeReplayGainScale(gain,peak); +} + +void flacMetadata(const FLAC__SeekableStreamDecoder *dec, + const FLAC__StreamMetadata *block, void *vdata) +{ + FlacData * data = (FlacData *)vdata; + + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + data->af->bits = block->data.stream_info.bits_per_sample; + data->af->bits = 16; + data->af->sampleRate = block->data.stream_info.sample_rate; + data->af->channels = block->data.stream_info.channels; + data->cb->totalTime = + ((float)block->data.stream_info.total_samples)/ + data->af->sampleRate; + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + flacParseReplayGain(block,data); + default: + break; + } } int flacSendChunk(FlacData * data) { + doReplayGain(data->chunk,data->chunk_length,data->af, + data->replayGainScale); + switch(sendDataToOutputBuffer(data->cb,data->dc,data->chunk, data->chunk_length,data->time,data->bitRate)) { @@ -341,70 +431,5 @@ FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec, return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } -int flac_getAudioFormatAndTime(char * file, AudioFormat * format, float * time) { - FLAC__Metadata_SimpleIterator * it; - FLAC__StreamMetadata * block = NULL; - int found = 0; - int ret = -1; - - if(!(it = FLAC__metadata_simple_iterator_new())) return -1; - if(!FLAC__metadata_simple_iterator_init(it,file,1,0)) { - FLAC__metadata_simple_iterator_delete(it); - return -1; - } - - do { - if(block) FLAC__metadata_object_delete(block); - block = FLAC__metadata_simple_iterator_get_block(it); - if(block->type == FLAC__METADATA_TYPE_STREAMINFO) found=1; - } while(!found && FLAC__metadata_simple_iterator_next(it)); - - if(found) { - format->bits = block->data.stream_info.bits_per_sample; - format->bits = 16; - format->sampleRate = block->data.stream_info.sample_rate; - format->channels = block->data.stream_info.channels; - *time = ((float)block->data.stream_info.total_samples)/ - format->sampleRate; - ret = 0; - } - - if(block) FLAC__metadata_object_delete(block); - FLAC__metadata_simple_iterator_delete(it); - - return ret; -} - -int getFlacTotalTime(char * file) { - float totalTime; - AudioFormat af; - - if(flac_getAudioFormatAndTime(file,&af,&totalTime)<0) return -1; - - return (int)(totalTime+0.5); -} - -int flac_decode(OutputBuffer * cb, AudioFormat * af, DecoderControl * dc) { - if(flac_getAudioFormatAndTime(dc->file,af,&(cb->totalTime))<0) { - ERROR("\"%s\" doesn't seem to be a flac\n",dc->file); - return -1; - } - - dc->state = DECODE_STATE_DECODE; - dc->start = 0; - - flacPlayFile(dc->file,cb,af,dc); - - if(dc->seek) dc->seek = 0; - - if(dc->stop) { - dc->state = DECODE_STATE_STOP; - dc->stop = 0; - } - else dc->state = DECODE_STATE_STOP; - - return 0; -} - #endif -/* vim:set shiftwidth=4 tabstop=8 expandtab: */ +/* vim:set shiftwidth=8 tabstop=8 expandtab: */ diff --git a/src/flac_decode.h b/src/flac_decode.h index db7662334..4858274f8 100644 --- a/src/flac_decode.h +++ b/src/flac_decode.h @@ -27,7 +27,5 @@ int flac_decode(OutputBuffer * cb, AudioFormat * af, DecoderControl * dc); -int getFlacTotalTime(char * file); - #endif -/* vim:set shiftwidth=4 tabstop=8 expandtab: */ +/* vim:set shiftwidth=8 tabstop=8 expandtab: */ diff --git a/src/replayGain.c b/src/replayGain.c new file mode 100644 index 000000000..ece583a06 --- /dev/null +++ b/src/replayGain.c @@ -0,0 +1,80 @@ + +#include "replayGain.h" + +#include "log.h" +#include "conf.h" + +#include +#include +#include + +/* Added 4/14/2004 by AliasMrJones */ +static int replayGainState = REPLAYGAIN_OFF; + +void initReplayGainState() { + if(!getConf()[CONF_REPLAYGAIN]) return; + + if(strcmp(getConf()[CONF_REPLAYGAIN],"track")==0) { + replayGainState = REPLAYGAIN_TRACK; + } + else if(strcmp(getConf()[CONF_REPLAYGAIN],"album")==0) { + replayGainState = REPLAYGAIN_ALBUM; + } + else { + ERROR("replaygain value \"%s\" is invalid\n", + getConf()[CONF_REPLAYGAIN]); + exit(EXIT_FAILURE); + } +} + +int getReplayGainState() { + return replayGainState; +} + +float computeReplayGainScale(float gain, float peak){ + float scale; + + if(gain == 0.0) return(1); + scale = pow(10.0, gain/20.0); + if(scale > 15.0) scale = 15.0; + + if (scale * peak > 1.0) { + scale = 1.0 / peak; + } + return(scale); +} + +void doReplayGain(char * buffer, int bufferSize, AudioFormat * format, + float scale) +{ + mpd_sint16 * buffer16 = (mpd_sint16 *)buffer; + mpd_sint8 * buffer8 = (mpd_sint8 *)buffer; + mpd_sint32 temp32; + + if(scale == 1.0) return; + switch(format->bits) { + case 16: + while(bufferSize > 0){ + temp32 = *buffer16; + temp32 *= scale; + *buffer16 = temp32>32767 ? 32767 : + (temp32<-32768 ? -32768 : temp32); + buffer16++; + bufferSize-=2; + } + break; + case 8: + while(bufferSize>0){ + temp32 = *buffer8; + temp32 *= scale; + *buffer8 = temp32>127 ? 127 : + (temp32<-128 ? -128 : temp32); + buffer8++; + bufferSize--; + } + break; + default: + ERROR("%i bits not supported by doReplaygain!\n", format->bits); + } +} +/* End of added code */ diff --git a/src/replayGain.h b/src/replayGain.h new file mode 100644 index 000000000..6ab3897c7 --- /dev/null +++ b/src/replayGain.h @@ -0,0 +1,19 @@ +#ifndef REPLAYGAIN_H +#define REPLAYGAIN_H + +#include "audio.h" + +#define REPLAYGAIN_OFF 0 +#define REPLAYGAIN_TRACK 1 +#define REPLAYGAIN_ALBUM 2 + +void initReplayGainState(); + +int getReplayGainState(); + +float computeReplayGainScale(float gain, float peak); + +void doReplayGain(char * buffer, int bufferSize, AudioFormat * format, + float scale); + +#endif