Initial revision 6759
[qball-mpd.git] / src / compress.c
blobd8db7ab64db835d1a6f54e2f05291194d238ee2d
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * Compressor logic by
6 * (c)2003-6 fluffy@beesbuzz.biz
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
28 #include "compress.h"
29 #include "utils.h"
31 #ifdef USE_X
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
35 static Display *display;
36 static Window window;
37 static Visual *visual;
38 static int screen;
39 static GC blackGC, whiteGC, blueGC, yellowGC, dkyellowGC, redGC;
40 #endif
42 static int *peaks;
43 static int gainCurrent, gainTarget;
45 static struct {
46 int show_mon;
47 int anticlip;
48 int target;
49 int gainmax;
50 int gainsmooth;
51 int buckets;
52 } prefs;
54 #ifdef USE_X
55 static int mon_init;
56 #endif
58 void CompressCfg(int show_mon, int anticlip, int target, int gainmax,
59 int gainsmooth, int buckets)
61 static int lastsize;
63 prefs.show_mon = show_mon;
64 prefs.anticlip = anticlip;
65 prefs.target = target;
66 prefs.gainmax = gainmax;
67 prefs.gainsmooth = gainsmooth;
68 prefs.buckets = buckets;
70 /* Allocate the peak structure */
71 peaks = xrealloc(peaks, sizeof(int)*prefs.buckets);
73 if (prefs.buckets > lastsize)
74 memset(peaks + lastsize, 0, sizeof(int)*(prefs.buckets
75 - lastsize));
76 lastsize = prefs.buckets;
78 #ifdef USE_X
79 /* Configure the monitor window if needed */
80 if (show_mon && !mon_init)
82 display = XOpenDisplay(getenv("DISPLAY"));
84 /* We really shouldn't try to init X if there's no X */
85 if (!display)
87 fprintf(stderr,
88 "X not detected; disabling monitor window\n");
89 show_mon = prefs.show_mon = 0;
93 if (show_mon && !mon_init)
95 XGCValues gcv;
96 XColor col;
98 gainCurrent = gainTarget = (1 << GAINSHIFT);
102 screen = DefaultScreen(display);
103 visual = DefaultVisual(display, screen);
104 window = XCreateSimpleWindow(display,
105 RootWindow(display, screen),
106 0, 0, prefs.buckets, 128 + 8, 0,
107 BlackPixel(display, screen),
108 WhitePixel(display, screen));
109 XStoreName(display, window, "AudioCompress monitor");
111 gcv.foreground = BlackPixel(display, screen);
112 blackGC = XCreateGC(display, window, GCForeground, &gcv);
113 gcv.foreground = WhitePixel(display, screen);
114 whiteGC = XCreateGC(display, window, GCForeground, &gcv);
115 col.red = 0;
116 col.green = 0;
117 col.blue = 65535;
118 XAllocColor(display, DefaultColormap(display, screen), &col);
119 gcv.foreground = col.pixel;
120 blueGC = XCreateGC(display, window, GCForeground, &gcv);
121 col.red = 65535;
122 col.green = 65535;
123 col.blue = 0;
124 XAllocColor(display, DefaultColormap(display, screen), &col);
125 gcv.foreground = col.pixel;
126 yellowGC = XCreateGC(display, window, GCForeground, &gcv);
127 col.red = 32767;
128 col.green = 32767;
129 col.blue = 0;
130 XAllocColor(display, DefaultColormap(display, screen), &col);
131 gcv.foreground = col.pixel;
132 dkyellowGC = XCreateGC(display, window, GCForeground, &gcv);
133 col.red = 65535;
134 col.green = 0;
135 col.blue = 0;
136 XAllocColor(display, DefaultColormap(display, screen), &col);
137 gcv.foreground = col.pixel;
138 redGC = XCreateGC(display, window, GCForeground, &gcv);
139 mon_init = 1;
142 if (mon_init)
144 if (show_mon)
145 XMapWindow(display, window);
146 else
147 XUnmapWindow(display, window);
148 XResizeWindow(display, window, prefs.buckets, 128 + 8);
149 XFlush(display);
151 #endif
154 void CompressFree(void)
156 #ifdef USE_X
157 if (mon_init)
159 XFreeGC(display, blackGC);
160 XFreeGC(display, whiteGC);
161 XFreeGC(display, blueGC);
162 XFreeGC(display, yellowGC);
163 XFreeGC(display, dkyellowGC);
164 XFreeGC(display, redGC);
165 XDestroyWindow(display, window);
166 XCloseDisplay(display);
168 #endif
170 if (peaks)
171 free(peaks);
174 void CompressDo(void *data, unsigned int length)
176 int16_t *audio = (int16_t *)data, *ap;
177 int peak, pos;
178 int i;
179 int gr, gf, gn;
180 static int pn = -1;
181 #ifdef STATS
182 static int clip;
183 #endif
184 static int clipped;
186 if (!peaks)
187 return;
189 if (pn == -1)
191 for (i = 0; i < prefs.buckets; i++)
192 peaks[i] = 0;
194 pn = (pn + 1)%prefs.buckets;
196 #ifdef DEBUG
197 fprintf(stderr, "modifyNative16(0x%08x, %d)\n",(unsigned int)data,
198 length);
199 #endif
201 /* Determine peak's value and position */
202 peak = 1;
203 pos = 0;
205 #ifdef DEBUG
206 fprintf(stderr, "finding peak(b=%d)\n", pn);
207 #endif
209 ap = audio;
210 for (i = 0; i < length/2; i++)
212 int val = *ap;
213 if (val > peak)
215 peak = val;
216 pos = i;
217 } else if (-val > peak)
219 peak = -val;
220 pos = i;
222 ap++;
224 peaks[pn] = peak;
226 /* Only draw if needed, of course */
227 #ifdef USE_X
228 if (prefs.show_mon)
230 /* current amplitude */
231 XDrawLine(display, window, whiteGC,
232 pn, 0,
234 127 -
235 (peaks[pn]*gainCurrent >> (GAINSHIFT + 8)));
237 /* amplification */
238 XDrawLine(display, window, yellowGC,
240 127 - (peaks[pn]*gainCurrent
241 >> (GAINSHIFT + 8)),
242 pn, 127);
244 /* peak */
245 XDrawLine(display, window, blackGC,
246 pn, 127 - (peaks[pn] >> 8), pn, 127);
248 /* clip indicator */
249 if (clipped)
250 XDrawLine(display, window, redGC,
251 (pn + prefs.buckets - 1)%prefs.buckets,
252 126 - clipped/(length*512),
253 (pn + prefs.buckets - 1)%prefs.buckets,
254 127);
255 clipped = 0;
257 /* target line */
258 /* XDrawPoint(display, window, redGC, */
259 /* pn, 127 - TARGET/256); */
260 /* amplification edge */
261 XDrawLine(display, window, dkyellowGC,
263 127 - (peaks[pn]*gainCurrent
264 >> (GAINSHIFT + 8)),
265 pn - 1,
266 127 -
267 (peaks[(pn + prefs.buckets
268 - 1)%prefs.buckets]*gainCurrent
269 >> (GAINSHIFT + 8)));
271 #endif
273 for (i = 0; i < prefs.buckets; i++)
275 if (peaks[i] > peak)
277 peak = peaks[i];
278 pos = 0;
282 /* Determine target gain */
283 gn = (1 << GAINSHIFT)*prefs.target/peak;
285 if (gn <(1 << GAINSHIFT))
286 gn = 1 << GAINSHIFT;
288 gainTarget = (gainTarget *((1 << prefs.gainsmooth) - 1) + gn)
289 >> prefs.gainsmooth;
291 /* Give it an extra insignifigant nudge to counteract possible
292 ** rounding error
295 if (gn < gainTarget)
296 gainTarget--;
297 else if (gn > gainTarget)
298 gainTarget++;
300 if (gainTarget > prefs.gainmax << GAINSHIFT)
301 gainTarget = prefs.gainmax << GAINSHIFT;
304 #ifdef USE_X
305 if (prefs.show_mon)
307 int x;
309 /* peak*gain */
310 XDrawPoint(display, window, redGC,
312 127 - (peak*gainCurrent
313 >> (GAINSHIFT + 8)));
315 /* gain indicator */
316 XFillRectangle(display, window, whiteGC, 0, 128,
317 prefs.buckets, 8);
318 x = (gainTarget - (1 << GAINSHIFT))*prefs.buckets
319 / ((prefs.gainmax - 1) << GAINSHIFT);
320 XDrawLine(display, window, redGC, x,
321 128, x, 128 + 8);
323 x = (gn - (1 << GAINSHIFT))*prefs.buckets
324 / ((prefs.gainmax - 1) << GAINSHIFT);
326 XDrawLine(display, window, blackGC,
327 x, 132 - 1,
328 x, 132 + 1);
330 /* blue peak line */
331 XDrawLine(display, window, blueGC,
332 0, 127 - (peak >> 8), prefs.buckets,
333 127 - (peak >> 8));
334 XFlush(display);
335 XDrawLine(display, window, whiteGC,
336 0, 127 - (peak >> 8), prefs.buckets,
337 127 - (peak >> 8));
339 #endif
341 /* See if a peak is going to clip */
342 gn = (1 << GAINSHIFT)*32768/peak;
344 if (gn < gainTarget)
346 gainTarget = gn;
348 if (prefs.anticlip)
349 pos = 0;
351 } else
353 /* We're ramping up, so draw it out over the whole frame */
354 pos = length;
357 /* Determine gain rate necessary to make target */
358 if (!pos)
359 pos = 1;
361 gr = ((gainTarget - gainCurrent) << 16)/pos;
363 /* Do the shiznit */
364 gf = gainCurrent << 16;
366 #ifdef STATS
367 fprintf(stderr, "\rgain = %2.2f%+.2e ",
368 gainCurrent*1.0/(1 << GAINSHIFT),
369 (gainTarget - gainCurrent)*1.0/(1 << GAINSHIFT));
370 #endif
372 ap = audio;
373 for (i = 0; i < length/2; i++)
375 int sample;
377 /* Interpolate the gain */
378 gainCurrent = gf >> 16;
379 if (i < pos)
380 gf += gr;
381 else if (i == pos)
382 gf = gainTarget << 16;
384 /* Amplify */
385 sample = (*ap)*gainCurrent >> GAINSHIFT;
386 if (sample < -32768)
388 #ifdef STATS
389 clip++;
390 #endif
391 clipped += -32768 - sample;
392 sample = -32768;
393 } else if (sample > 32767)
395 #ifdef STATS
396 clip++;
397 #endif
398 clipped += sample - 32767;
399 sample = 32767;
401 *ap++ = sample;
403 #ifdef STATS
404 fprintf(stderr, "clip %d b%-3d ", clip, pn);
405 #endif
407 #ifdef DEBUG
408 fprintf(stderr, "\ndone\n");
409 #endif