mp3: send 24 bit PCM data
libmad produces samples of more than 24 bit. Rounding that down to 16 bits using dithering makes those people lose quality who have a 24 bit capable sound device. Send 24 bit PCM data, and let the receiver decide whether to apply 16 bit dithering.
This commit is contained in:
parent
bf5774edbd
commit
2cc2420f8c
@ -51,77 +51,39 @@ enum muteframe {
|
|||||||
|
|
||||||
static int gaplessPlaybackEnabled;
|
static int gaplessPlaybackEnabled;
|
||||||
|
|
||||||
/* this is stolen from mpg321! */
|
static inline int32_t
|
||||||
struct audio_dither {
|
mad_fixed_to_24_sample(mad_fixed_t sample)
|
||||||
mad_fixed_t error[3];
|
|
||||||
mad_fixed_t random;
|
|
||||||
};
|
|
||||||
|
|
||||||
static unsigned long prng(unsigned long state)
|
|
||||||
{
|
{
|
||||||
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int16_t audio_linear_dither(mad_fixed_t sample,
|
|
||||||
struct audio_dither *dither)
|
|
||||||
{
|
|
||||||
mad_fixed_t output, mask, rnd;
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
bits = 16,
|
bits = 24,
|
||||||
scalebits = MAD_F_FRACBITS + 1 - bits,
|
|
||||||
MIN = -MAD_F_ONE,
|
MIN = -MAD_F_ONE,
|
||||||
MAX = MAD_F_ONE - 1
|
MAX = MAD_F_ONE - 1
|
||||||
};
|
};
|
||||||
|
|
||||||
sample += dither->error[0] - dither->error[1] + dither->error[2];
|
/* round */
|
||||||
|
sample = sample + (1L << (MAD_F_FRACBITS - bits));
|
||||||
|
|
||||||
dither->error[2] = dither->error[1];
|
/* clip */
|
||||||
dither->error[1] = dither->error[0] / 2;
|
if (sample > MAX)
|
||||||
|
sample = MAX;
|
||||||
|
else if (sample < MIN)
|
||||||
|
sample = MIN;
|
||||||
|
|
||||||
output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));
|
/* quantize */
|
||||||
|
return sample >> (MAD_F_FRACBITS + 1 - bits);
|
||||||
mask = (1L << scalebits) - 1;
|
|
||||||
|
|
||||||
rnd = prng(dither->random);
|
|
||||||
output += (rnd & mask) - (dither->random & mask);
|
|
||||||
|
|
||||||
dither->random = rnd;
|
|
||||||
|
|
||||||
if (output > MAX) {
|
|
||||||
output = MAX;
|
|
||||||
|
|
||||||
if (sample > MAX)
|
|
||||||
sample = MAX;
|
|
||||||
} else if (output < MIN) {
|
|
||||||
output = MIN;
|
|
||||||
|
|
||||||
if (sample < MIN)
|
|
||||||
sample = MIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
output &= ~mask;
|
|
||||||
|
|
||||||
dither->error[0] = sample - output;
|
|
||||||
|
|
||||||
return (int16_t)(output >> scalebits);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned dither_buffer(int16_t *dest0, const struct mad_synth *synth,
|
static void
|
||||||
struct audio_dither *dither,
|
mad_fixed_to_24_buffer(int32_t *dest, const struct mad_synth *synth,
|
||||||
unsigned int start, unsigned int end,
|
unsigned int start, unsigned int end,
|
||||||
unsigned int num_channels)
|
unsigned int num_channels)
|
||||||
{
|
{
|
||||||
int16_t *dest = dest0;
|
|
||||||
unsigned int i, c;
|
unsigned int i, c;
|
||||||
|
|
||||||
for (i = start; i < end; ++i) {
|
for (i = start; i < end; ++i) {
|
||||||
for (c = 0; c < num_channels; ++c)
|
for (c = 0; c < num_channels; ++c)
|
||||||
*dest++ = audio_linear_dither(synth->pcm.samples[c][i],
|
*dest++ = mad_fixed_to_24_sample(synth->pcm.samples[c][i]);
|
||||||
dither);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return dest - dest0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end of stolen stuff from mpg321 */
|
/* end of stolen stuff from mpg321 */
|
||||||
@ -145,7 +107,7 @@ typedef struct _mp3DecodeData {
|
|||||||
struct mad_synth synth;
|
struct mad_synth synth;
|
||||||
mad_timer_t timer;
|
mad_timer_t timer;
|
||||||
unsigned char readBuffer[READ_BUFFER_SIZE];
|
unsigned char readBuffer[READ_BUFFER_SIZE];
|
||||||
int16_t outputBuffer[MP3_DATA_OUTPUT_BUFFER_SIZE];
|
int32_t outputBuffer[MP3_DATA_OUTPUT_BUFFER_SIZE];
|
||||||
float totalTime;
|
float totalTime;
|
||||||
float elapsedTime;
|
float elapsedTime;
|
||||||
enum muteframe muteFrame;
|
enum muteframe muteFrame;
|
||||||
@ -164,7 +126,6 @@ typedef struct _mp3DecodeData {
|
|||||||
unsigned long bitRate;
|
unsigned long bitRate;
|
||||||
struct decoder *decoder;
|
struct decoder *decoder;
|
||||||
InputStream *inStream;
|
InputStream *inStream;
|
||||||
struct audio_dither dither;
|
|
||||||
enum mad_layer layer;
|
enum mad_layer layer;
|
||||||
} mp3DecodeData;
|
} mp3DecodeData;
|
||||||
|
|
||||||
@ -187,7 +148,6 @@ static void initMp3DecodeData(mp3DecodeData * data, struct decoder *decoder,
|
|||||||
data->decoder = decoder;
|
data->decoder = decoder;
|
||||||
data->inStream = inStream;
|
data->inStream = inStream;
|
||||||
data->layer = 0;
|
data->layer = 0;
|
||||||
memset(&(data->dither), 0, sizeof(struct audio_dither));
|
|
||||||
|
|
||||||
mad_stream_init(&data->stream);
|
mad_stream_init(&data->stream);
|
||||||
mad_stream_options(&data->stream, MAD_OPTION_IGNORECRC);
|
mad_stream_options(&data->stream, MAD_OPTION_IGNORECRC);
|
||||||
@ -933,10 +893,11 @@ mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
|
|||||||
|
|
||||||
i += num_samples;
|
i += num_samples;
|
||||||
|
|
||||||
num_samples = dither_buffer(data->outputBuffer,
|
mad_fixed_to_24_buffer(data->outputBuffer,
|
||||||
&data->synth, &data->dither,
|
&data->synth,
|
||||||
i - num_samples, i,
|
i - num_samples, i,
|
||||||
MAD_NCHANNELS(&(data->frame).header));
|
MAD_NCHANNELS(&(data->frame).header));
|
||||||
|
num_samples *= MAD_NCHANNELS(&(data->frame).header);
|
||||||
|
|
||||||
cmd = decoder_data(decoder, data->inStream,
|
cmd = decoder_data(decoder, data->inStream,
|
||||||
data->inStream->seekable,
|
data->inStream->seekable,
|
||||||
@ -1022,7 +983,7 @@ mp3Read(mp3DecodeData * data, ReplayGainInfo ** replayGainInfo)
|
|||||||
static void initAudioFormatFromMp3DecodeData(mp3DecodeData * data,
|
static void initAudioFormatFromMp3DecodeData(mp3DecodeData * data,
|
||||||
struct audio_format * af)
|
struct audio_format * af)
|
||||||
{
|
{
|
||||||
af->bits = 16;
|
af->bits = 24;
|
||||||
af->sample_rate = (data->frame).header.samplerate;
|
af->sample_rate = (data->frame).header.samplerate;
|
||||||
af->channels = MAD_NCHANNELS(&(data->frame).header);
|
af->channels = MAD_NCHANNELS(&(data->frame).header);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user