Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
| 8 | * $Id$ |
| 9 | * |
| 10 | * Copyright (C) 2005 Miika Pekkarinen |
| 11 | * |
| 12 | * All files in this archive are subject to the GNU General Public License. |
| 13 | * See the file COPYING in the source tree root for full license agreement. |
| 14 | * |
| 15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 16 | * KIND, either express or implied. |
| 17 | * |
| 18 | ****************************************************************************/ |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 19 | #include <inttypes.h> |
Miika Pekkarinen | 4613659 | 2005-06-27 21:12:09 +0000 | [diff] [blame] | 20 | #include <string.h> |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 21 | #include "dsp.h" |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 22 | #include "kernel.h" |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 23 | #include "playback.h" |
| 24 | #include "system.h" |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 25 | #include "settings.h" |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 26 | #include "replaygain.h" |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 27 | #include "debug.h" |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 28 | |
| 29 | /* The "dither" code to convert the 24-bit samples produced by libmad was |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 30 | * taken from the coolplayer project - coolplayer.sourceforge.net |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 31 | */ |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 32 | |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 33 | /* 16-bit samples are scaled based on these constants. The shift should be |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 34 | * no more than 15. |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 35 | */ |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 36 | #define WORD_SHIFT 12 |
| 37 | #define WORD_FRACBITS 27 |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 38 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 39 | #define NATIVE_DEPTH 16 |
| 40 | #define SAMPLE_BUF_SIZE 256 |
| 41 | #define RESAMPLE_BUF_SIZE (256 * 4) /* Enough for 11,025 Hz -> 44,100 Hz*/ |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 42 | #define DEFAULT_REPLAYGAIN 0x01000000 |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 43 | |
Christian Gmeiner | 6753fb5 | 2005-07-18 13:09:05 +0000 | [diff] [blame] | 44 | #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 45 | |
| 46 | #define INIT() asm volatile ("move.l #0xb0, %macsr") /* frac, round, clip */ |
Magnus Holmgren | 059a6c8 | 2005-07-30 13:47:16 +0000 | [diff] [blame] | 47 | /* Multiply two S.31 fractional integers and return the sign bit and the |
| 48 | * 31 most significant bits of the result. |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 49 | */ |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 50 | #define FRACMUL(x, y) \ |
| 51 | ({ \ |
| 52 | long t; \ |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 53 | asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \ |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 54 | "movclr.l %%acc0, %[t]\n\t" \ |
| 55 | : [t] "=r" (t) : [a] "r" (x), [b] "r" (y)); \ |
| 56 | t; \ |
| 57 | }) |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 58 | |
| 59 | /* Multiply one S.31-bit and one S8.23 fractional integer and return the |
Magnus Holmgren | 059a6c8 | 2005-07-30 13:47:16 +0000 | [diff] [blame] | 60 | * sign bit and the 31 most significant bits of the result. |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 61 | */ |
| 62 | #define FRACMUL_8(x, y) \ |
| 63 | ({ \ |
| 64 | long t; \ |
| 65 | long u; \ |
| 66 | asm volatile ("mac.l %[a], %[b], %%acc0\n\t" \ |
| 67 | "move.l %%accext01, %[u]\n\t" \ |
| 68 | "movclr.l %%acc0, %[t]\n\t" \ |
| 69 | : [t] "=r" (t), [u] "=r" (u) : [a] "r" (x), [b] "r" (y)); \ |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 70 | (t << 8) | (u & 0xff); \ |
| 71 | }) |
| 72 | |
| 73 | /* Multiply one S.31-bit and one S8.23 fractional integer and return the |
| 74 | * sign bit and the 31 most significant bits of the result. Load next value |
| 75 | * to multiply with into x from s (and increase s); x must contain the |
| 76 | * initial value. |
| 77 | */ |
| 78 | #define FRACMUL_8_LOOP(x, y, s) \ |
| 79 | ({ \ |
| 80 | long t; \ |
| 81 | long u; \ |
| 82 | asm volatile ("mac.l %[a], %[b], (%[c])+, %[a], %%acc0\n\t" \ |
| 83 | "move.l %%accext01, %[u]\n\t" \ |
| 84 | "movclr.l %%acc0, %[t]\n\t" \ |
| 85 | : [a] "+r" (x), [c] "+a" (s), [t] "=r" (t), [u] "=r" (u) \ |
| 86 | : [b] "r" (y)); \ |
| 87 | (t << 8) | (u & 0xff); \ |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 88 | }) |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 89 | |
| 90 | #else |
| 91 | |
| 92 | #define INIT() |
Magnus Holmgren | 059a6c8 | 2005-07-30 13:47:16 +0000 | [diff] [blame] | 93 | #define FRACMUL(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 31)) |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 94 | #define FRACMUL_8(x, y) (long) (((((long long) (x)) * ((long long) (y))) >> 23)) |
| 95 | #define FRACMUL_8_LOOP(x, y, s) \ |
| 96 | ({ \ |
| 97 | long t = x; \ |
| 98 | x = *(s)++; \ |
| 99 | (long) (((((long long) (t)) * ((long long) (y))) >> 23)); \ |
| 100 | }) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 101 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 102 | #endif |
| 103 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 104 | struct dsp_config |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 105 | { |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 106 | long frequency; |
| 107 | long clip_min; |
| 108 | long clip_max; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 109 | long track_gain; |
| 110 | long album_gain; |
| 111 | long track_peak; |
| 112 | long album_peak; |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 113 | long replaygain; /* Note that this is in S8.23 format. */ |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 114 | int sample_depth; |
| 115 | int sample_bytes; |
| 116 | int stereo_mode; |
| 117 | int frac_bits; |
| 118 | bool dither_enabled; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 119 | bool new_gain; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 120 | }; |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 121 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 122 | struct resample_data |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 123 | { |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 124 | long last_sample; |
| 125 | long phase; |
| 126 | long delta; |
| 127 | }; |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 128 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 129 | struct dither_data |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 130 | { |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 131 | long error[3]; |
| 132 | long random; |
| 133 | }; |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 134 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 135 | static struct dsp_config dsp_conf[2] IDATA_ATTR; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 136 | static struct dither_data dither_data[2] IDATA_ATTR; |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 137 | static struct resample_data resample_data[2][2] IDATA_ATTR; |
| 138 | |
| 139 | extern int current_codec; |
| 140 | struct dsp_config *dsp; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 141 | |
| 142 | /* The internal format is 32-bit samples, non-interleaved, stereo. This |
| 143 | * format is similar to the raw output from several codecs, so the amount |
| 144 | * of copying needed is minimized for that case. |
| 145 | */ |
| 146 | |
| 147 | static long sample_buf[SAMPLE_BUF_SIZE] IDATA_ATTR; |
| 148 | static long resample_buf[RESAMPLE_BUF_SIZE] IDATA_ATTR; |
| 149 | |
| 150 | |
| 151 | /* Convert at most count samples to the internal format, if needed. Returns |
| 152 | * number of samples ready for further processing. Updates src to point |
| 153 | * past the samples "consumed" and dst is set to point to the samples to |
| 154 | * consume. Note that for mono, dst[0] equals dst[1], as there is no point |
| 155 | * in processing the same data twice. |
| 156 | */ |
| 157 | static int convert_to_internal(char* src[], int count, long* dst[]) |
| 158 | { |
| 159 | count = MIN(SAMPLE_BUF_SIZE / 2, count); |
| 160 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 161 | if ((dsp->sample_depth <= NATIVE_DEPTH) |
| 162 | || (dsp->stereo_mode == STEREO_INTERLEAVED)) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 163 | { |
| 164 | dst[0] = &sample_buf[0]; |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 165 | dst[1] = (dsp->stereo_mode == STEREO_MONO) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 166 | ? dst[0] : &sample_buf[SAMPLE_BUF_SIZE / 2]; |
| 167 | } |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 168 | else |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 169 | { |
| 170 | dst[0] = (long*) src[0]; |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 171 | dst[1] = (long*) ((dsp->stereo_mode == STEREO_MONO) ? src[0] : src[1]); |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 172 | } |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 173 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 174 | if (dsp->sample_depth <= NATIVE_DEPTH) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 175 | { |
| 176 | short* s0 = (short*) src[0]; |
| 177 | long* d0 = dst[0]; |
| 178 | long* d1 = dst[1]; |
| 179 | int scale = WORD_SHIFT; |
| 180 | int i; |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 181 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 182 | if (dsp->stereo_mode == STEREO_INTERLEAVED) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 183 | { |
| 184 | for (i = 0; i < count; i++) |
| 185 | { |
| 186 | *d0++ = *s0++ << scale; |
| 187 | *d1++ = *s0++ << scale; |
Miika Pekkarinen | d54811f | 2005-07-02 16:52:30 +0000 | [diff] [blame] | 188 | } |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 189 | } |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 190 | else if (dsp->stereo_mode == STEREO_NONINTERLEAVED) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 191 | { |
| 192 | short* s1 = (short*) src[1]; |
| 193 | |
| 194 | for (i = 0; i < count; i++) |
| 195 | { |
| 196 | *d0++ = *s0++ << scale; |
| 197 | *d1++ = *s1++ << scale; |
| 198 | } |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 199 | } |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 200 | else |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 201 | { |
| 202 | for (i = 0; i < count; i++) |
| 203 | { |
| 204 | *d0++ = *s0++ << scale; |
| 205 | } |
| 206 | } |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 207 | } |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 208 | else if (dsp->stereo_mode == STEREO_INTERLEAVED) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 209 | { |
| 210 | long* s0 = (long*) src[0]; |
| 211 | long* d0 = dst[0]; |
| 212 | long* d1 = dst[1]; |
| 213 | int i; |
| 214 | |
| 215 | for (i = 0; i < count; i++) |
| 216 | { |
| 217 | *d0++ = *s0++; |
| 218 | *d1++ = *s0++; |
| 219 | } |
| 220 | } |
| 221 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 222 | if (dsp->stereo_mode == STEREO_NONINTERLEAVED) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 223 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 224 | src[0] += count * dsp->sample_bytes; |
| 225 | src[1] += count * dsp->sample_bytes; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 226 | } |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 227 | else if (dsp->stereo_mode == STEREO_INTERLEAVED) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 228 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 229 | src[0] += count * dsp->sample_bytes * 2; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 230 | } |
| 231 | else |
| 232 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 233 | src[0] += count * dsp->sample_bytes; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 234 | } |
| 235 | |
| 236 | return count; |
| 237 | } |
| 238 | |
| 239 | /* Linear resampling that introduces a one sample delay, because of our |
| 240 | * inability to look into the future at the end of a frame. |
| 241 | */ |
| 242 | |
| 243 | static long downsample(long *dst, long *src, int count, |
| 244 | struct resample_data *r) |
| 245 | { |
| 246 | long phase = r->phase; |
| 247 | long delta = r->delta; |
| 248 | long last_sample = r->last_sample; |
| 249 | int pos = phase >> 16; |
| 250 | int i = 1; |
| 251 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 252 | /* Do we need last sample of previous frame for interpolation? */ |
| 253 | if (pos > 0) |
| 254 | { |
| 255 | last_sample = src[pos - 1]; |
| 256 | } |
| 257 | |
| 258 | *dst++ = last_sample + FRACMUL((phase & 0xffff) << 15, |
| 259 | src[pos] - last_sample); |
| 260 | phase += delta; |
| 261 | |
| 262 | while ((pos = phase >> 16) < count) |
| 263 | { |
| 264 | *dst++ = src[pos - 1] + FRACMUL((phase & 0xffff) << 15, |
| 265 | src[pos] - src[pos - 1]); |
| 266 | phase += delta; |
| 267 | i++; |
| 268 | } |
| 269 | |
| 270 | /* Wrap phase accumulator back to start of next frame. */ |
| 271 | r->phase = phase - (count << 16); |
| 272 | r->delta = delta; |
| 273 | r->last_sample = src[count - 1]; |
| 274 | return i; |
| 275 | } |
| 276 | |
| 277 | static long upsample(long *dst, long *src, int count, struct resample_data *r) |
| 278 | { |
| 279 | long phase = r->phase; |
| 280 | long delta = r->delta; |
| 281 | long last_sample = r->last_sample; |
| 282 | int i = 0; |
| 283 | int pos; |
| 284 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 285 | while ((pos = phase >> 16) == 0) |
| 286 | { |
| 287 | *dst++ = last_sample + FRACMUL((phase & 0xffff) << 15, |
| 288 | src[pos] - last_sample); |
| 289 | phase += delta; |
| 290 | i++; |
| 291 | } |
| 292 | |
| 293 | while ((pos = phase >> 16) < count) |
| 294 | { |
| 295 | *dst++ = src[pos - 1] + FRACMUL((phase & 0xffff) << 15, |
| 296 | src[pos] - src[pos - 1]); |
| 297 | phase += delta; |
| 298 | i++; |
| 299 | } |
| 300 | |
| 301 | /* Wrap phase accumulator back to start of next frame. */ |
| 302 | r->phase = phase - (count << 16); |
| 303 | r->delta = delta; |
| 304 | r->last_sample = src[count - 1]; |
| 305 | return i; |
| 306 | } |
| 307 | |
| 308 | /* Resample count stereo samples. Updates the src array, if resampling is |
| 309 | * done, to refer to the resampled data. Returns number of stereo samples |
| 310 | * for further processing. |
| 311 | */ |
| 312 | static inline int resample(long* src[], int count) |
| 313 | { |
| 314 | long new_count; |
| 315 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 316 | if (dsp->frequency != NATIVE_FREQUENCY) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 317 | { |
| 318 | long* d0 = &resample_buf[0]; |
| 319 | /* Only process the second channel if needed. */ |
| 320 | long* d1 = (src[0] == src[1]) ? d0 |
| 321 | : &resample_buf[RESAMPLE_BUF_SIZE / 2]; |
| 322 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 323 | if (dsp->frequency < NATIVE_FREQUENCY) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 324 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 325 | new_count = upsample(d0, src[0], count, |
| 326 | &resample_data[current_codec][0]); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 327 | |
| 328 | if (d0 != d1) |
| 329 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 330 | upsample(d1, src[1], count, |
| 331 | &resample_data[current_codec][1]); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 332 | } |
| 333 | } |
| 334 | else |
| 335 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 336 | new_count = downsample(d0, src[0], count, |
| 337 | &resample_data[current_codec][0]); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 338 | |
| 339 | if (d0 != d1) |
| 340 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 341 | downsample(d1, src[1], count, |
| 342 | &resample_data[current_codec][1]); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 343 | } |
| 344 | } |
| 345 | |
| 346 | src[0] = d0; |
| 347 | src[1] = d1; |
| 348 | } |
| 349 | else |
| 350 | { |
| 351 | new_count = count; |
| 352 | } |
| 353 | |
| 354 | return new_count; |
| 355 | } |
| 356 | |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 357 | static inline long clip_sample(long sample, long min, long max) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 358 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 359 | if (sample > max) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 360 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 361 | sample = max; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 362 | } |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 363 | else if (sample < min) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 364 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 365 | sample = min; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 366 | } |
| 367 | |
| 368 | return sample; |
| 369 | } |
| 370 | |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 371 | /* The "dither" code to convert the 24-bit samples produced by libmad was |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 372 | * taken from the coolplayer project - coolplayer.sourceforge.net |
| 373 | */ |
| 374 | |
| 375 | static long dither_sample(long sample, long bias, long mask, |
| 376 | struct dither_data* dither) |
| 377 | { |
| 378 | long output; |
| 379 | long random; |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 380 | long min; |
| 381 | long max; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 382 | |
| 383 | /* Noise shape and bias */ |
| 384 | |
| 385 | sample += dither->error[0] - dither->error[1] + dither->error[2]; |
| 386 | dither->error[2] = dither->error[1]; |
| 387 | dither->error[1] = dither->error[0] / 2; |
| 388 | |
| 389 | output = sample + bias; |
| 390 | |
| 391 | /* Dither */ |
| 392 | |
| 393 | random = dither->random * 0x0019660dL + 0x3c6ef35fL; |
| 394 | sample += (random & mask) - (dither->random & mask); |
| 395 | dither->random = random; |
| 396 | |
| 397 | /* Clip and quantize */ |
| 398 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 399 | min = dsp->clip_min; |
| 400 | max = dsp->clip_max; |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 401 | sample = clip_sample(sample, min, max); |
| 402 | output = clip_sample(output, min, max) & ~mask; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 403 | |
| 404 | /* Error feedback */ |
| 405 | |
| 406 | dither->error[0] = sample - output; |
| 407 | |
| 408 | return output; |
| 409 | } |
| 410 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 411 | /* Apply a constant gain to the samples (e.g., for ReplayGain). May update |
| 412 | * the src array if gain was applied. |
| 413 | * Note that this must be called before the resampler. |
| 414 | */ |
| 415 | static void apply_gain(long* src[], int count) |
| 416 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 417 | if (dsp->replaygain) |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 418 | { |
| 419 | long* s0 = src[0]; |
| 420 | long* s1 = src[1]; |
| 421 | long* d0 = &sample_buf[0]; |
| 422 | long* d1 = (s0 == s1) ? d0 : &sample_buf[SAMPLE_BUF_SIZE / 2]; |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 423 | long gain = dsp->replaygain; |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 424 | long s; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 425 | long i; |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 426 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 427 | src[0] = d0; |
| 428 | src[1] = d1; |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 429 | s = *s0++; |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 430 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 431 | for (i = 0; i < count; i++) |
| 432 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 433 | *d0++ = FRACMUL_8_LOOP(s, gain, s0); |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 434 | } |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 435 | |
Dave Bryant | c5ebc8e | 2005-07-25 03:34:25 +0000 | [diff] [blame] | 436 | if (src [0] != src [1]) |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 437 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 438 | s = *s1++; |
| 439 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 440 | for (i = 0; i < count; i++) |
| 441 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 442 | *d1++ = FRACMUL_8_LOOP(s, gain, s1); |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 443 | } |
| 444 | } |
| 445 | } |
| 446 | } |
| 447 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 448 | static void write_samples(short* dst, long* src[], int count) |
| 449 | { |
| 450 | long* s0 = src[0]; |
| 451 | long* s1 = src[1]; |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 452 | int scale = dsp->frac_bits + 1 - NATIVE_DEPTH; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 453 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 454 | if (dsp->dither_enabled) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 455 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 456 | long bias = (1L << (dsp->frac_bits - NATIVE_DEPTH)); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 457 | long mask = (1L << scale) - 1; |
| 458 | |
| 459 | while (count-- > 0) |
| 460 | { |
| 461 | *dst++ = (short) (dither_sample(*s0++, bias, mask, &dither_data[0]) |
| 462 | >> scale); |
| 463 | *dst++ = (short) (dither_sample(*s1++, bias, mask, &dither_data[1]) |
| 464 | >> scale); |
| 465 | } |
| 466 | } |
| 467 | else |
| 468 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 469 | long min = dsp->clip_min; |
| 470 | long max = dsp->clip_max; |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 471 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 472 | while (count-- > 0) |
| 473 | { |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 474 | *dst++ = (short) (clip_sample(*s0++, min, max) >> scale); |
| 475 | *dst++ = (short) (clip_sample(*s1++, min, max) >> scale); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 476 | } |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | /* Process and convert src audio to dst based on the DSP configuration, |
| 481 | * reading size bytes of audio data. dst is assumed to be large enough; use |
| 482 | * dst_get_dest_size() to get the required size. src is an array of |
| 483 | * pointers; for mono and interleaved stereo, it contains one pointer to the |
| 484 | * start of the audio data; for non-interleaved stereo, it contains two |
| 485 | * pointers, one for each audio channel. Returns number of bytes written to |
| 486 | * dest. |
| 487 | */ |
| 488 | long dsp_process(char* dst, char* src[], long size) |
| 489 | { |
| 490 | long* tmp[2]; |
| 491 | long written = 0; |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 492 | long factor; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 493 | int samples; |
| 494 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 495 | dsp = &dsp_conf[current_codec]; |
| 496 | |
| 497 | factor = (dsp->stereo_mode != STEREO_MONO) ? 2 : 1; |
| 498 | size /= dsp->sample_bytes * factor; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 499 | INIT(); |
| 500 | dsp_set_replaygain(false); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 501 | |
| 502 | while (size > 0) |
| 503 | { |
| 504 | samples = convert_to_internal(src, size, tmp); |
| 505 | size -= samples; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 506 | apply_gain(tmp, samples); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 507 | samples = resample(tmp, samples); |
| 508 | write_samples((short*) dst, tmp, samples); |
| 509 | written += samples; |
| 510 | dst += samples * sizeof(short) * 2; |
| 511 | yield(); |
| 512 | } |
| 513 | |
| 514 | return written * sizeof(short) * 2; |
| 515 | } |
| 516 | |
| 517 | /* Given size bytes of input data, calculate the maximum number of bytes of |
| 518 | * output data that would be generated (the calculation is not entirely |
| 519 | * exact and rounds upwards to be on the safe side; during resampling, |
| 520 | * the number of samples generated depends on the current state of the |
| 521 | * resampler). |
| 522 | */ |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 523 | /* dsp_input_size MUST be called afterwards */ |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 524 | long dsp_output_size(long size) |
| 525 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 526 | dsp = &dsp_conf[current_codec]; |
| 527 | |
| 528 | if (dsp->sample_depth > NATIVE_DEPTH) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 529 | { |
| 530 | size /= 2; |
| 531 | } |
| 532 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 533 | if (dsp->frequency != NATIVE_FREQUENCY) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 534 | { |
| 535 | size = (long) ((((unsigned long) size * NATIVE_FREQUENCY) |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 536 | + (dsp->frequency - 1)) / dsp->frequency); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 537 | } |
| 538 | |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 539 | /* round to the next multiple of 2 (these are shorts) */ |
| 540 | size = (size + 1) & ~1; |
| 541 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 542 | if (dsp->stereo_mode == STEREO_MONO) |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 543 | { |
| 544 | size *= 2; |
| 545 | } |
| 546 | |
| 547 | /* now we have the size in bytes for two resampled channels, |
| 548 | * and the size in (short) must not exceed RESAMPLE_BUF_SIZE to |
| 549 | * avoid resample buffer overflow. One must call dsp_input_size() |
| 550 | * to get the correct input buffer size. */ |
| 551 | if (size > RESAMPLE_BUF_SIZE*2) |
| 552 | size = RESAMPLE_BUF_SIZE*2; |
| 553 | |
| 554 | return size; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 555 | } |
| 556 | |
| 557 | /* Given size bytes of output buffer, calculate number of bytes of input |
| 558 | * data that would be consumed in order to fill the output buffer. |
| 559 | */ |
| 560 | long dsp_input_size(long size) |
| 561 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 562 | dsp = &dsp_conf[current_codec]; |
| 563 | |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 564 | /* convert to number of output stereo samples. */ |
| 565 | size /= 2; |
| 566 | |
| 567 | /* Mono means we need half input samples to fill the output buffer */ |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 568 | if (dsp->stereo_mode == STEREO_MONO) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 569 | size /= 2; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 570 | |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 571 | /* size is now the number of resampled input samples. Convert to |
| 572 | original input samples. */ |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 573 | if (dsp->frequency != NATIVE_FREQUENCY) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 574 | { |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 575 | /* Use the real resampling delta = |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 576 | * (unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY, and |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 577 | * round towards zero to avoid buffer overflows. */ |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 578 | size = ((unsigned long)size * |
| 579 | resample_data[current_codec][0].delta) >> 16; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 580 | } |
| 581 | |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 582 | /* Convert back to bytes. */ |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 583 | if (dsp->sample_depth > NATIVE_DEPTH) |
Linus Nielsen Feltzing | 591d289 | 2005-08-10 23:17:55 +0000 | [diff] [blame] | 584 | size *= 4; |
| 585 | else |
| 586 | size *= 2; |
| 587 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 588 | return size; |
| 589 | } |
| 590 | |
| 591 | int dsp_stereo_mode(void) |
| 592 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 593 | dsp = &dsp_conf[current_codec]; |
| 594 | |
| 595 | return dsp->stereo_mode; |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 596 | } |
| 597 | |
| 598 | bool dsp_configure(int setting, void *value) |
| 599 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 600 | dsp = &dsp_conf[current_codec]; |
| 601 | |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 602 | switch (setting) |
| 603 | { |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 604 | case DSP_SET_FREQUENCY: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 605 | memset(&resample_data[current_codec][0], 0, |
| 606 | sizeof(struct resample_data) * 2); |
Linus Nielsen Feltzing | 315304a | 2005-08-10 22:56:24 +0000 | [diff] [blame] | 607 | /* Fall through!!! */ |
| 608 | case DSP_SWITCH_FREQUENCY: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 609 | dsp->frequency = ((int) value == 0) ? NATIVE_FREQUENCY : (int) value; |
| 610 | resample_data[current_codec][0].delta = |
| 611 | resample_data[current_codec][1].delta = |
| 612 | (unsigned long) dsp->frequency * 65536 / NATIVE_FREQUENCY; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 613 | break; |
| 614 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 615 | case DSP_SET_CLIP_MIN: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 616 | dsp->clip_min = (long) value; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 617 | break; |
| 618 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 619 | case DSP_SET_CLIP_MAX: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 620 | dsp->clip_max = (long) value; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 621 | break; |
| 622 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 623 | case DSP_SET_SAMPLE_DEPTH: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 624 | dsp->sample_depth = (long) value; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 625 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 626 | if (dsp->sample_depth <= NATIVE_DEPTH) |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 627 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 628 | dsp->frac_bits = WORD_FRACBITS; |
| 629 | dsp->sample_bytes = sizeof(short); |
| 630 | dsp->clip_max = ((1 << WORD_FRACBITS) - 1); |
| 631 | dsp->clip_min = -((1 << WORD_FRACBITS)); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 632 | } |
| 633 | else |
| 634 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 635 | dsp->frac_bits = (long) value; |
| 636 | dsp->sample_bytes = sizeof(long); |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 637 | } |
| 638 | |
| 639 | break; |
| 640 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 641 | case DSP_SET_STEREO_MODE: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 642 | dsp->stereo_mode = (long) value; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 643 | break; |
| 644 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 645 | case DSP_RESET: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 646 | dsp->dither_enabled = false; |
| 647 | dsp->stereo_mode = STEREO_NONINTERLEAVED; |
| 648 | dsp->clip_max = ((1 << WORD_FRACBITS) - 1); |
| 649 | dsp->clip_min = -((1 << WORD_FRACBITS)); |
| 650 | dsp->track_gain = 0; |
| 651 | dsp->album_gain = 0; |
| 652 | dsp->track_peak = 0; |
| 653 | dsp->album_peak = 0; |
| 654 | dsp->frequency = NATIVE_FREQUENCY; |
| 655 | dsp->sample_depth = NATIVE_DEPTH; |
| 656 | dsp->frac_bits = WORD_FRACBITS; |
| 657 | dsp->new_gain = true; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 658 | break; |
| 659 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 660 | case DSP_DITHER: |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 661 | memset(dither_data, 0, sizeof(dither_data)); |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 662 | dsp->dither_enabled = (bool) value; |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 663 | break; |
| 664 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 665 | case DSP_SET_TRACK_GAIN: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 666 | dsp->track_gain = (long) value; |
| 667 | dsp->new_gain = true; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 668 | break; |
| 669 | |
| 670 | case DSP_SET_ALBUM_GAIN: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 671 | dsp->album_gain = (long) value; |
| 672 | dsp->new_gain = true; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 673 | break; |
| 674 | |
| 675 | case DSP_SET_TRACK_PEAK: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 676 | dsp->track_peak = (long) value; |
| 677 | dsp->new_gain = true; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 678 | break; |
| 679 | |
| 680 | case DSP_SET_ALBUM_PEAK: |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 681 | dsp->album_peak = (long) value; |
| 682 | dsp->new_gain = true; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 683 | break; |
| 684 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 685 | default: |
| 686 | return 0; |
| 687 | } |
Magnus Holmgren | 08761aa | 2005-07-16 12:25:28 +0000 | [diff] [blame] | 688 | |
Miika Pekkarinen | d8cb703 | 2005-06-26 19:41:29 +0000 | [diff] [blame] | 689 | return 1; |
| 690 | } |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 691 | |
| 692 | void dsp_set_replaygain(bool always) |
| 693 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 694 | dsp = &dsp_conf[current_codec]; |
| 695 | |
| 696 | if (always || dsp->new_gain) |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 697 | { |
| 698 | long gain = 0; |
| 699 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 700 | dsp->new_gain = false; |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 701 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 702 | if (global_settings.replaygain || global_settings.replaygain_noclip) |
| 703 | { |
| 704 | long peak; |
| 705 | |
| 706 | if (global_settings.replaygain) |
| 707 | { |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 708 | gain = (global_settings.replaygain_track || !dsp->album_gain) |
| 709 | ? dsp->track_gain : dsp->album_gain; |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 710 | |
| 711 | if (global_settings.replaygain_preamp) |
| 712 | { |
| 713 | long preamp = get_replaygain_int( |
| 714 | global_settings.replaygain_preamp * 10); |
| 715 | |
| 716 | gain = (long) ((((int64_t) gain * preamp)) >> 24); |
| 717 | } |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 718 | } |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 719 | |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 720 | peak = (global_settings.replaygain_track || !dsp->album_peak) |
| 721 | ? dsp->track_peak : dsp->album_peak; |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 722 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 723 | if (gain == 0) |
| 724 | { |
| 725 | /* So that noclip can work even with no gain information. */ |
| 726 | gain = DEFAULT_REPLAYGAIN; |
| 727 | } |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 728 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 729 | if (global_settings.replaygain_noclip && (peak != 0) |
| 730 | && ((((int64_t) gain * peak) >> 24) >= DEFAULT_REPLAYGAIN)) |
| 731 | { |
| 732 | gain = (((int64_t) DEFAULT_REPLAYGAIN << 24) / peak); |
| 733 | } |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 734 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 735 | if (gain == DEFAULT_REPLAYGAIN) |
| 736 | { |
| 737 | /* Nothing to do, disable processing. */ |
| 738 | gain = 0; |
Magnus Holmgren | 5a8eac1 | 2005-08-11 18:56:20 +0000 | [diff] [blame] | 739 | |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 740 | } |
| 741 | } |
| 742 | |
Magnus Holmgren | 75ef312 | 2005-08-18 19:25:39 +0000 | [diff] [blame] | 743 | /* Store in S8.23 format to simplify calculations. */ |
Miika Pekkarinen | 159c52d | 2005-08-20 11:13:19 +0000 | [diff] [blame^] | 744 | dsp->replaygain = gain >> 1; |
Magnus Holmgren | 4a53787 | 2005-07-24 15:32:28 +0000 | [diff] [blame] | 745 | } |
| 746 | } |