Hi all!
Those acquainted with madlld (libMAD low-level demo) may have noticed the rather poor PCM output which comprises lots of noticeable sound dropouts.
This is related to a quick'n'dirty 16bit truncation done by MadFixedToUshort.
I spent several hours into gathering information about this problem and analysing the madplay package which provides better algorithms for this task.
Finally I wrote a header to be included in madlld.cpp.
I just want to keep you up to date with my implementation, so I've attached the header.
Please drop me a note if you find it useful.
Note that the copyright of the header still is retained by the madplay authors.
Good bye and have fun!
Jens Lippmann
---
How to incorporate the header:
1. Add these lines below the include section in madlld.cpp:
#include "madplay.h"
struct audio_stats Audio_stats;
struct audio_dither Audio_dither;
//#define MadFixedToUshort MadFixedToUshortOrig
#define MadFixedToUshort(a) (unsigned short)audio_linear_round(16,a,&Audio_stats)
//#define MadFixedToUshort(a) (unsigned short)audio_linear_dither(16,a,&Audio_dither,&Audio_stats)
2. Rename the function MadFixedToUshort into MadFixedToUshortOrig.
3. Clear Audio_stats and Audio_dither before you call MpegAudioDecoder (the decoding loop):
ZeroMemory(&Audio_stats,sizeof(Audio_stats));
ZeroMemory(&Audio_dither,sizeof(Audio_dither));
4. Convert your MP3 stream to PCM and listen with awe. ;->
---
The header follows below (I do not want to add it as an enclosure because it might not show up in the list archive then).
8< 8< 8< 8< 8< 8< 8< 8< 8< 8< 8< 8< 8< 8< 8<
/*
* madplay - MPEG audio decoder and player
* Copyright (C) 2000-2004 Robert Leslie
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
struct audio_stats {
unsigned long clipped_samples;
mad_fixed_t peak_clipping;
mad_fixed_t peak_sample;
};
struct audio_dither {
mad_fixed_t error[3];
mad_fixed_t random;
};
/*
* NAME: clip()
* DESCRIPTION: gather signal statistics while clipping
*/
static inline
void clip(mad_fixed_t *sample, struct audio_stats *stats)
{
enum {
MIN = -MAD_F_ONE,
MAX = MAD_F_ONE - 1
};
if (*sample >= stats->peak_sample) {
if (*sample > MAX) {
++stats->clipped_samples;
if (*sample - MAX > stats->peak_clipping)
stats->peak_clipping = *sample - MAX;
*sample = MAX;
}
stats->peak_sample = *sample;
}
else if (*sample < -stats->peak_sample) {
if (*sample < MIN) {
++stats->clipped_samples;
if (MIN - *sample > stats->peak_clipping)
stats->peak_clipping = MIN - *sample;
*sample = MIN;
}
stats->peak_sample = -*sample;
}
}
/*
* NAME: audio_linear_round()
* DESCRIPTION: generic linear sample quantize routine
*/
# if defined(_MSC_VER)
extern /* needed to satisfy bizarre MSVC++ interaction with inline */
# endif
inline
signed long audio_linear_round(unsigned int bits, mad_fixed_t sample,
struct audio_stats *stats)
{
/* round */
sample += (1L << (MAD_F_FRACBITS - bits));
/* clip */
clip(&sample, stats);
/* quantize and scale */
return sample >> (MAD_F_FRACBITS + 1 - bits);
}
/*
* NAME: prng()
* DESCRIPTION: 32-bit pseudo-random number generator
*/
static inline
unsigned long prng(unsigned long state)
{
return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
}
/*
* NAME: audio_linear_dither()
* DESCRIPTION: generic linear sample quantize and dither routine
*/
# if defined(_MSC_VER)
extern /* needed to satisfy bizarre MSVC++ interaction with inline */
# endif
inline
signed long audio_linear_dither(unsigned int bits, mad_fixed_t sample,
struct audio_dither *dither,
struct audio_stats *stats)
{
unsigned int scalebits;
mad_fixed_t output, mask, random;
enum {
MIN = -MAD_F_ONE,
MAX = MAD_F_ONE - 1
};
/* noise shape */
sample += dither->error[0] - dither->error[1] + dither->error[2];
dither->error[2] = dither->error[1];
dither->error[1] = dither->error[0] / 2;
/* bias */
output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));
scalebits = MAD_F_FRACBITS + 1 - bits;
mask = (1L << scalebits) - 1;
/* dither */
random = prng(dither->random);
output += (random & mask) - (dither->random & mask);
dither->random = random;
/* clip */
if (output >= stats->peak_sample) {
if (output > MAX) {
++stats->clipped_samples;
if (output - MAX > stats->peak_clipping)
stats->peak_clipping = output - MAX;
output = MAX;
if (sample > MAX)
sample = MAX;
}
stats->peak_sample = output;
}
else if (output < -stats->peak_sample) {
if (output < MIN) {
++stats->clipped_samples;
if (MIN - output > stats->peak_clipping)
stats->peak_clipping = MIN - output;
output = MIN;
if (sample < MIN)
sample = MIN;
}
stats->peak_sample = -output;
}
/* quantize */
output &= ~mask;
/* error feedback */
dither->error[0] = sample - output;
/* scale */
return output >> scalebits;
}