configure shout encoding quality and audio format
git-svn-id: https://svn.musicpd.org/mpd/trunk@2307 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
		
							
								
								
									
										48
									
								
								src/audio.c
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								src/audio.c
									
									
									
									
									
								
							| @@ -66,22 +66,27 @@ void getOutputAudioFormat(AudioFormat * inAudioFormat, | |||||||
|  |  | ||||||
| void initAudioConfig() { | void initAudioConfig() { | ||||||
|         char * conf = getConf()[CONF_AUDIO_OUTPUT_FORMAT]; |         char * conf = getConf()[CONF_AUDIO_OUTPUT_FORMAT]; | ||||||
|         char * test; |  | ||||||
|  |  | ||||||
|         if(NULL == conf) return; |         if(NULL == conf) return; | ||||||
|  |  | ||||||
|         audio_configFormat = malloc(sizeof(AudioFormat)); |         audio_configFormat = malloc(sizeof(AudioFormat)); | ||||||
|  |  | ||||||
|         memset(audio_configFormat,0,sizeof(AudioFormat)); | 	if(0 != parseAudioConfig(audio_configFormat, conf)) exit(EXIT_FAILURE); | ||||||
|  | } | ||||||
|  |  | ||||||
|         audio_configFormat->sampleRate = strtol(conf,&test,10); | int parseAudioConfig(AudioFormat * audioFormat, char * conf) { | ||||||
|  |         char * test; | ||||||
|  |  | ||||||
|  |         memset(audioFormat,0,sizeof(AudioFormat)); | ||||||
|  |  | ||||||
|  |         audioFormat->sampleRate = strtol(conf,&test,10); | ||||||
|         |         | ||||||
|         if(*test!=':') { |         if(*test!=':') { | ||||||
|                 ERROR("error parsing audio output format: %s\n",conf); |                 ERROR("error parsing audio output format: %s\n",conf); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1; | ||||||
|         } |         } | ||||||
|   |   | ||||||
|         /*switch(audio_configFormat->sampleRate) { |         /*switch(audioFormat->sampleRate) { | ||||||
|         case 48000: |         case 48000: | ||||||
|         case 44100: |         case 44100: | ||||||
|         case 32000: |         case 32000: | ||||||
| @@ -89,47 +94,50 @@ void initAudioConfig() { | |||||||
|                 break; |                 break; | ||||||
|         default: |         default: | ||||||
|                 ERROR("sample rate %i can not be used for audio output\n", |                 ERROR("sample rate %i can not be used for audio output\n", | ||||||
|                         (int)audio_configFormat->sampleRate); |                         (int)audioFormat->sampleRate); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1 | ||||||
|         }*/ |         }*/ | ||||||
|  |  | ||||||
|         if(audio_configFormat->sampleRate <= 0) { |         if(audioFormat->sampleRate <= 0) { | ||||||
|                 ERROR("sample rate %i is not >= 0\n", |                 ERROR("sample rate %i is not >= 0\n", | ||||||
|                                 (int)audio_configFormat->sampleRate); |                                 (int)audioFormat->sampleRate); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         audio_configFormat->bits = strtol(test+1,&test,10); |         audioFormat->bits = strtol(test+1,&test,10); | ||||||
|          |          | ||||||
|         if(*test!=':') { |         if(*test!=':') { | ||||||
|                 ERROR("error parsing audio output format: %s\n",conf); |                 ERROR("error parsing audio output format: %s\n",conf); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         switch(audio_configFormat->bits) { |         switch(audioFormat->bits) { | ||||||
|         case 16: |         case 16: | ||||||
|                 break; |                 break; | ||||||
|         default: |         default: | ||||||
|                 ERROR("bits %i can not be used for audio output\n", |                 ERROR("bits %i can not be used for audio output\n", | ||||||
|                         (int)audio_configFormat->bits); |                         (int)audioFormat->bits); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         audio_configFormat->channels = strtol(test+1,&test,10); |         audioFormat->channels = strtol(test+1,&test,10); | ||||||
|          |          | ||||||
|         if(*test!='\0') { |         if(*test!='\0') { | ||||||
|                 ERROR("error parsing audio output format: %s\n",conf); |                 ERROR("error parsing audio output format: %s\n",conf); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         switch(audio_configFormat->channels) { |         switch(audioFormat->channels) { | ||||||
|  | 	case 1: | ||||||
|         case 2: |         case 2: | ||||||
|                 break; |                 break; | ||||||
|         default: |         default: | ||||||
|                 ERROR("channels %i can not be used for audio output\n", |                 ERROR("channels %i can not be used for audio output\n", | ||||||
|                         (int)audio_configFormat->channels); |                         (int)audioFormat->channels); | ||||||
|                 exit(EXIT_FAILURE); | 		return -1; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| void finishAudioConfig() { | void finishAudioConfig() { | ||||||
|   | |||||||
| @@ -35,6 +35,8 @@ typedef struct _AudioFormat { | |||||||
|  |  | ||||||
| void getOutputAudioFormat(AudioFormat * inFormat, AudioFormat * outFormat); | void getOutputAudioFormat(AudioFormat * inFormat, AudioFormat * outFormat); | ||||||
|  |  | ||||||
|  | int parseAudioConfig(AudioFormat * audioFormat, char * conf); | ||||||
|  |  | ||||||
| void initAudioConfig(); | void initAudioConfig(); | ||||||
|  |  | ||||||
| void finishAudioConfig(); | void finishAudioConfig(); | ||||||
|   | |||||||
| @@ -24,6 +24,7 @@ | |||||||
| #include "conf.h" | #include "conf.h" | ||||||
| #include "log.h" | #include "log.h" | ||||||
| #include "sig_handlers.h" | #include "sig_handlers.h" | ||||||
|  | #include "pcm_utils.h" | ||||||
|  |  | ||||||
| #include <stdlib.h> | #include <stdlib.h> | ||||||
| #include <string.h> | #include <string.h> | ||||||
| @@ -54,6 +55,15 @@ typedef struct _ShoutData { | |||||||
| 	vorbis_comment vc; | 	vorbis_comment vc; | ||||||
|  |  | ||||||
| 	int serialno; | 	int serialno; | ||||||
|  |  | ||||||
|  | 	float quality; | ||||||
|  | 	AudioFormat outAudioFormat; | ||||||
|  | 	AudioFormat inAudioFormat; | ||||||
|  |  | ||||||
|  | 	char * convBuffer; | ||||||
|  | 	long convBufferLen; | ||||||
|  | 	/* shoud we convert the audio to a different format? */ | ||||||
|  | 	int audioFormatConvert; | ||||||
| } ShoutData; | } ShoutData; | ||||||
|  |  | ||||||
| static ShoutData * newShoutData() { | static ShoutData * newShoutData() { | ||||||
| @@ -61,6 +71,8 @@ static ShoutData * newShoutData() { | |||||||
|  |  | ||||||
| 	ret->shoutConn = shout_new(); | 	ret->shoutConn = shout_new(); | ||||||
| 	ret->serialno = rand(); | 	ret->serialno = rand(); | ||||||
|  | 	ret->convBuffer = NULL; | ||||||
|  | 	ret->convBufferLen = 0; | ||||||
|  |  | ||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
| @@ -112,6 +124,16 @@ static int shout_initDriver(AudioOutput * audioOutput) { | |||||||
| 		exit(EXIT_FAILURE); | 		exit(EXIT_FAILURE); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if(!getConf()[CONF_SHOUT_QUALITY]) { | ||||||
|  | 		ERROR("shout host defined but not shout quality\n"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if(!getConf()[CONF_SHOUT_FORMAT]) { | ||||||
|  | 		ERROR("shout host defined but not shout format\n"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	host = getConf()[CONF_SHOUT_HOST]; | 	host = getConf()[CONF_SHOUT_HOST]; | ||||||
| 	passwd = getConf()[CONF_SHOUT_PASSWD]; | 	passwd = getConf()[CONF_SHOUT_PASSWD]; | ||||||
| 	user = getConf()[CONF_SHOUT_USER]; | 	user = getConf()[CONF_SHOUT_USER]; | ||||||
| @@ -126,6 +148,20 @@ static int shout_initDriver(AudioOutput * audioOutput) { | |||||||
| 		exit(EXIT_FAILURE); | 		exit(EXIT_FAILURE); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	sd->quality = strtod(getConf()[CONF_SHOUT_QUALITY], &test); | ||||||
|  |  | ||||||
|  | 	if(*test != '\0' || sd->quality < 0.0 || sd->quality > 10.0) { | ||||||
|  | 		ERROR("shout quality \"%s\" is not a number in the range " | ||||||
|  | 				"0-10\n", getConf()[CONF_SHOUT_QUALITY]); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if(0 != parseAudioConfig(&(sd->outAudioFormat),  | ||||||
|  | 			getConf()[CONF_SHOUT_FORMAT]) ) | ||||||
|  | 	{ | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if(shout_set_host(sd->shoutConn, host) !=  SHOUTERR_SUCCESS || | 	if(shout_set_host(sd->shoutConn, host) !=  SHOUTERR_SUCCESS || | ||||||
| 		shout_set_port(sd->shoutConn, port) != SHOUTERR_SUCCESS || | 		shout_set_port(sd->shoutConn, port) != SHOUTERR_SUCCESS || | ||||||
| 		shout_set_password(sd->shoutConn, passwd) != SHOUTERR_SUCCESS || | 		shout_set_password(sd->shoutConn, passwd) != SHOUTERR_SUCCESS || | ||||||
| @@ -202,8 +238,8 @@ static int shout_openDevice(AudioOutput * audioOutput, | |||||||
|  |  | ||||||
| 	vorbis_info_init(&(sd->vi)); | 	vorbis_info_init(&(sd->vi)); | ||||||
|  |  | ||||||
| 	if( 0 != vorbis_encode_init_vbr(&(sd->vi), audioFormat->channels, | 	if( 0 != vorbis_encode_init_vbr(&(sd->vi), sd->outAudioFormat.channels, | ||||||
| 			audioFormat->sampleRate, 0.5) ) | 			sd->outAudioFormat.sampleRate, sd->quality) ) | ||||||
| 	{ | 	{ | ||||||
| 		ERROR("problem seting up vorbis encoder for shout\n"); | 		ERROR("problem seting up vorbis encoder for shout\n"); | ||||||
| 		vorbis_info_clear(&(sd->vi)); | 		vorbis_info_clear(&(sd->vi)); | ||||||
| @@ -230,19 +266,59 @@ static int shout_openDevice(AudioOutput * audioOutput, | |||||||
| 		write_page(sd); | 		write_page(sd); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	memcpy(&(sd->inAudioFormat), audioFormat, sizeof(AudioFormat)); | ||||||
|  |  | ||||||
|  | 	if(0 == memcmp(&(sd->inAudioFormat), &(sd->outAudioFormat),  | ||||||
|  | 			sizeof(AudioFormat)))  | ||||||
|  | 	{ | ||||||
|  | 		sd->audioFormatConvert = 0; | ||||||
|  | 	} | ||||||
|  | 	else sd->audioFormatConvert = 1; | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void shout_convertAudioFormat(ShoutData * sd, char ** chunkArgPtr, | ||||||
|  | 		int * sizeArgPtr) | ||||||
|  | { | ||||||
|  | 	int size = pcm_sizeOfOutputBufferForAudioFormatConversion( | ||||||
|  | 			&(sd->inAudioFormat), *sizeArgPtr,  | ||||||
|  | 			&(sd->outAudioFormat)); | ||||||
|  |  | ||||||
|  | 	if(size > sd->convBufferLen) { | ||||||
|  | 		sd->convBuffer = realloc(sd->convBuffer, size); | ||||||
|  | 		sd->convBufferLen = size; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	pcm_convertAudioFormat(&(sd->inAudioFormat), *chunkArgPtr, *sizeArgPtr, | ||||||
|  | 			&(sd->outAudioFormat), sd->convBuffer); | ||||||
|  | 	 | ||||||
|  | 	*sizeArgPtr = size; | ||||||
|  | 	*chunkArgPtr = sd->convBuffer; | ||||||
|  | } | ||||||
|  |  | ||||||
| static int shout_play(AudioOutput * audioOutput, char * playChunk, int size) { | static int shout_play(AudioOutput * audioOutput, char * playChunk, int size) { | ||||||
| 	int i,j; | 	int i,j; | ||||||
| 	ShoutData * sd = (ShoutData *)audioOutput->data; | 	ShoutData * sd = (ShoutData *)audioOutput->data; | ||||||
|  | 	float ** vorbbuf; | ||||||
|  | 	int samples; | ||||||
|  | 	int bytes = sd->outAudioFormat.bits/8; | ||||||
|  |  | ||||||
| 	float **vorbbuf = vorbis_analysis_buffer(&(sd->vd), size/4); | 	if(sd->audioFormatConvert) { | ||||||
|  | 		shout_convertAudioFormat(sd, &playChunk, &size); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for(i=0, j=0; i < size; i+=4, j++) { | 	samples = size/(bytes*sd->outAudioFormat.channels); | ||||||
| 		vorbbuf[0][j] = (*((mpd_sint16 *)(playChunk+i))) / 32768.0; |  | ||||||
| 		vorbbuf[1][j] = (*((mpd_sint16 *)(playChunk+i+2))) / 32768.0; | 	/* this is for only 16-bit audio */ | ||||||
|  |  | ||||||
|  | 	vorbbuf = vorbis_analysis_buffer(&(sd->vd), samples); | ||||||
|  |  | ||||||
|  | 	for(i=0; i<samples; i++) { | ||||||
|  | 		for(j=0; j<sd->outAudioFormat.channels; j++) { | ||||||
|  | 			vorbbuf[j][i] = (*((mpd_sint16 *)playChunk)) / 32768.0; | ||||||
|  | 			playChunk += bytes; | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	vorbis_analysis_wrote(&(sd->vd), size/4); | 	vorbis_analysis_wrote(&(sd->vd), size/4); | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ | |||||||
|  |  | ||||||
| #define CONF_COMMENT	'#' | #define CONF_COMMENT	'#' | ||||||
|  |  | ||||||
| #define CONF_NUMBER_OF_PARAMS		42 | #define CONF_NUMBER_OF_PARAMS		43 | ||||||
| #define CONF_NUMBER_OF_PATHS		6 | #define CONF_NUMBER_OF_PATHS		6 | ||||||
| #define CONF_NUMBER_OF_REQUIRED		5 | #define CONF_NUMBER_OF_REQUIRED		5 | ||||||
| #define CONF_NUMBER_OF_ALLOW_CATS	1 | #define CONF_NUMBER_OF_ALLOW_CATS	1 | ||||||
| @@ -138,7 +138,8 @@ char ** readConf(char * file) { | |||||||
| 		"shout_name", | 		"shout_name", | ||||||
| 		"shout_user", | 		"shout_user", | ||||||
| 		"shout_quality", | 		"shout_quality", | ||||||
| 		"id3v1_encoding" | 		"id3v1_encoding", | ||||||
|  | 		"shout_format" | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	int conf_absolutePaths[CONF_NUMBER_OF_PATHS] = { | 	int conf_absolutePaths[CONF_NUMBER_OF_PATHS] = { | ||||||
|   | |||||||
| @@ -63,6 +63,7 @@ | |||||||
| #define CONF_SHOUT_USER				39 | #define CONF_SHOUT_USER				39 | ||||||
| #define CONF_SHOUT_QUALITY			40 | #define CONF_SHOUT_QUALITY			40 | ||||||
| #define CONF_ID3V1_ENCODING			41 | #define CONF_ID3V1_ENCODING			41 | ||||||
|  | #define CONF_SHOUT_FORMAT			42 | ||||||
|  |  | ||||||
| #define CONF_CAT_CHAR				"\n" | #define CONF_CAT_CHAR				"\n" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -80,7 +80,7 @@ int sendDataToOutputBuffer(OutputBuffer * cb, InputStream * inStream, | |||||||
| 	} | 	} | ||||||
| 	else { | 	else { | ||||||
| 		datalen = pcm_sizeOfOutputBufferForAudioFormatConversion( | 		datalen = pcm_sizeOfOutputBufferForAudioFormatConversion( | ||||||
| 				&(dc->audioFormat), dataIn, dataInLen, | 				&(dc->audioFormat), dataInLen, | ||||||
| 				&(cb->audioFormat)); | 				&(cb->audioFormat)); | ||||||
| 		if(datalen > convBufferLen) { | 		if(datalen > convBufferLen) { | ||||||
| 			convBuffer = realloc(convBuffer,datalen); | 			convBuffer = realloc(convBuffer,datalen); | ||||||
|   | |||||||
| @@ -153,7 +153,7 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t | |||||||
| 	int dataBitLen; | 	int dataBitLen; | ||||||
|  |  | ||||||
| 	assert(outFormat->bits==16); | 	assert(outFormat->bits==16); | ||||||
| 	assert(outFormat->channels==2); | 	assert(outFormat->channels==2 || outFormat->channels==1); | ||||||
|  |  | ||||||
| 	/* converts */ | 	/* converts */ | ||||||
| 	switch(inFormat->bits) { | 	switch(inFormat->bits) { | ||||||
| @@ -185,7 +185,14 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* converts only between 16 bit audio between mono and stereo */ | 	/* converts only between 16 bit audio between mono and stereo */ | ||||||
|  | 	if(inFormat->channels == outFormat->channels) | ||||||
|  | 	{ | ||||||
|  | 		dataChannelConv = dataBitConv; | ||||||
|  | 		dataChannelLen = dataBitLen; | ||||||
|  | 	} | ||||||
|  | 	else { | ||||||
| 		switch(inFormat->channels) { | 		switch(inFormat->channels) { | ||||||
|  | 		/* convert from 1 -> 2 channels */ | ||||||
| 		case 1: | 		case 1: | ||||||
| 			dataChannelLen = (dataBitLen >> 1) << 2; | 			dataChannelLen = (dataBitLen >> 1) << 2; | ||||||
| 			if(dataChannelLen > channelConvBufferLength) { | 			if(dataChannelLen > channelConvBufferLength) { | ||||||
| @@ -204,14 +211,30 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			break; | 			break; | ||||||
|  | 		/* convert from 2 -> 1 channels */ | ||||||
| 		case 2: | 		case 2: | ||||||
| 		dataChannelConv = dataBitConv; | 			dataChannelLen = dataBitLen >> 1; | ||||||
| 		dataChannelLen = dataBitLen; | 			if(dataChannelLen > channelConvBufferLength) { | ||||||
|  | 				channelConvBuffer = realloc(channelConvBuffer, | ||||||
|  | 						dataChannelLen); | ||||||
|  | 				channelConvBufferLength = dataChannelLen; | ||||||
|  | 			} | ||||||
|  | 			dataChannelConv = channelConvBuffer; | ||||||
|  | 			{ | ||||||
|  | 				mpd_sint16 * in = (mpd_sint16 *)dataBitConv; | ||||||
|  | 				mpd_sint16 * out = (mpd_sint16 *)dataChannelConv; | ||||||
|  | 				int i, inSamples = dataBitLen >> 2; | ||||||
|  | 				for(i=0;i<inSamples;i++) { | ||||||
|  | 					*out = (*in++)/2; | ||||||
|  | 					*out++ += (*in++)/2; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
| 			break; | 			break; | ||||||
| 		default: | 		default: | ||||||
| 			ERROR("only 1 or 2 channels are supported for conversion!\n"); | 			ERROR("only 1 or 2 channels are supported for conversion!\n"); | ||||||
| 			exit(EXIT_FAILURE); | 			exit(EXIT_FAILURE); | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if(inFormat->sampleRate == outFormat->sampleRate) { | 	if(inFormat->sampleRate == outFormat->sampleRate) { | ||||||
| 		memcpy(outBuffer,dataChannelConv,dataChannelLen); | 		memcpy(outBuffer,dataChannelConv,dataChannelLen); | ||||||
| @@ -247,7 +270,7 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t | |||||||
| } | } | ||||||
|  |  | ||||||
| size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, | size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, | ||||||
| 		char * inBuffer, size_t inSize, AudioFormat * outFormat) | 		size_t inSize, AudioFormat * outFormat) | ||||||
| { | { | ||||||
| 	const int shift = sizeof(mpd_sint16); | 	const int shift = sizeof(mpd_sint16); | ||||||
| 	size_t outSize = inSize; | 	size_t outSize = inSize; | ||||||
|   | |||||||
| @@ -37,6 +37,6 @@ void pcm_convertAudioFormat(AudioFormat * inFormat, char * inBuffer, size_t | |||||||
|                 inSize, AudioFormat * outFormat, char * outBuffer); |                 inSize, AudioFormat * outFormat, char * outBuffer); | ||||||
|  |  | ||||||
| size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, | size_t pcm_sizeOfOutputBufferForAudioFormatConversion(AudioFormat * inFormat, | ||||||
| 		char * inBuffer, size_t inSize, AudioFormat * outFormat); | 		size_t inSize, AudioFormat * outFormat); | ||||||
| #endif | #endif | ||||||
| /* vim:set shiftwidth=8 tabstop=8 expandtab: */ | /* vim:set shiftwidth=8 tabstop=8 expandtab: */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Warren Dukes
					Warren Dukes