rewrite replaygain code, needs testing
git-svn-id: https://svn.musicpd.org/mpd/trunk@2482 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
		
							
								
								
									
										4
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								TODO
									
									
									
									
									
								
							| @@ -7,10 +7,6 @@ | ||||
| *) Steal resampling code from ices (i think this code works only for 16-bit) | ||||
|  | ||||
| *) Rewrite replaygain stuff: | ||||
| 	*) Replay gain struct with album and track gain's and peak's | ||||
| 	*) Pass these to replaygain function | ||||
| 	*) Don't have deocder plugins inquire weather or not to use replaygain, | ||||
| 		they should just parse tags and pass these to replaygain func | ||||
| 	*) If replaygain tag info present, average the replaygain gain's | ||||
| 		and peak's on the fly | ||||
| 	*) If NULL replaygain struct, then use the average replaygain gains | ||||
|   | ||||
| @@ -362,7 +362,7 @@ int aac_decode(OutputBuffer * cb, DecoderControl * dc, char * path) { | ||||
| 		sampleBufferLen = sampleCount*2; | ||||
|  | ||||
| 		sendDataToOutputBuffer(cb, NULL, dc, 0, sampleBuffer, | ||||
|                                 sampleBufferLen, time, bitRate); | ||||
|                                 sampleBufferLen, time, bitRate, NULL); | ||||
| 		if(dc->seek) { | ||||
|                         dc->seekError = 1; | ||||
|                         dc->seek = 0; | ||||
|   | ||||
| @@ -116,7 +116,8 @@ int audiofile_decode(OutputBuffer * cb, DecoderControl * dc, char * path) { | ||||
|                                         ret*fs, | ||||
| 					(float)current / | ||||
| 					(float)dc->audioFormat.sampleRate, | ||||
| 					bitRate); | ||||
| 					bitRate, | ||||
| 					NULL); | ||||
| 				if(dc->stop) break; | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -44,8 +44,8 @@ typedef struct { | ||||
| 	OutputBuffer * cb; | ||||
| 	DecoderControl * dc; | ||||
|         InputStream inStream; | ||||
|         float replayGainScale; | ||||
|         char * path; | ||||
| 	ReplayGainInfo * replayGainInfo; | ||||
| } FlacData; | ||||
|  | ||||
| /* this code is based on flac123, from flac-tools */ | ||||
| @@ -82,7 +82,7 @@ int flac_decode(OutputBuffer * cb, DecoderControl *dc, char * path) { | ||||
| 	data.bitRate = 0; | ||||
| 	data.cb = cb; | ||||
| 	data.dc = dc; | ||||
|         data.replayGainScale = 1.0; | ||||
| 	data.replayGainInfo = NULL; | ||||
|         data.path = path; | ||||
|  | ||||
|         if(openInputStream(&(data.inStream), path)<0) { | ||||
| @@ -195,6 +195,8 @@ int flac_decode(OutputBuffer * cb, DecoderControl *dc, char * path) { | ||||
| 	else dc->state = DECODE_STATE_STOP; | ||||
|  | ||||
| fail: | ||||
| 	if(data.replayGainInfo) freeReplayGainInfo(data.replayGainInfo); | ||||
|  | ||||
|         if(streamOpen) closeInputStream(&(data.inStream)); | ||||
|  | ||||
| 	if(flacDec) FLAC__seekable_stream_decoder_delete(flacDec); | ||||
| @@ -338,30 +340,28 @@ int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block, char * cmnt, | ||||
|  | ||||
| /* 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(NULL == data->replayGainInfo) { | ||||
| 		freeReplayGainInfo(data->replayGainInfo); | ||||
| 		data->replayGainInfo = NULL; | ||||
| 	} | ||||
|  | ||||
|         if(state == REPLAYGAIN_OFF) return; | ||||
| 	data->replayGainInfo = newReplayGainInfo(); | ||||
|  | ||||
|         found = flacFindVorbisCommentFloat(block,"replaygain_album_gain",&gain); | ||||
|         if(found) { | ||||
|                 flacFindVorbisCommentFloat(block,"replaygain_album_peak", | ||||
|                                 &peak); | ||||
|         } | ||||
| 	int found = 0; | ||||
|  | ||||
|         if(!found || state == REPLAYGAIN_TRACK) { | ||||
|                 found = flacFindVorbisCommentFloat(block, | ||||
| 				"replaygain_track_gain", &gain); | ||||
|                 if(found) { | ||||
|                         peak = 0.0; | ||||
|                         flacFindVorbisCommentFloat(block, | ||||
|                                         "replaygain_track_peak",&peak); | ||||
|                 } | ||||
|         } | ||||
|         found &= flacFindVorbisCommentFloat(block,"replaygain_album_gain", | ||||
| 					&data->replayGainInfo->albumGain); | ||||
|         found &= flacFindVorbisCommentFloat(block,"replaygain_album_peak", | ||||
|                                 	&data->replayGainInfo->albumPeak); | ||||
|         found &= flacFindVorbisCommentFloat(block,"replaygain_track_gain", | ||||
| 					&data->replayGainInfo->trackGain); | ||||
|         found &= flacFindVorbisCommentFloat(block,"replaygain_track_peak", | ||||
|                                 	&data->replayGainInfo->trackPeak); | ||||
|  | ||||
|         if(found) data->replayGainScale = computeReplayGainScale(gain,peak); | ||||
| 	if(!found) { | ||||
| 		freeReplayGainInfo(data->replayGainInfo); | ||||
| 		data->replayGainInfo = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void flacMetadata(const FLAC__SeekableStreamDecoder *dec,  | ||||
| @@ -391,11 +391,9 @@ void flacMetadata(const FLAC__SeekableStreamDecoder *dec, | ||||
| } | ||||
|  | ||||
| int flacSendChunk(FlacData * data) { | ||||
|         doReplayGain(data->chunk,data->chunk_length,&(data->dc->audioFormat), | ||||
|                         data->replayGainScale); | ||||
|  | ||||
| 	switch(sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk, | ||||
| 			data->chunk_length, data->time, data->bitRate))  | ||||
| 			data->chunk_length, data->time, data->bitRate, | ||||
| 			data->replayGainInfo))  | ||||
| 	{ | ||||
| 	case OUTPUT_BUFFER_DC_STOP: | ||||
| 		return -1; | ||||
|   | ||||
| @@ -191,7 +191,7 @@ int mod_decode(OutputBuffer * cb, DecoderControl * dc, char * path) { | ||||
| 		time += ret*secPerByte; | ||||
| 		sendDataToOutputBuffer(cb, NULL, dc, 0,  | ||||
| 					(char *)data->audio_buffer, ret, time, | ||||
| 					0); | ||||
| 					0, NULL); | ||||
| 	} | ||||
|  | ||||
| 	flushOutputBuffer(cb); | ||||
|   | ||||
| @@ -585,7 +585,8 @@ int mp3Read(mp3DecodeData * data, OutputBuffer * cb, DecoderControl * dc) { | ||||
|                                                 data->outputPtr- | ||||
| 						data->outputBuffer, | ||||
|                                                 data->elapsedTime, | ||||
|                                                 data->bitRate/1000); | ||||
|                                                 data->bitRate/1000, | ||||
| 						NULL); | ||||
|                                 if(ret == OUTPUT_BUFFER_DC_STOP) { | ||||
| 					data->flush = 0; | ||||
|                                         return DECODE_BREAK; | ||||
| @@ -710,7 +711,8 @@ int mp3_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) { | ||||
|                                 data.inStream->seekable, | ||||
|                                 data.outputBuffer, | ||||
|                                 data.outputPtr-data.outputBuffer, | ||||
|                                 data.elapsedTime,data.bitRate/1000); | ||||
|                                 data.elapsedTime,data.bitRate/1000, | ||||
| 				NULL); | ||||
| 	} | ||||
|  | ||||
| 	closeInputStream(inStream); | ||||
|   | ||||
| @@ -284,7 +284,7 @@ int mp4_decode(OutputBuffer * cb, DecoderControl * dc, char * path) { | ||||
| 		sampleBuffer+=offset*channels*2; | ||||
|  | ||||
| 		sendDataToOutputBuffer(cb, NULL, dc, 1, sampleBuffer, | ||||
| 				sampleBufferLen, time, bitRate); | ||||
| 				sampleBufferLen, time, bitRate, NULL); | ||||
| 		if(dc->stop) { | ||||
| 			eof = 1; | ||||
| 			break; | ||||
|   | ||||
| @@ -114,62 +114,45 @@ char * ogg_parseComment(char * comment, char * needle) { | ||||
|         return NULL; | ||||
| } | ||||
|  | ||||
| float ogg_getReplayGainScale(char ** comments) { | ||||
|         int trackGainFound = 0; | ||||
|         int albumGainFound = 0; | ||||
|         float trackGain = 1.0; | ||||
|         float albumGain = 1.0; | ||||
|         float trackPeak = 0.0; | ||||
|         float albumPeak = 0.0; | ||||
| void ogg_getReplayGainInfo(char ** comments, ReplayGainInfo ** infoPtr) { | ||||
|         char * temp; | ||||
|         int replayGainState = getReplayGainState(); | ||||
| 	int found = 0; | ||||
|  | ||||
|         if(replayGainState == REPLAYGAIN_OFF) return 1.0; | ||||
| 	if(*infoPtr) freeReplayGainInfo(*infoPtr); | ||||
| 	*infoPtr = newReplayGainInfo(); | ||||
|  | ||||
|         while(*comments) { | ||||
|                 if((temp = ogg_parseComment(*comments,"replaygain_track_gain")))  | ||||
|                 { | ||||
|                         trackGain = atof(temp); | ||||
|                         trackGainFound = 1; | ||||
|                         (*infoPtr)->trackGain = atof(temp); | ||||
| 			found = 1; | ||||
|                 } | ||||
|                 else if((temp = ogg_parseComment(*comments, | ||||
|                                         "replaygain_album_gain")))  | ||||
|                 { | ||||
|                         albumGain = atof(temp); | ||||
|                         albumGainFound = 1; | ||||
|                         (*infoPtr)->albumGain = atof(temp); | ||||
| 			found = 1; | ||||
|                 } | ||||
|                 else if((temp = ogg_parseComment(*comments, | ||||
|                                         "replaygain_track_peak")))  | ||||
|                 { | ||||
|                         trackPeak = atof(temp); | ||||
|                         (*infoPtr)->trackPeak = atof(temp); | ||||
| 			found = 1; | ||||
|                 } | ||||
|                 else if((temp = ogg_parseComment(*comments, | ||||
|                                         "replaygain_album_peak")))  | ||||
|                 { | ||||
|                         albumPeak = atof(temp); | ||||
|                         (*infoPtr)->albumPeak = atof(temp); | ||||
| 			found = 1; | ||||
|                 } | ||||
|  | ||||
|                 comments++; | ||||
|         } | ||||
|  | ||||
|         switch(replayGainState) { | ||||
|         case REPLAYGAIN_ALBUM: | ||||
|                 if(albumGainFound) { | ||||
|                         return computeReplayGainScale(albumGain,albumPeak); | ||||
|                 } | ||||
| 		else if(trackGainFound) { | ||||
|                 	return computeReplayGainScale(trackGain,trackPeak); | ||||
| 		} | ||||
|         case REPLAYGAIN_TRACK: | ||||
|                 if(trackGainFound) { | ||||
|                 	return computeReplayGainScale(trackGain,trackPeak); | ||||
|                 } | ||||
| 		else if(albumGainFound) { | ||||
|                 	return computeReplayGainScale(albumGain,albumPeak); | ||||
| 		} | ||||
|         } | ||||
|  | ||||
|         return 1.0; | ||||
| 	if(!found) { | ||||
| 		freeReplayGainInfo(*infoPtr); | ||||
| 		*infoPtr = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| MpdTag * oggCommentsParse(char ** comments) { | ||||
| @@ -248,7 +231,7 @@ int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) | ||||
| 	int chunkpos = 0; | ||||
| 	long bitRate = 0; | ||||
| 	long test; | ||||
|         float replayGainScale = 1.0; | ||||
|         ReplayGainInfo * replayGainInfo = NULL; | ||||
| 	char ** comments; | ||||
|  | ||||
|         data.inStream = inStream; | ||||
| @@ -304,7 +287,7 @@ int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) | ||||
| 			comments = ov_comment(&vf, -1)->user_comments; | ||||
| 			putOggCommentsIntoOutputBuffer(cb, inStream->metaName, | ||||
| 					comments); | ||||
|         		replayGainScale = ogg_getReplayGainScale(comments); | ||||
|         		ogg_getReplayGainInfo(comments, &replayGainInfo); | ||||
| 		} | ||||
|  | ||||
| 		prev_section = current_section; | ||||
| @@ -321,14 +304,13 @@ int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) | ||||
| 			if((test = ov_bitrate_instant(&vf))>0) { | ||||
| 				bitRate = test/1000; | ||||
| 			} | ||||
|                 	doReplayGain(chunk,chunkpos,&(dc->audioFormat),  | ||||
| 					replayGainScale); | ||||
| 			sendDataToOutputBuffer(cb, inStream, dc,  | ||||
| 						inStream->seekable,   | ||||
|                                         	chunk, chunkpos,  | ||||
| 						ov_pcm_tell(&vf)/ | ||||
| 						dc->audioFormat.sampleRate, | ||||
| 						bitRate); | ||||
| 						bitRate, | ||||
| 						replayGainInfo); | ||||
| 			chunkpos = 0; | ||||
| 			if(dc->stop) break; | ||||
| 		} | ||||
| @@ -337,9 +319,11 @@ int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) | ||||
| 	if(!dc->stop && chunkpos > 0) { | ||||
| 		sendDataToOutputBuffer(cb, NULL, dc, inStream->seekable, | ||||
| 				chunk, chunkpos, | ||||
| 				ov_time_tell(&vf), bitRate); | ||||
| 				ov_time_tell(&vf), bitRate, replayGainInfo); | ||||
| 	} | ||||
|  | ||||
| 	if(replayGainInfo) freeReplayGainInfo(replayGainInfo); | ||||
|  | ||||
| 	ov_clear(&vf); | ||||
|  | ||||
| 	flushOutputBuffer(cb); | ||||
|   | ||||
| @@ -64,7 +64,8 @@ void flushOutputBuffer(OutputBuffer * cb) { | ||||
|  | ||||
| int sendDataToOutputBuffer(OutputBuffer * cb, InputStream * inStream,   | ||||
|                 DecoderControl * dc, int seekable, char * dataIn,  | ||||
|                 long dataInLen, float time, mpd_uint16 bitRate) | ||||
|                 long dataInLen, float time, mpd_uint16 bitRate, | ||||
| 		ReplayGainInfo * replayGainInfo) | ||||
| { | ||||
|         mpd_uint16 dataToSend; | ||||
| 	mpd_uint16 chunkLeft; | ||||
| @@ -91,6 +92,8 @@ int sendDataToOutputBuffer(OutputBuffer * cb, InputStream * inStream, | ||||
| 				&(cb->audioFormat),data); | ||||
| 	} | ||||
|  | ||||
| 	doReplayGain(replayGainInfo, data, datalen, &cb->audioFormat); | ||||
|  | ||||
|         while(datalen) { | ||||
| 		if(currentChunk != cb->end) { | ||||
| 	        	int next = cb->end+1; | ||||
|   | ||||
| @@ -24,6 +24,7 @@ | ||||
| #include "audio.h" | ||||
| #include "inputStream.h" | ||||
| #include "metadataChunk.h" | ||||
| #include "replayGain.h" | ||||
|  | ||||
| #define OUTPUT_BUFFER_DC_STOP   -1 | ||||
| #define OUTPUT_BUFFER_DC_SEEK   -2 | ||||
| @@ -50,9 +51,16 @@ void flushOutputBuffer(OutputBuffer * cb); | ||||
|  | ||||
| /* we send inStream for buffering the inputStream while waiting to | ||||
|    send the next chunk */ | ||||
| int sendDataToOutputBuffer(OutputBuffer * cb, InputStream * inStream, | ||||
|                 DecoderControl * dc, int seekable, char * data, long datalen,  | ||||
|                 float time, mpd_uint16 bitRate); | ||||
| int sendDataToOutputBuffer( | ||||
| 		OutputBuffer * cb,  | ||||
| 		InputStream * inStream, | ||||
|                 DecoderControl * dc,  | ||||
| 		int seekable,  | ||||
| 		char * data,  | ||||
| 		long datalen,  | ||||
|                 float time,  | ||||
| 		mpd_uint16 bitRate,  | ||||
| 		ReplayGainInfo * replayGainInfo); | ||||
|  | ||||
| int copyMpdTagToOutputBuffer(OutputBuffer * cb, MpdTag * tag); | ||||
|  | ||||
|   | ||||
| @@ -71,11 +71,7 @@ void initReplayGainState() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| int getReplayGainState() { | ||||
| 	return replayGainState; | ||||
| } | ||||
|  | ||||
| float computeReplayGainScale(float gain, float peak) { | ||||
| static float computeReplayGainScale(float gain, float peak) { | ||||
| 	float scale; | ||||
|  | ||||
| 	if(gain == 0.0) return(1); | ||||
| @@ -89,14 +85,55 @@ float computeReplayGainScale(float gain, float 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; | ||||
| ReplayGainInfo * newReplayGainInfo() { | ||||
| 	ReplayGainInfo * ret = malloc(sizeof(ReplayGainInfo)); | ||||
|  | ||||
| 	ret->albumGain = 0.0; | ||||
| 	ret->albumPeak = 1.0; | ||||
|  | ||||
| 	ret->trackGain = 0.0; | ||||
| 	ret->trackPeak = 1.0; | ||||
|  | ||||
| 	/* set to -1 so that we know in doReplayGain to compute the scale */ | ||||
| 	ret->scale = -1.0; | ||||
|  | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| void freeReplayGainInfo(ReplayGainInfo * info) { | ||||
| 	free(info); | ||||
| } | ||||
|  | ||||
| void doReplayGain(ReplayGainInfo * info, char * buffer, int bufferSize,  | ||||
| 		AudioFormat * format) | ||||
| { | ||||
| 	mpd_sint16 * buffer16; | ||||
| 	mpd_sint8 * buffer8; | ||||
| 	mpd_sint32 temp32; | ||||
| 	float scale; | ||||
|  | ||||
| 	if(replayGainState == REPLAYGAIN_OFF || !info) return; | ||||
|  | ||||
| 	if(info->scale < 0) { | ||||
| 		switch(replayGainState) { | ||||
| 		case REPLAYGAIN_TRACK: | ||||
| 			info->scale = computeReplayGainScale(info->trackGain, | ||||
| 							info->trackPeak); | ||||
| 			break; | ||||
| 		default: | ||||
| 			info->scale = computeReplayGainScale(info->albumGain, | ||||
| 							info->albumPeak); | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if(info->scale <= 1.01 && info->scale >= 0.99) return; | ||||
|  | ||||
| 	buffer16 = (mpd_sint16 *)buffer; | ||||
| 	buffer8 = (mpd_sint8 *)buffer; | ||||
|  | ||||
| 	scale = info->scale; | ||||
|  | ||||
| 	if(scale == 1.0) return; | ||||
| 	switch(format->bits) { | ||||
| 		case 16: | ||||
| 			while(bufferSize > 0){ | ||||
| @@ -122,4 +159,3 @@ void doReplayGain(char * buffer, int bufferSize, AudioFormat * format, | ||||
| 			ERROR("%i bits not supported by doReplaygain!\n", format->bits); | ||||
| 	} | ||||
| } | ||||
| /* End of added code */ | ||||
|   | ||||
| @@ -26,13 +26,23 @@ | ||||
| #define REPLAYGAIN_TRACK	1 | ||||
| #define REPLAYGAIN_ALBUM	2 | ||||
|  | ||||
| typedef struct _ReplayGainInfo { | ||||
| 	float albumGain; | ||||
| 	float albumPeak; | ||||
| 	float trackGain; | ||||
| 	float trackPeak; | ||||
|  | ||||
| 	/* used internally by mpd, to mess with it*/ | ||||
| 	float scale; | ||||
| } ReplayGainInfo; | ||||
|  | ||||
| ReplayGainInfo * newReplayGainInfo(); | ||||
|  | ||||
| void freeReplayGainInfo(ReplayGainInfo * info); | ||||
|  | ||||
| void initReplayGainState(); | ||||
|  | ||||
| int getReplayGainState(); | ||||
|  | ||||
| float computeReplayGainScale(float gain, float peak); | ||||
|  | ||||
| void doReplayGain(char * buffer, int bufferSize, AudioFormat * format,  | ||||
| 		float scale); | ||||
| void doReplayGain(ReplayGainInfo * info, char * buffer, int bufferSize,  | ||||
| 		AudioFormat * format); | ||||
|  | ||||
| #endif | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Warren Dukes
					Warren Dukes