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:
parent
413bf61ea1
commit
12597322a2
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,32 +185,55 @@ 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 */
|
||||||
switch(inFormat->channels) {
|
if(inFormat->channels == outFormat->channels)
|
||||||
case 1:
|
{
|
||||||
dataChannelLen = (dataBitLen >> 1) << 2;
|
|
||||||
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 >> 1;
|
|
||||||
for(i=0;i<inSamples;i++) {
|
|
||||||
*out++ = *in;
|
|
||||||
*out++ = *in++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
dataChannelConv = dataBitConv;
|
dataChannelConv = dataBitConv;
|
||||||
dataChannelLen = dataBitLen;
|
dataChannelLen = dataBitLen;
|
||||||
break;
|
}
|
||||||
default:
|
else {
|
||||||
ERROR("only 1 or 2 channels are supported for conversion!\n");
|
switch(inFormat->channels) {
|
||||||
exit(EXIT_FAILURE);
|
/* convert from 1 -> 2 channels */
|
||||||
|
case 1:
|
||||||
|
dataChannelLen = (dataBitLen >> 1) << 2;
|
||||||
|
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 >> 1;
|
||||||
|
for(i=0;i<inSamples;i++) {
|
||||||
|
*out++ = *in;
|
||||||
|
*out++ = *in++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
/* convert from 2 -> 1 channels */
|
||||||
|
case 2:
|
||||||
|
dataChannelLen = dataBitLen >> 1;
|
||||||
|
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;
|
||||||
|
default:
|
||||||
|
ERROR("only 1 or 2 channels are supported for conversion!\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(inFormat->sampleRate == outFormat->sampleRate) {
|
if(inFormat->sampleRate == outFormat->sampleRate) {
|
||||||
@ -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: */
|
||||||
|
Loading…
Reference in New Issue
Block a user