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; }