Initial revision 6759
[qball-mpd.git] / src / .svn / text-base / compress.c.svn-base
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
4  *
5  * Compressor logic by
6  * (c)2003-6 fluffy@beesbuzz.biz
7  *
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.
12  *
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
20  *
21  */
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)
81         {
82                 display = XOpenDisplay(getenv("DISPLAY"));
84                 /* We really shouldn't try to init X if there's no X */
85                 if (!display)
86                 {
87                         fprintf(stderr,
88                                 "X not detected; disabling monitor window\n");
89                         show_mon = prefs.show_mon = 0;
90                 }
91         }
93         if (show_mon && !mon_init)
94         {
95                 XGCValues gcv;
96                 XColor col;
97         
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");
110         
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;
140         }
142         if (mon_init)
143         {
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);
150         }
151 #endif
154 void CompressFree(void)
156 #ifdef USE_X
157         if (mon_init)
158         {
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);
167         }
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)
190         {
191                 for (i = 0; i < prefs.buckets; i++)
192                         peaks[i] = 0;
193         }
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++)
211         {
212                 int val = *ap;
213                 if (val > peak)
214                 {
215                         peak = val;
216                         pos = i;
217                 } else if (-val > peak)
218                 {
219                         peak = -val;
220                         pos = i;
221                 }
222                 ap++;
223         }
224         peaks[pn] = peak;
226         /* Only draw if needed, of course */
227 #ifdef USE_X
228         if (prefs.show_mon)
229         {
230                 /* current amplitude */
231                 XDrawLine(display, window, whiteGC,
232                           pn, 0,
233                           pn,
234                           127 -
235                           (peaks[pn]*gainCurrent >> (GAINSHIFT + 8)));
237                 /* amplification */
238                 XDrawLine(display, window, yellowGC,
239                           pn,
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,
262                           pn,
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)));
270         }
271 #endif
273         for (i = 0; i < prefs.buckets; i++)
274         {
275                 if (peaks[i] > peak)
276                 {
277                         peak = peaks[i];
278                         pos = 0;
279                 }
280         }
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
293         */
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)
306         {
307                 int x;
309                 /* peak*gain */
310                 XDrawPoint(display, window, redGC,
311                            pn,
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));
338         }
339 #endif
341         /* See if a peak is going to clip */
342         gn = (1 << GAINSHIFT)*32768/peak;
344         if (gn < gainTarget)
345         {
346                 gainTarget = gn;
348                 if (prefs.anticlip)
349                         pos = 0;
351         } else
352         {
353                 /* We're ramping up, so draw it out over the whole frame */
354                 pos = length;
355         }
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++)
374         {
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)
387                 {
388 #ifdef STATS
389                         clip++;
390 #endif
391                         clipped += -32768 - sample;
392                         sample = -32768;
393                 } else if (sample > 32767)
394                 {
395 #ifdef STATS
396                         clip++;
397 #endif
398                         clipped += sample - 32767;
399                         sample = 32767;
400                 }
401                 *ap++ = sample;
402         }
403 #ifdef STATS
404         fprintf(stderr, "clip %d b%-3d ", clip, pn);
405 #endif
407 #ifdef DEBUG
408         fprintf(stderr, "\ndone\n");
409 #endif