aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Makefile.am4
-rw-r--r--src/conf.c5
-rw-r--r--src/conf.h1
-rw-r--r--src/flac_decode.c205
-rw-r--r--src/flac_decode.h4
-rw-r--r--src/replayGain.c80
-rw-r--r--src/replayGain.h19
7 files changed, 221 insertions, 97 deletions
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 <stdio.h>
#include <string.h>
@@ -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,
}
}
+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 *meta, void *data)
+ 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 <string.h>
+#include <math.h>
+#include <stdlib.h>
+
+/* 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