aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rw-r--r--NEWS1
-rw-r--r--src/AudioCompress/compress.c184
-rw-r--r--src/AudioCompress/compress.h40
-rw-r--r--src/compress.c411
-rw-r--r--src/compress.h48
-rw-r--r--src/normalize.c13
-rw-r--r--src/normalize.h2
8 files changed, 236 insertions, 467 deletions
diff --git a/Makefile.am b/Makefile.am
index 58d1964e..3ca89e16 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -116,7 +116,7 @@ mpd_headers = \
src/mixer/pulse_mixer_plugin.h \
src/daemon.h \
src/normalize.h \
- src/compress.h \
+ src/AudioCompress/compress.h \
src/buffer.h \
src/pipe.h \
src/chunk.h \
@@ -253,7 +253,7 @@ src_mpd_SOURCES = \
src/event_pipe.c \
src/daemon.c \
src/normalize.c \
- src/compress.c \
+ src/AudioCompress/compress.c \
src/buffer.c \
src/pipe.c \
src/chunk.c \
diff --git a/NEWS b/NEWS
index 60a19799..ab17b672 100644
--- a/NEWS
+++ b/NEWS
@@ -62,6 +62,7 @@ ver 0.16 (20??/??/??)
- support .mpdignore files in the music directory
- sort songs by album name first, then disc/track number
- rescan after metadata_to_use change
+* normalize: upgraded to AudioCompress 2.0
* log unused/unknown block parameters
* removed the deprecated "error_file" option
* save state when stopped
diff --git a/src/AudioCompress/compress.c b/src/AudioCompress/compress.c
new file mode 100644
index 00000000..bf72b2ea
--- /dev/null
+++ b/src/AudioCompress/compress.c
@@ -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;
+}
+
diff --git a/src/AudioCompress/compress.h b/src/AudioCompress/compress.h
new file mode 100644
index 00000000..cc875c6d
--- /dev/null
+++ b/src/AudioCompress/compress.h
@@ -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
diff --git a/src/compress.c b/src/compress.c
deleted file mode 100644
index 27f223bd..00000000
--- a/src/compress.c
+++ /dev/null
@@ -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
-}
-
diff --git a/src/compress.h b/src/compress.h
deleted file mode 100644
index 3e3afb56..00000000
--- a/src/compress.h
+++ /dev/null
@@ -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
diff --git a/src/normalize.c b/src/normalize.c
index f8304cd1..f9201df6 100644
--- a/src/normalize.c
+++ b/src/normalize.c
@@ -19,7 +19,7 @@
#include "config.h"
#include "normalize.h"
-#include "compress.h"
+#include "AudioCompress/compress.h"
#include "conf.h"
#include "audio_format.h"
@@ -27,24 +27,27 @@
int normalizationEnabled;
+static struct Compressor *compressor;
+
void initNormalization(void)
{
normalizationEnabled = config_get_bool(CONF_VOLUME_NORMALIZATION,
DEFAULT_VOLUME_NORMALIZATION);
if (normalizationEnabled)
- CompressCfg(0, ANTICLIP, TARGET, GAINMAX, GAINSMOOTH, BUCKETS);
+ compressor = Compressor_new(0);
}
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)
{
if ((format->bits != 16) || (format->channels != 2)) return;
- CompressDo(buffer, bufferSize);
+ Compressor_Process_int16(compressor, buffer, bufferSize / 2);
}
diff --git a/src/normalize.h b/src/normalize.h
index a8144951..2834f07f 100644
--- a/src/normalize.h
+++ b/src/normalize.h
@@ -28,7 +28,7 @@ void initNormalization(void);
void finishNormalization(void);
-void normalizeData(char *buffer, int bufferSize,
+void normalizeData(void *buffer, int bufferSize,
const struct audio_format *format);
#endif /* !NORMALIZE_H */