compress: upgraded to AudioCompress 2.0
Copied sources from http://beesbuzz.biz/code/audiocompress/AudioCompress-2.0.tar.gz [mk: created this patch under fluffy's name and fixed some gcc signed/unsigned comparison warnings]
This commit is contained in:
parent
3857bb9990
commit
4076523198
@ -116,7 +116,7 @@ mpd_headers = \
|
|||||||
src/mixer/pulse_mixer_plugin.h \
|
src/mixer/pulse_mixer_plugin.h \
|
||||||
src/daemon.h \
|
src/daemon.h \
|
||||||
src/normalize.h \
|
src/normalize.h \
|
||||||
src/compress.h \
|
src/AudioCompress/compress.h \
|
||||||
src/buffer.h \
|
src/buffer.h \
|
||||||
src/pipe.h \
|
src/pipe.h \
|
||||||
src/chunk.h \
|
src/chunk.h \
|
||||||
@ -253,7 +253,7 @@ src_mpd_SOURCES = \
|
|||||||
src/event_pipe.c \
|
src/event_pipe.c \
|
||||||
src/daemon.c \
|
src/daemon.c \
|
||||||
src/normalize.c \
|
src/normalize.c \
|
||||||
src/compress.c \
|
src/AudioCompress/compress.c \
|
||||||
src/buffer.c \
|
src/buffer.c \
|
||||||
src/pipe.c \
|
src/pipe.c \
|
||||||
src/chunk.c \
|
src/chunk.c \
|
||||||
|
1
NEWS
1
NEWS
@ -62,6 +62,7 @@ ver 0.16 (20??/??/??)
|
|||||||
- support .mpdignore files in the music directory
|
- support .mpdignore files in the music directory
|
||||||
- sort songs by album name first, then disc/track number
|
- sort songs by album name first, then disc/track number
|
||||||
- rescan after metadata_to_use change
|
- rescan after metadata_to_use change
|
||||||
|
* normalize: upgraded to AudioCompress 2.0
|
||||||
* log unused/unknown block parameters
|
* log unused/unknown block parameters
|
||||||
* removed the deprecated "error_file" option
|
* removed the deprecated "error_file" option
|
||||||
* save state when stopped
|
* save state when stopped
|
||||||
|
184
src/AudioCompress/compress.c
Normal file
184
src/AudioCompress/compress.c
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
/* compress.c
|
||||||
|
* Compressor logic
|
||||||
|
*
|
||||||
|
* (c)2007 busybee (http://beesbuzz.biz/
|
||||||
|
* Licensed under the terms of the LGPL. See the file COPYING for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "compress.h"
|
||||||
|
|
||||||
|
struct Compressor {
|
||||||
|
//! The compressor's preferences
|
||||||
|
struct CompressorConfig prefs;
|
||||||
|
|
||||||
|
//! History of the peak values
|
||||||
|
int *peaks;
|
||||||
|
|
||||||
|
//! History of the gain values
|
||||||
|
int *gain;
|
||||||
|
|
||||||
|
//! History of clip amounts
|
||||||
|
int *clipped;
|
||||||
|
|
||||||
|
unsigned int pos;
|
||||||
|
unsigned int bufsz;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Compressor *Compressor_new(unsigned int history)
|
||||||
|
{
|
||||||
|
struct Compressor *obj = malloc(sizeof(struct Compressor));
|
||||||
|
|
||||||
|
obj->prefs.target = TARGET;
|
||||||
|
obj->prefs.maxgain = GAINMAX;
|
||||||
|
obj->prefs.smooth = GAINSMOOTH;
|
||||||
|
|
||||||
|
obj->peaks = obj->gain = obj->clipped = NULL;
|
||||||
|
obj->bufsz = 0;
|
||||||
|
obj->pos = 0;
|
||||||
|
|
||||||
|
Compressor_setHistory(obj, history);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compressor_delete(struct Compressor *obj)
|
||||||
|
{
|
||||||
|
if (obj->peaks)
|
||||||
|
free(obj->peaks);
|
||||||
|
if (obj->gain)
|
||||||
|
free(obj->gain);
|
||||||
|
if (obj->clipped)
|
||||||
|
free(obj->clipped);
|
||||||
|
free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int *resizeArray(int *data, int newsz, int oldsz)
|
||||||
|
{
|
||||||
|
data = realloc(data, newsz*sizeof(int));
|
||||||
|
if (newsz > oldsz)
|
||||||
|
memset(data + oldsz, 0, sizeof(int)*(newsz - oldsz));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compressor_setHistory(struct Compressor *obj, unsigned int history)
|
||||||
|
{
|
||||||
|
if (!history)
|
||||||
|
history = BUCKETS;
|
||||||
|
|
||||||
|
obj->peaks = resizeArray(obj->peaks, history, obj->bufsz);
|
||||||
|
obj->gain = resizeArray(obj->gain, history, obj->bufsz);
|
||||||
|
obj->clipped = resizeArray(obj->clipped, history, obj->bufsz);
|
||||||
|
obj->bufsz = history;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CompressorConfig *Compressor_getConfig(struct Compressor *obj)
|
||||||
|
{
|
||||||
|
return &obj->prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compressor_Process_int16(struct Compressor *obj, int16_t *audio,
|
||||||
|
unsigned int count)
|
||||||
|
{
|
||||||
|
struct CompressorConfig *prefs = Compressor_getConfig(obj);
|
||||||
|
int16_t *ap;
|
||||||
|
unsigned int i;
|
||||||
|
int *peaks = obj->peaks;
|
||||||
|
int curGain = obj->gain[obj->pos];
|
||||||
|
int newGain;
|
||||||
|
int peakVal = 1;
|
||||||
|
int peakPos = 0;
|
||||||
|
int slot = (obj->pos + 1) % obj->bufsz;
|
||||||
|
int *clipped = obj->clipped + slot;
|
||||||
|
unsigned int ramp = count;
|
||||||
|
int delta;
|
||||||
|
|
||||||
|
ap = audio;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int val = *ap++;
|
||||||
|
if (val < 0)
|
||||||
|
val = -val;
|
||||||
|
if (val > peakVal)
|
||||||
|
{
|
||||||
|
peakVal = val;
|
||||||
|
peakPos = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peaks[slot] = peakVal;
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0; i < obj->bufsz; i++)
|
||||||
|
{
|
||||||
|
if (peaks[i] > peakVal)
|
||||||
|
{
|
||||||
|
peakVal = peaks[i];
|
||||||
|
peakPos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Determine target gain
|
||||||
|
newGain = (1 << 10)*prefs->target/peakVal;
|
||||||
|
|
||||||
|
//! Adjust the gain with inertia from the previous gain value
|
||||||
|
newGain = (curGain*((1 << prefs->smooth) - 1) + newGain)
|
||||||
|
>> prefs->smooth;
|
||||||
|
|
||||||
|
//! Make sure it's no more than the maximum gain value
|
||||||
|
if (newGain > (prefs->maxgain << 10))
|
||||||
|
newGain = prefs->maxgain << 10;
|
||||||
|
|
||||||
|
//! Make sure it's no less than 1:1
|
||||||
|
if (newGain < (1 << 10))
|
||||||
|
newGain = 1 << 10;
|
||||||
|
|
||||||
|
//! Make sure the adjusted gain won't cause clipping
|
||||||
|
if ((peakVal*newGain >> 10) > 32767)
|
||||||
|
{
|
||||||
|
newGain = (32767 << 10)/peakVal;
|
||||||
|
//! Truncate the ramp time
|
||||||
|
ramp = peakPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
//! Record the new gain
|
||||||
|
obj->gain[slot] = newGain;
|
||||||
|
|
||||||
|
if (!ramp)
|
||||||
|
ramp = 1;
|
||||||
|
if (!curGain)
|
||||||
|
curGain = 1 << 10;
|
||||||
|
delta = (newGain - curGain) / (int)ramp;
|
||||||
|
|
||||||
|
ap = audio;
|
||||||
|
*clipped = 0;
|
||||||
|
for (i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int sample;
|
||||||
|
|
||||||
|
//! Amplify the sample
|
||||||
|
sample = *ap*curGain >> 10;
|
||||||
|
if (sample < -32768)
|
||||||
|
{
|
||||||
|
*clipped += -32768 - sample;
|
||||||
|
sample = -32768;
|
||||||
|
} else if (sample > 32767)
|
||||||
|
{
|
||||||
|
*clipped += sample - 32767;
|
||||||
|
sample = 32767;
|
||||||
|
}
|
||||||
|
*ap++ = sample;
|
||||||
|
|
||||||
|
//! Adjust the gain
|
||||||
|
if (i < ramp)
|
||||||
|
curGain += delta;
|
||||||
|
else
|
||||||
|
curGain = newGain;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj->pos = slot;
|
||||||
|
}
|
||||||
|
|
40
src/AudioCompress/compress.h
Normal file
40
src/AudioCompress/compress.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*! compress.h
|
||||||
|
* interface to audio compression
|
||||||
|
*
|
||||||
|
* (c)2007 busybee (http://beesbuzz.biz/)
|
||||||
|
* Licensed under the terms of the LGPL. See the file COPYING for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef COMPRESS_H
|
||||||
|
#define COMPRESS_H
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
//! Configuration values for the compressor object
|
||||||
|
struct CompressorConfig {
|
||||||
|
int target;
|
||||||
|
int maxgain;
|
||||||
|
int smooth;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Compressor;
|
||||||
|
|
||||||
|
//! Create a new compressor (use history value of 0 for default)
|
||||||
|
struct Compressor *Compressor_new(unsigned int history);
|
||||||
|
|
||||||
|
//! Delete a compressor
|
||||||
|
void Compressor_delete(struct Compressor *);
|
||||||
|
|
||||||
|
//! Set the history length
|
||||||
|
void Compressor_setHistory(struct Compressor *, unsigned int history);
|
||||||
|
|
||||||
|
//! Get the configuration for a compressor
|
||||||
|
struct CompressorConfig *Compressor_getConfig(struct Compressor *);
|
||||||
|
|
||||||
|
//! Process 16-bit signed data
|
||||||
|
void Compressor_Process_int16(struct Compressor *, int16_t *data, unsigned int count);
|
||||||
|
|
||||||
|
//! TODO: Compressor_Process_int32, Compressor_Process_float, others as needed
|
||||||
|
|
||||||
|
//! TODO: functions for getting at the peak/gain/clip history buffers (for monitoring)
|
||||||
|
#endif
|
411
src/compress.c
411
src/compress.c
@ -1,411 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2003-2009 The Music Player Daemon Project
|
|
||||||
* http://www.musicpd.org
|
|
||||||
*
|
|
||||||
* 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.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Imported from AudioCompress by J. Shagam <fluffy@beesbuzz.biz>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "compress.h"
|
|
||||||
|
|
||||||
#include <glib.h>
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef USE_X
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
#include <X11/Xutil.h>
|
|
||||||
|
|
||||||
static Display *display;
|
|
||||||
static Window window;
|
|
||||||
static Visual *visual;
|
|
||||||
static int screen;
|
|
||||||
static GC blackGC, whiteGC, blueGC, yellowGC, dkyellowGC, redGC;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int *peaks;
|
|
||||||
static int gainCurrent, gainTarget;
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
int show_mon;
|
|
||||||
int anticlip;
|
|
||||||
int target;
|
|
||||||
int gainmax;
|
|
||||||
int gainsmooth;
|
|
||||||
unsigned buckets;
|
|
||||||
} prefs;
|
|
||||||
|
|
||||||
#ifdef USE_X
|
|
||||||
static int mon_init;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void CompressCfg(int show_mon, int anticlip, int target, int gainmax,
|
|
||||||
int gainsmooth, unsigned buckets)
|
|
||||||
{
|
|
||||||
static unsigned lastsize;
|
|
||||||
|
|
||||||
prefs.show_mon = show_mon;
|
|
||||||
prefs.anticlip = anticlip;
|
|
||||||
prefs.target = target;
|
|
||||||
prefs.gainmax = gainmax;
|
|
||||||
prefs.gainsmooth = gainsmooth;
|
|
||||||
prefs.buckets = buckets;
|
|
||||||
|
|
||||||
/* Allocate the peak structure */
|
|
||||||
peaks = g_realloc(peaks, sizeof(int)*prefs.buckets);
|
|
||||||
|
|
||||||
if (prefs.buckets > lastsize)
|
|
||||||
memset(peaks + lastsize, 0, sizeof(int)*(prefs.buckets
|
|
||||||
- lastsize));
|
|
||||||
lastsize = prefs.buckets;
|
|
||||||
|
|
||||||
#ifdef USE_X
|
|
||||||
/* Configure the monitor window if needed */
|
|
||||||
if (show_mon && !mon_init)
|
|
||||||
{
|
|
||||||
display = XOpenDisplay(getenv("DISPLAY"));
|
|
||||||
|
|
||||||
/* We really shouldn't try to init X if there's no X */
|
|
||||||
if (!display)
|
|
||||||
{
|
|
||||||
fprintf(stderr,
|
|
||||||
"X not detected; disabling monitor window\n");
|
|
||||||
show_mon = prefs.show_mon = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (show_mon && !mon_init)
|
|
||||||
{
|
|
||||||
XGCValues gcv;
|
|
||||||
XColor col;
|
|
||||||
|
|
||||||
gainCurrent = gainTarget = (1 << GAINSHIFT);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
screen = DefaultScreen(display);
|
|
||||||
visual = DefaultVisual(display, screen);
|
|
||||||
window = XCreateSimpleWindow(display,
|
|
||||||
RootWindow(display, screen),
|
|
||||||
0, 0, prefs.buckets, 128 + 8, 0,
|
|
||||||
BlackPixel(display, screen),
|
|
||||||
WhitePixel(display, screen));
|
|
||||||
XStoreName(display, window, "AudioCompress monitor");
|
|
||||||
|
|
||||||
gcv.foreground = BlackPixel(display, screen);
|
|
||||||
blackGC = XCreateGC(display, window, GCForeground, &gcv);
|
|
||||||
gcv.foreground = WhitePixel(display, screen);
|
|
||||||
whiteGC = XCreateGC(display, window, GCForeground, &gcv);
|
|
||||||
col.red = 0;
|
|
||||||
col.green = 0;
|
|
||||||
col.blue = 65535;
|
|
||||||
XAllocColor(display, DefaultColormap(display, screen), &col);
|
|
||||||
gcv.foreground = col.pixel;
|
|
||||||
blueGC = XCreateGC(display, window, GCForeground, &gcv);
|
|
||||||
col.red = 65535;
|
|
||||||
col.green = 65535;
|
|
||||||
col.blue = 0;
|
|
||||||
XAllocColor(display, DefaultColormap(display, screen), &col);
|
|
||||||
gcv.foreground = col.pixel;
|
|
||||||
yellowGC = XCreateGC(display, window, GCForeground, &gcv);
|
|
||||||
col.red = 32767;
|
|
||||||
col.green = 32767;
|
|
||||||
col.blue = 0;
|
|
||||||
XAllocColor(display, DefaultColormap(display, screen), &col);
|
|
||||||
gcv.foreground = col.pixel;
|
|
||||||
dkyellowGC = XCreateGC(display, window, GCForeground, &gcv);
|
|
||||||
col.red = 65535;
|
|
||||||
col.green = 0;
|
|
||||||
col.blue = 0;
|
|
||||||
XAllocColor(display, DefaultColormap(display, screen), &col);
|
|
||||||
gcv.foreground = col.pixel;
|
|
||||||
redGC = XCreateGC(display, window, GCForeground, &gcv);
|
|
||||||
mon_init = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mon_init)
|
|
||||||
{
|
|
||||||
if (show_mon)
|
|
||||||
XMapWindow(display, window);
|
|
||||||
else
|
|
||||||
XUnmapWindow(display, window);
|
|
||||||
XResizeWindow(display, window, prefs.buckets, 128 + 8);
|
|
||||||
XFlush(display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompressFree(void)
|
|
||||||
{
|
|
||||||
#ifdef USE_X
|
|
||||||
if (mon_init)
|
|
||||||
{
|
|
||||||
XFreeGC(display, blackGC);
|
|
||||||
XFreeGC(display, whiteGC);
|
|
||||||
XFreeGC(display, blueGC);
|
|
||||||
XFreeGC(display, yellowGC);
|
|
||||||
XFreeGC(display, dkyellowGC);
|
|
||||||
XFreeGC(display, redGC);
|
|
||||||
XDestroyWindow(display, window);
|
|
||||||
XCloseDisplay(display);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_free(peaks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CompressDo(void *data, unsigned int length)
|
|
||||||
{
|
|
||||||
int16_t *audio = (int16_t *)data, *ap;
|
|
||||||
int peak;
|
|
||||||
unsigned int i, pos;
|
|
||||||
int gr, gf, gn;
|
|
||||||
static int pn = -1;
|
|
||||||
#ifdef STATS
|
|
||||||
static int clip;
|
|
||||||
#endif
|
|
||||||
static int clipped;
|
|
||||||
|
|
||||||
if (!peaks)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (pn == -1)
|
|
||||||
{
|
|
||||||
for (i = 0; i < prefs.buckets; i++)
|
|
||||||
peaks[i] = 0;
|
|
||||||
}
|
|
||||||
pn = (pn + 1)%prefs.buckets;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "modifyNative16(0x%08x, %d)\n",(unsigned int)data,
|
|
||||||
length);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Determine peak's value and position */
|
|
||||||
peak = 1;
|
|
||||||
pos = 0;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "finding peak(b=%d)\n", pn);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ap = audio;
|
|
||||||
for (i = 0; i < length/2; i++)
|
|
||||||
{
|
|
||||||
int val = *ap;
|
|
||||||
if (val > peak)
|
|
||||||
{
|
|
||||||
peak = val;
|
|
||||||
pos = i;
|
|
||||||
} else if (-val > peak)
|
|
||||||
{
|
|
||||||
peak = -val;
|
|
||||||
pos = i;
|
|
||||||
}
|
|
||||||
ap++;
|
|
||||||
}
|
|
||||||
peaks[pn] = peak;
|
|
||||||
|
|
||||||
/* Only draw if needed, of course */
|
|
||||||
#ifdef USE_X
|
|
||||||
if (prefs.show_mon)
|
|
||||||
{
|
|
||||||
/* current amplitude */
|
|
||||||
XDrawLine(display, window, whiteGC,
|
|
||||||
pn, 0,
|
|
||||||
pn,
|
|
||||||
127 -
|
|
||||||
(peaks[pn]*gainCurrent >> (GAINSHIFT + 8)));
|
|
||||||
|
|
||||||
/* amplification */
|
|
||||||
XDrawLine(display, window, yellowGC,
|
|
||||||
pn,
|
|
||||||
127 - (peaks[pn]*gainCurrent
|
|
||||||
>> (GAINSHIFT + 8)),
|
|
||||||
pn, 127);
|
|
||||||
|
|
||||||
/* peak */
|
|
||||||
XDrawLine(display, window, blackGC,
|
|
||||||
pn, 127 - (peaks[pn] >> 8), pn, 127);
|
|
||||||
|
|
||||||
/* clip indicator */
|
|
||||||
if (clipped)
|
|
||||||
XDrawLine(display, window, redGC,
|
|
||||||
(pn + prefs.buckets - 1)%prefs.buckets,
|
|
||||||
126 - clipped/(length*512),
|
|
||||||
(pn + prefs.buckets - 1)%prefs.buckets,
|
|
||||||
127);
|
|
||||||
clipped = 0;
|
|
||||||
|
|
||||||
/* target line */
|
|
||||||
/* XDrawPoint(display, window, redGC, */
|
|
||||||
/* pn, 127 - TARGET/256); */
|
|
||||||
/* amplification edge */
|
|
||||||
XDrawLine(display, window, dkyellowGC,
|
|
||||||
pn,
|
|
||||||
127 - (peaks[pn]*gainCurrent
|
|
||||||
>> (GAINSHIFT + 8)),
|
|
||||||
pn - 1,
|
|
||||||
127 -
|
|
||||||
(peaks[(pn + prefs.buckets
|
|
||||||
- 1)%prefs.buckets]*gainCurrent
|
|
||||||
>> (GAINSHIFT + 8)));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i = 0; i < prefs.buckets; i++)
|
|
||||||
{
|
|
||||||
if (peaks[i] > peak)
|
|
||||||
{
|
|
||||||
peak = peaks[i];
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine target gain */
|
|
||||||
gn = (1 << GAINSHIFT)*prefs.target/peak;
|
|
||||||
|
|
||||||
if (gn <(1 << GAINSHIFT))
|
|
||||||
gn = 1 << GAINSHIFT;
|
|
||||||
|
|
||||||
gainTarget = (gainTarget *((1 << prefs.gainsmooth) - 1) + gn)
|
|
||||||
>> prefs.gainsmooth;
|
|
||||||
|
|
||||||
/* Give it an extra insignifigant nudge to counteract possible
|
|
||||||
** rounding error
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (gn < gainTarget)
|
|
||||||
gainTarget--;
|
|
||||||
else if (gn > gainTarget)
|
|
||||||
gainTarget++;
|
|
||||||
|
|
||||||
if (gainTarget > prefs.gainmax << GAINSHIFT)
|
|
||||||
gainTarget = prefs.gainmax << GAINSHIFT;
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef USE_X
|
|
||||||
if (prefs.show_mon)
|
|
||||||
{
|
|
||||||
int x;
|
|
||||||
|
|
||||||
/* peak*gain */
|
|
||||||
XDrawPoint(display, window, redGC,
|
|
||||||
pn,
|
|
||||||
127 - (peak*gainCurrent
|
|
||||||
>> (GAINSHIFT + 8)));
|
|
||||||
|
|
||||||
/* gain indicator */
|
|
||||||
XFillRectangle(display, window, whiteGC, 0, 128,
|
|
||||||
prefs.buckets, 8);
|
|
||||||
x = (gainTarget - (1 << GAINSHIFT))*prefs.buckets
|
|
||||||
/ ((prefs.gainmax - 1) << GAINSHIFT);
|
|
||||||
XDrawLine(display, window, redGC, x,
|
|
||||||
128, x, 128 + 8);
|
|
||||||
|
|
||||||
x = (gn - (1 << GAINSHIFT))*prefs.buckets
|
|
||||||
/ ((prefs.gainmax - 1) << GAINSHIFT);
|
|
||||||
|
|
||||||
XDrawLine(display, window, blackGC,
|
|
||||||
x, 132 - 1,
|
|
||||||
x, 132 + 1);
|
|
||||||
|
|
||||||
/* blue peak line */
|
|
||||||
XDrawLine(display, window, blueGC,
|
|
||||||
0, 127 - (peak >> 8), prefs.buckets,
|
|
||||||
127 - (peak >> 8));
|
|
||||||
XFlush(display);
|
|
||||||
XDrawLine(display, window, whiteGC,
|
|
||||||
0, 127 - (peak >> 8), prefs.buckets,
|
|
||||||
127 - (peak >> 8));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* See if a peak is going to clip */
|
|
||||||
gn = (1 << GAINSHIFT)*32768/peak;
|
|
||||||
|
|
||||||
if (gn < gainTarget)
|
|
||||||
{
|
|
||||||
gainTarget = gn;
|
|
||||||
|
|
||||||
if (prefs.anticlip)
|
|
||||||
pos = 0;
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
/* We're ramping up, so draw it out over the whole frame */
|
|
||||||
pos = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine gain rate necessary to make target */
|
|
||||||
if (!pos)
|
|
||||||
pos = 1;
|
|
||||||
|
|
||||||
gr = ((gainTarget - gainCurrent) << 16)/(int)pos;
|
|
||||||
|
|
||||||
/* Do the shiznit */
|
|
||||||
gf = gainCurrent << 16;
|
|
||||||
|
|
||||||
#ifdef STATS
|
|
||||||
fprintf(stderr, "\rgain = %2.2f%+.2e ",
|
|
||||||
gainCurrent*1.0/(1 << GAINSHIFT),
|
|
||||||
(gainTarget - gainCurrent)*1.0/(1 << GAINSHIFT));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ap = audio;
|
|
||||||
for (i = 0; i < length/2; i++)
|
|
||||||
{
|
|
||||||
int sample;
|
|
||||||
|
|
||||||
/* Interpolate the gain */
|
|
||||||
gainCurrent = gf >> 16;
|
|
||||||
if (i < pos)
|
|
||||||
gf += gr;
|
|
||||||
else if (i == pos)
|
|
||||||
gf = gainTarget << 16;
|
|
||||||
|
|
||||||
/* Amplify */
|
|
||||||
sample = (*ap)*gainCurrent >> GAINSHIFT;
|
|
||||||
if (sample < -32768)
|
|
||||||
{
|
|
||||||
#ifdef STATS
|
|
||||||
clip++;
|
|
||||||
#endif
|
|
||||||
clipped += -32768 - sample;
|
|
||||||
sample = -32768;
|
|
||||||
} else if (sample > 32767)
|
|
||||||
{
|
|
||||||
#ifdef STATS
|
|
||||||
clip++;
|
|
||||||
#endif
|
|
||||||
clipped += sample - 32767;
|
|
||||||
sample = 32767;
|
|
||||||
}
|
|
||||||
*ap++ = sample;
|
|
||||||
}
|
|
||||||
#ifdef STATS
|
|
||||||
fprintf(stderr, "clip %d b%-3d ", clip, pn);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
fprintf(stderr, "\ndone\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2003-2009 The Music Player Daemon Project
|
|
||||||
* http://www.musicpd.org
|
|
||||||
*
|
|
||||||
* 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.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Imported from AudioCompress by J. Shagam <fluffy@beesbuzz.biz>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MPD_COMPRESS_H
|
|
||||||
#define MPD_COMPRESS_H
|
|
||||||
|
|
||||||
/* These are copied from the AudioCompress config.h, mainly because CompressDo
|
|
||||||
* needs GAINSHIFT defined. The rest are here so they can be used as defaults
|
|
||||||
* to pass to CompressCfg(). -- jat */
|
|
||||||
#define ANTICLIP 0 /* Strict clipping protection */
|
|
||||||
#define TARGET 25000 /* Target level */
|
|
||||||
#define GAINMAX 32 /* The maximum amount to amplify by */
|
|
||||||
#define GAINSHIFT 10 /* How fine-grained the gain is */
|
|
||||||
#define GAINSMOOTH 8 /* How much inertia ramping has*/
|
|
||||||
#define BUCKETS 400 /* How long of a history to store */
|
|
||||||
|
|
||||||
void CompressCfg(int monitor,
|
|
||||||
int anticlip,
|
|
||||||
int target,
|
|
||||||
int maxgain,
|
|
||||||
int smooth,
|
|
||||||
unsigned buckets);
|
|
||||||
|
|
||||||
void CompressDo(void *data, unsigned int numSamples);
|
|
||||||
|
|
||||||
void CompressFree(void);
|
|
||||||
|
|
||||||
#endif
|
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "normalize.h"
|
#include "normalize.h"
|
||||||
#include "compress.h"
|
#include "AudioCompress/compress.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "audio_format.h"
|
#include "audio_format.h"
|
||||||
|
|
||||||
@ -27,24 +27,27 @@
|
|||||||
|
|
||||||
int normalizationEnabled;
|
int normalizationEnabled;
|
||||||
|
|
||||||
|
static struct Compressor *compressor;
|
||||||
|
|
||||||
void initNormalization(void)
|
void initNormalization(void)
|
||||||
{
|
{
|
||||||
normalizationEnabled = config_get_bool(CONF_VOLUME_NORMALIZATION,
|
normalizationEnabled = config_get_bool(CONF_VOLUME_NORMALIZATION,
|
||||||
DEFAULT_VOLUME_NORMALIZATION);
|
DEFAULT_VOLUME_NORMALIZATION);
|
||||||
|
|
||||||
if (normalizationEnabled)
|
if (normalizationEnabled)
|
||||||
CompressCfg(0, ANTICLIP, TARGET, GAINMAX, GAINSMOOTH, BUCKETS);
|
compressor = Compressor_new(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void finishNormalization(void)
|
void finishNormalization(void)
|
||||||
{
|
{
|
||||||
if (normalizationEnabled) CompressFree();
|
if (normalizationEnabled)
|
||||||
|
Compressor_delete(compressor);
|
||||||
}
|
}
|
||||||
|
|
||||||
void normalizeData(char *buffer, int bufferSize,
|
void normalizeData(void *buffer, int bufferSize,
|
||||||
const struct audio_format *format)
|
const struct audio_format *format)
|
||||||
{
|
{
|
||||||
if ((format->bits != 16) || (format->channels != 2)) return;
|
if ((format->bits != 16) || (format->channels != 2)) return;
|
||||||
|
|
||||||
CompressDo(buffer, bufferSize);
|
Compressor_Process_int16(compressor, buffer, bufferSize / 2);
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ void initNormalization(void);
|
|||||||
|
|
||||||
void finishNormalization(void);
|
void finishNormalization(void);
|
||||||
|
|
||||||
void normalizeData(char *buffer, int bufferSize,
|
void normalizeData(void *buffer, int bufferSize,
|
||||||
const struct audio_format *format);
|
const struct audio_format *format);
|
||||||
|
|
||||||
#endif /* !NORMALIZE_H */
|
#endif /* !NORMALIZE_H */
|
||||||
|
Loading…
Reference in New Issue
Block a user