Mercurial > audlegacy-plugins
changeset 2356:c5fa65cb26ca
make an experimental copy to update to new sound engine
author | Yoshiki Yazawa <yaz@cc.rim.or.jp> |
---|---|
date | Tue, 05 Feb 2008 01:11:50 +0900 |
parents | 0962a6325b9b |
children | 10a6c273d940 |
files | src/madplug_x/Makefile src/madplug_x/SFMT-alti.c src/madplug_x/SFMT-alti.h src/madplug_x/SFMT-params.h src/madplug_x/SFMT-params19937.h src/madplug_x/SFMT-sse2.c src/madplug_x/SFMT-sse2.h src/madplug_x/SFMT.c src/madplug_x/SFMT.h src/madplug_x/TODO src/madplug_x/configure.c src/madplug_x/decoder.c src/madplug_x/dither.c src/madplug_x/input.c src/madplug_x/input.h src/madplug_x/plugin.c src/madplug_x/plugin.h src/madplug_x/replaygain.c src/madplug_x/replaygain.h src/madplug_x/tuple.c src/madplug_x/tuple.h src/madplug_x/xing.c src/madplug_x/xing.h |
diffstat | 23 files changed, 5229 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/Makefile Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,20 @@ +PLUGIN = madplug${PLUGIN_SUFFIX} + +SRCS = configure.c \ + dither.c \ + input.c \ + replaygain.c \ + decoder.c \ + tuple.c \ + plugin.c \ + xing.c + +include ../../buildsys.mk +include ../../extra.mk + +plugindir := ${plugindir}/${INPUT_PLUGIN_DIR} + +CFLAGS += ${PLUGIN_CFLAGS} +CPPFLAGS += ${PLUGIN_CPPFLAGS} ${DBUS_CFLAGS} ${GTK_CFLAGS} ${GLIB_CFLAGS} ${PANGO_CFLAGS} ${MOWGLI_CFLAGS} ${ARCH_DEFINES} ${SIMD_CFLAGS} ${MAD_CFLAGS} -I../.. +LDFLAGS += ${AUDLDFLAGS} +LIBS += ${MAD_LIBS} -laudid3tag ${GTK_LIBS} ${GLIB_LIBS} ${PANGO_LIBS} ${MOWGLI_LIBS}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT-alti.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,126 @@ +/** + * This function represents the recursion formula in AltiVec and BIG ENDIAN. + * @param a a 128-bit part of the internal state array + * @param b a 128-bit part of the internal state array + * @param c a 128-bit part of the internal state array + * @param d a 128-bit part of the internal state array + * @return output + */ +inline static vector unsigned int vec_recursion(vector unsigned int a, + vector unsigned int b, + vector unsigned int c, + vector unsigned int d) { + + const vector unsigned int sl1 = (vector unsigned int){SL1, SL1, SL1, SL1}; + const vector unsigned int sr1 = (vector unsigned int){SR1, SR1, SR1, SR1}; +#ifdef ONLY64 + const vector unsigned int mask = (vector unsigned int) + {MSK2, MSK1, MSK4, MSK3}; + const vector unsigned char perm_sl = ALTI_SL2_PERM64; + const vector unsigned char perm_sr = ALTI_SR2_PERM64; +#else + const vector unsigned int mask = (vector unsigned int) + {MSK1, MSK2, MSK3, MSK4}; + const vector unsigned char perm_sl = ALTI_SL2_PERM; + const vector unsigned char perm_sr = ALTI_SR2_PERM; +#endif + vector unsigned int v, w, x, y, z; + x = vec_perm(a, (vector unsigned int)perm_sl, perm_sl); + v = a; + y = vec_sr(b, sr1); + z = vec_perm(c, (vector unsigned int)perm_sr, perm_sr); + w = vec_sl(d, sl1); + z = vec_xor(z, w); + y = vec_and(y, mask); + v = vec_xor(v, x); + z = vec_xor(z, y); + z = vec_xor(z, v); + return z; +} + +/** + * This function fills the internal state array with pseudorandom + * integers. + */ +inline static void gen_rand_all(void) { + int i; + vector unsigned int r, r1, r2; + + r1 = sfmt[N - 2].s; + r2 = sfmt[N - 1].s; + for (i = 0; i < N - POS1; i++) { + r = vec_recursion(sfmt[i].s, sfmt[i + POS1].s, r1, r2); + sfmt[i].s = r; + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = vec_recursion(sfmt[i].s, sfmt[i + POS1 - N].s, r1, r2); + sfmt[i].s = r; + r1 = r2; + r2 = r; + } +} + +/** + * This function fills the user-specified array with pseudorandom + * integers. + * + * @param array an 128-bit array to be filled by pseudorandom numbers. + * @param size number of 128-bit pseudorandom numbers to be generated. + */ +inline static void gen_rand_array(w128_t array[], int size) { + int i, j; + vector unsigned int r, r1, r2; + + r1 = sfmt[N - 2].s; + r2 = sfmt[N - 1].s; + for (i = 0; i < N - POS1; i++) { + r = vec_recursion(sfmt[i].s, sfmt[i + POS1].s, r1, r2); + array[i].s = r; + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = vec_recursion(sfmt[i].s, array[i + POS1 - N].s, r1, r2); + array[i].s = r; + r1 = r2; + r2 = r; + } + /* main loop */ + for (; i < size - N; i++) { + r = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2); + array[i].s = r; + r1 = r2; + r2 = r; + } + for (j = 0; j < 2 * N - size; j++) { + sfmt[j].s = array[j + size - N].s; + } + for (; i < size; i++) { + r = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2); + array[i].s = r; + sfmt[j++].s = r; + r1 = r2; + r2 = r; + } +} + +#ifndef ONLY64 +/** + * This function swaps high and low 32-bit of 64-bit integers in user + * specified array. + * + * @param array an 128-bit array to be swapped. + * @param size size of 128-bit array. + */ +inline static void swap(w128_t array[], int size) { + int i; + const vector unsigned char perm = (vector unsigned char) + {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11}; + + for (i = 0; i < size; i++) { + array[i].s = vec_perm(array[i].s, (vector unsigned int)perm, perm); + } +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT-alti.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,156 @@ +/** + * @file SFMT-alti.h + * + * @brief SIMD oriented Fast Mersenne Twister(SFMT) + * pseudorandom number generator + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * Copyright (C) 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software. + * see LICENSE.txt + */ + +#ifndef SFMT_ALTI_H +#define SFMT_ALTI_H + +inline static vector unsigned int vec_recursion(vector unsigned int a, + vector unsigned int b, + vector unsigned int c, + vector unsigned int d) + ALWAYSINLINE; + +/** + * This function represents the recursion formula in AltiVec and BIG ENDIAN. + * @param a a 128-bit part of the interal state array + * @param b a 128-bit part of the interal state array + * @param c a 128-bit part of the interal state array + * @param d a 128-bit part of the interal state array + * @return output + */ +inline static vector unsigned int vec_recursion(vector unsigned int a, + vector unsigned int b, + vector unsigned int c, + vector unsigned int d) { + + const vector unsigned int sl1 = ALTI_SL1; + const vector unsigned int sr1 = ALTI_SR1; +#ifdef ONLY64 + const vector unsigned int mask = ALTI_MSK64; + const vector unsigned char perm_sl = ALTI_SL2_PERM64; + const vector unsigned char perm_sr = ALTI_SR2_PERM64; +#else + const vector unsigned int mask = ALTI_MSK; + const vector unsigned char perm_sl = ALTI_SL2_PERM; + const vector unsigned char perm_sr = ALTI_SR2_PERM; +#endif + vector unsigned int v, w, x, y, z; + x = vec_perm(a, (vector unsigned int)perm_sl, perm_sl); + v = a; + y = vec_sr(b, sr1); + z = vec_perm(c, (vector unsigned int)perm_sr, perm_sr); + w = vec_sl(d, sl1); + z = vec_xor(z, w); + y = vec_and(y, mask); + v = vec_xor(v, x); + z = vec_xor(z, y); + z = vec_xor(z, v); + return z; +} + +/** + * This function fills the internal state array with pseudorandom + * integers. + */ +inline static void gen_rand_all(void) { + int i; + vector unsigned int r, r1, r2; + + r1 = sfmt[N - 2].s; + r2 = sfmt[N - 1].s; + for (i = 0; i < N - POS1; i++) { + r = vec_recursion(sfmt[i].s, sfmt[i + POS1].s, r1, r2); + sfmt[i].s = r; + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = vec_recursion(sfmt[i].s, sfmt[i + POS1 - N].s, r1, r2); + sfmt[i].s = r; + r1 = r2; + r2 = r; + } +} + +/** + * This function fills the user-specified array with pseudorandom + * integers. + * + * @param array an 128-bit array to be filled by pseudorandom numbers. + * @param size number of 128-bit pesudorandom numbers to be generated. + */ +inline static void gen_rand_array(w128_t *array, int size) { + int i, j; + vector unsigned int r, r1, r2; + + r1 = sfmt[N - 2].s; + r2 = sfmt[N - 1].s; + for (i = 0; i < N - POS1; i++) { + r = vec_recursion(sfmt[i].s, sfmt[i + POS1].s, r1, r2); + array[i].s = r; + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = vec_recursion(sfmt[i].s, array[i + POS1 - N].s, r1, r2); + array[i].s = r; + r1 = r2; + r2 = r; + } + /* main loop */ + for (; i < size - N; i++) { + r = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2); + array[i].s = r; + r1 = r2; + r2 = r; + } + for (j = 0; j < 2 * N - size; j++) { + sfmt[j].s = array[j + size - N].s; + } + for (; i < size; i++) { + r = vec_recursion(array[i - N].s, array[i + POS1 - N].s, r1, r2); + array[i].s = r; + sfmt[j++].s = r; + r1 = r2; + r2 = r; + } +} + +#ifndef ONLY64 +#if defined(__APPLE__) +#define ALTI_SWAP (vector unsigned char) \ + (4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11) +#else +#define ALTI_SWAP {4, 5, 6, 7, 0, 1, 2, 3, 12, 13, 14, 15, 8, 9, 10, 11} +#endif +/** + * This function swaps high and low 32-bit of 64-bit integers in user + * specified array. + * + * @param array an 128-bit array to be swaped. + * @param size size of 128-bit array. + */ +inline static void swap(w128_t *array, int size) { + int i; + const vector unsigned char perm = ALTI_SWAP; + + for (i = 0; i < size; i++) { + array[i].s = vec_perm(array[i].s, (vector unsigned int)perm, perm); + } +} +#endif + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT-params.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,97 @@ +#ifndef SFMT_PARAMS_H +#define SFMT_PARAMS_H + +#if !defined(MEXP) +#ifdef __GNUC__ + #warning "MEXP is not defined. I assume MEXP is 19937." +#endif + #define MEXP 19937 +#endif +/*----------------- + BASIC DEFINITIONS + -----------------*/ +/** Mersenne Exponent. The period of the sequence + * is a multiple of 2^MEXP-1. + * #define MEXP 19937 */ +/** SFMT generator has an internal state array of 128-bit integers, + * and N is its size. */ +#define N (MEXP / 128 + 1) +/** N32 is the size of internal state array when regarded as an array + * of 32-bit integers.*/ +#define N32 (N * 4) +/** N64 is the size of internal state array when regarded as an array + * of 64-bit integers.*/ +#define N64 (N * 2) + +/*---------------------- + the parameters of SFMT + following definitions are in paramsXXXX.h file. + ----------------------*/ +/** the pick up position of the array. +#define POS1 122 +*/ + +/** the parameter of shift left as four 32-bit registers. +#define SL1 18 + */ + +/** the parameter of shift left as one 128-bit register. + * The 128-bit integer is shifted by (SL2 * 8) bits. +#define SL2 1 +*/ + +/** the parameter of shift right as four 32-bit registers. +#define SR1 11 +*/ + +/** the parameter of shift right as one 128-bit register. + * The 128-bit integer is shifted by (SL2 * 8) bits. +#define SR2 1 +*/ + +/** A bitmask, used in the recursion. These parameters are introduced + * to break symmetry of SIMD. +#define MSK1 0xdfffffefU +#define MSK2 0xddfecb7fU +#define MSK3 0xbffaffffU +#define MSK4 0xbffffff6U +*/ + +/** These definitions are part of a 128-bit period certification vector. +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0xc98e126aU +*/ + +#if MEXP == 607 + #include "SFMT-params607.h" +#elif MEXP == 1279 + #include "SFMT-params1279.h" +#elif MEXP == 2281 + #include "SFMT-params2281.h" +#elif MEXP == 4253 + #include "SFMT-params4253.h" +#elif MEXP == 11213 + #include "SFMT-params11213.h" +#elif MEXP == 19937 + #include "SFMT-params19937.h" +#elif MEXP == 44497 + #include "SFMT-params44497.h" +#elif MEXP == 86243 + #include "SFMT-params86243.h" +#elif MEXP == 132049 + #include "SFMT-params132049.h" +#elif MEXP == 216091 + #include "SFMT-params216091.h" +#else +#ifdef __GNUC__ + #error "MEXP is not valid." + #undef MEXP +#else + #undef MEXP +#endif + +#endif + +#endif /* SFMT_PARAMS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT-params19937.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,46 @@ +#ifndef SFMT_PARAMS19937_H +#define SFMT_PARAMS19937_H + +#define POS1 122 +#define SL1 18 +#define SL2 1 +#define SR1 11 +#define SR2 1 +#define MSK1 0xdfffffefU +#define MSK2 0xddfecb7fU +#define MSK3 0xbffaffffU +#define MSK4 0xbffffff6U +#define PARITY1 0x00000001U +#define PARITY2 0x00000000U +#define PARITY3 0x00000000U +#define PARITY4 0x13c9e684U + + +/* PARAMETERS FOR ALTIVEC */ +#if defined(__APPLE__) /* For OSX */ + #define ALTI_SL1 (vector unsigned int)(SL1, SL1, SL1, SL1) + #define ALTI_SR1 (vector unsigned int)(SR1, SR1, SR1, SR1) + #define ALTI_MSK (vector unsigned int)(MSK1, MSK2, MSK3, MSK4) + #define ALTI_MSK64 \ + (vector unsigned int)(MSK2, MSK1, MSK4, MSK3) + #define ALTI_SL2_PERM \ + (vector unsigned char)(1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8) + #define ALTI_SL2_PERM64 \ + (vector unsigned char)(1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0) + #define ALTI_SR2_PERM \ + (vector unsigned char)(7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14) + #define ALTI_SR2_PERM64 \ + (vector unsigned char)(15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14) +#else /* For OTHER OSs(Linux?) */ + #define ALTI_SL1 {SL1, SL1, SL1, SL1} + #define ALTI_SR1 {SR1, SR1, SR1, SR1} + #define ALTI_MSK {MSK1, MSK2, MSK3, MSK4} + #define ALTI_MSK64 {MSK2, MSK1, MSK4, MSK3} + #define ALTI_SL2_PERM {1,2,3,23,5,6,7,0,9,10,11,4,13,14,15,8} + #define ALTI_SL2_PERM64 {1,2,3,4,5,6,7,31,9,10,11,12,13,14,15,0} + #define ALTI_SR2_PERM {7,0,1,2,11,4,5,6,15,8,9,10,17,12,13,14} + #define ALTI_SR2_PERM64 {15,0,1,2,3,4,5,6,17,8,9,10,11,12,13,14} +#endif /* For OSX */ +#define IDSTR "SFMT-19937:122-18-1-11-1:dfffffef-ddfecb7f-bffaffff-bffffff6" + +#endif /* SFMT_PARAMS19937_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT-sse2.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,113 @@ +/** + * @file SFMT-sse2.c + * @brief SIMD oriented Fast Mersenne Twister(SFMT) for intel SSE2 + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * @note We assume LITTLE ENDIAN in this file + * + * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software, see LICENSE.txt + */ + +/** + * This function represents the recursion formula. + * @param a a 128-bit part of the interal state array + * @param b a 128-bit part of the interal state array + * @param c a 128-bit part of the interal state array + * @param d a 128-bit part of the interal state array + * @param mask 128-bit mask + * @return output + */ +inline static __m128i mm_recursion(__m128i *a, __m128i *b, + __m128i c, __m128i d, __m128i mask) { + __m128i v, x, y, z; + + x = _mm_load_si128(a); + y = _mm_srli_epi32(*b, SR1); + z = _mm_srli_si128(c, SR2); + v = _mm_slli_epi32(d, SL1); + z = _mm_xor_si128(z, x); + z = _mm_xor_si128(z, v); + x = _mm_slli_si128(x, SL2); + y = _mm_and_si128(y, mask); + z = _mm_xor_si128(z, x); + z = _mm_xor_si128(z, y); + return z; +} + +/** + * This function fills the internal state array with psedorandom + * integers. + */ +inline void gen_rand_all(void) { + int i; + __m128i r, r1, r2, mask; + mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1); + + r1 = _mm_load_si128(&sfmt[N - 2].si); + r2 = _mm_load_si128(&sfmt[N - 1].si); + for (i = 0; i < N - POS1; i++) { + r = mm_recursion(&sfmt[i].si, &sfmt[i + POS1].si, r1, r2, mask); + _mm_store_si128(&sfmt[i].si, r); + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = mm_recursion(&sfmt[i].si, &sfmt[i + POS1 - N].si, r1, r2, mask); + _mm_store_si128(&sfmt[i].si, r); + r1 = r2; + r2 = r; + } +} + +/** + * This function fills the user-specified array with psedorandom + * integers. + * + * @param array an 128-bit array to be filled by pseudorandom numbers. + * @param size number of 128-bit pesudorandom numbers to be generated. + */ +inline static void gen_rand_array(w128_t array[], int size) { + int i, j; + __m128i r, r1, r2, mask; + mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1); + + r1 = _mm_load_si128(&sfmt[N - 2].si); + r2 = _mm_load_si128(&sfmt[N - 1].si); + for (i = 0; i < N - POS1; i++) { + r = mm_recursion(&sfmt[i].si, &sfmt[i + POS1].si, r1, r2, mask); + _mm_store_si128(&array[i].si, r); + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = mm_recursion(&sfmt[i].si, &array[i + POS1 - N].si, r1, r2, mask); + _mm_store_si128(&array[i].si, r); + r1 = r2; + r2 = r; + } + /* main loop */ + for (; i < size - N; i++) { + r = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, + mask); + _mm_store_si128(&array[i].si, r); + r1 = r2; + r2 = r; + } + for (j = 0; j < 2 * N - size; j++) { + r = _mm_load_si128(&array[j + size - N].si); + _mm_store_si128(&sfmt[j].si, r); + } + for (; i < size; i++) { + r = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, + mask); + _mm_store_si128(&array[i].si, r); + _mm_store_si128(&sfmt[j++].si, r); + r1 = r2; + r2 = r; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT-sse2.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,121 @@ +/** + * @file SFMT-sse2.h + * @brief SIMD oriented Fast Mersenne Twister(SFMT) for Intel SSE2 + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * @note We assume LITTLE ENDIAN in this file + * + * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software, see LICENSE.txt + */ + +#ifndef SFMT_SSE2_H +#define SFMT_SSE2_H + +inline static __m128i mm_recursion(__m128i *a, __m128i *b, __m128i c, + __m128i d, __m128i mask) ALWAYSINLINE; + +/** + * This function represents the recursion formula. + * @param a a 128-bit part of the interal state array + * @param b a 128-bit part of the interal state array + * @param c a 128-bit part of the interal state array + * @param d a 128-bit part of the interal state array + * @param mask 128-bit mask + * @return output + */ +inline static __m128i mm_recursion(__m128i *a, __m128i *b, + __m128i c, __m128i d, __m128i mask) { + __m128i v, x, y, z; + + x = _mm_load_si128(a); + y = _mm_srli_epi32(*b, SR1); + z = _mm_srli_si128(c, SR2); + v = _mm_slli_epi32(d, SL1); + z = _mm_xor_si128(z, x); + z = _mm_xor_si128(z, v); + x = _mm_slli_si128(x, SL2); + y = _mm_and_si128(y, mask); + z = _mm_xor_si128(z, x); + z = _mm_xor_si128(z, y); + return z; +} + +/** + * This function fills the internal state array with pseudorandom + * integers. + */ +inline static void gen_rand_all(void) { + int i; + __m128i r, r1, r2, mask; + mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1); + + r1 = _mm_load_si128(&sfmt[N - 2].si); + r2 = _mm_load_si128(&sfmt[N - 1].si); + for (i = 0; i < N - POS1; i++) { + r = mm_recursion(&sfmt[i].si, &sfmt[i + POS1].si, r1, r2, mask); + _mm_store_si128(&sfmt[i].si, r); + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = mm_recursion(&sfmt[i].si, &sfmt[i + POS1 - N].si, r1, r2, mask); + _mm_store_si128(&sfmt[i].si, r); + r1 = r2; + r2 = r; + } +} + +/** + * This function fills the user-specified array with pseudorandom + * integers. + * + * @param array an 128-bit array to be filled by pseudorandom numbers. + * @param size number of 128-bit pesudorandom numbers to be generated. + */ +inline static void gen_rand_array(w128_t *array, int size) { + int i, j; + __m128i r, r1, r2, mask; + mask = _mm_set_epi32(MSK4, MSK3, MSK2, MSK1); + + r1 = _mm_load_si128(&sfmt[N - 2].si); + r2 = _mm_load_si128(&sfmt[N - 1].si); + for (i = 0; i < N - POS1; i++) { + r = mm_recursion(&sfmt[i].si, &sfmt[i + POS1].si, r1, r2, mask); + _mm_store_si128(&array[i].si, r); + r1 = r2; + r2 = r; + } + for (; i < N; i++) { + r = mm_recursion(&sfmt[i].si, &array[i + POS1 - N].si, r1, r2, mask); + _mm_store_si128(&array[i].si, r); + r1 = r2; + r2 = r; + } + /* main loop */ + for (; i < size - N; i++) { + r = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, + mask); + _mm_store_si128(&array[i].si, r); + r1 = r2; + r2 = r; + } + for (j = 0; j < 2 * N - size; j++) { + r = _mm_load_si128(&array[j + size - N].si); + _mm_store_si128(&sfmt[j].si, r); + } + for (; i < size; i++) { + r = mm_recursion(&array[i - N].si, &array[i + POS1 - N].si, r1, r2, + mask); + _mm_store_si128(&array[i].si, r); + _mm_store_si128(&sfmt[j++].si, r); + r1 = r2; + r2 = r; + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,618 @@ +/** + * @file SFMT.c + * @brief SIMD oriented Fast Mersenne Twister(SFMT) + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * Copyright (C) 2006,2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software, see LICENSE.txt + */ +#include <string.h> +#include <assert.h> +#include "SFMT.h" +#include "SFMT-params.h" + +#if defined(__BIG_ENDIAN__) && !defined(__amd64) && !defined(BIG_ENDIAN64) +#define BIG_ENDIAN64 1 +#endif +#if defined(HAVE_ALTIVEC) && !defined(BIG_ENDIAN64) +#define BIG_ENDIAN64 1 +#endif +#if defined(ONLY64) && !defined(BIG_ENDIAN64) + #if defined(__GNUC__) + #error "-DONLY64 must be specified with -DBIG_ENDIAN64" + #endif +#undef ONLY64 +#endif +/*------------------------------------------------------ + 128-bit SIMD data type for Altivec, SSE2 or standard C + ------------------------------------------------------*/ +#if defined(HAVE_ALTIVEC) + #if !defined(__APPLE__) + #include <altivec.h> + #endif +/** 128-bit data structure */ +union W128_T { + vector unsigned int s; + uint32_t u[4]; +}; +/** 128-bit data type */ +typedef union W128_T w128_t; + +#elif defined(HAVE_SSE2) + #include <emmintrin.h> + +/** 128-bit data structure */ +union W128_T { + __m128i si; + uint32_t u[4]; +}; +/** 128-bit data type */ +typedef union W128_T w128_t; + +#else + +/** 128-bit data structure */ +struct W128_T { + uint32_t u[4]; +}; +/** 128-bit data type */ +typedef struct W128_T w128_t; + +#endif + +/*-------------------------------------- + FILE GLOBAL VARIABLES + internal state, index counter and flag + --------------------------------------*/ +/** the 128-bit internal state array */ +static w128_t sfmt[N]; +/** the 32bit integer pointer to the 128-bit internal state array */ +static uint32_t *psfmt32 = &sfmt[0].u[0]; +#if !defined(BIG_ENDIAN64) || defined(ONLY64) +/** the 64bit integer pointer to the 128-bit internal state array */ +static uint64_t *psfmt64 = (uint64_t *)&sfmt[0].u[0]; +#endif +/** index counter to the 32-bit internal state array */ +static int idx; +/** a flag: it is 0 if and only if the internal state is not yet + * initialized. */ +static int initialized = 0; +/** a parity check vector which certificate the period of 2^{MEXP} */ +static uint32_t parity[4] = {PARITY1, PARITY2, PARITY3, PARITY4}; + +/*---------------- + STATIC FUNCTIONS + ----------------*/ +inline static int idxof(int i); +inline static void rshift128(w128_t *out, w128_t const *in, int shift); +inline static void lshift128(w128_t *out, w128_t const *in, int shift); +inline static void gen_rand_all(void); +inline static void gen_rand_array(w128_t *array, int size); +inline static uint32_t func1(uint32_t x); +inline static uint32_t func2(uint32_t x); +static void period_certification(void); +#if defined(BIG_ENDIAN64) && !defined(ONLY64) +inline static void swap(w128_t *array, int size); +#endif + +#if defined(HAVE_ALTIVEC) + #include "SFMT-alti.h" +#elif defined(HAVE_SSE2) + #include "SFMT-sse2.h" +#endif + +/** + * This function simulate a 64-bit index of LITTLE ENDIAN + * in BIG ENDIAN machine. + */ +#ifdef ONLY64 +inline static int idxof(int i) { + return i ^ 1; +} +#else +inline static int idxof(int i) { + return i; +} +#endif +/** + * This function simulates SIMD 128-bit right shift by the standard C. + * The 128-bit integer given in in is shifted by (shift * 8) bits. + * This function simulates the LITTLE ENDIAN SIMD. + * @param out the output of this function + * @param in the 128-bit data to be shifted + * @param shift the shift value + */ +#ifdef ONLY64 +inline static void rshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]); + tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]); + + oh = th >> (shift * 8); + ol = tl >> (shift * 8); + ol |= th << (64 - shift * 8); + out->u[0] = (uint32_t)(ol >> 32); + out->u[1] = (uint32_t)ol; + out->u[2] = (uint32_t)(oh >> 32); + out->u[3] = (uint32_t)oh; +} +#else +inline static void rshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]); + tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]); + + oh = th >> (shift * 8); + ol = tl >> (shift * 8); + ol |= th << (64 - shift * 8); + out->u[1] = (uint32_t)(ol >> 32); + out->u[0] = (uint32_t)ol; + out->u[3] = (uint32_t)(oh >> 32); + out->u[2] = (uint32_t)oh; +} +#endif +/** + * This function simulates SIMD 128-bit left shift by the standard C. + * The 128-bit integer given in in is shifted by (shift * 8) bits. + * This function simulates the LITTLE ENDIAN SIMD. + * @param out the output of this function + * @param in the 128-bit data to be shifted + * @param shift the shift value + */ +#ifdef ONLY64 +inline static void lshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[2] << 32) | ((uint64_t)in->u[3]); + tl = ((uint64_t)in->u[0] << 32) | ((uint64_t)in->u[1]); + + oh = th << (shift * 8); + ol = tl << (shift * 8); + oh |= tl >> (64 - shift * 8); + out->u[0] = (uint32_t)(ol >> 32); + out->u[1] = (uint32_t)ol; + out->u[2] = (uint32_t)(oh >> 32); + out->u[3] = (uint32_t)oh; +} +#else +inline static void lshift128(w128_t *out, w128_t const *in, int shift) { + uint64_t th, tl, oh, ol; + + th = ((uint64_t)in->u[3] << 32) | ((uint64_t)in->u[2]); + tl = ((uint64_t)in->u[1] << 32) | ((uint64_t)in->u[0]); + + oh = th << (shift * 8); + ol = tl << (shift * 8); + oh |= tl >> (64 - shift * 8); + out->u[1] = (uint32_t)(ol >> 32); + out->u[0] = (uint32_t)ol; + out->u[3] = (uint32_t)(oh >> 32); + out->u[2] = (uint32_t)oh; +} +#endif + +/** + * This function represents the recursion formula. + * @param r output + * @param a a 128-bit part of the internal state array + * @param b a 128-bit part of the internal state array + * @param c a 128-bit part of the internal state array + * @param d a 128-bit part of the internal state array + */ +#ifdef ONLY64 +inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c, + w128_t *d) { + w128_t x; + w128_t y; + + lshift128(&x, a, SL2); + rshift128(&y, c, SR2); + r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK2) ^ y.u[0] + ^ (d->u[0] << SL1); + r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK1) ^ y.u[1] + ^ (d->u[1] << SL1); + r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK4) ^ y.u[2] + ^ (d->u[2] << SL1); + r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK3) ^ y.u[3] + ^ (d->u[3] << SL1); +} +#else +inline static void do_recursion(w128_t *r, w128_t *a, w128_t *b, w128_t *c, + w128_t *d) { + w128_t x; + w128_t y; + + lshift128(&x, a, SL2); + rshift128(&y, c, SR2); + r->u[0] = a->u[0] ^ x.u[0] ^ ((b->u[0] >> SR1) & MSK1) ^ y.u[0] + ^ (d->u[0] << SL1); + r->u[1] = a->u[1] ^ x.u[1] ^ ((b->u[1] >> SR1) & MSK2) ^ y.u[1] + ^ (d->u[1] << SL1); + r->u[2] = a->u[2] ^ x.u[2] ^ ((b->u[2] >> SR1) & MSK3) ^ y.u[2] + ^ (d->u[2] << SL1); + r->u[3] = a->u[3] ^ x.u[3] ^ ((b->u[3] >> SR1) & MSK4) ^ y.u[3] + ^ (d->u[3] << SL1); +} +#endif + +#if (!defined(HAVE_ALTIVEC)) && (!defined(HAVE_SSE2)) +/** + * This function fills the internal state array with pseudorandom + * integers. + */ +inline static void gen_rand_all(void) { + int i; + w128_t *r1, *r2; + + r1 = &sfmt[N - 2]; + r2 = &sfmt[N - 1]; + for (i = 0; i < N - POS1; i++) { + do_recursion(&sfmt[i], &sfmt[i], &sfmt[i + POS1], r1, r2); + r1 = r2; + r2 = &sfmt[i]; + } + for (; i < N; i++) { + do_recursion(&sfmt[i], &sfmt[i], &sfmt[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &sfmt[i]; + } +} + +/** + * This function fills the user-specified array with pseudorandom + * integers. + * + * @param array an 128-bit array to be filled by pseudorandom numbers. + * @param size number of 128-bit pseudorandom numbers to be generated. + */ +inline static void gen_rand_array(w128_t *array, int size) { + int i, j; + w128_t *r1, *r2; + + r1 = &sfmt[N - 2]; + r2 = &sfmt[N - 1]; + for (i = 0; i < N - POS1; i++) { + do_recursion(&array[i], &sfmt[i], &sfmt[i + POS1], r1, r2); + r1 = r2; + r2 = &array[i]; + } + for (; i < N; i++) { + do_recursion(&array[i], &sfmt[i], &array[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &array[i]; + } + for (; i < size - N; i++) { + do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &array[i]; + } + for (j = 0; j < 2 * N - size; j++) { + sfmt[j] = array[j + size - N]; + } + for (; i < size; i++, j++) { + do_recursion(&array[i], &array[i - N], &array[i + POS1 - N], r1, r2); + r1 = r2; + r2 = &array[i]; + sfmt[j] = array[i]; + } +} +#endif + +#if defined(BIG_ENDIAN64) && !defined(ONLY64) && !defined(HAVE_ALTIVEC) +inline static void swap(w128_t *array, int size) { + int i; + uint32_t x, y; + + for (i = 0; i < size; i++) { + x = array[i].u[0]; + y = array[i].u[2]; + array[i].u[0] = array[i].u[1]; + array[i].u[2] = array[i].u[3]; + array[i].u[1] = x; + array[i].u[3] = y; + } +} +#endif +/** + * This function represents a function used in the initialization + * by init_by_array + * @param x 32-bit integer + * @return 32-bit integer + */ +static uint32_t func1(uint32_t x) { + return (x ^ (x >> 27)) * (uint32_t)1664525UL; +} + +/** + * This function represents a function used in the initialization + * by init_by_array + * @param x 32-bit integer + * @return 32-bit integer + */ +static uint32_t func2(uint32_t x) { + return (x ^ (x >> 27)) * (uint32_t)1566083941UL; +} + +/** + * This function certificate the period of 2^{MEXP} + */ +static void period_certification(void) { + int inner = 0; + int i, j; + uint32_t work; + + for (i = 0; i < 4; i++) + inner ^= psfmt32[idxof(i)] & parity[i]; + for (i = 16; i > 0; i >>= 1) + inner ^= inner >> i; + inner &= 1; + /* check OK */ + if (inner == 1) { + return; + } + /* check NG, and modification */ + for (i = 0; i < 4; i++) { + work = 1; + for (j = 0; j < 32; j++) { + if ((work & parity[i]) != 0) { + psfmt32[idxof(i)] ^= work; + return; + } + work = work << 1; + } + } +} + +/*---------------- + PUBLIC FUNCTIONS + ----------------*/ +/** + * This function returns the identification string. + * The string shows the word size, the Mersenne exponent, + * and all parameters of this generator. + */ +const char *get_idstring(void) { + return IDSTR; +} + +/** + * This function returns the minimum size of array used for \b + * fill_array32() function. + * @return minimum size of array used for fill_array32() function. + */ +int get_min_array_size32(void) { + return N32; +} + +/** + * This function returns the minimum size of array used for \b + * fill_array64() function. + * @return minimum size of array used for fill_array64() function. + */ +int get_min_array_size64(void) { + return N64; +} + +#ifndef ONLY64 +/** + * This function generates and returns 32-bit pseudorandom number. + * init_gen_rand or init_by_array must be called before this function. + * @return 32-bit pseudorandom number + */ +uint32_t gen_rand32(void) { + uint32_t r; + + assert(initialized); + if (idx >= N32) { + gen_rand_all(); + idx = 0; + } + r = psfmt32[idx++]; + return r; +} +#endif +/** + * This function generates and returns 64-bit pseudorandom number. + * init_gen_rand or init_by_array must be called before this function. + * The function gen_rand64 should not be called after gen_rand32, + * unless an initialization is again executed. + * @return 64-bit pseudorandom number + */ +uint64_t gen_rand64(void) { +#if defined(BIG_ENDIAN64) && !defined(ONLY64) + uint32_t r1, r2; +#else + uint64_t r; +#endif + + assert(initialized); + assert(idx % 2 == 0); + + if (idx >= N32) { + gen_rand_all(); + idx = 0; + } +#if defined(BIG_ENDIAN64) && !defined(ONLY64) + r1 = psfmt32[idx]; + r2 = psfmt32[idx + 1]; + idx += 2; + return ((uint64_t)r2 << 32) | r1; +#else + r = psfmt64[idx / 2]; + idx += 2; + return r; +#endif +} + +#ifndef ONLY64 +/** + * This function generates pseudorandom 32-bit integers in the + * specified array[] by one call. The number of pseudorandom integers + * is specified by the argument size, which must be at least 624 and a + * multiple of four. The generation by this function is much faster + * than the following gen_rand function. + * + * For initialization, init_gen_rand or init_by_array must be called + * before the first call of this function. This function can not be + * used after calling gen_rand function, without initialization. + * + * @param array an array where pseudorandom 32-bit integers are filled + * by this function. The pointer to the array must be \b "aligned" + * (namely, must be a multiple of 16) in the SIMD version, since it + * refers to the address of a 128-bit integer. In the standard C + * version, the pointer is arbitrary. + * + * @param size the number of 32-bit pseudorandom integers to be + * generated. size must be a multiple of 4, and greater than or equal + * to (MEXP / 128 + 1) * 4. + * + * @note \b memalign or \b posix_memalign is available to get aligned + * memory. Mac OSX doesn't have these functions, but \b malloc of OSX + * returns the pointer to the aligned memory block. + */ +void fill_array32(uint32_t *array, int size) { + assert(initialized); + assert(idx == N32); + assert(size % 4 == 0); + assert(size >= N32); + + gen_rand_array((w128_t *)array, size / 4); + idx = N32; +} +#endif + +/** + * This function generates pseudorandom 64-bit integers in the + * specified array[] by one call. The number of pseudorandom integers + * is specified by the argument size, which must be at least 312 and a + * multiple of two. The generation by this function is much faster + * than the following gen_rand function. + * + * For initialization, init_gen_rand or init_by_array must be called + * before the first call of this function. This function can not be + * used after calling gen_rand function, without initialization. + * + * @param array an array where pseudorandom 64-bit integers are filled + * by this function. The pointer to the array must be "aligned" + * (namely, must be a multiple of 16) in the SIMD version, since it + * refers to the address of a 128-bit integer. In the standard C + * version, the pointer is arbitrary. + * + * @param size the number of 64-bit pseudorandom integers to be + * generated. size must be a multiple of 2, and greater than or equal + * to (MEXP / 128 + 1) * 2 + * + * @note \b memalign or \b posix_memalign is available to get aligned + * memory. Mac OSX doesn't have these functions, but \b malloc of OSX + * returns the pointer to the aligned memory block. + */ +void fill_array64(uint64_t *array, int size) { + assert(initialized); + assert(idx == N32); + assert(size % 2 == 0); + assert(size >= N64); + + gen_rand_array((w128_t *)array, size / 2); + idx = N32; + +#if defined(BIG_ENDIAN64) && !defined(ONLY64) + swap((w128_t *)array, size /2); +#endif +} + +/** + * This function initializes the internal state array with a 32-bit + * integer seed. + * + * @param seed a 32-bit integer used as the seed. + */ +void init_gen_rand(uint32_t seed) { + int i; + + psfmt32[idxof(0)] = seed; + for (i = 1; i < N32; i++) { + psfmt32[idxof(i)] = 1812433253UL * (psfmt32[idxof(i - 1)] + ^ (psfmt32[idxof(i - 1)] >> 30)) + + i; + } + idx = N32; + period_certification(); + initialized = 1; +} + +/** + * This function initializes the internal state array, + * with an array of 32-bit integers used as the seeds + * @param init_key the array of 32-bit integers, used as a seed. + * @param key_length the length of init_key. + */ +void init_by_array(uint32_t *init_key, int key_length) { + int i, j, count; + uint32_t r; + int lag; + int mid; + int size = N * 4; + + if (size >= 623) { + lag = 11; + } else if (size >= 68) { + lag = 7; + } else if (size >= 39) { + lag = 5; + } else { + lag = 3; + } + mid = (size - lag) / 2; + + memset(sfmt, 0x8b, sizeof(sfmt)); + if (key_length + 1 > N32) { + count = key_length + 1; + } else { + count = N32; + } + r = func1(psfmt32[idxof(0)] ^ psfmt32[idxof(mid)] + ^ psfmt32[idxof(N32 - 1)]); + psfmt32[idxof(mid)] += r; + r += key_length; + psfmt32[idxof(mid + lag)] += r; + psfmt32[idxof(0)] = r; + + count--; + for (i = 1, j = 0; (j < count) && (j < key_length); j++) { + r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)] + ^ psfmt32[idxof((i + N32 - 1) % N32)]); + psfmt32[idxof((i + mid) % N32)] += r; + r += init_key[j] + i; + psfmt32[idxof((i + mid + lag) % N32)] += r; + psfmt32[idxof(i)] = r; + i = (i + 1) % N32; + } + for (; j < count; j++) { + r = func1(psfmt32[idxof(i)] ^ psfmt32[idxof((i + mid) % N32)] + ^ psfmt32[idxof((i + N32 - 1) % N32)]); + psfmt32[idxof((i + mid) % N32)] += r; + r += i; + psfmt32[idxof((i + mid + lag) % N32)] += r; + psfmt32[idxof(i)] = r; + i = (i + 1) % N32; + } + for (j = 0; j < N32; j++) { + r = func2(psfmt32[idxof(i)] + psfmt32[idxof((i + mid) % N32)] + + psfmt32[idxof((i + N32 - 1) % N32)]); + psfmt32[idxof((i + mid) % N32)] ^= r; + r -= i; + psfmt32[idxof((i + mid + lag) % N32)] ^= r; + psfmt32[idxof(i)] = r; + i = (i + 1) % N32; + } + + idx = N32; + period_certification(); + initialized = 1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/SFMT.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,125 @@ +/** + * @file SFMT.h + * + * @brief SIMD oriented Fast Mersenne Twister(SFMT) pseudorandom + * number generator + * + * @author Mutsuo Saito (Hiroshima University) + * @author Makoto Matsumoto (Hiroshima University) + * + * Copyright (C) 2006, 2007 Mutsuo Saito, Makoto Matsumoto and Hiroshima + * University. All rights reserved. + * + * The new BSD License is applied to this software. + * see LICENSE.txt + * + * @note We assume that your system has inttypes.h. If your system + * doesn't have inttypes.h, you have to typedef uint32_t and uint64_t, + * and you have to define PRIu64 and PRIx64 in this file as follows: + * @verbatim + typedef unsigned int uint32_t + typedef unsigned long long uint64_t + #define PRIu64 "llu" + #define PRIx64 "llx" +@endverbatim + * uint32_t must be exactly 32-bit unsigned integer type (no more, no + * less), and uint64_t must be exactly 64-bit unsigned integer type. + * PRIu64 and PRIx64 are used for printf function to print 64-bit + * unsigned int and 64-bit unsigned int in hexadecimal format. + */ + +#ifndef SFMT_H +#define SFMT_H + +#include <stdio.h> + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) + #include <inttypes.h> +#elif defined(_MSC_VER) || defined(__BORLANDC__) + typedef unsigned int uint32_t; + typedef unsigned __int64 uint64_t; + #define inline __inline +#else + #include <inttypes.h> + #if defined(__GNUC__) + #define inline __inline__ + #endif +#endif + +#ifndef PRIu64 + #if defined(_MSC_VER) || defined(__BORLANDC__) + #define PRIu64 "I64u" + #define PRIx64 "I64x" + #else + #define PRIu64 "llu" + #define PRIx64 "llx" + #endif +#endif + +#if defined(__GNUC__) +#define ALWAYSINLINE __attribute__((always_inline)) +#endif + +uint32_t gen_rand32(void); +uint64_t gen_rand64(void); +void fill_array32(uint32_t *array, int size); +void fill_array64(uint64_t *array, int size); +void init_gen_rand(uint32_t seed); +void init_by_array(uint32_t *init_key, int key_length); +const char *get_idstring(void); +int get_min_array_size32(void); +int get_min_array_size64(void); + +/* These real versions are due to Isaku Wada */ +/** generates a random number on [0,1]-real-interval */ +inline static double to_real1(uint32_t v) +{ + return v * (1.0/4294967295.0); + /* divided by 2^32-1 */ +} + +/** generates a random number on [0,1]-real-interval */ +inline static double genrand_real1(void) +{ + return to_real1(gen_rand32()); +} + +/** generates a random number on [0,1)-real-interval */ +inline static double to_real2(uint32_t v) +{ + return v * (1.0/4294967296.0); + /* divided by 2^32 */ +} + +/** generates a random number on [0,1)-real-interval */ +inline static double genrand_real2(void) +{ + return to_real2(gen_rand32()); +} + +/** generates a random number on (0,1)-real-interval */ +inline static double to_real3(uint32_t v) +{ + return (((double)v) + 0.5)*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/** generates a random number on (0,1)-real-interval */ +inline static double genrand_real3(void) +{ + return to_real3(gen_rand32()); +} +/** These real versions are due to Isaku Wada */ + +/** generates a random number on [0,1) with 53-bit resolution*/ +inline static double to_res53(uint64_t v) +{ + return v * (1.0/18446744073709551616.0L); +} + +/** generates a random number on [0,1) with 53-bit resolution*/ +inline static double genrand_res53(void) +{ + return to_res53(gen_rand64()); +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/TODO Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,16 @@ +-: to do, X: done, x: test needed, ?: uncertainty + +X remove info->stop +X frame_name == ID3_FRAME_COMMENT code highly depends on address of strings. strcmp() is safer way. +X replace copy right notices +X import title override feature +X import neno's configure dialog +X bug fix: press F5 on not yet played stream hangs. +- tidy up mad_info_t +- remove pb_mutex ? +x replace tag editor with neno's +X rename files for convenience of taking diff +- utf8 id3v1 writing ?? (it requires modification to libid3tag.) +X complete tag deletion code +- stream recording feature ?? +- simplify info/metadata functions
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/configure.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,621 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define AUD_DEBUG 1 */ + +#include "plugin.h" + +#include <gtk/gtk.h> +#include <math.h> +#include <audacious/configdb.h> + +static GtkWidget *configure_win = NULL; +static audmad_config_t *oldconfig = NULL; // undo storage + +static audmad_config_t * +duplicate_config(audmad_config_t *orig) +{ + audmad_config_t *copy = g_memdup(orig, sizeof(audmad_config_t)); + + copy->replaygain.preamp0_db = g_strdup(orig->replaygain.preamp0_db); + copy->replaygain.preamp1_db = g_strdup(orig->replaygain.preamp1_db); + copy->replaygain.preamp2_db = g_strdup(orig->replaygain.preamp2_db); + copy->id3_format = g_strdup(orig->id3_format); + + return copy; +} + +static void +dispose_config(audmad_config_t *config) +{ + g_free(config->replaygain.preamp0_db); + g_free(config->replaygain.preamp1_db); + g_free(config->replaygain.preamp2_db); + g_free(config->id3_format); + g_free(config); +} + +static void +update_config(gpointer widgets) +{ + const gchar *text = NULL; + + AUDDBG("updating\n"); + + GtkWidget *dither = g_object_get_data(widgets, "dither"); + GtkWidget *reopen = g_object_get_data(widgets, "reopen"); + GtkWidget *fast_playback = g_object_get_data(widgets, "fast_playback"); + GtkWidget *use_xing = g_object_get_data(widgets, "use_xing"); + GtkWidget *sjis = g_object_get_data(widgets, "sjis"); + GtkWidget *show_avg = g_object_get_data(widgets, "show_avg"); + GtkWidget *RG_enable = g_object_get_data(widgets, "RG_enable"); + GtkWidget *preamp0 = g_object_get_data(widgets, "preamp0"); + GtkWidget *preamp1 = g_object_get_data(widgets, "preamp1"); + GtkWidget *preamp2 = g_object_get_data(widgets, "preamp2"); + GtkWidget *trackMode = g_object_get_data(widgets, "trackMode"); + GtkWidget *adaptive_scaler = g_object_get_data(widgets, "adaptive_scaler"); + GtkWidget *anti_clip = g_object_get_data(widgets, "anti_clip"); + GtkWidget *title_override = g_object_get_data(widgets, "title_override"); + GtkWidget *title_id3_entry = g_object_get_data(widgets, "title_id3_entry"); + + //audio + audmad_config->dither = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dither)); + audmad_config->force_reopen_audio = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(reopen)); + + //metadata + audmad_config->fast_play_time_calc = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(fast_playback)); + audmad_config->use_xing = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(use_xing)); + audmad_config->sjis = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sjis)); + + //misc + audmad_config->show_avg_vbr_bitrate = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(show_avg)); + + //gain control + text = gtk_entry_get_text(GTK_ENTRY(preamp0)); + g_free(audmad_config->replaygain.preamp0_db); + if(atof(text) > 12.0) + audmad_config->replaygain.preamp0_db = g_strdup("+12.0"); + else if(atof(text) < -12.0) + audmad_config->replaygain.preamp0_db = g_strdup("-12.0"); + else + audmad_config->replaygain.preamp0_db = g_strdup(text); + + gtk_entry_set_text(GTK_ENTRY(preamp0), audmad_config->replaygain.preamp0_db); + + audmad_config->replaygain.enable = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(RG_enable)); + audmad_config->replaygain.track_mode = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(trackMode)); + + text = gtk_entry_get_text(GTK_ENTRY(preamp1)); + g_free(audmad_config->replaygain.preamp1_db); + if(atof(text) > 12.0) + audmad_config->replaygain.preamp1_db = g_strdup("+12.0"); + else if(atof(text) < -12.0) + audmad_config->replaygain.preamp1_db = g_strdup("-12.0"); + else + audmad_config->replaygain.preamp1_db = g_strdup(text); + + gtk_entry_set_text(GTK_ENTRY(preamp1), audmad_config->replaygain.preamp1_db); + + text = gtk_entry_get_text(GTK_ENTRY(preamp2)); + g_free(audmad_config->replaygain.preamp2_db); + if(atof(text) > 12.0) + audmad_config->replaygain.preamp2_db = g_strdup("+12.0"); + else if(atof(text) < -12.0) + audmad_config->replaygain.preamp2_db = g_strdup("-12.0"); + else + audmad_config->replaygain.preamp2_db = g_strdup(text); + + gtk_entry_set_text(GTK_ENTRY(preamp2), audmad_config->replaygain.preamp2_db); + + audmad_config->replaygain.anti_clip = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(anti_clip)); + audmad_config->replaygain.adaptive_scaler = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(adaptive_scaler)); + + //text + audmad_config->title_override = + gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_override)); + + text = gtk_entry_get_text(GTK_ENTRY(title_id3_entry)); + g_free(audmad_config->id3_format); + audmad_config->id3_format = g_strdup(text); + +} + +static void +save_config(void) +{ + ConfigDb *db = aud_cfg_db_open(); + + AUDDBG("saving\n"); + + audmad_config_compute(audmad_config); + + //audio + aud_cfg_db_set_bool(db, "MAD", "dither", audmad_config->dither); + aud_cfg_db_set_bool(db, "MAD", "force_reopen_audio", + audmad_config->force_reopen_audio); + //metadata + aud_cfg_db_set_bool(db, "MAD", "fast_play_time_calc", + audmad_config->fast_play_time_calc); + aud_cfg_db_set_bool(db, "MAD", "use_xing", audmad_config->use_xing); + aud_cfg_db_set_bool(db, "MAD", "sjis", audmad_config->sjis); + + //misc + aud_cfg_db_set_bool(db, "MAD", "show_avg_vbr_bitrate", + audmad_config->show_avg_vbr_bitrate); + + //gain control + aud_cfg_db_set_string(db, "MAD", "RG.preamp0_db", + audmad_config->replaygain.preamp0_db); + aud_cfg_db_set_bool(db, "MAD", "RG.enable", + audmad_config->replaygain.enable); + aud_cfg_db_set_bool(db, "MAD", "RG.track_mode", + audmad_config->replaygain.track_mode); + aud_cfg_db_set_string(db, "MAD", "RG.preamp1_db", + audmad_config->replaygain.preamp1_db); + aud_cfg_db_set_string(db, "MAD", "RG.preamp2_db", + audmad_config->replaygain.preamp2_db); + aud_cfg_db_set_bool(db, "MAD", "RG.anti_clip", + audmad_config->replaygain.anti_clip); + aud_cfg_db_set_bool(db, "MAD", "RG.adaptive_scaler", + audmad_config->replaygain.adaptive_scaler); + + //text + aud_cfg_db_set_bool(db, "MAD", "title_override", audmad_config->title_override); + aud_cfg_db_set_string(db, "MAD", "id3_format", audmad_config->id3_format); + + aud_cfg_db_close(db); +} + +static void +configure_win_cancel(GtkWidget *widget, gpointer widgets) +{ + AUDDBG("cancel\n"); + dispose_config(audmad_config); + audmad_config = oldconfig; + oldconfig = NULL; + save_config(); + gtk_widget_destroy(configure_win); + g_object_unref(widgets); +} + +static void +configure_win_ok(GtkWidget *widget, gpointer widgets) +{ + AUDDBG("ok\n"); + update_config(widgets); + save_config(); + gtk_widget_destroy(configure_win); + g_object_unref(widgets); + dispose_config(oldconfig); + oldconfig = NULL; +} + +static void +configure_destroy(GtkWidget *w, gpointer data) +{ +} + +static void +RG_enable_cb(GtkWidget *w, gpointer widgets) +{ + GtkWidget *type_vbox = g_object_get_data(widgets, "type_vbox"); + GtkWidget *rgtypeSet = g_object_get_data(widgets, "rgtypeSet"); + GtkWidget *preamp1_hbox = g_object_get_data(widgets, "preamp1_hbox"); + GtkWidget *preamp2_hbox = g_object_get_data(widgets, "preamp2_hbox"); + GtkWidget *anti_clip = g_object_get_data(widgets, "anti_clip"); + gboolean enabled; + + update_config(widgets); + save_config(); + + enabled = audmad_config->replaygain.enable; + + gtk_widget_set_sensitive(type_vbox, enabled); + gtk_widget_set_sensitive(rgtypeSet, enabled); + gtk_widget_set_sensitive(preamp1_hbox, enabled); + gtk_widget_set_sensitive(preamp2_hbox, enabled); + gtk_widget_set_sensitive(anti_clip, enabled); +} + +static void +RG_type_track_cb(GtkWidget *w, gpointer widgets) +{ + GtkToggleButton *tb = GTK_TOGGLE_BUTTON(g_object_get_data(widgets, "trackMode")); + + if (gtk_toggle_button_get_active(tb)) + audmad_config->replaygain.track_mode = TRUE; +} + +static void +RG_type_album_cb(GtkWidget *w, gpointer widgets) +{ + GtkToggleButton *tb = GTK_TOGGLE_BUTTON(g_object_get_data(widgets, "albumMode")); + + if (gtk_toggle_button_get_active(tb)) + audmad_config->replaygain.track_mode = FALSE; +} + +static void +simple_update_cb(GtkWidget *w, gpointer widgets) +{ + update_config(widgets); + save_config(); +} + +static void +entry_changed_cb(GtkWidget *w, gpointer widgets) +{ + simple_update_cb(w, widgets); +} + +static void +title_override_cb(GtkWidget *w, gpointer widgets) +{ + GtkWidget *title_override = g_object_get_data(widgets, "title_override"); + GtkWidget *title_id3_entry = g_object_get_data(widgets, "title_id3_entry"); + GtkWidget *title_id3_label = g_object_get_data(widgets, "title_id3_label"); + gboolean override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_override)); + + update_config(widgets); + save_config(); + + gtk_widget_set_sensitive(title_id3_entry, override); + gtk_widget_set_sensitive(title_id3_label, override); +} + +void +audmad_configure(void) +{ + GtkWidget *vbox; + GtkWidget *bbox, *ok, *cancel; + GtkWidget *label, *preamp0_hbox, *preamp1_hbox, *preamp2_hbox; + GtkWidget *notebook, *vbox2, *title_id3_label, *title_id3_box; + + GtkWidget *fast_playback, *use_xing, *dither, *sjis, *show_avg, *reopen; + GtkWidget *RG_enable, *type_vbox, *rg_vbox, *preamp0, *preamp1, *preamp2; + GtkWidget *trackMode, *albumMode, *adaptive_scaler, *anti_clip; + GtkWidget *title_override, *title_id3_entry; + GtkWidget *rgtypeFrame, *replaygainFrame, *metadataFrame, *audioFrame, *miscFrame; + GtkWidget *metadata_vbox, *audio_vbox, *misc_vbox; + + gpointer widgets = g_object_new(G_TYPE_OBJECT, NULL); + + if(oldconfig) { + dispose_config(oldconfig); + oldconfig = NULL; + } + + oldconfig = duplicate_config(audmad_config); //for undo + + if (configure_win != NULL) { + gtk_widget_show(configure_win); + return; + } + + configure_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_type_hint(GTK_WINDOW(configure_win), GDK_WINDOW_TYPE_HINT_DIALOG); + + g_signal_connect(G_OBJECT(configure_win), "destroy", + G_CALLBACK(gtk_widget_destroyed), &configure_win); + g_signal_connect(G_OBJECT(configure_win), "destroy", + G_CALLBACK(configure_destroy), &configure_win); + + gtk_window_set_title(GTK_WINDOW(configure_win), + _("MPEG Audio Plugin Configuration")); + gtk_window_set_policy(GTK_WINDOW(configure_win), FALSE, FALSE, FALSE); + gtk_container_border_width(GTK_CONTAINER(configure_win), 10); + + vbox = gtk_vbox_new(FALSE, 10); + gtk_container_add(GTK_CONTAINER(configure_win), vbox); + + notebook = gtk_notebook_new(); + gtk_box_pack_start(GTK_BOX(vbox), notebook, FALSE, FALSE, 0); + + + /********************************************************************************/ + + + vbox2 = gtk_vbox_new(FALSE, 5); + + // audio frame + audioFrame = gtk_frame_new(_("Audio Settings")); + gtk_container_border_width(GTK_CONTAINER(audioFrame), 5); + + audio_vbox = gtk_vbox_new(FALSE, 5); + + gtk_container_add(GTK_CONTAINER(audioFrame), audio_vbox); + gtk_container_add(GTK_CONTAINER(vbox2), audioFrame); + + dither = gtk_check_button_new_with_label + (_("Dither output when rounding to 16-bit")); + g_object_set_data(widgets, "dither", dither); + gtk_box_pack_start(GTK_BOX(audio_vbox), dither, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dither), + audmad_config->dither); + g_signal_connect(G_OBJECT(dither), "clicked", G_CALLBACK(simple_update_cb), widgets); + + reopen = gtk_check_button_new_with_label(_("Force reopen audio when audio type changed")); + g_object_set_data(widgets, "reopen", reopen); + gtk_box_pack_start(GTK_BOX(audio_vbox), reopen, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reopen), + audmad_config->force_reopen_audio); + g_signal_connect(G_OBJECT(reopen), "clicked", G_CALLBACK(simple_update_cb), widgets); + + + // metadata frame + metadataFrame = gtk_frame_new(_("Metadata Settings")); + gtk_container_border_width(GTK_CONTAINER(metadataFrame), 5); + + metadata_vbox = gtk_vbox_new(FALSE, 5); + + gtk_container_add(GTK_CONTAINER(metadataFrame), metadata_vbox); + gtk_container_add(GTK_CONTAINER(vbox2), metadataFrame); + + fast_playback = + gtk_check_button_new_with_label(_("Enable fast play-length calculation")); + g_object_set_data(widgets, "fast_playback", fast_playback); + gtk_box_pack_start(GTK_BOX(metadata_vbox), fast_playback, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_playback), + audmad_config->fast_play_time_calc); + g_signal_connect(G_OBJECT(fast_playback), "clicked", G_CALLBACK(simple_update_cb), widgets); + + use_xing = gtk_check_button_new_with_label(_("Parse XING headers")); + g_object_set_data(widgets, "use_xing", use_xing); + gtk_box_pack_start(GTK_BOX(metadata_vbox), use_xing, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(use_xing), + audmad_config->use_xing); + g_signal_connect(G_OBJECT(use_xing), "clicked", G_CALLBACK(simple_update_cb), widgets); + + sjis = gtk_check_button_new_with_label(_("Use SJIS to write ID3 tags (not recommended)")); + g_object_set_data(widgets, "sjis", sjis); + gtk_box_pack_start(GTK_BOX(metadata_vbox), sjis, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(sjis), audmad_config->sjis); + g_signal_connect(G_OBJECT(sjis), "clicked", G_CALLBACK(simple_update_cb), widgets); + + // misc frame + miscFrame = gtk_frame_new(_("Miscellaneous Settings")); + gtk_container_border_width(GTK_CONTAINER(miscFrame), 5); + + misc_vbox = gtk_vbox_new(FALSE, 5); + + gtk_container_add(GTK_CONTAINER(miscFrame), misc_vbox); + gtk_container_add(GTK_CONTAINER(vbox2), miscFrame); + + + show_avg = gtk_check_button_new_with_label(_("Display average bitrate for VBR")); + g_object_set_data(widgets, "show_avg", show_avg); + gtk_box_pack_start(GTK_BOX(misc_vbox), show_avg, FALSE, FALSE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_avg), + audmad_config->show_avg_vbr_bitrate); + g_signal_connect(G_OBJECT(show_avg), "clicked", G_CALLBACK(simple_update_cb), widgets); + + // add to notebook + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(_("General"))); + + + + /*********************************************************************************/ + + + vbox2 = gtk_vbox_new(FALSE, 10); + gtk_container_border_width(GTK_CONTAINER(vbox2), 5); + + // overall preamp + label = gtk_label_new(_("Base gain (dB):")); + preamp0_hbox = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox2), preamp0_hbox, TRUE, TRUE, 0); + + preamp0 = gtk_entry_new(); + g_object_set_data(widgets, "preamp0", preamp0); + gtk_widget_set_usize(preamp0, 80, -1); + + gtk_entry_set_text(GTK_ENTRY(preamp0), audmad_config->replaygain.preamp0_db); + gtk_box_pack_start(GTK_BOX(preamp0_hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(preamp0_hbox), preamp0, FALSE, TRUE, 0); + g_signal_connect(preamp0, "changed", G_CALLBACK(entry_changed_cb), widgets); + + // replaygain frame + replaygainFrame = gtk_frame_new(_("ReplayGain Settings")); + gtk_container_border_width(GTK_CONTAINER(replaygainFrame), 5); + g_object_set_data(widgets, "replaygainFrame", replaygainFrame); + + rg_vbox = gtk_vbox_new(FALSE, 5); + g_object_set_data(widgets, "rg_vbox", rg_vbox); + gtk_container_add(GTK_CONTAINER(replaygainFrame), rg_vbox); + gtk_container_add(GTK_CONTAINER(vbox2), replaygainFrame); + + + // enable/disable replaygain + RG_enable = gtk_check_button_new_with_label(_("Enable ReplayGain processing")); + g_object_set_data(widgets, "RG_enable", RG_enable); + gtk_box_pack_start(GTK_BOX(rg_vbox), RG_enable, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(RG_enable), + audmad_config->replaygain.enable); + + g_signal_connect(G_OBJECT(RG_enable), "clicked", G_CALLBACK(RG_enable_cb), widgets); + + + // replaygin type radio button + rgtypeFrame = gtk_frame_new(_("ReplayGain Type")); + g_object_set_data(widgets, "rgtypeFrame", rgtypeFrame); + + type_vbox = gtk_vbox_new(FALSE, 5); + g_object_set_data(widgets, "type_vbox", type_vbox); + + gtk_container_set_border_width(GTK_CONTAINER(type_vbox), 5); + gtk_container_add(GTK_CONTAINER(rgtypeFrame), type_vbox); + gtk_container_add(GTK_CONTAINER(rg_vbox), rgtypeFrame); + + trackMode = gtk_radio_button_new_with_label(NULL, _("Use Track Gain")); + g_object_set_data(widgets, "trackMode", trackMode); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(trackMode), audmad_config->replaygain.track_mode); + gtk_box_pack_start(GTK_BOX(type_vbox), trackMode, FALSE, FALSE, 0); + + + albumMode = + gtk_radio_button_new_with_label(gtk_radio_button_group(GTK_RADIO_BUTTON(trackMode)), + _("Use Album Gain")); + g_object_set_data(widgets, "albumMode", albumMode); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(albumMode), !audmad_config->replaygain.track_mode); + gtk_box_pack_start(GTK_BOX(type_vbox), albumMode, FALSE, FALSE, 0); + + // xxx + g_signal_connect(G_OBJECT(trackMode), "toggled", G_CALLBACK(RG_type_track_cb), widgets); + g_signal_connect(G_OBJECT(albumMode), "toggled", G_CALLBACK(RG_type_album_cb), widgets); + + + + // preamp for the files with RG info + label = gtk_label_new(_("Pre-gain with RG info (dB):")); + preamp1_hbox = gtk_hbox_new(FALSE, 5); + g_object_set_data(widgets, "preamp1_hbox", preamp1_hbox); + gtk_box_pack_start(GTK_BOX(rg_vbox), preamp1_hbox, TRUE, TRUE, 0); + + preamp1 = gtk_entry_new(); + g_object_set_data(widgets, "preamp1", preamp1); + gtk_widget_set_usize(preamp1, 80, -1); + gtk_entry_set_text(GTK_ENTRY(preamp1), + audmad_config->replaygain.preamp1_db); + gtk_box_pack_start(GTK_BOX(preamp1_hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(preamp1_hbox), preamp1, FALSE, TRUE, 0); + g_signal_connect(preamp1, "changed", G_CALLBACK(entry_changed_cb), widgets); + + + // preamp for the files without RG info + label = gtk_label_new(_("Pre-gain without RG info (dB):")); + preamp2_hbox = gtk_hbox_new(FALSE, 5); + g_object_set_data(widgets, "preamp2_hbox", preamp2_hbox); + gtk_box_pack_start(GTK_BOX(rg_vbox), preamp2_hbox, TRUE, TRUE, 0); + + preamp2 = gtk_entry_new(); + g_object_set_data(widgets, "preamp2", preamp2); + gtk_widget_set_usize(preamp2, 80, -1); + gtk_entry_set_text(GTK_ENTRY(preamp2), + audmad_config->replaygain.preamp2_db); + gtk_box_pack_start(GTK_BOX(preamp2_hbox), label, FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX(preamp2_hbox), preamp2, FALSE, TRUE, 0); + g_signal_connect(preamp2, "changed", G_CALLBACK(entry_changed_cb), widgets); + + + // clipping prevention + anti_clip = gtk_check_button_new_with_label(_("Enable peak info clip prevention")); + g_object_set_data(widgets, "anti_clip", anti_clip); + gtk_box_pack_start(GTK_BOX(rg_vbox), anti_clip, TRUE, TRUE, 0); + + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(anti_clip), + audmad_config->replaygain.anti_clip); + + g_signal_connect(G_OBJECT(anti_clip), "clicked", + G_CALLBACK(simple_update_cb), widgets); + + // sensitivity + if(!audmad_config->replaygain.enable) { + gtk_widget_set_sensitive(type_vbox, FALSE); + gtk_widget_set_sensitive(rgtypeFrame, FALSE); + gtk_widget_set_sensitive(preamp1_hbox, FALSE); + gtk_widget_set_sensitive(preamp2_hbox, FALSE); + gtk_widget_set_sensitive(anti_clip, FALSE); + } + /* end of replaygainFrame */ + + + // adaptive scale + adaptive_scaler = gtk_check_button_new_with_label(_("Enable adaptive scaler clip prevention")); + g_object_set_data(widgets, "adaptive_scaler", adaptive_scaler); + gtk_box_pack_start(GTK_BOX(vbox2), adaptive_scaler, TRUE, TRUE, 0); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(adaptive_scaler), + audmad_config->replaygain.adaptive_scaler); + g_signal_connect(G_OBJECT(adaptive_scaler), "clicked", + G_CALLBACK(simple_update_cb), widgets); + + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(_("Gain Control"))); + + + /*********************************************************************************/ + + + vbox2 = gtk_vbox_new(FALSE, 5); + + title_override = gtk_check_button_new_with_label(_("Override generic titles")); + g_object_set_data(widgets, "title_override", title_override); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(title_override), + audmad_config->title_override); + gtk_box_pack_start(GTK_BOX(vbox2), title_override, FALSE, + FALSE, 0); + g_signal_connect(G_OBJECT(title_override), "clicked", + G_CALLBACK(title_override_cb), widgets); + + title_id3_box = gtk_hbox_new(FALSE, 5); + gtk_box_pack_start(GTK_BOX(vbox2), title_id3_box, FALSE, FALSE, 0); + + title_id3_label = gtk_label_new(_("ID3 format:")); + g_object_set_data(widgets, "title_id3_label", title_id3_label); + gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_label, FALSE, FALSE, 0); + gtk_widget_set_sensitive(title_id3_label, audmad_config->title_override); + + title_id3_entry = gtk_entry_new(); + g_object_set_data(widgets, "title_id3_entry", title_id3_entry); + gtk_entry_set_text(GTK_ENTRY(title_id3_entry), audmad_config->id3_format); + gtk_box_pack_start(GTK_BOX(title_id3_box), title_id3_entry, TRUE, TRUE, 0); + g_signal_connect(title_id3_entry, "changed", G_CALLBACK(entry_changed_cb), widgets); + gtk_widget_set_sensitive(title_id3_entry, audmad_config->title_override); + + gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(_("Title"))); + + + + /*********************************************************************************/ + + + bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + cancel = gtk_button_new_from_stock(GTK_STOCK_CANCEL); + + g_signal_connect(G_OBJECT(cancel), "clicked", + G_CALLBACK(configure_win_cancel), widgets); + + gtk_box_pack_start(GTK_BOX(bbox), cancel, TRUE, TRUE, 0); + + ok = gtk_button_new_from_stock(GTK_STOCK_OK); + + g_signal_connect(G_OBJECT(ok), "clicked", + G_CALLBACK(configure_win_ok), widgets); + + gtk_box_pack_start(GTK_BOX(bbox), ok, TRUE, TRUE, 0); + gtk_widget_grab_default(ok); + + gtk_widget_show_all(configure_win); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/decoder.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,733 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define AUD_DEBUG 1 */ + +#include <math.h> +#include <assert.h> +#include <pthread.h> +#include <signal.h> + +#include <audacious/plugin.h> +#include <audacious/output.h> +#include <audacious/util.h> +#include <sys/time.h> +#include "plugin.h" +#include "input.h" + +#define BUFFER_SIZE 16*1024 +#define N_AVERAGE_FRAMES 10 + +extern int triangular_dither_noise(int nbits); + +/** + * Scale PCM data + */ +static inline signed int +scale(mad_fixed_t sample, struct mad_info_t *file_info) +{ + gdouble scale = -1; + static double a_scale = -1; + static int iter = 0; + + if (audmad_config->replaygain.enable) { + if (file_info->has_replaygain) { + // apply track gain if it is available and track mode is specified + if(file_info->replaygain_track_scale != -1) { + scale = file_info->replaygain_track_scale; + } + // apply album gain if available + if(!audmad_config->replaygain.track_mode && + file_info->replaygain_album_scale != -1) { + scale = file_info->replaygain_album_scale; + } + + // apply preamp1 + scale *= audmad_config->replaygain.preamp1_scale; + + if (audmad_config->replaygain.anti_clip) { +#ifdef AUD_DEBUG + if(iter % 100000 == 0) + AUDDBG("track_peak = %f\n", file_info->replaygain_track_peak); +#endif + if(scale * file_info->replaygain_track_peak >= 1.0) + scale = 1.0 / file_info->replaygain_track_peak; + } + } + else { + // apply preamp2 for files without RG info + scale = audmad_config->replaygain.preamp2_scale; + } + } + else { + scale = 1.0; + } + + // apply global gain + if (audmad_config->replaygain.preamp0_scale != 1) + scale = scale * audmad_config->replaygain.preamp0_scale; + + // adaptive scaler clip prevention + if (audmad_config->replaygain.adaptive_scaler) { + double x; + double a = 0.1; + int interval = 10000; + + if(a_scale == -1.0) + a_scale = scale; + + x = mad_f_todouble(sample) * a_scale; + + // clippling avoidance + if(x > 1.0) { + a_scale = a_scale + a * (1.0 - x); + AUDDBG("down: x = %f a_scale = %f\n", x, a_scale); + } + // slowly go back to defined scale + else if(iter % interval == 0 && a_scale < scale){ + a_scale = a_scale + 1.0e-4; + AUDDBG("up: a_scale = %f\n", a_scale); + } + + x = mad_f_todouble(sample) * a_scale; + sample = x * (MAD_F_ONE); + } + else { + a_scale = scale; + sample *= scale; + } + + iter++; + + int n_bits_to_loose = MAD_F_FRACBITS + 1 - 16; + + /* round */ + /* add half of the bits_to_loose range to round */ + sample += (1L << (n_bits_to_loose - 1)); + +#ifdef DEBUG_DITHER + mad_fixed_t no_dither = sample; +#endif + + /* dither one bit of actual output */ + if (audmad_config->dither) { + int dither = triangular_dither_noise(n_bits_to_loose + 1); + sample += dither; + } + + /* clip */ + /* make sure we are between -1 and 1 */ + if (sample >= MAD_F_ONE) { + sample = MAD_F_ONE - 1; + } + else if (sample < -MAD_F_ONE) { + sample = -MAD_F_ONE; + } + + /* quantize */ + /* + * Turn our mad_fixed_t into an integer. + * Shift all but 16-bits of the fractional part + * off the right hand side and shift an extra place + * to get the sign bit. + */ + sample >>= n_bits_to_loose; +#ifdef DEBUG_DITHER + static int n_zeros = 0; + no_dither >>= n_bits_to_loose; + if (no_dither - sample == 0) + n_zeros++; + else { + g_message("dither: %d %d", n_zeros, no_dither - sample); + n_zeros = 0; + } +#endif + return sample; +} + +void +write_output(struct mad_info_t *info, struct mad_pcm *pcm, + struct mad_header *header) +{ + unsigned int nsamples; + mad_fixed_t const *left_ch, *right_ch; + char *output; + int olen = 0; + int pos = 0; + + nsamples = pcm->length; + left_ch = pcm->samples[0]; + right_ch = pcm->samples[1]; + olen = nsamples * MAD_NCHANNELS(header) * 2; + output = (char *) g_malloc(olen * sizeof(char)); + + while (nsamples--) { + signed int sample; + /* output sample(s) in 16-bit signed little-endian PCM */ + sample = scale(*left_ch++, info); + output[pos++] = (sample >> 0) & 0xff; + output[pos++] = (sample >> 8) & 0xff; + + if (MAD_NCHANNELS(header) == 2) { + sample = scale(*right_ch++, info); + output[pos++] = (sample >> 0) & 0xff; + output[pos++] = (sample >> 8) & 0xff; + } + } + assert(pos == olen); + if (!info->playback->playing) { + g_free(output); + return; + } + info->playback->pass_audio(info->playback, + FMT_S16_LE, MAD_NCHANNELS(header), olen, output, &(info->playback->playing)); + g_free(output); +} + +/** + * Decode all headers in the file and fill in stats + * @return FALSE if scan failed. + */ +gboolean +scan_file(struct mad_info_t * info, gboolean fast) +{ + struct mad_stream stream; + struct mad_header header; + int remainder = 0; + int data_used = 0; + int len = 0; + int tagsize = 0; + unsigned char buffer[BUFFER_SIZE]; + struct mad_frame frame; /* to read xing data */ + gboolean has_xing = FALSE; + guint bitrate_frames = 0; + double xing_bitrate = 0.0; + double accum_bitrate = 0.0; + + mad_stream_init(&stream); + mad_stream_options(&stream, 0); // check CRC + mad_header_init(&header); + mad_frame_init(&frame); + xing_init(&info->xing); + + info->bitrate = 0; + info->pos = mad_timer_zero; + info->duration = mad_timer_zero; // should be cleared before loop, if we use it as break condition. + + if(info->fileinfo_request == TRUE) { + aud_tuple_associate_int(info->tuple, FIELD_LENGTH, NULL, -1); + info->fileinfo_request = FALSE; + } + + AUDDBG("f: scan_file\n"); + AUDDBG("scan_file frames = %d\n", info->frames); + + while (1) { + remainder = stream.bufend - stream.next_frame; + + /* + if (remainder >= BUFFER_SIZE) + { + printf("oh dear.. remainder = %d\n", remainder); + } + */ + + memcpy(buffer, stream.this_frame, remainder); + len = input_get_data(info, buffer + remainder, + BUFFER_SIZE - remainder); + + if (len <= 0) { + AUDDBG("scan_file: len <= 0 len = %d\n", len); + break; + } + + mad_stream_buffer(&stream, buffer, len + remainder); + + while (!fast || (fast && info->frames < N_AVERAGE_FRAMES)) { + if (mad_header_decode(&header, &stream) == -1) { + if (stream.error == MAD_ERROR_BUFLEN) { + break; + } + if (!MAD_RECOVERABLE(stream.error)) { + AUDDBG("(fatal) error decoding header %d: %s\n", + info->frames, mad_stream_errorstr(&stream)); + AUDDBG("remainder = %d\n", remainder); + AUDDBG("len = %d\n", len); + break; + } + if (stream.error == MAD_ERROR_LOSTSYNC) { + /* ignore LOSTSYNC due to ID3 tags */ + tagsize = id3_tag_query(stream.this_frame, + stream.bufend - + stream.this_frame); + if (tagsize > 0) { + AUDDBG("skipping id3_tag: %d\n", tagsize); + mad_stream_skip(&stream, tagsize); + continue; + } + } + + AUDDBG("(recovered) error decoding header %d: %s\n", + info->frames, mad_stream_errorstr(&stream)); + AUDDBG("remainder = %d\n", remainder); + AUDDBG("len = %d\n", len); + + continue; + } + info->frames++; + +#ifdef DEBUG_INTENSIVELY + AUDDBG("header bitrate = %ld\n", header.bitrate); + AUDDBG("duration = %ul\n", + mad_timer_count(header.duration, + MAD_UNITS_MILLISECONDS)); + AUDDBG("size = %d\n", stream.next_frame - stream.this_frame); +#endif + if(aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL) == -1) + mad_timer_add(&info->duration, header.duration); + else { + gint length = aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL); + + info->duration.seconds = length / 1000; + info->duration.fraction = length % 1000; + } + data_used += stream.next_frame - stream.this_frame; + if (info->frames == 1) { + /* most of these *should* remain constant */ + info->bitrate = header.bitrate; + info->freq = header.samplerate; + info->channels = MAD_NCHANNELS(&header); + info->mpeg_layer = header.layer; + info->mode = header.mode; + + if (audmad_config->use_xing) { + frame.header = header; + if (mad_frame_decode(&frame, &stream) == -1) { + AUDDBG("xing frame decode failed\n"); + goto no_xing; + } + if (xing_parse(&info->xing, stream.anc_ptr, stream.anc_bitlen) == 0) { + AUDDBG("xing header found\n"); + has_xing = TRUE; + info->vbr = TRUE; /* otherwise xing header would have been 'Info' */ + + AUDDBG("xing: bytes = %ld frames = %ld\n", info->xing.bytes, info->xing.frames); + + /* we have enough info to calculate bitrate and duration */ + if(info->xing.bytes && info->xing.frames) { + xing_bitrate = 8 * (double)info->xing.bytes * 38 / (double)info->xing.frames; //38fps in MPEG1. +#ifdef AUD_DEBUG + { + gint tmp = (gint)(info->xing.bytes * 8 / xing_bitrate); + AUDDBG("xing: bitrate = %4.1f kbps\n", xing_bitrate / 1000); + AUDDBG("xing: duration = %d:%02d\n", tmp / 60, tmp % 60); + } +#endif + } + continue; + } +#ifdef AUD_DEBUG + else { + AUDDBG("no usable xing header\n"); + continue; + } +#endif + } /* xing */ + + } + else { + /* perhaps we have a VBR file */ + if (info->bitrate != header.bitrate) + info->vbr = TRUE; + if (info->vbr) { + accum_bitrate += (double)header.bitrate; + bitrate_frames++; + } + /* check for changin layer/samplerate/channels */ + if (info->mpeg_layer != header.layer) + g_warning("layer varies!!"); + if (info->freq != header.samplerate) + g_warning("samplerate varies!!"); + if (info->channels != MAD_NCHANNELS(&header)) + g_warning("number of channels varies!!"); + } + no_xing: + if (fast && info->frames >= N_AVERAGE_FRAMES) { + float frame_size = ((double) data_used) / N_AVERAGE_FRAMES; + + AUDDBG("bitrate = %ld samplerate = %d\n", header.bitrate, header.samplerate); + AUDDBG("data_used = %d info->frames = %d info->size = %d tagsize = %d frame_size = %lf\n", + data_used, info->frames, info->size, tagsize, frame_size); + + if(info->size != 0) + info->frames = (info->size - tagsize) / frame_size; + + AUDDBG("info->frames = %d\n", info->frames); + + if(aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL) == -1) { + if(xing_bitrate > 0.0) { + /* calc duration with xing info */ + double tmp = 8 * (double)info->xing.bytes * 1000 / xing_bitrate; + info->duration.seconds = (guint)(tmp / 1000); + info->duration.fraction = (guint)(tmp - info->duration.seconds * 1000); + } + else { + info->duration.seconds /= N_AVERAGE_FRAMES; + info->duration.fraction /= N_AVERAGE_FRAMES; + mad_timer_multiply(&info->duration, info->frames); + } + } + else { + gint length = aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL); + + info->duration.seconds = length / 1000; + info->duration.fraction = length % 1000; + } +#ifdef AUD_DEBUG + AUDDBG("using fast playtime calculation\n"); + AUDDBG("data used = %d [tagsize=%d framesize=%f]\n", + data_used, tagsize, frame_size); + AUDDBG("frames = %d, frequency = %d, channels = %d\n", + info->frames, info->freq, info->channels); + long millis = mad_timer_count(info->duration, + MAD_UNITS_MILLISECONDS); + AUDDBG("duration = %ld:%02ld\n", millis / 1000 / 60, (millis / 1000) % 60); +#endif /* DEBUG */ + break; + } + } /* while */ + if (stream.error != MAD_ERROR_BUFLEN) + break; + } + + if (info->xing.frames) + info->frames = info->xing.frames; + + if (info->vbr && xing_bitrate != 0) { + info->bitrate = (guint)xing_bitrate; + } + else if (info->vbr && xing_bitrate == 0 && bitrate_frames != 0) { + info->bitrate = accum_bitrate / bitrate_frames; + } + + aud_tuple_associate_int(info->tuple, FIELD_BITRATE, NULL, info->bitrate / 1000); + + mad_frame_finish(&frame); + mad_header_finish(&header); + mad_stream_finish(&stream); + xing_finish(&info->xing); + + AUDDBG("scan_file: info->frames = %d\n", info->frames); + AUDDBG("e: scan_file\n"); + + return (info->frames != 0 || info->remote == TRUE); +} + +/* sanity check for audio open parameters */ +static gboolean +check_audio_param(struct mad_info_t *info) +{ + if(info->fmt < FMT_U8 || info->fmt > FMT_S16_NE) + return FALSE; + if(info->freq < 0) // not sure about maximum frequency. --yaz + return FALSE; + if(info->channels < 1 || info->channels > 2) + return FALSE; + + return TRUE; +} + +gpointer +decode_loop(gpointer arg) +{ + unsigned char buffer[BUFFER_SIZE]; + int len; + gboolean seek_skip = FALSE; + int remainder = 0; + gint tlen; + unsigned int iteration = 0; + + /* mad structs */ + struct mad_stream stream; + struct mad_frame frame; + struct mad_synth synth; + + /* track info is passed in as thread argument */ + struct mad_info_t *info = (struct mad_info_t *) arg; + + AUDDBG("f: decode\n"); + + /* init mad stuff */ + mad_frame_init(&frame); + mad_stream_init(&stream); + mad_stream_options(&stream, MAD_OPTION_IGNORECRC); + mad_synth_init(&synth); + + if(!info->playback){ + AUDDBG("decode: playback == NULL\n"); + return NULL; + } + + AUDDBG("decode: fmt = %d freq = %d channels = %d\n", info->fmt, info->freq, info->channels); + + if(check_audio_param(info) == FALSE) + return NULL; + + if (!info->playback->output->open_audio(info->fmt, info->freq, info->channels)) { + g_mutex_lock(pb_mutex); + info->playback->error = TRUE; + info->playback->eof = 1; + g_mutex_unlock(pb_mutex); + g_message("failed to open audio output: %s", + info->playback->output->description); + return NULL; + } + + /* set mainwin title */ + if (info->title) + g_free(info->title); + info->title = aud_tuple_formatter_make_title_string(info->tuple, audmad_config->title_override == TRUE ? + audmad_config->id3_format : aud_get_gentitle_format()); + + tlen = (gint) mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS), + info->playback->set_params(info->playback, info->title, + (tlen == 0 || info->size <= 0) ? -1 : tlen, + info->bitrate, info->freq, info->channels); + + AUDDBG("decode: tlen = %d\n", tlen); + + /* main loop */ + do { + if (!info->playback->playing) { + AUDDBG("decode: stop signaled\n"); + break; + } + if (seek_skip) + remainder = 0; + else { + remainder = stream.bufend - stream.next_frame; + memcpy(buffer, stream.this_frame, remainder); + } + len = input_get_data(info, buffer + remainder, + BUFFER_SIZE - remainder); + + input_process_remote_metadata(info); + + if (len <= 0) { + AUDDBG("finished decoding\n"); + break; + } + len += remainder; + if (len < MAD_BUFFER_GUARD) { + int i; + for (i = len; i < MAD_BUFFER_GUARD; i++) + buffer[i] = 0; + len = MAD_BUFFER_GUARD; + } + + mad_stream_buffer(&stream, buffer, len); + + if (seek_skip) { + + AUDDBG("skipping: %d\n", seek_skip); + + int skip = 2; + do { + if (mad_frame_decode(&frame, &stream) == 0) { + mad_timer_add(&info->pos, frame.header.duration); + if (--skip == 0) + mad_synth_frame(&synth, &frame); + } + else if (!MAD_RECOVERABLE(stream.error)) { + g_mutex_lock(pb_mutex); + info->playback->error = TRUE; + info->playback->eof = 1; + g_mutex_unlock(pb_mutex); + break; + } + } + while (skip); + seek_skip = FALSE; + } + + while (info->playback->playing) { + if (info->seek != -1 && info->size > 0) { + + AUDDBG("seeking: %ld\n", info->seek); + + int new_position; + gulong milliseconds = + mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS); + if (info->seek >= milliseconds) + info->seek = milliseconds; + + mad_timer_set(&info->pos, 0, info->seek, 1000); // in millisecond + new_position = + ((double) info->seek / (double) milliseconds) * info->size; + + if(new_position < 0) + new_position = 0; + + AUDDBG("seeking to: %d bytes\n", new_position); + + if (aud_vfs_fseek(info->infile, new_position, SEEK_SET) == -1) + audmad_error("failed to seek to: %d", new_position); + mad_frame_mute(&frame); + mad_synth_mute(&synth); + stream.error = MAD_ERROR_BUFLEN; + info->playback->output->flush(mad_timer_count(info->pos, MAD_UNITS_MILLISECONDS)); + stream.sync = 0; + info->seek = -1; + seek_skip = TRUE; + break; + } + + if (mad_header_decode(&frame.header, &stream) == -1) { + if (!MAD_RECOVERABLE(stream.error)) { + break; + } + if (stream.error == MAD_ERROR_LOSTSYNC) { + /* ignore LOSTSYNC due to ID3 tags */ + int tagsize = id3_tag_query(stream.this_frame, + stream.bufend - + stream.this_frame); + if (tagsize > 0) { + mad_stream_skip(&stream, tagsize); + continue; + } + } + + AUDDBG("(recovered) error decoding header %d: %s\n", + info->current_frame, + mad_stream_errorstr(&stream)); + + continue; + } + + info->bitrate = frame.header.bitrate; + + if (!audmad_config->show_avg_vbr_bitrate && info->vbr && (iteration % 40 == 0)) { + +#ifdef DEBUG_INTENSIVELY + AUDDBG("decode vbr tlen = %d\n", tlen); +#endif + info->playback->set_params(info->playback, info->title, + (tlen == 0 || info->size <= 0) ? -1 : tlen, + info->bitrate, info->freq, info->channels); + } + iteration++; + + if (mad_frame_decode(&frame, &stream) == -1) { + if (!MAD_RECOVERABLE(stream.error)) + break; + + AUDDBG("(recovered) error decoding frame %d: %s\n", + info->current_frame, + mad_stream_errorstr(&stream)); + + } + + info->current_frame++; + + if (info->freq != frame.header.samplerate + || info->channels != + (guint) MAD_NCHANNELS(&frame.header)) { + + AUDDBG("change in audio type detected\n"); + AUDDBG("old: frequency = %d, channels = %d\n", info->freq, + info->channels); + AUDDBG("new: frequency = %d, channels = %d\n", + frame.header.samplerate, + (guint) MAD_NCHANNELS(&frame.header)); + + info->freq = frame.header.samplerate; + info->channels = MAD_NCHANNELS(&frame.header); + + if(audmad_config->force_reopen_audio && check_audio_param(info)) { + gint current_time = info->playback->output->output_time(); + + AUDDBG("re-opening audio due to change in audio type\n"); + + info->playback->output->close_audio(); + if (!info->playback->output->open_audio(info->fmt, info->freq, + info->channels)) { + g_mutex_lock(pb_mutex); + info->playback->error = TRUE; + info->playback->eof = 1; + g_mutex_unlock(pb_mutex); + g_message("failed to re-open audio output: %s", + info->playback->output->description); + return NULL; + } + // restore time and advance 0.5sec + info->seek = current_time + 500; + } + } + + if (!info->playback->playing) + break; + mad_synth_frame(&synth, &frame); + mad_stream_sync(&stream); + + write_output(info, &synth.pcm, &frame.header); + mad_timer_add(&info->pos, frame.header.duration); + } + } + while (stream.error == MAD_ERROR_BUFLEN); + + /* free mad stuff */ + mad_frame_finish(&frame); + mad_stream_finish(&stream); + mad_synth_finish(&synth); + + if (info->playback->playing) { + GTimeVal sleeptime; + + info->playback->output->buffer_free(); + info->playback->output->buffer_free(); + while (info->playback->output->buffer_playing()) { + + AUDDBG("f: buffer_playing=%d\n", info->playback->output->buffer_playing()); + + g_get_current_time(&sleeptime); + g_time_val_add(&sleeptime, 500000); + + g_mutex_lock(mad_mutex); + g_cond_timed_wait(mad_cond, mad_mutex, &sleeptime); + g_mutex_unlock(mad_mutex); + if (!info->playback->playing) { + break; + } + + } + } + + AUDDBG("e: decode\n"); + + aud_tuple_free(info->tuple); + info->tuple = NULL; + + info->playback->output->close_audio(); + g_mutex_lock(mad_mutex); + info->playback->playing = 0; + g_mutex_unlock(mad_mutex); + return NULL; /* dummy */ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/dither.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <assert.h> +#include "../../config.h" + +#define MEXP 19937 + +#ifdef HAVE_SSE2 +#define SSE2 1 +#endif +#ifdef HAVE_ALTIVEC +#define ALTIVEC 1 +#endif + +#include "SFMT.h" +#include "SFMT.c" + +int +triangular_dither_noise(int nbits) +{ + // parameter nbits : the peak-to-peak amplitude desired (in bits) + // use with nbits set to 2 + nber of bits to be trimmed. + // (because triangular is made from two uniformly distributed processes, + // it starts at 2 bits peak-to-peak amplitude) + // see The Theory of Dithered Quantization by Robert Alexander Wannamaker + // for complete proof of why that's optimal + + int v = (gen_rand32() / 2 - gen_rand32() / 2); // in ]-2^31, 2^31[ + //int signe = (v>0) ? 1 : -1; + int P = 1 << (32 - nbits); // the power of 2 + v /= P; + // now v in ]-2^(nbits-1), 2^(nbits-1) [ + + return v; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/input.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,626 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#ifdef HAVE_ASSERT_H +#include <assert.h> +#endif /* HAVE_ASSERT_H */ + +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif /* HAVE_SYS_TYPES_H */ + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif /* HAVE_SYS_SOCKET_H */ + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif /* HAVE_NETINET_IN_H */ + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif /* HAVE_ARPA_INET_H */ + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif /* HAVE_NETDB_H */ + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif /* HAVE_SYS_STAT_H */ + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif /* HAVE_SYS_TIME_H */ + +#include <fcntl.h> +#include <errno.h> +#include <audacious/util.h> + +#include "input.h" +#include "replaygain.h" + +#define DIR_SEPARATOR '/' +#define HEADER_SIZE 256 +#define LINE_LENGTH 256 + +extern gboolean scan_file(struct mad_info_t *info, gboolean fast); + +/** + * init the mad_info_t struct. + */ +gboolean +input_init(struct mad_info_t * info, const char *url, VFSFile *fd) +{ + AUDDBG("f: input_init\n"); + + memset(info, 0, sizeof(struct mad_info_t)); // all fields are cleared to 0 --yaz + + info->fmt = FMT_S16_LE; + info->channels = -1; + info->mpeg_layer = -1; + info->size = -1; + info->freq = -1; + info->seek = -1; + info->duration = mad_timer_zero; + info->pos = mad_timer_zero; + info->url = g_strdup(url); + info->filename = g_strdup(url); + + // from input_read_replaygain() + info->replaygain_album_scale = -1; + info->replaygain_track_scale = -1; + info->mp3gain_undo = -77; + info->mp3gain_minmax = -77; + + if(!fd){ + info->infile = aud_vfs_fopen(info->filename, "rb"); + if (info->infile == NULL) { + return FALSE; + } + } + else{ + AUDDBG("input_init: aud_vfs_dup\n"); + + info->infile = aud_vfs_dup(fd); + } + + // obtain file size + info->size = aud_vfs_fsize(info->infile); + info->remote = info->size == 0 ? TRUE : FALSE; //proxy connection may result in non-zero size. + if(aud_vfs_is_remote((gchar *)url)) + info->remote = TRUE; + + info->fileinfo_request = FALSE; + + AUDDBG("i: info->size = %lu\n", (long unsigned int)info->size); + AUDDBG("e: input_init\n"); + + return TRUE; +} + +/* return length in letters */ +size_t +mad_ucs4len(id3_ucs4_t *ucs) +{ + id3_ucs4_t *ptr = ucs; + size_t len = 0; + + while(*ptr++ != 0) + len++; + + return len; +} + +/* duplicate id3_ucs4_t string. new string will be terminated with 0. */ +id3_ucs4_t * +mad_ucs4dup(id3_ucs4_t *org) +{ + id3_ucs4_t *new = NULL; + size_t len = mad_ucs4len(org); + + new = g_malloc0((len + 1) * sizeof(id3_ucs4_t)); + memcpy(new, org, len * sizeof(id3_ucs4_t)); + *(new + len) = 0; //terminate + + return new; +} + +#define BYTES(x) ((x) * sizeof(id3_ucs4_t)) + +id3_ucs4_t * +mad_parse_genre(const id3_ucs4_t *string) +{ + id3_ucs4_t *ret = NULL; + id3_ucs4_t *tmp = NULL; + id3_ucs4_t *genre = NULL; + id3_ucs4_t *ptr, *end, *tail, *tp; + size_t ret_len = 0; //num of ucs4 char! + size_t tmp_len = 0; + size_t string_len = 0; + gboolean is_num = TRUE; + + if(!string) + return NULL; + + string_len = mad_ucs4len((id3_ucs4_t *)string); + tail = (id3_ucs4_t *)string + string_len; + + if(BYTES(string_len + 1) > 1024) { + ret = g_malloc0(BYTES(string_len + 1)); + } + else { + ret = g_malloc0(1024); + } + + for(ptr = (id3_ucs4_t *)string; ptr <= tail && *ptr != 0; ptr++) { + if(*ptr == '(') { + if(ptr < tail && *(++ptr) == '(') { // escaped text like: ((something) + for(end = ptr; *end != ')' && *end != 0;) { // copy "(something)" + end++; + } + end++; //include trailing ')' + memcpy(ret, ptr, BYTES(end - ptr)); + ret_len += (end - ptr); + *(ret + ret_len) = 0; //terminate + ptr = end + 1; + } + else if (ptr <= tail && *ptr != 0) { + // reference to an id3v1 genre code + for(end = ptr; *end != ')' && *end != 0;) { + end++; + } + + tmp = g_malloc0(BYTES(end - ptr + 1)); + memcpy(tmp, ptr, BYTES(end - ptr)); + *(tmp + (end - ptr)) = 0; //terminate + ptr += end - ptr; + + genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); + + g_free(tmp); + tmp = NULL; + + tmp_len = mad_ucs4len(genre); + + memcpy(ret + ret_len, genre, BYTES(tmp_len)); + + ret_len += tmp_len; + *(ret + ret_len) = 0; //terminate + } + } + else { + for(end = ptr; *end != '(' && *end != 0; ) { + end++; + } + // scan string to determine whether a genre code number or not + tp = ptr; + is_num = TRUE; + while(tp < end) { + if(*tp < '0' || *tp > '9') { // anything else than number appears. + is_num = FALSE; + break; + } + tp++; + } + + if(is_num) { + AUDDBG("is_num!\n"); + + tmp = g_malloc0(BYTES(end - ptr + 1)); + memcpy(tmp, ptr, BYTES(end - ptr)); + *(tmp + (end - ptr)) = 0; //terminate + ptr += end - ptr; + + genre = (id3_ucs4_t *)id3_genre_name((const id3_ucs4_t *)tmp); + AUDDBG("genre length = %d\n", mad_ucs4len(genre)); + + g_free(tmp); + tmp = NULL; + + tmp_len = mad_ucs4len(genre); + + memcpy(ret + ret_len, genre, BYTES(tmp_len)); + + ret_len += tmp_len; + *(ret + ret_len) = 0; //terminate + } + else { // plain text + AUDDBG("plain!\n"); + AUDDBG("ret_len = %d\n", ret_len); + AUDDBG("end - ptr = %d\n", BYTES(end - ptr)); + + memcpy(ret + ret_len, ptr, BYTES(end - ptr)); + ret_len = ret_len + (end - ptr); + *(ret + ret_len) = 0; //terminate + ptr += (end - ptr); + } + } + } + return ret; +} + +gchar * +input_id3_get_string(struct id3_tag * tag, const gchar *frame_name) +{ + gchar *rtn0 = NULL, *rtn = NULL; + const id3_ucs4_t *string_const = NULL; + id3_ucs4_t *string = NULL; + struct id3_frame *frame; + union id3_field *field; + int encoding = -1; + + frame = id3_tag_findframe(tag, frame_name, 0); + if (!frame) + return NULL; + + field = id3_frame_field(frame, 0); + encoding = id3_field_gettextencoding(field); + + if (!strcmp(frame_name, ID3_FRAME_COMMENT)) + field = id3_frame_field(frame, 3); + else + field = id3_frame_field(frame, 1); + + if (!field) + return NULL; + + if (!strcmp(frame_name, ID3_FRAME_COMMENT)) + string_const = id3_field_getfullstring(field); + else + string_const = id3_field_getstrings(field, 0); + + if (!string_const) + return NULL; + + if (!strcmp(frame_name, ID3_FRAME_GENRE)) { + string = mad_parse_genre(string_const); + } + else { + string = mad_ucs4dup((id3_ucs4_t *)string_const); + } + + if (!string) + return NULL; + + switch (encoding) { + case ID3_FIELD_TEXTENCODING_ISO_8859_1: + rtn0 = (gchar *)id3_ucs4_latin1duplicate(string); + rtn = aud_str_to_utf8(rtn0); + g_free(rtn0); + break; + case ID3_FIELD_TEXTENCODING_UTF_8: + default: + rtn = (gchar *)id3_ucs4_utf8duplicate(string); + break; + } + g_free((void *)string); + + AUDDBG("i: string = %s\n", rtn); + + return rtn; +} + +static void +input_set_and_free_tag(struct id3_tag *tag, Tuple *tuple, const gchar *frame, const gint nfield) +{ + gchar *scratch = input_id3_get_string(tag, frame); + + aud_tuple_associate_string(tuple, nfield, NULL, scratch); + aud_tuple_associate_string(tuple, -1, frame, scratch); + + g_free(scratch); +} + +static void +input_alloc_tag(struct mad_info_t *info) +{ + Tuple *tuple; + + if (info->tuple == NULL) { + tuple = aud_tuple_new(); + info->tuple = tuple; + aud_tuple_associate_int(info->tuple, FIELD_LENGTH, NULL, -1); + } +} + +/** + * read the ID3 tag + */ +static void +input_read_tag(struct mad_info_t *info) +{ + gchar *string = NULL; + Tuple *tuple; + glong curpos = 0; + + AUDDBG("f: input_read_tag\n"); + + if (info->tuple != NULL) + aud_tuple_free(info->tuple); + + tuple = aud_tuple_new_from_filename(info->filename); + info->tuple = tuple; + + if(info->infile) { + curpos = aud_vfs_ftell(info->infile); + info->id3file = id3_file_vfsopen(info->infile, ID3_FILE_MODE_READONLY); + } + else { + info->id3file = id3_file_open(info->filename, ID3_FILE_MODE_READONLY); + } + + if (!info->id3file) { + AUDDBG("read_tag: no id3file\n"); + return; + } + + info->tag = id3_file_tag(info->id3file); + if (!info->tag) { + AUDDBG("read_tag: no tag\n"); + return; + } + + input_set_and_free_tag(info->tag, tuple, ID3_FRAME_ARTIST, FIELD_ARTIST); + input_set_and_free_tag(info->tag, tuple, ID3_FRAME_TITLE, FIELD_TITLE); + input_set_and_free_tag(info->tag, tuple, ID3_FRAME_ALBUM, FIELD_ALBUM); + input_set_and_free_tag(info->tag, tuple, ID3_FRAME_GENRE, FIELD_GENRE); + input_set_and_free_tag(info->tag, tuple, ID3_FRAME_COMMENT, FIELD_COMMENT); + + string = input_id3_get_string(info->tag, ID3_FRAME_TRACK); + if (string) { + aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, atoi(string)); + g_free(string); + string = NULL; + } + + // year + string = NULL; + string = input_id3_get_string(info->tag, ID3_FRAME_YEAR); //TDRC + if (!string) + string = input_id3_get_string(info->tag, "TYER"); + + if (string) { + aud_tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(string)); + g_free(string); + string = NULL; + } + + // length + string = input_id3_get_string(info->tag, "TLEN"); + if (string && atoi(string)) { + aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, atoi(string)); + AUDDBG("input_read_tag: TLEN = %d\n", atoi(string)); + g_free(string); + string = NULL; + } else + aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, -1); + + aud_tuple_associate_string(tuple, FIELD_CODEC, NULL, "MPEG Audio (MP3)"); + aud_tuple_associate_string(tuple, FIELD_QUALITY, NULL, "lossy"); + + info->title = aud_tuple_formatter_make_title_string(tuple, audmad_config->title_override == TRUE ? + audmad_config->id3_format : aud_get_gentitle_format()); + + // for connection via proxy, we have to stop transfer once. I can't explain the reason. + if (info->infile != NULL) { + aud_vfs_fseek(info->infile, -1, SEEK_SET); // an impossible request + aud_vfs_fseek(info->infile, curpos, SEEK_SET); + } + + AUDDBG("e: input_read_tag\n"); +} + +void +input_process_remote_metadata(struct mad_info_t *info) +{ + gboolean metadata = FALSE; + + if(info->remote && mad_timer_count(info->duration, MAD_UNITS_SECONDS) <= 0){ + gchar *tmp = NULL; + +#ifdef DEBUG_INTENSIVELY + AUDDBG("process_remote_meta\n"); +#endif + g_free(info->title); + info->title = NULL; + aud_tuple_disassociate(info->tuple, FIELD_TITLE, NULL); + aud_tuple_disassociate(info->tuple, FIELD_ALBUM, NULL); + + tmp = aud_vfs_get_metadata(info->infile, "track-name"); + if(tmp){ + metadata = TRUE; + gchar *scratch; + + scratch = aud_str_to_utf8(tmp); + aud_tuple_associate_string(info->tuple, FIELD_TITLE, NULL, scratch); + g_free(scratch); + + g_free(tmp); + tmp = NULL; + } + + tmp = aud_vfs_get_metadata(info->infile, "stream-name"); + if(tmp){ + metadata = TRUE; + gchar *scratch; + + scratch = aud_str_to_utf8(tmp); + aud_tuple_associate_string(info->tuple, FIELD_ALBUM, NULL, scratch); + aud_tuple_associate_string(info->tuple, -1, "stream", scratch); + g_free(scratch); + + g_free(tmp); + tmp = NULL; + } + + if (metadata) + tmp = aud_tuple_formatter_process_string(info->tuple, "${?title:${title}}${?stream: (${stream})}"); + else { + gchar *realfn = g_filename_from_uri(info->filename, NULL, NULL); + gchar *tmp2 = g_path_get_basename(realfn ? realfn : info->filename); // info->filename is uri. --yaz + tmp = aud_str_to_utf8(tmp2); + g_free(tmp2); tmp2 = NULL; + g_free(realfn); realfn = NULL; +// tmp = g_strdup(g_basename(info->filename)); //XXX maybe ok. --yaz + } + + /* call set_info only if tmp is different from prev_tmp */ + if ( ( ( info->prev_title != NULL ) && ( strcmp(info->prev_title,tmp) ) ) || + ( info->prev_title == NULL ) ) + { + info->playback->set_params(info->playback, tmp, + -1, // indicate the stream is unseekable + info->bitrate, info->freq, info->channels); + if (info->prev_title) + g_free(info->prev_title); + info->prev_title = g_strdup(tmp); + } + + g_free(tmp); + } +} + + +/** + * Retrieve meta-information about URL. + * For local files this means ID3 tag etc. + */ +gboolean +input_get_info(struct mad_info_t *info, gboolean fast_scan) +{ +#ifdef AUD_DEBUG + gchar *tmp = g_filename_to_utf8(info->filename, -1, NULL, NULL, NULL); + AUDDBG("f: input_get_info: %s, fast_scan = %s\n", tmp, fast_scan ? "TRUE" : "FALSE"); + g_free(tmp); +#endif /* DEBUG */ + + input_alloc_tag(info); + input_read_tag(info); + + if(!info->remote) { // reduce startup delay + read_replaygain(info); + } + + /* scan mp3 file, decoding headers */ + if (scan_file(info, fast_scan) == FALSE) { + AUDDBG("input_get_info: scan_file failed\n"); + return FALSE; + } + + /* reset the input file to the start */ + aud_vfs_fseek(info->infile, 0, SEEK_SET); + info->offset = 0; + + /* use the filename for the title as a last resort */ + if (!info->title) { + char *pos = strrchr(info->filename, DIR_SEPARATOR); //XXX info->filename is uri. --yaz + if (pos) + info->title = g_strdup(pos + 1); + else + info->title = g_strdup(info->filename); //XXX info->filename is uri. --yaz + } + + AUDDBG("e: input_get_info\n"); + return TRUE; +} + + + +/** + * Read data from the source given my madinfo into the buffer + * provided. Return the number of bytes read. + * @return 0 on EOF + * @return -1 on error + */ +// this function may be called before info->playback initialized. +int +input_get_data(struct mad_info_t *info, guchar * buffer, + int buffer_size) +{ + int len = 0; +#ifdef DEBUG_INTENSIVELY + AUDDBG ("f: input_get_data: %d\n", buffer_size); +#endif + /* simply read to data from the file */ + len = aud_vfs_fread(buffer, 1, buffer_size, info->infile); //aud_vfs_fread returns num of elements. + + if(len == 0 && info->playback){ + info->playback->eof = TRUE; + } + +#ifdef DEBUG_INTENSIVELY + AUDDBG ("e: input_get_data: size=%d offset=%d\n", len, info->offset); +#endif + + info->offset += len; + return len; +} + +/** + * Free up all mad_info_t related resourses. + */ +gboolean +input_term(struct mad_info_t * info) +{ + AUDDBG("f: input_term\n"); + + if (info->title) + g_free(info->title); + if (info->url) + g_free(info->url); + if (info->filename) + g_free(info->filename); + if (info->infile) + aud_vfs_fclose(info->infile); + if (info->id3file) + id3_file_close(info->id3file); + + if (info->replaygain_album_str) + g_free(info->replaygain_album_str); + if (info->replaygain_track_str) + g_free(info->replaygain_track_str); + if (info->replaygain_album_peak_str) + g_free(info->replaygain_album_peak_str); + if (info->replaygain_track_peak_str) + g_free(info->replaygain_track_peak_str); + if (info->mp3gain_undo_str) + g_free(info->mp3gain_undo_str); + if (info->mp3gain_minmax_str) + g_free(info->mp3gain_minmax_str); + + if (info->tuple) { + aud_tuple_free(info->tuple); + info->tuple = NULL; + } + + if (info->prev_title) + g_free(info->prev_title); + + /* set everything to zero in case it gets used again. */ + memset(info, 0, sizeof(struct mad_info_t)); + + AUDDBG("e: input_term\n"); + + return TRUE; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/input.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,33 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef INPUT_H +#define INPUT_H + +#include "plugin.h" +gboolean input_init(struct mad_info_t *songinfo, const gchar * url, VFSFile *fd); +gboolean input_term(struct mad_info_t *songinfo); +gboolean input_get_info(struct mad_info_t *songinfo, gboolean fast_scan); +gint input_get_data(struct mad_info_t *songinfo, guchar * buffer, + gint buffer_size); +gchar *input_id3_get_string(struct id3_tag *tag, const gchar *frame_name); + +#endif /* ! INPUT_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/plugin.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,839 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* #define AUD_DEBUG 1 */ + +#include "config.h" +#include "plugin.h" +#include "input.h" + +#include <math.h> + +#include <gtk/gtk.h> +#include <audacious/util.h> +#include <audacious/configdb.h> +#include <stdarg.h> +#include <fcntl.h> +#include <audacious/vfs.h> +#include <sys/stat.h> +#include "SFMT.h" +#include "tuple.h" + +/* + * Global variables + */ +audmad_config_t *audmad_config; /**< global configuration */ +GMutex *mad_mutex; +GMutex *pb_mutex; +GCond *mad_cond; + +/* + * static variables + */ +static GThread *decode_thread; /**< the single decoder thread */ +static struct mad_info_t info; /**< info for current track */ + +#ifndef NOGUI +static GtkWidget *error_dialog = 0; +#endif + +extern gboolean scan_file(struct mad_info_t *info, gboolean fast); + +static gint mp3_bitrate_table[5][16] = { + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, /* MPEG1 L1 */ + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, /* MPEG1 L2 */ + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }, /* MPEG1 L3 */ + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, /* MPEG2(.5) L1 */ + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } /* MPEG2(.5) L2,L3 */ +}; + +static gint mp3_samplerate_table[4][4] = { + { 11025, 12000, 8000, -1 }, /* MPEG2.5 */ + { -1, -1, -1, -1 }, /* Reserved */ + { 22050, 24000, 16000, -1 }, /* MPEG2 */ + { 44100, 48000, 32000, -1 } /* MPEG1 */ +}; + +/* + * Function extname (filename) + * + * Return pointer within filename to its extenstion, or NULL if + * filename has no extension. + * + */ +static gchar * +extname(const char *filename) +{ + gchar *ext = strrchr(filename, '.'); + + if (ext != NULL) + ++ext; + + return ext; +} + + +void +audmad_config_compute(audmad_config_t *config) +{ + /* set some config parameters by parsing text fields + (RG default gain, etc..) + */ + const gchar *text; + gdouble x; + + text = config->replaygain.preamp0_db; + if ( text != NULL ) + x = g_strtod(text, NULL); + else + x = 0; + config->replaygain.preamp0_scale = (x != 0) ? pow(10.0, x / 20) : 1; + AUDDBG("RG.preamp0=[%s] -> %g -> %g\n", text, x, config->preamp0_scale); + + text = config->replaygain.preamp1_db; + if ( text != NULL ) + x = g_strtod(text, NULL); + else + x = 0; + config->replaygain.preamp1_scale = (x != 0) ? pow(10.0, x / 20) : 1; + AUDDBG("RG.preamp1=[%s] -> %g -> %g\n", text, x, + config->replaygain.preamp1_scale); + + text = config->replaygain.preamp2_db; + if ( text != NULL ) + x = g_strtod(text, NULL); + else + x = 0; + config->replaygain.preamp2_scale = (x != 0) ? pow(10.0, x / 20) : 1; + AUDDBG("RG.preamp2=[%s] -> %g -> %g\n", text, x, + config->replaygain.preamp2_scale); +} + +static void +audmad_init() +{ + ConfigDb *db = NULL; + + audmad_config = g_malloc0(sizeof(audmad_config_t)); + + audmad_config->dither = TRUE; + audmad_config->force_reopen_audio = FALSE; + audmad_config->fast_play_time_calc = TRUE; + audmad_config->use_xing = TRUE; + audmad_config->sjis = FALSE; + audmad_config->show_avg_vbr_bitrate = TRUE; + audmad_config->replaygain.enable = TRUE; + audmad_config->replaygain.track_mode = FALSE; + audmad_config->replaygain.anti_clip = FALSE; + audmad_config->replaygain.adaptive_scaler = FALSE; + audmad_config->title_override = FALSE; + + + db = aud_cfg_db_open(); + if (db) { + //audio + aud_cfg_db_get_bool(db, "MAD", "dither", &audmad_config->dither); + aud_cfg_db_get_bool(db, "MAD", "force_reopen_audio", + &audmad_config->force_reopen_audio); + + //metadata + aud_cfg_db_get_bool(db, "MAD", "fast_play_time_calc", + &audmad_config->fast_play_time_calc); + aud_cfg_db_get_bool(db, "MAD", "use_xing", + &audmad_config->use_xing); + aud_cfg_db_get_bool(db, "MAD", "sjis", &audmad_config->sjis); + + //misc + aud_cfg_db_get_bool(db, "MAD", "show_avg_vbr_bitrate", + &audmad_config->show_avg_vbr_bitrate); + + //gain control + aud_cfg_db_get_string(db, "MAD", "RG.preamp0_db", + &audmad_config->replaygain.preamp0_db); + aud_cfg_db_get_bool(db, "MAD", "RG.enable", + &audmad_config->replaygain.enable); + aud_cfg_db_get_bool(db, "MAD", "RG.track_mode", + &audmad_config->replaygain.track_mode); + aud_cfg_db_get_string(db, "MAD", "RG.preamp1_db", + &audmad_config->replaygain.preamp1_db); + aud_cfg_db_get_string(db, "MAD", "RG.preamp2_db", + &audmad_config->replaygain.preamp2_db); + aud_cfg_db_get_bool(db, "MAD", "RG.anti_clip", + &audmad_config->replaygain.anti_clip); + aud_cfg_db_get_bool(db, "MAD", "RG.adaptive_scaler", + &audmad_config->replaygain.adaptive_scaler); + + //text + aud_cfg_db_get_bool(db, "MAD", "title_override", + &audmad_config->title_override); + aud_cfg_db_get_string(db, "MAD", "id3_format", + &audmad_config->id3_format); + + aud_cfg_db_close(db); + } + + mad_mutex = g_mutex_new(); + pb_mutex = g_mutex_new(); + mad_cond = g_cond_new(); + audmad_config_compute(audmad_config); + + if (!audmad_config->replaygain.preamp0_db) + audmad_config->replaygain.preamp0_db = g_strdup("+0.00"); + + if (!audmad_config->replaygain.preamp1_db) + audmad_config->replaygain.preamp1_db = g_strdup("+6.00"); + if (!audmad_config->replaygain.preamp2_db) + audmad_config->replaygain.preamp2_db = g_strdup("+0.00"); + + if (!audmad_config->id3_format) + audmad_config->id3_format = g_strdup("(none)"); + + init_gen_rand(4357); + + aud_mime_set_plugin("audio/mpeg", mad_plugin); +} + +static void +audmad_cleanup() +{ + g_free(audmad_config->replaygain.preamp0_db); + g_free(audmad_config->replaygain.preamp1_db); + g_free(audmad_config->replaygain.preamp2_db); + g_free(audmad_config->id3_format); + g_free(audmad_config); + + g_cond_free(mad_cond); + g_mutex_free(mad_mutex); + g_mutex_free(pb_mutex); +} + +static gboolean +mp3_head_check(guint32 head, gint *frameSize) +{ + gint version, layer, bitIndex, bitRate, sampleIndex, sampleRate, padding; + + /* http://www.mp3-tech.org/programmer/frame_header.html + * Bits 21-31 must be set (frame sync) + */ + if ((head & 0xffe00000) != 0xffe00000) + return FALSE; + + /* check if layer bits (17-18) are good */ + layer = (head >> 17) & 0x3; + if (!layer) + return FALSE; /* 00 = reserved */ + layer = 4 - layer; + + /* check if bitrate index bits (12-15) are acceptable */ + bitIndex = (head >> 12) & 0xf; + + /* 1111 and 0000 are reserved values for all layers */ + if (bitIndex == 0xf || bitIndex == 0) + return FALSE; + + /* check samplerate index bits (10-11) */ + sampleIndex = (head >> 10) & 0x3; + if (sampleIndex == 0x3) + return FALSE; + + /* check version bits (19-20) and get bitRate */ + version = (head >> 19) & 0x03; + switch (version) { + case 0: /* 00 = MPEG Version 2.5 */ + case 2: /* 10 = MPEG Version 2 */ + if (layer == 1) + bitRate = mp3_bitrate_table[3][bitIndex]; + else + bitRate = mp3_bitrate_table[4][bitIndex]; + break; + + case 1: /* 01 = reserved */ + return FALSE; + + case 3: /* 11 = MPEG Version 1 */ + bitRate = mp3_bitrate_table[layer][bitIndex]; + break; + + default: + return FALSE; + } + + /* check layer II restrictions vs. bitrate */ + if (layer == 2) { + gint chanMode = (head >> 6) & 0x3; + + if (chanMode == 0x3) { + /* single channel with bitrate > 192 */ + if (bitRate > 192) + return FALSE; + } else { + /* any other mode with bitrates 32-56 and 80. + * NOTICE! this check is not entirely correct, but I think + * it is sufficient in most cases. + */ + if (((bitRate >= 32 && bitRate <= 56) || bitRate == 80)) + return FALSE; + } + } + + /* calculate approx. frame size */ + padding = (head >> 9) & 1; + sampleRate = mp3_samplerate_table[version][sampleIndex]; + if (layer == 1) + *frameSize = ((12 * bitRate * 1000 / sampleRate) + padding) * 4; + else + *frameSize = (144 * bitRate * 1000) / (sampleRate + padding); + + /* check if bits 16 - 19 are all set (MPEG 1 Layer I, not protected?) */ + if (((head >> 19) & 1) == 1 && + ((head >> 17) & 3) == 3 && ((head >> 16) & 1) == 1) + return FALSE; + + /* not sure why we check this, but ok! */ + if ((head & 0xffff0000) == 0xfffe0000) + return FALSE; + + return TRUE; +} + +static int +mp3_head_convert(const guchar * hbuf) +{ + return ((unsigned long) hbuf[0] << 24) | + ((unsigned long) hbuf[1] << 16) | + ((unsigned long) hbuf[2] << 8) | (unsigned long) hbuf[3]; +} + +// audacious vfs fast version +static int +audmad_is_our_fd(char *filename, VFSFile *fin) +{ + guint32 check; + gchar *ext = extname(filename); + gint cyc = 0, chkcount = 0, chksize = 4096; + guchar buf[4]; + guchar tmp[4096]; + gint ret, i, frameSize; + + info.remote = aud_vfs_is_remote(filename); + + /* I've seen some flac files beginning with id3 frames.. + so let's exclude known non-mp3 filename extensions */ + if ((ext != NULL) && + (!strcasecmp("flac", ext) || !strcasecmp("mpc", ext) || + !strcasecmp("tta", ext) || !strcasecmp("ogg", ext) || + !strcasecmp("wma", ext) ) + ) + return 0; + + if (fin == NULL) { + g_message("fin = NULL"); + return 0; + } + + if(aud_vfs_fread(buf, 1, 4, fin) == 0) { + gchar *tmp = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); + g_message("aud_vfs_fread failed @1 %s", tmp); + g_free(tmp); + return 0; + } + + check = mp3_head_convert(buf); + + if (memcmp(buf, "ID3", 3) == 0) + return 1; + else if (memcmp(buf, "OggS", 4) == 0) + return 0; + else if (memcmp(buf, "RIFF", 4) == 0) + { + aud_vfs_fseek(fin, 4, SEEK_CUR); + if(aud_vfs_fread(buf, 1, 4, fin) == 0) { + gchar *tmp = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); + g_message("aud_vfs_fread failed @2 %s", tmp); + g_free(tmp); + return 0; + } + + if (memcmp(buf, "RMP3", 4) == 0) + return 1; + } + + // check data for frame header + while (!mp3_head_check(check, &frameSize)) + { + if((ret = aud_vfs_fread(tmp, 1, chksize, fin)) == 0){ + gchar *tmp = g_filename_to_utf8(filename, -1, NULL, NULL, NULL); + g_message("aud_vfs_fread failed @3 %s", tmp); + g_free(tmp); + return 0; + } + for (i = 0; i < ret; i++) + { + check <<= 8; + check |= tmp[i]; + + if (mp3_head_check(check, &frameSize)) { + /* when the first matching frame header is found, we check for + * another frame by seeking to the approximate start of the + * next header ... also reduce the check size. + */ + if (++chkcount >= 3) return 1; + aud_vfs_fseek(fin, frameSize-4, SEEK_CUR); + check = 0; + chksize = 8; + } + } + + if (++cyc > 32) + return 0; + } + + return 1; +} + +// audacious vfs version +static int +audmad_is_our_file(char *filename) +{ + VFSFile *fin = NULL; + gint rtn; + + fin = aud_vfs_fopen(filename, "rb"); + + if (fin == NULL) + return 0; + + rtn = audmad_is_our_fd(filename, fin); + aud_vfs_fclose(fin); + + return rtn; +} + +static void +audmad_stop(InputPlayback *playback) +{ + AUDDBG("f: audmad_stop\n"); + g_mutex_lock(mad_mutex); + info.playback = playback; + g_mutex_unlock(mad_mutex); + + if (decode_thread) { + + g_mutex_lock(mad_mutex); + info.playback->playing = 0; + g_mutex_unlock(mad_mutex); + g_cond_signal(mad_cond); + + AUDDBG("waiting for thread\n"); + g_thread_join(decode_thread); + AUDDBG("thread done\n"); + + input_term(&info); + decode_thread = NULL; + + } + AUDDBG("e: audmad_stop\n"); +} + +static void +audmad_play_file(InputPlayback *playback) +{ + gboolean rtn; + gchar *url = playback->filename; + +#ifdef AUD_DEBUG + { + gchar *tmp = g_filename_to_utf8(url, -1, NULL, NULL, NULL); + AUDDBG("playing %s\n", tmp); + g_free(tmp); + } +#endif /* DEBUG */ + + if (input_init(&info, url, NULL) == FALSE) { + g_message("error initialising input"); + return; + } + + // remote access must use fast scan. + rtn = input_get_info(&info, aud_vfs_is_remote(url) ? TRUE : audmad_config->fast_play_time_calc); + + if (rtn == FALSE) { + g_message("error reading input info"); + /* + * return; + * commenting this return seems to be a hacky fix for the damn lastfm plugin playback + * that used to work only for nenolod because of his fsck-ing lastfm subscription :p + */ + } + g_mutex_lock(pb_mutex); + info.playback = playback; + info.playback->playing = 1; + g_mutex_unlock(pb_mutex); + + decode_thread = g_thread_self(); + playback->set_pb_ready(playback); + decode_loop(&info); +} + +static void +audmad_pause(InputPlayback *playback, short paused) +{ + g_mutex_lock(pb_mutex); + info.playback = playback; + g_mutex_unlock(pb_mutex); + playback->output->pause(paused); +} + +static void +audmad_mseek(InputPlayback *playback, gulong millisecond) +{ + g_mutex_lock(pb_mutex); + info.playback = playback; + info.seek = millisecond; + g_mutex_unlock(pb_mutex); +} + +static void +audmad_seek(InputPlayback *playback, gint time) +{ + audmad_mseek(playback, time * 1000); +} + +/** + * Scan the given file or URL. + * Fills in the title string and the track length in milliseconds. + */ +static void +audmad_get_song_info(char *url, char **title, int *length) +{ + struct mad_info_t myinfo; +#ifdef AUD_DEBUG + gchar *tmp = g_filename_to_utf8(url, -1, NULL, NULL, NULL); + AUDDBG("f: audmad_get_song_info: %s\n", tmp); + g_free(tmp); +#endif /* DEBUG */ + + if (input_init(&myinfo, url, NULL) == FALSE) { + AUDDBG("error initialising input\n"); + return; + } + + if (input_get_info(&myinfo, info.remote ? TRUE : audmad_config->fast_play_time_calc) == TRUE) { + if(aud_tuple_get_string(myinfo.tuple, -1, "track-name")) + *title = g_strdup(aud_tuple_get_string(myinfo.tuple, -1, "track-name")); + else + *title = g_strdup(url); + + *length = aud_tuple_get_int(myinfo.tuple, FIELD_LENGTH, NULL); + if(*length == -1) + *length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); + } + else { + *title = g_strdup(url); + *length = -1; + } + input_term(&myinfo); + AUDDBG("e: audmad_get_song_info\n"); +} + +static gboolean +audmad_fill_info(struct mad_info_t *info, VFSFile *fd) +{ + if (fd == NULL || info == NULL) return FALSE; + AUDDBG("f: audmad_fill_info(): %s\n", fd->uri); + + if (input_init(info, fd->uri, fd) == FALSE) { + AUDDBG("audmad_fill_info(): error initialising input\n"); + return FALSE; + } + + info->fileinfo_request = FALSE; /* we don't need to read tuple again */ + return input_get_info(info, aud_vfs_is_remote(fd->uri) ? TRUE : audmad_config->fast_play_time_calc); +} + +static void +audmad_about() +{ + static GtkWidget *aboutbox; + gchar *scratch; + + if (aboutbox != NULL) + return; + + scratch = g_strdup_printf( + _("Audacious MPEG Audio Plugin\n" + "\n" + "Compiled against libMAD version: %d.%d.%d%s\n" + "\n" + "Written by:\n" + " William Pitcock <nenolod@sacredspiral.co.uk>\n" + " Yoshiki Yazawa <yaz@cc.rim.or.jp>\n" + "\n" + "Portions derived from XMMS-MAD by:\n" + " Sam Clegg\n" + "\n" + "ReplayGain support by:\n" + " Samuel Krempp"), + MAD_VERSION_MAJOR, MAD_VERSION_MINOR, MAD_VERSION_PATCH, + MAD_VERSION_EXTRA); + + aboutbox = audacious_info_dialog(_("About MPEG Audio Plugin"), + scratch, + _("Ok"), FALSE, NULL, NULL); + + g_free(scratch); + + g_signal_connect(G_OBJECT(aboutbox), "destroy", + G_CALLBACK(gtk_widget_destroyed), &aboutbox); +} + +/** + * Display a GTK box containing the given error message. + * Taken from mpg123 plugin. + */ +void +audmad_error(char *error, ...) +{ +#ifndef NOGUI + if (!error_dialog) { + va_list args; + char string[256]; + va_start(args, error); + vsnprintf(string, 256, error, args); + va_end(args); + GDK_THREADS_ENTER(); + error_dialog = + audacious_info_dialog(_("Error"), string, _("Ok"), FALSE, 0, 0); + gtk_signal_connect(GTK_OBJECT(error_dialog), "destroy", + GTK_SIGNAL_FUNC(gtk_widget_destroyed), + &error_dialog); + GDK_THREADS_LEAVE(); + } +#endif /* !NOGUI */ +} + +extern void audmad_configure(); + +static void +__set_and_free(Tuple *tuple, gint nfield, gchar *name, gchar *value) +{ + aud_tuple_associate_string(tuple, nfield, name, value); + g_free(value); +} + +// tuple stuff +static Tuple * +__audmad_get_song_tuple(char *filename, VFSFile *fd) +{ + Tuple *tuple = NULL; + gchar *string = NULL; + + struct id3_file *id3file = NULL; + struct id3_tag *tag = NULL; + + struct mad_info_t myinfo; + + gboolean local_fd = FALSE; + int length; + +#ifdef AUD_DEBUG + string = aud_str_to_utf8(filename); + AUDDBG("f: mad: audmad_get_song_tuple: %s\n", string); + g_free(string); + string = NULL; +#endif + + /* isn't is obfuscated? --eugene */ + + if(info.remote && mad_timer_count(info.duration, MAD_UNITS_SECONDS) <= 0){ + if((fd && aud_vfs_is_streaming(fd)) || (info.playback && info.playback->playing)) { + gchar *tmp = NULL; + tuple = aud_tuple_new_from_filename(filename); + +#ifdef AUD_DEBUG + if(info.playback) + AUDDBG("info.playback->playing = %d\n",info.playback->playing); +#endif + tmp = aud_vfs_get_metadata(info.infile ? info.infile : fd, "track-name"); + if(tmp){ + gchar *scratch; + + scratch = aud_str_to_utf8(tmp); + aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, scratch); + g_free(tmp); + g_free(scratch); + + tmp = NULL; + } + tmp = aud_vfs_get_metadata(info.infile ? info.infile : fd, "stream-name"); + if(tmp){ + gchar *scratch; + + scratch = aud_str_to_utf8(tmp); + aud_tuple_associate_string(tuple, FIELD_TITLE, NULL, scratch); + g_free(tmp); + g_free(scratch); + + tmp = NULL; + } + + AUDDBG("audmad_get_song_tuple: track_name = %s\n", aud_tuple_get_string(tuple, -1, "track-name")); + AUDDBG("audmad_get_song_tuple: stream_name = %s\n", aud_tuple_get_string(tuple, -1, "stream-name")); + aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, -1); + aud_tuple_associate_int(tuple, FIELD_MTIME, NULL, 0); // this indicates streaming + AUDDBG("get_song_tuple: remote: tuple\n"); + return tuple; + } + AUDDBG("get_song_tuple: remote: NULL\n"); + } /* info.remote */ + + // if !fd, pre-open the file with aud_vfs_fopen() and reuse fd. + if(!fd) { + fd = aud_vfs_fopen(filename, "rb"); + if(!fd) + return NULL; + local_fd = TRUE; + } + + if (!audmad_fill_info(&myinfo, fd)) { + AUDDBG("get_song_tuple: error obtaining info\n"); + if (local_fd) aud_vfs_fclose(fd); + return NULL; + } + + tuple = aud_tuple_new(); + aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, -1); + + id3file = id3_file_vfsopen(fd, ID3_FILE_MODE_READONLY); + + if (id3file) { + + tag = id3_file_tag(id3file); + if (tag) { + __set_and_free(tuple, FIELD_ARTIST, NULL, input_id3_get_string(tag, ID3_FRAME_ARTIST)); + __set_and_free(tuple, FIELD_ALBUM, NULL, input_id3_get_string(tag, ID3_FRAME_ALBUM)); + __set_and_free(tuple, FIELD_TITLE, NULL, input_id3_get_string(tag, ID3_FRAME_TITLE)); + + // year + string = NULL; + string = input_id3_get_string(tag, ID3_FRAME_YEAR); //TDRC + if (!string) + string = input_id3_get_string(tag, "TYER"); + + if (string) { + aud_tuple_associate_int(tuple, FIELD_YEAR, NULL, atoi(string)); + g_free(string); + string = NULL; + } + + __set_and_free(tuple, FIELD_FILE_NAME, NULL, aud_uri_to_display_basename(filename)); + __set_and_free(tuple, FIELD_FILE_PATH, NULL, aud_uri_to_display_dirname(filename)); + aud_tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, extname(filename)); + + // length + length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); + aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, length); + + // track number + string = input_id3_get_string(tag, ID3_FRAME_TRACK); + if (string) { + aud_tuple_associate_int(tuple, FIELD_TRACK_NUMBER, NULL, atoi(string)); + g_free(string); + string = NULL; + } + // genre + __set_and_free(tuple, FIELD_GENRE, NULL, input_id3_get_string(tag, ID3_FRAME_GENRE)); + __set_and_free(tuple, FIELD_COMMENT, NULL, input_id3_get_string(tag, ID3_FRAME_COMMENT)); + AUDDBG("genre = %s\n", aud_tuple_get_string(tuple, FIELD_GENRE, NULL)); + } + id3_file_close(id3file); + } // id3file + else { // no id3tag + __set_and_free(tuple, FIELD_FILE_NAME, NULL, aud_uri_to_display_basename(filename)); + __set_and_free(tuple, FIELD_FILE_PATH, NULL, aud_uri_to_display_dirname(filename)); + aud_tuple_associate_string(tuple, FIELD_FILE_EXT, NULL, extname(filename)); + // length + length = mad_timer_count(myinfo.duration, MAD_UNITS_MILLISECONDS); + aud_tuple_associate_int(tuple, FIELD_LENGTH, NULL, length); + } + + aud_tuple_associate_string(tuple, FIELD_QUALITY, NULL, "lossy"); + aud_tuple_associate_int(tuple, FIELD_BITRATE, NULL, myinfo.bitrate / 1000); + + string = g_strdup_printf("MPEG-1 Audio Layer %d", myinfo.mpeg_layer); + aud_tuple_associate_string(tuple, FIELD_CODEC, NULL, string); + g_free(string); + + aud_tuple_associate_string(tuple, FIELD_MIMETYPE, NULL, "audio/mpeg"); + + input_term(&myinfo); + + if(local_fd) + aud_vfs_fclose(fd); + + AUDDBG("e: mad: audmad_get_song_tuple\n"); + return tuple; +} + +static Tuple * +audmad_get_song_tuple(char *filename) +{ + return __audmad_get_song_tuple(filename, NULL); +} + +static Tuple * +audmad_probe_for_tuple(char *filename, VFSFile *fd) +{ + if (!audmad_is_our_fd(filename, fd)) + return NULL; + + aud_vfs_rewind(fd); + + return __audmad_get_song_tuple(filename, fd); +} + +static gchar *fmts[] = { "mp3", "mp2", "mpg", "bmu", NULL }; + +InputPlugin mad_ip = { + .description = "MPEG Audio Plugin", + .init = audmad_init, + .about = audmad_about, + .configure = audmad_configure, + .is_our_file = audmad_is_our_file, + .play_file = audmad_play_file, + .stop = audmad_stop, + .pause = audmad_pause, + .seek = audmad_seek, + .cleanup = audmad_cleanup, + .get_song_info = audmad_get_song_info, + .get_song_tuple = audmad_get_song_tuple, + .is_our_file_from_vfs = audmad_is_our_fd, + .vfs_extensions = fmts, + .mseek = audmad_mseek, + .probe_for_tuple = audmad_probe_for_tuple, + .update_song_tuple = audmad_update_song_tuple, +}; + +InputPlugin *madplug_iplist[] = { &mad_ip, NULL }; + +SIMPLE_INPUT_PLUGIN(madplug, madplug_iplist); + +InputPlugin *mad_plugin = &mad_ip;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/plugin.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,149 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef AUD_MAD_H +#define AUD_MAD_H + +/* #define AUD_DEBUG 1 */ +/* #define DEBUG_INTENSIVELY 1 */ +/* #define DEBUG_DITHER 1 */ + +#undef G_LOG_DOMAIN +#define G_LOG_DOMAIN "MADPlug" + +#undef PACKAGE +#define PACKAGE "audacious-plugins" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <audacious/plugin.h> +#include <audacious/main.h> +#include <audacious/util.h> +#include <audacious/strings.h> +#include <audacious/i18n.h> +#include <audacious/id3tag.h> +#include <mad.h> + +#include "xing.h" + +struct mad_info_t +{ + /* InputPlayback */ + InputPlayback *playback; + + /* seek time */ + gulong seek; /**< seek time in milliseconds */ + + /* state */ + guint current_frame;/**< current mp3 frame */ + mad_timer_t pos; /**< current play position */ + + /* song info */ + guint vbr; /**< bool: is vbr? */ + guint bitrate; /**< avg. bitrate */ + guint freq; /**< sample freq. */ + guint mpeg_layer; /**< mpeg layer */ + guint mode; /**< mpeg stereo mode */ + guint channels; + gint frames; /**< total mp3 frames or -1 */ + gint fmt; /**< sample format */ + gint size; /**< file size in bytes or -1 */ + gchar *title; /**< title for xmms */ + mad_timer_t duration; /**< total play time */ + struct id3_tag *tag; + struct id3_file *id3file; + struct xing xing; + Tuple *tuple; /* audacious tuple data */ + gchar *prev_title; /* used to optimize set_info calls */ + + /* replay parameters */ + gboolean has_replaygain; + double replaygain_album_scale; // -1 if not set + double replaygain_track_scale; + gchar *replaygain_album_str; + gchar *replaygain_track_str; + double replaygain_album_peak; // -1 if not set + double replaygain_track_peak; + gchar *replaygain_album_peak_str; + gchar *replaygain_track_peak_str; + double mp3gain_undo; // -1 if not set + double mp3gain_minmax; + gchar *mp3gain_undo_str; + gchar *mp3gain_minmax_str; + + /* data access */ + gchar *url; + gchar *filename; + VFSFile *infile; + gint offset; + + /* flags */ + gboolean remote; + gboolean fileinfo_request; + +}; + +typedef struct audmad_config_t +{ + gboolean fast_play_time_calc; + gboolean use_xing; + gboolean dither; + gboolean sjis; + + struct + { + gchar *preamp0_db; // gain applied to samples at decoding stage. + gdouble preamp0_scale; // pow(10, pregain/20) + gboolean enable; + gboolean track_mode; + gchar *preamp1_db; // preamp used with RG info. + gdouble preamp1_scale; + gchar *preamp2_db; // preamp used without RG info. + gdouble preamp2_scale; + gboolean anti_clip; + gboolean adaptive_scaler; + } replaygain; + + gboolean title_override; + gchar *id3_format; + gboolean show_avg_vbr_bitrate; + gboolean force_reopen_audio; +} audmad_config_t; + +// global variables +extern InputPlugin *mad_plugin; +extern audmad_config_t *audmad_config; + +// gcond +extern GMutex *mad_mutex; +extern GMutex *pb_mutex; +extern GCond *mad_cond; + +// prototypes +void audmad_config_compute(struct audmad_config_t *config); +void input_process_remote_metadata(struct mad_info_t *info); +gpointer decode_loop(gpointer arg); +void audmad_error(gchar * fmt, ...); +void audmad_configure(void); + +#endif /* !AUD_MAD_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/replaygain.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,349 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * Copyright (C) 2001-2007 Samuel Krempp + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "plugin.h" +#include <stdlib.h> +#include <math.h> +#include <ctype.h> +#include <assert.h> +#include "replaygain.h" + +static unsigned long +Read_LE_Uint32(const unsigned char *p) +{ + return ((unsigned long) p[0] << 0) | + ((unsigned long) p[1] << 8) | + ((unsigned long) p[2] << 16) | ((unsigned long) p[3] << 24); +} + +static int +uncase_strcmp(const char *s1, const char *s2) +{ + int l1 = strlen(s1); + int l2 = strlen(s2); + int i; + for (i = 0; i < l1 && i < l2; ++i) { + if (toupper(s1[i]) < toupper(s2[i])) + return -1; + } + if (l1 == l2) + return 0; + return (l1 < l2) ? -1 : +1; +} + +static gdouble +strgain2double(gchar * s, int len) +{ + gdouble res = g_strtod(s, NULL); // gain, in dB. + if (res == 0) + return 1; + return pow(10, res / 20); +} + +// Reads APE v2.0 tag ending at current pos in fp + +static int +ReadAPE2Tag(VFSFile * fp, struct mad_info_t *file_info) +{ + unsigned long vsize; + unsigned long isize; + unsigned long flags; + char *buff; + char *p; + char *end; + struct APETagFooterStruct T, *tp; + unsigned long TagLen; + unsigned long TagCount; + + tp = &T; + + if (aud_vfs_fseek(fp, -sizeof(T), SEEK_CUR) != 0) + return 18; + if (aud_vfs_fread(tp, 1, sizeof(T), fp) != sizeof T) + return 2; + if (memcmp(tp->ID, "APETAGEX", sizeof(tp->ID)) != 0) + return 3; + if (Read_LE_Uint32(tp->Version) != 2000) + return 4; + TagLen = Read_LE_Uint32(tp->Length); + if (TagLen < sizeof(T)) + return 5; + if (aud_vfs_fseek(fp, -TagLen, SEEK_CUR) != 0) + return 6; + if ((buff = (char *) malloc(TagLen)) == NULL) { + return 7; + } + if (aud_vfs_fread(buff, 1, TagLen - sizeof(T), fp) != TagLen - sizeof(T)) { + free(buff); + return 8; + } + + AUDDBG("ver = %ld\n", Read_LE_Uint32(tp->Version)); + AUDDBG("taglen = %ld\n", TagLen); + + TagCount = Read_LE_Uint32(tp->TagCount); + end = buff + TagLen - sizeof(T); + for (p = buff; p < end && TagCount--;) { + vsize = Read_LE_Uint32((unsigned char *)p); + p += 4; + flags = Read_LE_Uint32((unsigned char *)p); + p += 4; + isize = strlen((char *) p); + + if (isize > 0 && vsize > 0) { + gdouble *scale = NULL; + gchar **str = NULL; + if (uncase_strcmp(p, "REPLAYGAIN_ALBUM_GAIN") == 0) { + scale = &file_info->replaygain_album_scale; + str = &file_info->replaygain_album_str; + } + if (uncase_strcmp(p, "REPLAYGAIN_TRACK_GAIN") == 0) { + scale = &file_info->replaygain_track_scale; + str = &file_info->replaygain_track_str; + } + if (str != NULL) { + assert(scale != NULL); + *scale = strgain2double(p + isize + 1, vsize); + *str = g_strndup(p + isize + 1, vsize); + } + //* case of peak info tags : */ + str = NULL; + if (uncase_strcmp(p, "REPLAYGAIN_TRACK_PEAK") == 0) { + scale = &file_info->replaygain_track_peak; + str = &file_info->replaygain_track_peak_str; + } + if (uncase_strcmp(p, "REPLAYGAIN_ALBUM_PEAK") == 0) { + scale = &file_info->replaygain_album_peak; + str = &file_info->replaygain_album_peak_str; + } + if (str != NULL) { + *scale = g_strtod(p + isize + 1, NULL); + *str = g_strndup(p + isize + 1, vsize); + } + + /* mp3gain additional tags : + the gain tag translates to scale = 2^(gain/4), + i.e., in dB : 20*log(2)/log(10)*gain/4 + -> 1.501*gain dB + */ + if (uncase_strcmp(p, "MP3GAIN_UNDO") == 0) { + str = &file_info->mp3gain_undo_str; + scale = &file_info->mp3gain_undo; + assert(4 < vsize); /* this tag is +left,+right */ + *str = g_strndup(p + isize + 1, vsize); + *scale = 1.50515 * atoi(*str); + } + if (uncase_strcmp(p, "MP3GAIN_MINMAX") == 0) { + str = &file_info->mp3gain_minmax_str; + scale = &file_info->mp3gain_minmax; + *str = g_strndup(p + isize + 1, vsize); + assert(4 < vsize); /* this tag is min,max */ + *scale = 1.50515 * (atoi((*str) + 4) - atoi(*str)); + } + } + p += isize + 1 + vsize; + } + + free(buff); + + return 0; +} + +static int +find_offset(VFSFile * fp) +{ + static const char *key = "APETAGEX"; + char buff[20000]; + int N = 0; + if (aud_vfs_fseek(fp, -20000, SEEK_CUR) != 0); + if ((N = aud_vfs_fread(buff, 1, 20000, fp)) < 16) + return 1; + int matched = 0; + int i, last_match = -1; + for (i = 0; i < N; ++i) { + if (buff[i] == key[matched]) + ++matched; + else { + if (matched == 5 && buff[i] == 'P') + matched = 2; // got "APET" + "AP" + else + matched = 0; + } + if (matched == 8) { + last_match = i; + matched = 0; + } + } + if (last_match == -1) + return 1; + return last_match + 1 - 8 + sizeof(struct APETagFooterStruct) - N; +} + +/* Eugene Zagidullin: + * Read ReplayGain info from foobar2000-style id3v2 frames */ + +static int +ReadId3v2TXXX(struct mad_info_t *file_info) +{ + int i; + char *key; + char *value; + struct id3_frame *frame; + + AUDDBG("f: ReadId3v2TXXX\n"); + + /* tag must be read before! */ + if (! file_info->tag ) { + AUDDBG("id3v2 not found\n"); + return 0; + } + + /* Partially based on code from MPD (http://www.musicpd.org/) */ + for (i = 0; (frame = id3_tag_findframe(file_info->tag, "TXXX", i)); i++) { + if (frame->nfields < 3) + continue; + + key = (char *) + id3_ucs4_latin1duplicate(id3_field_getstring + (&frame->fields[1])); + value = (char *) + id3_ucs4_latin1duplicate(id3_field_getstring + (&frame->fields[2])); + + if (strcasecmp(key, "replaygain_track_gain") == 0) { + file_info->replaygain_track_scale = strgain2double(value, strlen(value)); + file_info->replaygain_track_str = g_strdup(value); + } else if (strcasecmp(key, "replaygain_album_gain") == 0) { + file_info->replaygain_album_scale = strgain2double(value, strlen(value)); + file_info->replaygain_album_str = g_strdup(value); + } else if (strcasecmp(key, "replaygain_track_peak") == 0) { + file_info->replaygain_track_peak = g_strtod(value, NULL); + file_info->replaygain_track_peak_str = g_strdup(value); + } else if (strcasecmp(key, "replaygain_album_peak") == 0) { + file_info->replaygain_album_peak = g_strtod(value, NULL); + file_info->replaygain_album_peak_str = g_strdup(value); + } + + free(key); + free(value); + } + + if (file_info->replaygain_track_scale != -1 || file_info->replaygain_album_scale != -1) + { + file_info->has_replaygain = TRUE; + return 1; + } + + return 0; +} + +void +read_replaygain(struct mad_info_t *file_info) +{ + VFSFile *fp; + glong curpos = 0; + + AUDDBG("f: read_replaygain\n"); + + file_info->has_replaygain = FALSE; + file_info->replaygain_album_scale = -1; + file_info->replaygain_track_scale = -1; + file_info->mp3gain_undo = -77; + file_info->mp3gain_minmax = -77; + + if (ReadId3v2TXXX(file_info)) { + AUDDBG("found ReplayGain info in id3v2 tag\n"); +#ifdef AUD_DEBUG + gchar *tmp = g_filename_to_utf8(file_info->filename, -1, NULL, NULL, NULL); + + AUDDBG("RG album scale= %g, RG track scale = %g, in %s\n", + file_info->replaygain_album_scale, + file_info->replaygain_track_scale, tmp); + g_free(tmp); +#endif + return; + } + + + /* APEv2 stuff */ + if (file_info->infile) { + fp = aud_vfs_dup(file_info->infile); + curpos = aud_vfs_ftell(fp); + } + else { + if ((fp = aud_vfs_fopen(file_info->filename, "rb")) == NULL) + return; + } + + if (aud_vfs_fseek(fp, 0L, SEEK_END) != 0) { + aud_vfs_fclose(fp); + return; + } + + long pos = aud_vfs_ftell(fp); + int res = -1; + int try = 0; + while (res != 0 && try < 10) { + // try skipping an id3 tag + aud_vfs_fseek(fp, pos, SEEK_SET); + aud_vfs_fseek(fp, try * -128, SEEK_CUR); + res = ReadAPE2Tag(fp, file_info); + ++try; + } + if (res != 0) { + // try brute search (don't want to parse all possible kinds of tags..) + aud_vfs_fseek(fp, pos, SEEK_SET); + int offs = find_offset(fp); + if (offs <= 0) { // found ! + aud_vfs_fseek(fp, pos, SEEK_SET); + aud_vfs_fseek(fp, offs, SEEK_CUR); + res = ReadAPE2Tag(fp, file_info); + if (res != 0) { + g_message + ("hmpf, was supposed to find a tag.. offs=%d, res=%d", + offs, res); + } + } + else + AUDDBG("replaygain: not found\n"); + } +#ifdef AUD_DEBUG + if (res == 0) { // got APE tags, show the result + gchar *tmp = g_filename_to_utf8(file_info->filename, -1, NULL, NULL, NULL); + AUDDBG("RG album scale= %g, RG track scale = %g, in %s\n", + file_info->replaygain_album_scale, + file_info->replaygain_track_scale, tmp); + g_free(tmp); + } +#endif + + if (file_info->replaygain_album_scale != -1 + || file_info->replaygain_track_scale != -1) + file_info->has_replaygain = TRUE; + + if (file_info->infile) + aud_vfs_fseek(fp, curpos, SEEK_SET); + + aud_vfs_fclose(fp); + + AUDDBG("e: read_replaygain\n"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/replaygain.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,44 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * Copyright (C) 2001-2007 Samuel Krempp + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "plugin.h" +#include "stdlib.h" +#include "math.h" +#include "ctype.h" + +#ifndef __replaygain_h__ +#define __replaygain_h__ + +struct APETagFooterStruct +{ + unsigned char ID[8]; + unsigned char Version[4]; + unsigned char Length[4]; + unsigned char TagCount[4]; + unsigned char Flags[4]; + unsigned char Reserved[8]; +}; + +/* prototypes */ +void read_replaygain(struct mad_info_t *file_info); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/tuple.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,188 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include "plugin.h" +#include "tuple.h" +#include "input.h" + +#include <math.h> +#include <string.h> + +#include <glib.h> +#include <glib/gprintf.h> + +#include <audacious/util.h> +#include <audacious/plugin.h> +#include <audacious/id3tag.h> + +#include <langinfo.h> + +static void +update_id3_frame(struct id3_tag *tag, const char *frame_name, const char *data, int sjis) +{ + int res; + struct id3_frame *frame; + union id3_field *field; + id3_ucs4_t *ucs4; + + if (data == NULL) + return; + + /* printf ("updating id3: %s: %s\n", frame_name, data); + + + An empty string removes the frame altogether. + */ + if (strlen(data) == 0) { + while ((frame = id3_tag_findframe(tag, frame_name, 0))) { + AUDDBG("madplug: detachframe\n"); + id3_tag_detachframe(tag, frame); + } + return; + } + + frame = id3_tag_findframe(tag, frame_name, 0); + if (!frame) { + AUDDBG("frame_new\n"); + frame = id3_frame_new(frame_name); + id3_tag_attachframe(tag, frame); + } + + /* setup ucs4 string */ + if(sjis) { + ucs4 = id3_latin1_ucs4duplicate((id3_latin1_t *) data); + } + else { + ucs4 = id3_utf8_ucs4duplicate((id3_utf8_t *) data); + } + + /* set encoding */ + field = id3_frame_field(frame, 0); + id3_field_settextencoding(field, sjis ? ID3_FIELD_TEXTENCODING_ISO_8859_1 : + ID3_FIELD_TEXTENCODING_UTF_8); + + /* setup genre code */ + if (!strcmp(frame_name, ID3_FRAME_GENRE)) { + char *tmp; + int index = id3_genre_number(ucs4); + g_free(ucs4); + + if(index == -1) { /* unknown genre. remove TCON frame. */ + AUDDBG("madplug: remove genre frame\n"); + id3_tag_detachframe(tag, frame); + } + else { /* meaningful genre */ + tmp = g_strdup_printf("%d", index); + ucs4 = id3_latin1_ucs4duplicate((unsigned char *) tmp); + } + + } + + /* write string */ + if (!strcmp(frame_name, ID3_FRAME_COMMENT)) { + field = id3_frame_field(frame, 3); + field->type = ID3_FIELD_TYPE_STRINGFULL; + res = id3_field_setfullstring(field, ucs4); + } + else { + field = id3_frame_field(frame, 1); + field->type = ID3_FIELD_TYPE_STRINGLIST; + res = id3_field_setstrings(field, 1, &ucs4); + } + + if (res != 0) + g_print("error setting id3 field: %s\n", frame_name); +} + +static void +update_id3_frame_from_tuple(struct id3_tag *id3tag, const char *field, Tuple *tuple, int fieldn, int sjis) +{ + int val; + char *text, *text2; + const char *encoding = sjis ? "SJIS" : "UTF-8"; + + if(aud_tuple_get_value_type(tuple, fieldn, NULL) == TUPLE_INT) { + val = aud_tuple_get_int(tuple, fieldn, NULL); + if(val > 0) { + text2 = g_strdup_printf("%d", val); + AUDDBG("madplug: updating field:\"%s\"=\"%s\", enc %s\n", field, text2, encoding); + update_id3_frame(id3tag, field, text2, 0); + g_free(text2); + } else { + update_id3_frame(id3tag, field, "", 0); /* will be detached */ + } + + } else if(aud_tuple_get_value_type(tuple, fieldn, NULL) == TUPLE_STRING) { + text = (char*)aud_tuple_get_string(tuple, fieldn, NULL); + text2 = g_convert(text, strlen(text), encoding, "UTF-8", NULL, NULL, NULL); + AUDDBG("madplug: updating field:\"%s\"=\"%s\", enc %s\n", field, text2, encoding); + update_id3_frame(id3tag, field, text2, sjis); + g_free(text2); + } +} + +gboolean +audmad_update_song_tuple(Tuple *tuple, VFSFile *fd) +{ + struct id3_file *id3file; + struct id3_tag *id3tag; + gchar *text; + struct mad_info_t songinfo; + + if ((id3file = id3_file_vfsopen(fd, ID3_FILE_MODE_READWRITE)) == NULL) return FALSE; + + id3tag = id3_file_tag(id3file); + if (!id3tag) { + AUDDBG("no id3tag\n. append new tag.\n"); + id3tag = id3_tag_new(); + id3_tag_clearframes(id3tag); + id3tag->options |= ID3_TAG_OPTION_APPENDEDTAG | ID3_TAG_OPTION_ID3V1; + } + + id3_tag_options(id3tag, ID3_TAG_OPTION_ID3V1, ~0); /* enables id3v1. TODO: make id3v1 optional */ + + update_id3_frame_from_tuple(id3tag, ID3_FRAME_TITLE, tuple, FIELD_TITLE, audmad_config->sjis); + update_id3_frame_from_tuple(id3tag, ID3_FRAME_ARTIST, tuple, FIELD_ARTIST, audmad_config->sjis); + update_id3_frame_from_tuple(id3tag, ID3_FRAME_ALBUM, tuple, FIELD_ALBUM, audmad_config->sjis); + update_id3_frame_from_tuple(id3tag, ID3_FRAME_YEAR, tuple, FIELD_YEAR, audmad_config->sjis); + update_id3_frame_from_tuple(id3tag, ID3_FRAME_COMMENT, tuple, FIELD_COMMENT, audmad_config->sjis); + update_id3_frame_from_tuple(id3tag, ID3_FRAME_TRACK, tuple, FIELD_TRACK_NUMBER, audmad_config->sjis); + update_id3_frame_from_tuple(id3tag, ID3_FRAME_GENRE, tuple, FIELD_GENRE, audmad_config->sjis); + + if(!id3_tag_findframe(id3tag, "TLEN", 0) && input_init(&songinfo, fd->uri, fd) && !songinfo.remote) { + AUDDBG("update TLEN frame\n"); + songinfo.fileinfo_request = FALSE; /* we don't need to read tuple again */ + input_get_info(&songinfo, FALSE); + text = g_strdup_printf("%ld", mad_timer_count(songinfo.duration, MAD_UNITS_MILLISECONDS)); + AUDDBG("TLEN: \"%s\"\n", text); + update_id3_frame(id3tag, "TLEN", text, 0); + g_free(text); + input_term(&songinfo); + } + + if (id3_file_update(id3file) != 0) return FALSE; + + id3_file_close(id3file); + return TRUE; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/tuple.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,30 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin + * + * Portions derived from xmms-mad: + * Copyright (C) 2001-2002 Sam Clegg - See COPYING + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef TUPLE_H +#define TUPLE_H + +#include <glib.h> +#include <audacious/plugin.h> + +gboolean audmad_update_song_tuple(Tuple *tuple, VFSFile *fd); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/xing.c Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,97 @@ +/* + * mad plugin for audacious + * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa + * + * Derived from: mad - MPEG audio decoder + * Copyright (C) 2000-2001 Robert Leslie + * + * 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; under version 2 of the License. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <mad.h> + +#include "xing.h" + +#define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g') + +/* + * NAME: xing->init() + * DESCRIPTION: initialize Xing structure + */ +void +xing_init(struct xing *xing) +{ + xing->flags = 0; + xing->frames = 0; + xing->bytes = 0; + xing->toc[0] = '\0'; + xing->scale = 0; +} + +/* + * NAME: xing->parse() + * DESCRIPTION: parse a Xing VBR header + */ +int +xing_parse(struct xing *xing, struct mad_bitptr ptr, + unsigned int bitlen) +{ + if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC) + goto fail; + + xing->flags = mad_bit_read(&ptr, 32); + bitlen -= 64; + + if (xing->flags & XING_FRAMES) { + if (bitlen < 32) + goto fail; + + xing->frames = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + if (xing->flags & XING_BYTES) { + if (bitlen < 32) + goto fail; + + xing->bytes = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + if (xing->flags & XING_TOC) { + int i; + + if (bitlen < 800) + goto fail; + + for (i = 0; i < 100; ++i) + xing->toc[i] = mad_bit_read(&ptr, 8); + + bitlen -= 800; + } + + if (xing->flags & XING_SCALE) { + if (bitlen < 32) + goto fail; + + xing->scale = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + return 0; + + fail: + xing->flags = 0; + return -1; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/madplug_x/xing.h Tue Feb 05 01:11:50 2008 +0900 @@ -0,0 +1,48 @@ +/* + * mad - MPEG audio decoder + * Copyright (C) 2000-2001 Robert Leslie + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XING_H +#define XING_H + +#include "mad.h" + +struct xing +{ + long flags; /* valid fields (see below) */ + unsigned long frames; /* total number of frames */ + unsigned long bytes; /* total number of bytes */ + unsigned char toc[100]; /* 100-point seek table */ + long scale; /* ?? */ +}; + +enum +{ + XING_FRAMES = 0x00000001L, + XING_BYTES = 0x00000002L, + XING_TOC = 0x00000004L, + XING_SCALE = 0x00000008L +}; + +void xing_init(struct xing *); + +#define xing_finish(xing) /* nothing */ + +int xing_parse(struct xing *, struct mad_bitptr, unsigned int); + +#endif