diff options
47 files changed, 6711 insertions, 386 deletions
diff --git a/Makefile.am b/Makefile.am index bc58549e..34bff094 100644 --- a/Makefile.am +++ b/Makefile.am @@ -36,7 +36,8 @@ deadbeef_SOURCES =\ gettext.h\ ringbuf.c ringbuf.h\ dsppreset.c dsppreset.h\ - replaygain.c replaygain.h + replaygain.c replaygain.h\ + fftsg.c # ConvertUTF/ConvertUTF.c ConvertUTF/ConvertUTF.h @@ -41,13 +41,14 @@ most of them are optional, which means deadbeef will build and run without them, libsndfile: for sndfile plugin libcdio + libcddb: for cd audio plugin ffmpeg < 0.11: for ffmpeg plugin (versions >= 0.11 are not supported) - xlib: for global hotkeys + xlib: for global hotkeys and for gtkui opengl support dbus: for notification daemon support (OSD current song notifications) pulseaudio: for PulseAudio output plugin faad2: for AAC plugin zlib: for Audio Overload plugin (psf, psf2, etc), GME (for vgz) libzip: for vfs_zip plugin yasm: required to build assembly portions of ffap plugin on supported platforms (x86, x86_64) + gtkglext: for gtkui opengl support actual package names for your Linux distribution may vary. diff --git a/configure.ac b/configure.ac index 30c811c7..ca065e90 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_INIT([deadbeef], [0.5.6-rc3]) +AC_INIT([deadbeef], [devel]) AC_CONFIG_HEADER(config.h) @@ -104,6 +104,7 @@ AC_ARG_ENABLE(m3u, [AS_HELP_STRING([--enable-m3u ], [build m3u plugin AC_ARG_ENABLE(vfs-zip, [AS_HELP_STRING([--enable-vfs-zip ], [build vfs_zip plugin (default: auto)])], [enable_vfs_zip=$enableval], [enable_vfs_zip=yes]) AC_ARG_ENABLE(converter, [AS_HELP_STRING([--enable-converter ], [build converter plugin (default: auto)])], [enable_converter=$enableval], [enable_converter=yes]) AC_ARG_ENABLE(artwork-imlib2, [AS_HELP_STRING([--enable-artwork-imlib2 ], [use imlib2 in artwork plugin (default: auto)])], [enable_artwork_imlib2=$enableval], [enable_artwork_imlib2=yes]) +AC_ARG_ENABLE(medialib, [AS_HELP_STRING([--enable-medialib ], [build medialibrary plugin (default: auto)])], [enable_medialib=$enableval], [enable_medialib=yes]) AC_ARG_ENABLE(dumb, [AS_HELP_STRING([--enable-dumb ], [build DUMB plugin (default: auto)])], [enable_dumb=$enableval], [enable_dumb=yes]) AC_ARG_ENABLE(shn, [AS_HELP_STRING([--enable-shn ], [build SHN plugin (default: auto)])], [enable_shn=$enableval], [enable_shn=yes]) AC_ARG_ENABLE(psf, [AS_HELP_STRING([--enable-psf ], [build AOSDK-based PSF(,QSF,SSF,DSF) plugin (default: auto)])], [enable_psf=$enableval], [enable_psf=yes]) @@ -191,7 +192,7 @@ if test "x$enable_gtk2" == "xyes" ; then if test "x$enable_staticlink" != "xno" ; then HAVE_GTK2=yes else - PKG_CHECK_MODULES(GTK2_DEPS, gtk+-2.0 >= 2.12 gthread-2.0 glib-2.0, HAVE_GTK2=yes, HAVE_GTK2=no) + PKG_CHECK_MODULES(GTK2_DEPS, gtk+-2.0 >= 2.12 gthread-2.0 glib-2.0 gtkglext-1.0, HAVE_GTK2=yes, HAVE_GTK2=no) fi AC_CHECK_LIB([SM], [main], [HAVE_SM=yes;SM_LIBS="-lSM";AC_SUBST(SM_LIBS)]) AC_CHECK_LIB([ICE], [main], [HAVE_ICE=yes;ICE_LIBS="-lICE";AC_SUBST(ICE_LIBS)]) @@ -577,6 +578,10 @@ if test "x$enable_converter" != "xno" ; then fi fi +if test "x$enable_medialib" != "xno" ; then + HAVE_MEDIALIB=yes +fi + if test "x$enable_shellexecui" != "xno" ; then if test "x$HAVE_GTK2" = "xyes" || test "x$HAVE_GTK3" = "xyes" ; then HAVE_SHELLEXECUI=yes @@ -603,8 +608,7 @@ if test "x$enable_alac" != "xno" ; then HAVE_ALAC=yes fi - -PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/pulse plugins/notify plugins/musepack plugins/wildmidi plugins/tta plugins/dca plugins/aac plugins/mms plugins/shellexec plugins/dsp_libsrc plugins/m3u plugins/vfs_zip plugins/converter plugins/dumb plugins/shn plugins/ao plugins/mono2stereo plugins/shellexecui plugins/alac" +PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/pulse plugins/notify plugins/musepack plugins/wildmidi plugins/tta plugins/dca plugins/aac plugins/mms plugins/shellexec plugins/dsp_libsrc plugins/m3u plugins/vfs_zip plugins/converter plugins/dumb plugins/shn plugins/ao plugins/mono2stereo plugins/shellexecui plugins/alac plugins/medialib" AM_CONDITIONAL(HAVE_VORBIS, test "x$HAVE_VORBISPLUGIN" = "xyes") AM_CONDITIONAL(HAVE_FLAC, test "x$HAVE_FLACPLUGIN" = "xyes") @@ -645,6 +649,7 @@ AM_CONDITIONAL(HAVE_IMLIB2, test "x$HAVE_IMLIB2" = "xyes") AM_CONDITIONAL(HAVE_JPEG, test "x$HAVE_JPEG" = "xyes") AM_CONDITIONAL(HAVE_PNG, test "x$HAVE_PNG" = "xyes") AM_CONDITIONAL(HAVE_YASM, test "x$HAVE_YASM" = "xyes") +AM_CONDITIONAL(HAVE_MEDIALIB, test "x$HAVE_MEDIALIB" = "xyes") AM_CONDITIONAL(HAVE_DUMB, test "x$HAVE_DUMB" = "xyes") AM_CONDITIONAL(HAVE_PSF, test "x$HAVE_PSF" = "xyes") AM_CONDITIONAL(HAVE_SHN, test "x$HAVE_SHN" = "xyes") @@ -718,6 +723,7 @@ PRINT_PLUGIN_INFO([dsp_src],[High quality samplerate conversion using libsampler PRINT_PLUGIN_INFO([m3u],[M3U and PLS playlist support],[test "x$HAVE_M3U" = "xyes"]) PRINT_PLUGIN_INFO([vfs_zip],[zip archive support],[test "x$HAVE_VFS_ZIP" = "xyes"]) PRINT_PLUGIN_INFO([converter],[plugin for converting files to any formats],[test "x$HAVE_CONVERTER" = "xyes"]) +PRINT_PLUGIN_INFO([medialib],[media library support plugin],[test "x$HAVE_MEDIALIB" = "xyes"]) PRINT_PLUGIN_INFO([psf],[PSF format plugin, using AOSDK],[test "x$HAVE_PSF" = "xyes"]) PRINT_PLUGIN_INFO([dumb],[DUMB module plugin, for MOD, S3M, etc],[test "x$HAVE_DUMB" = "xyes"]) PRINT_PLUGIN_INFO([shn],[SHN plugin based on xmms-shn],[test "x$HAVE_SHN" = "xyes"]) @@ -765,6 +771,7 @@ plugins/dsp_libsrc/Makefile plugins/m3u/Makefile plugins/vfs_zip/Makefile plugins/converter/Makefile +plugins/medialib/Makefile plugins/dumb/Makefile plugins/ao/Makefile plugins/shn/Makefile @@ -273,12 +273,17 @@ enum { DB_EV_ACTIONSCHANGED = 20, // plugin actions were changed, e.g. for reinitializing gui DB_EV_DSPCHAINCHANGED = 21, // emitted when any parameter of the main dsp chain has been changed + // new in 1.2 + DB_EV_SELCHANGED = 20, // selection changed in playlist p1 iter p2 + DB_EV_FIRST = 1000, DB_EV_SONGCHANGED = 1000, // current song changed from one to another, ctx=ddb_event_trackchange_t DB_EV_SONGSTARTED = 1001, // song started playing, ctx=ddb_event_track_t DB_EV_SONGFINISHED = 1002, // song finished playing, ctx=ddb_event_track_t DB_EV_TRACKINFOCHANGED = 1004, // trackinfo was changed (included medatata and playback status), ctx=ddb_event_track_t DB_EV_SEEKED = 1005, // seek happened, ctx=ddb_event_playpos_t + // new in 1.4 + DB_EV_TRACKFOCUSCURRENT = 1006, // user wants to highlight/find the current playing track DB_EV_MAX }; @@ -305,6 +310,14 @@ enum ddb_sort_order_t { DDB_SORT_RANDOM, // available since API 1.3 }; +enum ddb_audio_data_type_t { + DDB_AUDIO_WAVEFORM, + DDB_AUDIO_FREQ, +}; + +// audio memory constants +#define DDB_AUDIO_MEMORY_FRAMES 512 + // typecasting macros #define DB_PLUGIN(x) ((DB_plugin_t *)(x)) #define DB_CALLBACK(x) ((DB_callback_t)(x)) @@ -767,6 +780,14 @@ typedef struct { // fast way to test if a field exists in playitem int (*pl_meta_exists) (DB_playItem_t *it, const char *key); + + // FIXME ******* devel branch only ******* + // access real-time audio data (e.g. for visualization) + // returns data size in bytes + // fmt and data will be filled with last bytes that came to the output plugin + // data size must be float[DDB_AUDIO_MEMORY_FRAMES] + void (*audio_get_waveform_data) (int type, float *data); + } DB_functions_t; // NOTE: an item placement must be selected like this diff --git a/fftsg.c b/fftsg.c new file mode 100644 index 00000000..265b722e --- /dev/null +++ b/fftsg.c @@ -0,0 +1,3322 @@ +/* + Copyright Takuya OOURA, 1996-2001 + http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + + You may use, copy, modify and distribute this code for any purpose (include commercial use) and without fee. + Please refer to this package when you modify this code. +*/ + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :split-radix + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, float *, int *, float *); + void rdft(int, int, float *, int *, float *); + void ddct(int, int, float *, int *, float *); + void ddst(int, int, float *, int *, float *); + void dfct(int, float *, float *, int *, float *); + void dfst(int, float *, float *, int *, float *); +macro definitions + USE_CDFT_PTHREADS : default=not defined + CDFT_THREADS_BEGIN_N : must be >= 512, default=8192 + CDFT_4THREADS_BEGIN_N : must be >= 512, default=65536 + USE_CDFT_WINTHREADS : default=not defined + CDFT_THREADS_BEGIN_N : must be >= 512, default=32768 + CDFT_4THREADS_BEGIN_N : must be >= 512, default=524288 + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + <case1> + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k<n + <case2> + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k<n + (notes: sum_j=0^n-1 is a summation from j=0 to n-1) + [usage] + <case1> + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (float *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j<n + output data + a[2*k] = Re(X[k]), + a[2*k+1] = Im(X[k]), 0<=k<n + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + <case1> RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0<k<n/2 + <case2> IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k<n + [usage] + <case1> + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + <case1> + output data + a[2*k] = R[k], 0<=k<n/2 + a[2*k+1] = I[k], 0<k<n/2 + a[1] = R[n/2] + <case2> + input data + a[2*j] = R[j], 0<=j<n/2 + a[2*j+1] = I[j], 0<j<n/2 + a[1] = R[n/2] + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + <case1> IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k<n + <case2> DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k<n + [usage] + <case1> + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = C[k], 0<=k<n + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + <case1> IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k<n + <case2> DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0<k<=n + [usage] + <case1> + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + <case2> + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + <case1> + input data + a[j] = A[j], 0<j<n + a[0] = A[n] + output data + a[k] = S[k], 0<=k<n + <case2> + output data + a[k] = S[k], 0<k<n + a[0] = S[n] + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (float *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (float *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0<k<n + [usage] + ip[0] = 0; // first time only + dfst(n, a, t, ip, w); + [parameters] + n :data length + 1 (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (float *) + output data + a[k] = S[k], 0<k<n + (a[0] is used for work area) + t[0...n/2-1] :work area (float *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (float *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + + +void cdft(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void cftfsub(int n, float *a, int *ip, int nw, float *w); + void cftbsub(int n, float *a, int *ip, int nw, float *w); + int nw; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + if (isgn >= 0) { + cftfsub(n, a, ip, nw, w); + } else { + cftbsub(n, a, ip, nw, w); + } +} + + +void rdft(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void cftfsub(int n, float *a, int *ip, int nw, float *w); + void cftbsub(int n, float *a, int *ip, int nw, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + int nw, nc; + float xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } +} + + +void ddct(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void cftfsub(int n, float *a, int *ip, int nw, float *w); + void cftbsub(int n, float *a, int *ip, int nw, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + void dctsub(int n, float *a, int nc, float *c); + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = a[j] - a[j - 1]; + a[j] += a[j - 1]; + } + a[1] = a[0] - xr; + a[0] += xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } + dctsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = a[j] - a[j + 1]; + a[j] += a[j + 1]; + } + a[n - 1] = xr; + } +} + + +void ddst(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void cftfsub(int n, float *a, int *ip, int nw, float *w); + void cftbsub(int n, float *a, int *ip, int nw, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + void dstsub(int n, float *a, int nc, float *c); + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = -a[j] - a[j - 1]; + a[j] -= a[j - 1]; + } + a[1] = a[0] + xr; + a[0] -= xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + cftbsub(n, a, ip, nw, w); + } else if (n == 4) { + cftbsub(n, a, ip, nw, w); + } + } + dstsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + cftfsub(n, a, ip, nw, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, ip, nw, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = -a[j] - a[j + 1]; + a[j] -= a[j + 1]; + } + a[n - 1] = -xr; + } +} + + +void dfct(int n, float *a, float *t, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void cftfsub(int n, float *a, int *ip, int nw, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void dctsub(int n, float *a, int nc, float *c); + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + m = n >> 1; + yi = a[m]; + xi = a[0] + a[n]; + a[0] -= a[n]; + t[0] = xi - yi; + t[m] = xi + yi; + if (n > 2) { + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] - a[n - j]; + xi = a[j] + a[n - j]; + yr = a[k] - a[n - k]; + yi = a[k] + a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi - yi; + t[k] = xi + yi; + } + t[mh] = a[mh] + a[n - mh]; + a[mh] -= a[n - mh]; + dctsub(m, a, nc, w + nw); + if (m > 4) { + cftfsub(m, a, ip, nw, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, ip, nw, w); + } + a[n - 1] = a[0] - a[1]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] + a[j + 1]; + a[2 * j - 1] = a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dctsub(m, t, nc, w + nw); + if (m > 4) { + cftfsub(m, t, ip, nw, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, ip, nw, w); + } + a[n - l] = t[0] - t[1]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = t[j] - t[j + 1]; + a[k + l] = t[j] + t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 0; j < mh; j++) { + k = m - j; + t[j] = t[m + k] - t[m + j]; + t[k] = t[m + k] + t[m + j]; + } + t[mh] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + a[n] = t[2] - t[1]; + a[0] = t[2] + t[1]; + } else { + a[1] = a[0]; + a[2] = t[0]; + a[0] = t[1]; + } +} + + +void dfst(int n, float *a, float *t, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void cftfsub(int n, float *a, int *ip, int nw, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void dstsub(int n, float *a, int nc, float *c); + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + if (n > 2) { + m = n >> 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] + a[n - j]; + xi = a[j] - a[n - j]; + yr = a[k] + a[n - k]; + yi = a[k] - a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi + yi; + t[k] = xi - yi; + } + t[0] = a[mh] - a[n - mh]; + a[mh] += a[n - mh]; + a[0] = a[m]; + dstsub(m, a, nc, w + nw); + if (m > 4) { + cftfsub(m, a, ip, nw, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, ip, nw, w); + } + a[n - 1] = a[1] - a[0]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] - a[j + 1]; + a[2 * j - 1] = -a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dstsub(m, t, nc, w + nw); + if (m > 4) { + cftfsub(m, t, ip, nw, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, ip, nw, w); + } + a[n - l] = t[1] - t[0]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = -t[j] - t[j + 1]; + a[k + l] = t[j] - t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + t[j] = t[m + k] + t[m + j]; + t[k] = t[m + k] - t[m + j]; + } + t[0] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + } + a[0] = 0; +} + + +/* -------- initializing routines -------- */ + + +#include <math.h> + +void makewt(int nw, int *ip, float *w) +{ + void makeipt(int nw, int *ip); + int j, nwh, nw0, nw1; + float delta, wn4r, wk1r, wk1i, wk3r, wk3i; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + wn4r = cos(delta * nwh); + w[0] = 1; + w[1] = wn4r; + if (nwh == 4) { + w[2] = cos(delta * 2); + w[3] = sin(delta * 2); + } else if (nwh > 4) { + makeipt(nw, ip); + w[2] = 0.5 / cos(delta * 2); + w[3] = 0.5 / cos(delta * 6); + for (j = 4; j < nwh; j += 4) { + w[j] = cos(delta * j); + w[j + 1] = sin(delta * j); + w[j + 2] = cos(3 * delta * j); + w[j + 3] = -sin(3 * delta * j); + } + } + nw0 = 0; + while (nwh > 2) { + nw1 = nw0 + nwh; + nwh >>= 1; + w[nw1] = 1; + w[nw1 + 1] = wn4r; + if (nwh == 4) { + wk1r = w[nw0 + 4]; + wk1i = w[nw0 + 5]; + w[nw1 + 2] = wk1r; + w[nw1 + 3] = wk1i; + } else if (nwh > 4) { + wk1r = w[nw0 + 4]; + wk3r = w[nw0 + 6]; + w[nw1 + 2] = 0.5 / wk1r; + w[nw1 + 3] = 0.5 / wk3r; + for (j = 4; j < nwh; j += 4) { + wk1r = w[nw0 + 2 * j]; + wk1i = w[nw0 + 2 * j + 1]; + wk3r = w[nw0 + 2 * j + 2]; + wk3i = w[nw0 + 2 * j + 3]; + w[nw1 + j] = wk1r; + w[nw1 + j + 1] = wk1i; + w[nw1 + j + 2] = wk3r; + w[nw1 + j + 3] = wk3i; + } + } + nw0 = nw1; + } + } +} + + +void makeipt(int nw, int *ip) +{ + int j, l, m, m2, p, q; + + ip[2] = 0; + ip[3] = 16; + m = 2; + for (l = nw; l > 32; l >>= 2) { + m2 = m << 1; + q = m2 << 3; + for (j = m; j < m2; j++) { + p = ip[j] << 2; + ip[m + j] = p; + ip[m2 + j] = p + q; + } + m = m2; + } +} + + +void makect(int nc, int *ip, float *c) +{ + int j, nch; + float delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atan(1.0) / nch; + c[0] = cos(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cos(delta * j); + c[nc - j] = 0.5 * sin(delta * j); + } + } +} + + +/* -------- child routines -------- */ + + +#ifdef USE_CDFT_PTHREADS +#define USE_CDFT_THREADS +#ifndef CDFT_THREADS_BEGIN_N +#define CDFT_THREADS_BEGIN_N 8192 +#endif +#ifndef CDFT_4THREADS_BEGIN_N +#define CDFT_4THREADS_BEGIN_N 65536 +#endif +#include <pthread.h> +#include <stdio.h> +#include <stdlib.h> +#define cdft_thread_t pthread_t +#define cdft_thread_create(thp,func,argp) { \ + if (pthread_create(thp, NULL, func, (void *) argp) != 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#define cdft_thread_wait(th) { \ + if (pthread_join(th, NULL) != 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#endif /* USE_CDFT_PTHREADS */ + + +#ifdef USE_CDFT_WINTHREADS +#define USE_CDFT_THREADS +#ifndef CDFT_THREADS_BEGIN_N +#define CDFT_THREADS_BEGIN_N 32768 +#endif +#ifndef CDFT_4THREADS_BEGIN_N +#define CDFT_4THREADS_BEGIN_N 524288 +#endif +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#define cdft_thread_t HANDLE +#define cdft_thread_create(thp,func,argp) { \ + DWORD thid; \ + *(thp) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) func, (LPVOID) argp, 0, &thid); \ + if (*(thp) == 0) { \ + fprintf(stderr, "cdft thread error\n"); \ + exit(1); \ + } \ +} +#define cdft_thread_wait(th) { \ + WaitForSingleObject(th, INFINITE); \ + CloseHandle(th); \ +} +#endif /* USE_CDFT_WINTHREADS */ + + +void cftfsub(int n, float *a, int *ip, int nw, float *w) +{ + void bitrv2(int n, int *ip, float *a); + void bitrv216(float *a); + void bitrv208(float *a); + void cftf1st(int n, float *a, float *w); + void cftrec4(int n, float *a, int nw, float *w); + void cftleaf(int n, int isplt, float *a, int nw, float *w); + void cftfx41(int n, float *a, int nw, float *w); + void cftf161(float *a, float *w); + void cftf081(float *a, float *w); + void cftf040(float *a); + void cftx020(float *a); +#ifdef USE_CDFT_THREADS + void cftrec4_th(int n, float *a, int nw, float *w); +#endif /* USE_CDFT_THREADS */ + + if (n > 8) { + if (n > 32) { + cftf1st(n, a, &w[nw - (n >> 2)]); +#ifdef USE_CDFT_THREADS + if (n > CDFT_THREADS_BEGIN_N) { + cftrec4_th(n, a, nw, w); + } else +#endif /* USE_CDFT_THREADS */ + if (n > 512) { + cftrec4(n, a, nw, w); + } else if (n > 128) { + cftleaf(n, 1, a, nw, w); + } else { + cftfx41(n, a, nw, w); + } + bitrv2(n, ip, a); + } else if (n == 32) { + cftf161(a, &w[nw - 8]); + bitrv216(a); + } else { + cftf081(a, w); + bitrv208(a); + } + } else if (n == 8) { + cftf040(a); + } else if (n == 4) { + cftx020(a); + } +} + + +void cftbsub(int n, float *a, int *ip, int nw, float *w) +{ + void bitrv2conj(int n, int *ip, float *a); + void bitrv216neg(float *a); + void bitrv208neg(float *a); + void cftb1st(int n, float *a, float *w); + void cftrec4(int n, float *a, int nw, float *w); + void cftleaf(int n, int isplt, float *a, int nw, float *w); + void cftfx41(int n, float *a, int nw, float *w); + void cftf161(float *a, float *w); + void cftf081(float *a, float *w); + void cftb040(float *a); + void cftx020(float *a); +#ifdef USE_CDFT_THREADS + void cftrec4_th(int n, float *a, int nw, float *w); +#endif /* USE_CDFT_THREADS */ + + if (n > 8) { + if (n > 32) { + cftb1st(n, a, &w[nw - (n >> 2)]); +#ifdef USE_CDFT_THREADS + if (n > CDFT_THREADS_BEGIN_N) { + cftrec4_th(n, a, nw, w); + } else +#endif /* USE_CDFT_THREADS */ + if (n > 512) { + cftrec4(n, a, nw, w); + } else if (n > 128) { + cftleaf(n, 1, a, nw, w); + } else { + cftfx41(n, a, nw, w); + } + bitrv2conj(n, ip, a); + } else if (n == 32) { + cftf161(a, &w[nw - 8]); + bitrv216neg(a); + } else { + cftf081(a, w); + bitrv208neg(a); + } + } else if (n == 8) { + cftb040(a); + } else if (n == 4) { + cftx020(a); + } +} + + +void bitrv2(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, nh, nm; + float xr, xi, yr, yi; + + m = 1; + for (l = n >> 2; l > 8; l >>= 2) { + m <<= 1; + } + nh = n >> 1; + nm = 4 * m; + if (l == 8) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + 2 * ip[m + k]; + k1 = 4 * k + 2 * ip[m + j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + 2 * ip[m + k]; + j1 = k1 + 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= 2; + k1 -= nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh + 2; + k1 += nh + 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh - nm; + k1 += 2 * nm - 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + ip[m + k]; + k1 = 4 * k + ip[m + j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + ip[m + k]; + j1 = k1 + 2; + k1 += nh; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } +} + + +void bitrv2conj(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, nh, nm; + float xr, xi, yr, yi; + + m = 1; + for (l = n >> 2; l > 8; l >>= 2) { + m <<= 1; + } + nh = n >> 1; + nm = 4 * m; + if (l == 8) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + 2 * ip[m + k]; + k1 = 4 * k + 2 * ip[m + j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + 2 * ip[m + k]; + j1 = k1 + 2; + k1 += nh; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + j1 += nm; + k1 += 2 * nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= 2; + k1 -= nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh + 2; + k1 += nh + 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh - nm; + k1 += 2 * nm - 2; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + } + } else { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 4 * j + ip[m + k]; + k1 = 4 * k + ip[m + j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nh; + k1 += 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += 2; + k1 += nh; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += nm; + k1 += nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nh; + k1 -= 2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 -= nm; + k1 -= nm; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 4 * k + ip[m + k]; + j1 = k1 + 2; + k1 += nh; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + j1 += nm; + k1 += nm; + a[j1 - 1] = -a[j1 - 1]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + a[k1 + 3] = -a[k1 + 3]; + } + } +} + + +void bitrv216(float *a) +{ + float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, + x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x7r = a[14]; + x7i = a[15]; + x8r = a[16]; + x8i = a[17]; + x10r = a[20]; + x10i = a[21]; + x11r = a[22]; + x11i = a[23]; + x12r = a[24]; + x12i = a[25]; + x13r = a[26]; + x13i = a[27]; + x14r = a[28]; + x14i = a[29]; + a[2] = x8r; + a[3] = x8i; + a[4] = x4r; + a[5] = x4i; + a[6] = x12r; + a[7] = x12i; + a[8] = x2r; + a[9] = x2i; + a[10] = x10r; + a[11] = x10i; + a[14] = x14r; + a[15] = x14i; + a[16] = x1r; + a[17] = x1i; + a[20] = x5r; + a[21] = x5i; + a[22] = x13r; + a[23] = x13i; + a[24] = x3r; + a[25] = x3i; + a[26] = x11r; + a[27] = x11i; + a[28] = x7r; + a[29] = x7i; +} + + +void bitrv216neg(float *a) +{ + float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, + x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, + x13r, x13i, x14r, x14i, x15r, x15i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x6r = a[12]; + x6i = a[13]; + x7r = a[14]; + x7i = a[15]; + x8r = a[16]; + x8i = a[17]; + x9r = a[18]; + x9i = a[19]; + x10r = a[20]; + x10i = a[21]; + x11r = a[22]; + x11i = a[23]; + x12r = a[24]; + x12i = a[25]; + x13r = a[26]; + x13i = a[27]; + x14r = a[28]; + x14i = a[29]; + x15r = a[30]; + x15i = a[31]; + a[2] = x15r; + a[3] = x15i; + a[4] = x7r; + a[5] = x7i; + a[6] = x11r; + a[7] = x11i; + a[8] = x3r; + a[9] = x3i; + a[10] = x13r; + a[11] = x13i; + a[12] = x5r; + a[13] = x5i; + a[14] = x9r; + a[15] = x9i; + a[16] = x1r; + a[17] = x1i; + a[18] = x14r; + a[19] = x14i; + a[20] = x6r; + a[21] = x6i; + a[22] = x10r; + a[23] = x10i; + a[24] = x2r; + a[25] = x2i; + a[26] = x12r; + a[27] = x12i; + a[28] = x4r; + a[29] = x4i; + a[30] = x8r; + a[31] = x8i; +} + + +void bitrv208(float *a) +{ + float x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i; + + x1r = a[2]; + x1i = a[3]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x6r = a[12]; + x6i = a[13]; + a[2] = x4r; + a[3] = x4i; + a[6] = x6r; + a[7] = x6i; + a[8] = x1r; + a[9] = x1i; + a[12] = x3r; + a[13] = x3i; +} + + +void bitrv208neg(float *a) +{ + float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, + x5r, x5i, x6r, x6i, x7r, x7i; + + x1r = a[2]; + x1i = a[3]; + x2r = a[4]; + x2i = a[5]; + x3r = a[6]; + x3i = a[7]; + x4r = a[8]; + x4i = a[9]; + x5r = a[10]; + x5i = a[11]; + x6r = a[12]; + x6i = a[13]; + x7r = a[14]; + x7i = a[15]; + a[2] = x7r; + a[3] = x7i; + a[4] = x3r; + a[5] = x3i; + a[6] = x5r; + a[7] = x5i; + a[8] = x1r; + a[9] = x1i; + a[10] = x6r; + a[11] = x6i; + a[12] = x2r; + a[13] = x2i; + a[14] = x4r; + a[15] = x4i; +} + + +void cftf1st(int n, float *a, float *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + float wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, + wd1r, wd1i, wd3r, wd3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = a[1] + a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = a[1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j2] = x1r - x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + csc1 = w[2]; + csc3 = w[3]; + wd1r = 1; + wd1i = 0; + wd3r = 1; + wd3i = 0; + k = 0; + for (j = 2; j < mh - 2; j += 4) { + k += 4; + wk1r = csc1 * (wd1r + w[k]); + wk1i = csc1 * (wd1i + w[k + 1]); + wk3r = csc3 * (wd3r + w[k + 2]); + wk3i = csc3 * (wd3i + w[k + 3]); + wd1r = w[k]; + wd1i = w[k + 1]; + wd3r = w[k + 2]; + wd3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = a[j + 1] + a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = a[j + 1] - a[j2 + 1]; + y0r = a[j + 2] + a[j2 + 2]; + y0i = a[j + 3] + a[j2 + 3]; + y1r = a[j + 2] - a[j2 + 2]; + y1i = a[j + 3] - a[j2 + 3]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 + 2] + a[j3 + 2]; + y2i = a[j1 + 3] + a[j3 + 3]; + y3r = a[j1 + 2] - a[j3 + 2]; + y3i = a[j1 + 3] - a[j3 + 3]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j + 2] = y0r + y2r; + a[j + 3] = y0i + y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j1 + 2] = y0r - y2r; + a[j1 + 3] = y0i - y2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r - y3i; + x0i = y1i + y3r; + a[j2 + 2] = wd1r * x0r - wd1i * x0i; + a[j2 + 3] = wd1r * x0i + wd1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + x0r = y1r + y3i; + x0i = y1i - y3r; + a[j3 + 2] = wd3r * x0r + wd3i * x0i; + a[j3 + 3] = wd3r * x0i - wd3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + y0r = a[j0 - 2] + a[j2 - 2]; + y0i = a[j0 - 1] + a[j2 - 1]; + y1r = a[j0 - 2] - a[j2 - 2]; + y1i = a[j0 - 1] - a[j2 - 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 - 2] + a[j3 - 2]; + y2i = a[j1 - 1] + a[j3 - 1]; + y3r = a[j1 - 2] - a[j3 - 2]; + y3i = a[j1 - 1] - a[j3 - 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j0 - 2] = y0r + y2r; + a[j0 - 1] = y0i + y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j1 - 2] = y0r - y2r; + a[j1 - 1] = y0i - y2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = y1r - y3i; + x0i = y1i + y3r; + a[j2 - 2] = wd1i * x0r - wd1r * x0i; + a[j2 - 1] = wd1i * x0i + wd1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + x0r = y1r + y3i; + x0i = y1i - y3r; + a[j3 - 2] = wd3i * x0r + wd3r * x0i; + a[j3 - 1] = wd3i * x0i - wd3r * x0r; + } + wk1r = csc1 * (wd1r + wn4r); + wk1i = csc1 * (wd1i + wn4r); + wk3r = csc3 * (wd3r - wn4r); + wk3i = csc3 * (wd3i - wn4r); + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0 - 2] + a[j2 - 2]; + x0i = a[j0 - 1] + a[j2 - 1]; + x1r = a[j0 - 2] - a[j2 - 2]; + x1i = a[j0 - 1] - a[j2 - 1]; + x2r = a[j1 - 2] + a[j3 - 2]; + x2i = a[j1 - 1] + a[j3 - 1]; + x3r = a[j1 - 2] - a[j3 - 2]; + x3i = a[j1 - 1] - a[j3 - 1]; + a[j0 - 2] = x0r + x2r; + a[j0 - 1] = x0i + x2i; + a[j1 - 2] = x0r - x2r; + a[j1 - 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2 - 2] = wk1r * x0r - wk1i * x0i; + a[j2 - 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 - 2] = wk3r * x0r + wk3i * x0i; + a[j3 - 1] = wk3r * x0i - wk3i * x0r; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); + x0r = a[j0 + 2] + a[j2 + 2]; + x0i = a[j0 + 3] + a[j2 + 3]; + x1r = a[j0 + 2] - a[j2 + 2]; + x1i = a[j0 + 3] - a[j2 + 3]; + x2r = a[j1 + 2] + a[j3 + 2]; + x2i = a[j1 + 3] + a[j3 + 3]; + x3r = a[j1 + 2] - a[j3 + 2]; + x3i = a[j1 + 3] - a[j3 + 3]; + a[j0 + 2] = x0r + x2r; + a[j0 + 3] = x0i + x2i; + a[j1 + 2] = x0r - x2r; + a[j1 + 3] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2 + 2] = wk1i * x0r - wk1r * x0i; + a[j2 + 3] = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3 + 2] = wk3i * x0r + wk3r * x0i; + a[j3 + 3] = wk3i * x0i - wk3r * x0r; +} + + +void cftb1st(int n, float *a, float *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + float wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, + wd1r, wd1i, wd3r, wd3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = -a[1] - a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = -a[1] + a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i - x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j2] = x1r + x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r - x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + csc1 = w[2]; + csc3 = w[3]; + wd1r = 1; + wd1i = 0; + wd3r = 1; + wd3i = 0; + k = 0; + for (j = 2; j < mh - 2; j += 4) { + k += 4; + wk1r = csc1 * (wd1r + w[k]); + wk1i = csc1 * (wd1i + w[k + 1]); + wk3r = csc3 * (wd3r + w[k + 2]); + wk3i = csc3 * (wd3i + w[k + 3]); + wd1r = w[k]; + wd1i = w[k + 1]; + wd3r = w[k + 2]; + wd3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = -a[j + 1] - a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = -a[j + 1] + a[j2 + 1]; + y0r = a[j + 2] + a[j2 + 2]; + y0i = -a[j + 3] - a[j2 + 3]; + y1r = a[j + 2] - a[j2 + 2]; + y1i = -a[j + 3] + a[j2 + 3]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 + 2] + a[j3 + 2]; + y2i = a[j1 + 3] + a[j3 + 3]; + y3r = a[j1 + 2] - a[j3 + 2]; + y3i = a[j1 + 3] - a[j3 + 3]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j + 2] = y0r + y2r; + a[j + 3] = y0i - y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j1 + 2] = y0r - y2r; + a[j1 + 3] = y0i + y2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = y1r + y3i; + x0i = y1i + y3r; + a[j2 + 2] = wd1r * x0r - wd1i * x0i; + a[j2 + 3] = wd1r * x0i + wd1i * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + x0r = y1r - y3i; + x0i = y1i - y3r; + a[j3 + 2] = wd3r * x0r + wd3i * x0i; + a[j3 + 3] = wd3r * x0i - wd3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = -a[j0 + 1] - a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = -a[j0 + 1] + a[j2 + 1]; + y0r = a[j0 - 2] + a[j2 - 2]; + y0i = -a[j0 - 1] - a[j2 - 1]; + y1r = a[j0 - 2] - a[j2 - 2]; + y1i = -a[j0 - 1] + a[j2 - 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + y2r = a[j1 - 2] + a[j3 - 2]; + y2i = a[j1 - 1] + a[j3 - 1]; + y3r = a[j1 - 2] - a[j3 - 2]; + y3i = a[j1 - 1] - a[j3 - 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i - x2i; + a[j0 - 2] = y0r + y2r; + a[j0 - 1] = y0i - y2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + a[j1 - 2] = y0r - y2r; + a[j1 - 1] = y0i + y2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = y1r + y3i; + x0i = y1i + y3r; + a[j2 - 2] = wd1i * x0r - wd1r * x0i; + a[j2 - 1] = wd1i * x0i + wd1r * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + x0r = y1r - y3i; + x0i = y1i - y3r; + a[j3 - 2] = wd3i * x0r + wd3r * x0i; + a[j3 - 1] = wd3i * x0i - wd3r * x0r; + } + wk1r = csc1 * (wd1r + wn4r); + wk1i = csc1 * (wd1i + wn4r); + wk3r = csc3 * (wd3r - wn4r); + wk3i = csc3 * (wd3i - wn4r); + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0 - 2] + a[j2 - 2]; + x0i = -a[j0 - 1] - a[j2 - 1]; + x1r = a[j0 - 2] - a[j2 - 2]; + x1i = -a[j0 - 1] + a[j2 - 1]; + x2r = a[j1 - 2] + a[j3 - 2]; + x2i = a[j1 - 1] + a[j3 - 1]; + x3r = a[j1 - 2] - a[j3 - 2]; + x3i = a[j1 - 1] - a[j3 - 1]; + a[j0 - 2] = x0r + x2r; + a[j0 - 1] = x0i - x2i; + a[j1 - 2] = x0r - x2r; + a[j1 - 1] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2 - 2] = wk1r * x0r - wk1i * x0i; + a[j2 - 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3 - 2] = wk3r * x0r + wk3i * x0i; + a[j3 - 1] = wk3r * x0i - wk3i * x0r; + x0r = a[j0] + a[j2]; + x0i = -a[j0 + 1] - a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = -a[j0 + 1] + a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i - x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); + x0r = a[j0 + 2] + a[j2 + 2]; + x0i = -a[j0 + 3] - a[j2 + 3]; + x1r = a[j0 + 2] - a[j2 + 2]; + x1i = -a[j0 + 3] + a[j2 + 3]; + x2r = a[j1 + 2] + a[j3 + 2]; + x2i = a[j1 + 3] + a[j3 + 3]; + x3r = a[j1 + 2] - a[j3 + 2]; + x3i = a[j1 + 3] - a[j3 + 3]; + a[j0 + 2] = x0r + x2r; + a[j0 + 3] = x0i - x2i; + a[j1 + 2] = x0r - x2r; + a[j1 + 3] = x0i + x2i; + x0r = x1r + x3i; + x0i = x1i + x3r; + a[j2 + 2] = wk1i * x0r - wk1r * x0i; + a[j2 + 3] = wk1i * x0i + wk1r * x0r; + x0r = x1r - x3i; + x0i = x1i - x3r; + a[j3 + 2] = wk3i * x0r + wk3r * x0i; + a[j3 + 3] = wk3i * x0i - wk3r * x0r; +} + + +#ifdef USE_CDFT_THREADS +struct cdft_arg_st { + int n0; + int n; + float *a; + int nw; + float *w; +}; +typedef struct cdft_arg_st cdft_arg_t; + + +void cftrec4_th(int n, float *a, int nw, float *w) +{ + void *cftrec1_th(void *p); + void *cftrec2_th(void *p); + int i, idiv4, m, nthread; + cdft_thread_t th[4]; + cdft_arg_t ag[4]; + + nthread = 2; + idiv4 = 0; + m = n >> 1; + if (n > CDFT_4THREADS_BEGIN_N) { + nthread = 4; + idiv4 = 1; + m >>= 1; + } + for (i = 0; i < nthread; i++) { + ag[i].n0 = n; + ag[i].n = m; + ag[i].a = &a[i * m]; + ag[i].nw = nw; + ag[i].w = w; + if (i != idiv4) { + cdft_thread_create(&th[i], cftrec1_th, &ag[i]); + } else { + cdft_thread_create(&th[i], cftrec2_th, &ag[i]); + } + } + for (i = 0; i < nthread; i++) { + cdft_thread_wait(th[i]); + } +} + + +void *cftrec1_th(void *p) +{ + int cfttree(int n, int j, int k, float *a, int nw, float *w); + void cftleaf(int n, int isplt, float *a, int nw, float *w); + void cftmdl1(int n, float *a, float *w); + int isplt, j, k, m, n, n0, nw; + float *a, *w; + + n0 = ((cdft_arg_t *) p)->n0; + n = ((cdft_arg_t *) p)->n; + a = ((cdft_arg_t *) p)->a; + nw = ((cdft_arg_t *) p)->nw; + w = ((cdft_arg_t *) p)->w; + m = n0; + while (m > 512) { + m >>= 2; + cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); + } + cftleaf(m, 1, &a[n - m], nw, w); + k = 0; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } + return (void *) 0; +} + + +void *cftrec2_th(void *p) +{ + int cfttree(int n, int j, int k, float *a, int nw, float *w); + void cftleaf(int n, int isplt, float *a, int nw, float *w); + void cftmdl2(int n, float *a, float *w); + int isplt, j, k, m, n, n0, nw; + float *a, *w; + + n0 = ((cdft_arg_t *) p)->n0; + n = ((cdft_arg_t *) p)->n; + a = ((cdft_arg_t *) p)->a; + nw = ((cdft_arg_t *) p)->nw; + w = ((cdft_arg_t *) p)->w; + k = 1; + m = n0; + while (m > 512) { + m >>= 2; + k <<= 2; + cftmdl2(m, &a[n - m], &w[nw - m]); + } + cftleaf(m, 0, &a[n - m], nw, w); + k >>= 1; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } + return (void *) 0; +} +#endif /* USE_CDFT_THREADS */ + + +void cftrec4(int n, float *a, int nw, float *w) +{ + int cfttree(int n, int j, int k, float *a, int nw, float *w); + void cftleaf(int n, int isplt, float *a, int nw, float *w); + void cftmdl1(int n, float *a, float *w); + int isplt, j, k, m; + + m = n; + while (m > 512) { + m >>= 2; + cftmdl1(m, &a[n - m], &w[nw - (m >> 1)]); + } + cftleaf(m, 1, &a[n - m], nw, w); + k = 0; + for (j = n - m; j > 0; j -= m) { + k++; + isplt = cfttree(m, j, k, a, nw, w); + cftleaf(m, isplt, &a[j - m], nw, w); + } +} + + +int cfttree(int n, int j, int k, float *a, int nw, float *w) +{ + void cftmdl1(int n, float *a, float *w); + void cftmdl2(int n, float *a, float *w); + int i, isplt, m; + + if ((k & 3) != 0) { + isplt = k & 1; + if (isplt != 0) { + cftmdl1(n, &a[j - n], &w[nw - (n >> 1)]); + } else { + cftmdl2(n, &a[j - n], &w[nw - n]); + } + } else { + m = n; + for (i = k; (i & 3) == 0; i >>= 2) { + m <<= 2; + } + isplt = i & 1; + if (isplt != 0) { + while (m > 128) { + cftmdl1(m, &a[j - m], &w[nw - (m >> 1)]); + m >>= 2; + } + } else { + while (m > 128) { + cftmdl2(m, &a[j - m], &w[nw - m]); + m >>= 2; + } + } + } + return isplt; +} + + +void cftleaf(int n, int isplt, float *a, int nw, float *w) +{ + void cftmdl1(int n, float *a, float *w); + void cftmdl2(int n, float *a, float *w); + void cftf161(float *a, float *w); + void cftf162(float *a, float *w); + void cftf081(float *a, float *w); + void cftf082(float *a, float *w); + + if (n == 512) { + cftmdl1(128, a, &w[nw - 64]); + cftf161(a, &w[nw - 8]); + cftf162(&a[32], &w[nw - 32]); + cftf161(&a[64], &w[nw - 8]); + cftf161(&a[96], &w[nw - 8]); + cftmdl2(128, &a[128], &w[nw - 128]); + cftf161(&a[128], &w[nw - 8]); + cftf162(&a[160], &w[nw - 32]); + cftf161(&a[192], &w[nw - 8]); + cftf162(&a[224], &w[nw - 32]); + cftmdl1(128, &a[256], &w[nw - 64]); + cftf161(&a[256], &w[nw - 8]); + cftf162(&a[288], &w[nw - 32]); + cftf161(&a[320], &w[nw - 8]); + cftf161(&a[352], &w[nw - 8]); + if (isplt != 0) { + cftmdl1(128, &a[384], &w[nw - 64]); + cftf161(&a[480], &w[nw - 8]); + } else { + cftmdl2(128, &a[384], &w[nw - 128]); + cftf162(&a[480], &w[nw - 32]); + } + cftf161(&a[384], &w[nw - 8]); + cftf162(&a[416], &w[nw - 32]); + cftf161(&a[448], &w[nw - 8]); + } else { + cftmdl1(64, a, &w[nw - 32]); + cftf081(a, &w[nw - 8]); + cftf082(&a[16], &w[nw - 8]); + cftf081(&a[32], &w[nw - 8]); + cftf081(&a[48], &w[nw - 8]); + cftmdl2(64, &a[64], &w[nw - 64]); + cftf081(&a[64], &w[nw - 8]); + cftf082(&a[80], &w[nw - 8]); + cftf081(&a[96], &w[nw - 8]); + cftf082(&a[112], &w[nw - 8]); + cftmdl1(64, &a[128], &w[nw - 32]); + cftf081(&a[128], &w[nw - 8]); + cftf082(&a[144], &w[nw - 8]); + cftf081(&a[160], &w[nw - 8]); + cftf081(&a[176], &w[nw - 8]); + if (isplt != 0) { + cftmdl1(64, &a[192], &w[nw - 32]); + cftf081(&a[240], &w[nw - 8]); + } else { + cftmdl2(64, &a[192], &w[nw - 64]); + cftf082(&a[240], &w[nw - 8]); + } + cftf081(&a[192], &w[nw - 8]); + cftf082(&a[208], &w[nw - 8]); + cftf081(&a[224], &w[nw - 8]); + } +} + + +void cftmdl1(int n, float *a, float *w) +{ + int j, j0, j1, j2, j3, k, m, mh; + float wn4r, wk1r, wk1i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + mh = n >> 3; + m = 2 * mh; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] + a[j2]; + x0i = a[1] + a[j2 + 1]; + x1r = a[0] - a[j2]; + x1i = a[1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + a[j2] = x1r - x3i; + a[j2 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + wn4r = w[1]; + k = 0; + for (j = 2; j < mh; j += 2) { + k += 4; + wk1r = w[k]; + wk1i = w[k + 1]; + wk3r = w[k + 2]; + wk3i = w[k + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] + a[j2]; + x0i = a[j + 1] + a[j2 + 1]; + x1r = a[j] - a[j2]; + x1i = a[j + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1r * x0r - wk1i * x0i; + a[j2 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r + wk3i * x0i; + a[j3 + 1] = wk3r * x0i - wk3i * x0r; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wk1i * x0r - wk1r * x0i; + a[j2 + 1] = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3i * x0r + wk3r * x0i; + a[j3 + 1] = wk3i * x0i - wk3r * x0r; + } + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] + a[j2]; + x0i = a[j0 + 1] + a[j2 + 1]; + x1r = a[j0] - a[j2]; + x1i = a[j0 + 1] - a[j2 + 1]; + x2r = a[j1] + a[j3]; + x2i = a[j1 + 1] + a[j3 + 1]; + x3r = a[j1] - a[j3]; + x3i = a[j1 + 1] - a[j3 + 1]; + a[j0] = x0r + x2r; + a[j0 + 1] = x0i + x2i; + a[j1] = x0r - x2r; + a[j1 + 1] = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j2] = wn4r * (x0r - x0i); + a[j2 + 1] = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = -wn4r * (x0r + x0i); + a[j3 + 1] = -wn4r * (x0i - x0r); +} + + +void cftmdl2(int n, float *a, float *w) +{ + int j, j0, j1, j2, j3, k, kr, m, mh; + float wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i; + + mh = n >> 3; + m = 2 * mh; + wn4r = w[1]; + j1 = m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[0] - a[j2 + 1]; + x0i = a[1] + a[j2]; + x1r = a[0] + a[j2 + 1]; + x1i = a[1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wn4r * (x2r - x2i); + y0i = wn4r * (x2i + x2r); + a[0] = x0r + y0r; + a[1] = x0i + y0i; + a[j1] = x0r - y0r; + a[j1 + 1] = x0i - y0i; + y0r = wn4r * (x3r - x3i); + y0i = wn4r * (x3i + x3r); + a[j2] = x1r - y0i; + a[j2 + 1] = x1i + y0r; + a[j3] = x1r + y0i; + a[j3 + 1] = x1i - y0r; + k = 0; + kr = 2 * m; + for (j = 2; j < mh; j += 2) { + k += 4; + wk1r = w[k]; + wk1i = w[k + 1]; + wk3r = w[k + 2]; + wk3i = w[k + 3]; + kr -= 4; + wd1i = w[kr]; + wd1r = w[kr + 1]; + wd3i = w[kr + 2]; + wd3r = w[kr + 3]; + j1 = j + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j] - a[j2 + 1]; + x0i = a[j + 1] + a[j2]; + x1r = a[j] + a[j2 + 1]; + x1i = a[j + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wk1r * x0r - wk1i * x0i; + y0i = wk1r * x0i + wk1i * x0r; + y2r = wd1r * x2r - wd1i * x2i; + y2i = wd1r * x2i + wd1i * x2r; + a[j] = y0r + y2r; + a[j + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wk3r * x1r + wk3i * x1i; + y0i = wk3r * x1i - wk3i * x1r; + y2r = wd3r * x3r + wd3i * x3i; + y2i = wd3r * x3i - wd3i * x3r; + a[j2] = y0r + y2r; + a[j2 + 1] = y0i + y2i; + a[j3] = y0r - y2r; + a[j3 + 1] = y0i - y2i; + j0 = m - j; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] - a[j2 + 1]; + x0i = a[j0 + 1] + a[j2]; + x1r = a[j0] + a[j2 + 1]; + x1i = a[j0 + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wd1i * x0r - wd1r * x0i; + y0i = wd1i * x0i + wd1r * x0r; + y2r = wk1i * x2r - wk1r * x2i; + y2i = wk1i * x2i + wk1r * x2r; + a[j0] = y0r + y2r; + a[j0 + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wd3i * x1r + wd3r * x1i; + y0i = wd3i * x1i - wd3r * x1r; + y2r = wk3i * x3r + wk3r * x3i; + y2i = wk3i * x3i - wk3r * x3r; + a[j2] = y0r + y2r; + a[j2 + 1] = y0i + y2i; + a[j3] = y0r - y2r; + a[j3 + 1] = y0i - y2i; + } + wk1r = w[m]; + wk1i = w[m + 1]; + j0 = mh; + j1 = j0 + m; + j2 = j1 + m; + j3 = j2 + m; + x0r = a[j0] - a[j2 + 1]; + x0i = a[j0 + 1] + a[j2]; + x1r = a[j0] + a[j2 + 1]; + x1i = a[j0 + 1] - a[j2]; + x2r = a[j1] - a[j3 + 1]; + x2i = a[j1 + 1] + a[j3]; + x3r = a[j1] + a[j3 + 1]; + x3i = a[j1 + 1] - a[j3]; + y0r = wk1r * x0r - wk1i * x0i; + y0i = wk1r * x0i + wk1i * x0r; + y2r = wk1i * x2r - wk1r * x2i; + y2i = wk1i * x2i + wk1r * x2r; + a[j0] = y0r + y2r; + a[j0 + 1] = y0i + y2i; + a[j1] = y0r - y2r; + a[j1 + 1] = y0i - y2i; + y0r = wk1i * x1r - wk1r * x1i; + y0i = wk1i * x1i + wk1r * x1r; + y2r = wk1r * x3r - wk1i * x3i; + y2i = wk1r * x3i + wk1i * x3r; + a[j2] = y0r - y2r; + a[j2 + 1] = y0i - y2i; + a[j3] = y0r + y2r; + a[j3 + 1] = y0i + y2i; +} + + +void cftfx41(int n, float *a, int nw, float *w) +{ + void cftf161(float *a, float *w); + void cftf162(float *a, float *w); + void cftf081(float *a, float *w); + void cftf082(float *a, float *w); + + if (n == 128) { + cftf161(a, &w[nw - 8]); + cftf162(&a[32], &w[nw - 32]); + cftf161(&a[64], &w[nw - 8]); + cftf161(&a[96], &w[nw - 8]); + } else { + cftf081(a, &w[nw - 8]); + cftf082(&a[16], &w[nw - 8]); + cftf081(&a[32], &w[nw - 8]); + cftf081(&a[48], &w[nw - 8]); + } +} + + +void cftf161(float *a, float *w) +{ + float wn4r, wk1r, wk1i, + x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, + y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, + y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; + + wn4r = w[1]; + wk1r = w[2]; + wk1i = w[3]; + x0r = a[0] + a[16]; + x0i = a[1] + a[17]; + x1r = a[0] - a[16]; + x1i = a[1] - a[17]; + x2r = a[8] + a[24]; + x2i = a[9] + a[25]; + x3r = a[8] - a[24]; + x3i = a[9] - a[25]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y4r = x0r - x2r; + y4i = x0i - x2i; + y8r = x1r - x3i; + y8i = x1i + x3r; + y12r = x1r + x3i; + y12i = x1i - x3r; + x0r = a[2] + a[18]; + x0i = a[3] + a[19]; + x1r = a[2] - a[18]; + x1i = a[3] - a[19]; + x2r = a[10] + a[26]; + x2i = a[11] + a[27]; + x3r = a[10] - a[26]; + x3i = a[11] - a[27]; + y1r = x0r + x2r; + y1i = x0i + x2i; + y5r = x0r - x2r; + y5i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y9r = wk1r * x0r - wk1i * x0i; + y9i = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + y13r = wk1i * x0r - wk1r * x0i; + y13i = wk1i * x0i + wk1r * x0r; + x0r = a[4] + a[20]; + x0i = a[5] + a[21]; + x1r = a[4] - a[20]; + x1i = a[5] - a[21]; + x2r = a[12] + a[28]; + x2i = a[13] + a[29]; + x3r = a[12] - a[28]; + x3i = a[13] - a[29]; + y2r = x0r + x2r; + y2i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y10r = wn4r * (x0r - x0i); + y10i = wn4r * (x0i + x0r); + x0r = x1r + x3i; + x0i = x1i - x3r; + y14r = wn4r * (x0r + x0i); + y14i = wn4r * (x0i - x0r); + x0r = a[6] + a[22]; + x0i = a[7] + a[23]; + x1r = a[6] - a[22]; + x1i = a[7] - a[23]; + x2r = a[14] + a[30]; + x2i = a[15] + a[31]; + x3r = a[14] - a[30]; + x3i = a[15] - a[31]; + y3r = x0r + x2r; + y3i = x0i + x2i; + y7r = x0r - x2r; + y7i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + y11r = wk1i * x0r - wk1r * x0i; + y11i = wk1i * x0i + wk1r * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + y15r = wk1r * x0r - wk1i * x0i; + y15i = wk1r * x0i + wk1i * x0r; + x0r = y12r - y14r; + x0i = y12i - y14i; + x1r = y12r + y14r; + x1i = y12i + y14i; + x2r = y13r - y15r; + x2i = y13i - y15i; + x3r = y13r + y15r; + x3i = y13i + y15i; + a[24] = x0r + x2r; + a[25] = x0i + x2i; + a[26] = x0r - x2r; + a[27] = x0i - x2i; + a[28] = x1r - x3i; + a[29] = x1i + x3r; + a[30] = x1r + x3i; + a[31] = x1i - x3r; + x0r = y8r + y10r; + x0i = y8i + y10i; + x1r = y8r - y10r; + x1i = y8i - y10i; + x2r = y9r + y11r; + x2i = y9i + y11i; + x3r = y9r - y11r; + x3i = y9i - y11i; + a[16] = x0r + x2r; + a[17] = x0i + x2i; + a[18] = x0r - x2r; + a[19] = x0i - x2i; + a[20] = x1r - x3i; + a[21] = x1i + x3r; + a[22] = x1r + x3i; + a[23] = x1i - x3r; + x0r = y5r - y7i; + x0i = y5i + y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + x0r = y5r + y7i; + x0i = y5i - y7r; + x3r = wn4r * (x0r - x0i); + x3i = wn4r * (x0i + x0r); + x0r = y4r - y6i; + x0i = y4i + y6r; + x1r = y4r + y6i; + x1i = y4i - y6r; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[10] = x0r - x2r; + a[11] = x0i - x2i; + a[12] = x1r - x3i; + a[13] = x1i + x3r; + a[14] = x1r + x3i; + a[15] = x1i - x3r; + x0r = y0r + y2r; + x0i = y0i + y2i; + x1r = y0r - y2r; + x1i = y0i - y2i; + x2r = y1r + y3r; + x2i = y1i + y3i; + x3r = y1r - y3r; + x3i = y1i - y3i; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x0r - x2r; + a[3] = x0i - x2i; + a[4] = x1r - x3i; + a[5] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; +} + + +void cftf162(float *a, float *w) +{ + float wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, + x0r, x0i, x1r, x1i, x2r, x2i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, + y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, + y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i; + + wn4r = w[1]; + wk1r = w[4]; + wk1i = w[5]; + wk3r = w[6]; + wk3i = -w[7]; + wk2r = w[8]; + wk2i = w[9]; + x1r = a[0] - a[17]; + x1i = a[1] + a[16]; + x0r = a[8] - a[25]; + x0i = a[9] + a[24]; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + y0r = x1r + x2r; + y0i = x1i + x2i; + y4r = x1r - x2r; + y4i = x1i - x2i; + x1r = a[0] + a[17]; + x1i = a[1] - a[16]; + x0r = a[8] + a[25]; + x0i = a[9] - a[24]; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + y8r = x1r - x2i; + y8i = x1i + x2r; + y12r = x1r + x2i; + y12i = x1i - x2r; + x0r = a[2] - a[19]; + x0i = a[3] + a[18]; + x1r = wk1r * x0r - wk1i * x0i; + x1i = wk1r * x0i + wk1i * x0r; + x0r = a[10] - a[27]; + x0i = a[11] + a[26]; + x2r = wk3i * x0r - wk3r * x0i; + x2i = wk3i * x0i + wk3r * x0r; + y1r = x1r + x2r; + y1i = x1i + x2i; + y5r = x1r - x2r; + y5i = x1i - x2i; + x0r = a[2] + a[19]; + x0i = a[3] - a[18]; + x1r = wk3r * x0r - wk3i * x0i; + x1i = wk3r * x0i + wk3i * x0r; + x0r = a[10] + a[27]; + x0i = a[11] - a[26]; + x2r = wk1r * x0r + wk1i * x0i; + x2i = wk1r * x0i - wk1i * x0r; + y9r = x1r - x2r; + y9i = x1i - x2i; + y13r = x1r + x2r; + y13i = x1i + x2i; + x0r = a[4] - a[21]; + x0i = a[5] + a[20]; + x1r = wk2r * x0r - wk2i * x0i; + x1i = wk2r * x0i + wk2i * x0r; + x0r = a[12] - a[29]; + x0i = a[13] + a[28]; + x2r = wk2i * x0r - wk2r * x0i; + x2i = wk2i * x0i + wk2r * x0r; + y2r = x1r + x2r; + y2i = x1i + x2i; + y6r = x1r - x2r; + y6i = x1i - x2i; + x0r = a[4] + a[21]; + x0i = a[5] - a[20]; + x1r = wk2i * x0r - wk2r * x0i; + x1i = wk2i * x0i + wk2r * x0r; + x0r = a[12] + a[29]; + x0i = a[13] - a[28]; + x2r = wk2r * x0r - wk2i * x0i; + x2i = wk2r * x0i + wk2i * x0r; + y10r = x1r - x2r; + y10i = x1i - x2i; + y14r = x1r + x2r; + y14i = x1i + x2i; + x0r = a[6] - a[23]; + x0i = a[7] + a[22]; + x1r = wk3r * x0r - wk3i * x0i; + x1i = wk3r * x0i + wk3i * x0r; + x0r = a[14] - a[31]; + x0i = a[15] + a[30]; + x2r = wk1i * x0r - wk1r * x0i; + x2i = wk1i * x0i + wk1r * x0r; + y3r = x1r + x2r; + y3i = x1i + x2i; + y7r = x1r - x2r; + y7i = x1i - x2i; + x0r = a[6] + a[23]; + x0i = a[7] - a[22]; + x1r = wk1i * x0r + wk1r * x0i; + x1i = wk1i * x0i - wk1r * x0r; + x0r = a[14] + a[31]; + x0i = a[15] - a[30]; + x2r = wk3i * x0r - wk3r * x0i; + x2i = wk3i * x0i + wk3r * x0r; + y11r = x1r + x2r; + y11i = x1i + x2i; + y15r = x1r - x2r; + y15i = x1i - x2i; + x1r = y0r + y2r; + x1i = y0i + y2i; + x2r = y1r + y3r; + x2i = y1i + y3i; + a[0] = x1r + x2r; + a[1] = x1i + x2i; + a[2] = x1r - x2r; + a[3] = x1i - x2i; + x1r = y0r - y2r; + x1i = y0i - y2i; + x2r = y1r - y3r; + x2i = y1i - y3i; + a[4] = x1r - x2i; + a[5] = x1i + x2r; + a[6] = x1r + x2i; + a[7] = x1i - x2r; + x1r = y4r - y6i; + x1i = y4i + y6r; + x0r = y5r - y7i; + x0i = y5i + y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[8] = x1r + x2r; + a[9] = x1i + x2i; + a[10] = x1r - x2r; + a[11] = x1i - x2i; + x1r = y4r + y6i; + x1i = y4i - y6r; + x0r = y5r + y7i; + x0i = y5i - y7r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[12] = x1r - x2i; + a[13] = x1i + x2r; + a[14] = x1r + x2i; + a[15] = x1i - x2r; + x1r = y8r + y10r; + x1i = y8i + y10i; + x2r = y9r - y11r; + x2i = y9i - y11i; + a[16] = x1r + x2r; + a[17] = x1i + x2i; + a[18] = x1r - x2r; + a[19] = x1i - x2i; + x1r = y8r - y10r; + x1i = y8i - y10i; + x2r = y9r + y11r; + x2i = y9i + y11i; + a[20] = x1r - x2i; + a[21] = x1i + x2r; + a[22] = x1r + x2i; + a[23] = x1i - x2r; + x1r = y12r - y14i; + x1i = y12i + y14r; + x0r = y13r + y15i; + x0i = y13i - y15r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[24] = x1r + x2r; + a[25] = x1i + x2i; + a[26] = x1r - x2r; + a[27] = x1i - x2i; + x1r = y12r + y14i; + x1i = y12i - y14r; + x0r = y13r - y15i; + x0i = y13i + y15r; + x2r = wn4r * (x0r - x0i); + x2i = wn4r * (x0i + x0r); + a[28] = x1r - x2i; + a[29] = x1i + x2r; + a[30] = x1r + x2i; + a[31] = x1i - x2r; +} + + +void cftf081(float *a, float *w) +{ + float wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[1]; + x0r = a[0] + a[8]; + x0i = a[1] + a[9]; + x1r = a[0] - a[8]; + x1i = a[1] - a[9]; + x2r = a[4] + a[12]; + x2i = a[5] + a[13]; + x3r = a[4] - a[12]; + x3i = a[5] - a[13]; + y0r = x0r + x2r; + y0i = x0i + x2i; + y2r = x0r - x2r; + y2i = x0i - x2i; + y1r = x1r - x3i; + y1i = x1i + x3r; + y3r = x1r + x3i; + y3i = x1i - x3r; + x0r = a[2] + a[10]; + x0i = a[3] + a[11]; + x1r = a[2] - a[10]; + x1i = a[3] - a[11]; + x2r = a[6] + a[14]; + x2i = a[7] + a[15]; + x3r = a[6] - a[14]; + x3i = a[7] - a[15]; + y4r = x0r + x2r; + y4i = x0i + x2i; + y6r = x0r - x2r; + y6i = x0i - x2i; + x0r = x1r - x3i; + x0i = x1i + x3r; + x2r = x1r + x3i; + x2i = x1i - x3r; + y5r = wn4r * (x0r - x0i); + y5i = wn4r * (x0r + x0i); + y7r = wn4r * (x2r - x2i); + y7i = wn4r * (x2r + x2i); + a[8] = y1r + y5r; + a[9] = y1i + y5i; + a[10] = y1r - y5r; + a[11] = y1i - y5i; + a[12] = y3r - y7i; + a[13] = y3i + y7r; + a[14] = y3r + y7i; + a[15] = y3i - y7r; + a[0] = y0r + y4r; + a[1] = y0i + y4i; + a[2] = y0r - y4r; + a[3] = y0i - y4i; + a[4] = y2r - y6i; + a[5] = y2i + y6r; + a[6] = y2r + y6i; + a[7] = y2i - y6r; +} + + +void cftf082(float *a, float *w) +{ + float wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, + y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, + y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i; + + wn4r = w[1]; + wk1r = w[2]; + wk1i = w[3]; + y0r = a[0] - a[9]; + y0i = a[1] + a[8]; + y1r = a[0] + a[9]; + y1i = a[1] - a[8]; + x0r = a[4] - a[13]; + x0i = a[5] + a[12]; + y2r = wn4r * (x0r - x0i); + y2i = wn4r * (x0i + x0r); + x0r = a[4] + a[13]; + x0i = a[5] - a[12]; + y3r = wn4r * (x0r - x0i); + y3i = wn4r * (x0i + x0r); + x0r = a[2] - a[11]; + x0i = a[3] + a[10]; + y4r = wk1r * x0r - wk1i * x0i; + y4i = wk1r * x0i + wk1i * x0r; + x0r = a[2] + a[11]; + x0i = a[3] - a[10]; + y5r = wk1i * x0r - wk1r * x0i; + y5i = wk1i * x0i + wk1r * x0r; + x0r = a[6] - a[15]; + x0i = a[7] + a[14]; + y6r = wk1i * x0r - wk1r * x0i; + y6i = wk1i * x0i + wk1r * x0r; + x0r = a[6] + a[15]; + x0i = a[7] - a[14]; + y7r = wk1r * x0r - wk1i * x0i; + y7i = wk1r * x0i + wk1i * x0r; + x0r = y0r + y2r; + x0i = y0i + y2i; + x1r = y4r + y6r; + x1i = y4i + y6i; + a[0] = x0r + x1r; + a[1] = x0i + x1i; + a[2] = x0r - x1r; + a[3] = x0i - x1i; + x0r = y0r - y2r; + x0i = y0i - y2i; + x1r = y4r - y6r; + x1i = y4i - y6i; + a[4] = x0r - x1i; + a[5] = x0i + x1r; + a[6] = x0r + x1i; + a[7] = x0i - x1r; + x0r = y1r - y3i; + x0i = y1i + y3r; + x1r = y5r - y7r; + x1i = y5i - y7i; + a[8] = x0r + x1r; + a[9] = x0i + x1i; + a[10] = x0r - x1r; + a[11] = x0i - x1i; + x0r = y1r + y3i; + x0i = y1i - y3r; + x1r = y5r + y7r; + x1i = y5i + y7i; + a[12] = x0r - x1i; + a[13] = x0i + x1r; + a[14] = x0r + x1i; + a[15] = x0i - x1r; +} + + +void cftf040(float *a) +{ + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[4]; + x0i = a[1] + a[5]; + x1r = a[0] - a[4]; + x1i = a[1] - a[5]; + x2r = a[2] + a[6]; + x2i = a[3] + a[7]; + x3r = a[2] - a[6]; + x3i = a[3] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[6] = x1r + x3i; + a[7] = x1i - x3r; +} + + +void cftb040(float *a) +{ + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[4]; + x0i = a[1] + a[5]; + x1r = a[0] - a[4]; + x1i = a[1] - a[5]; + x2r = a[2] + a[6]; + x2i = a[3] + a[7]; + x3r = a[2] - a[6]; + x3i = a[3] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[2] = x1r + x3i; + a[3] = x1i - x3r; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[6] = x1r - x3i; + a[7] = x1i + x3r; +} + + +void cftx020(float *a) +{ + float x0r, x0i; + + x0r = a[0] - a[2]; + x0i = a[1] - a[3]; + a[0] += a[2]; + a[1] += a[3]; + a[2] = x0r; + a[3] = x0i; +} + + +void rftfsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void rftbsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void dctsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[j] - wkr * a[k]; + a[j] = wkr * a[j] + wki * a[k]; + a[k] = xr; + } + a[m] *= c[0]; +} + + +void dstsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[k] - wkr * a[j]; + a[k] = wkr * a[k] + wki * a[j]; + a[j] = xr; + } + a[m] *= c[0]; +} + @@ -2911,6 +2911,48 @@ junk_load_comm_frame (int version_major, playItem_t *it, uint8_t *readptr, int s return 0; } +/* Parse RVA2 tag */ +/* Currently only supports tags wich set master volume and are labeled "track" + * or "album". Also only supports peak value if stored as 16 bits. */ +static int junk_id3v2_load_rva2 (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) { + char *rva_desc = readptr; + unsigned rva_desc_len = strnlen(rva_desc, synched_size); + + if(rva_desc_len == synched_size) { /* tag too short */ + return -1; + } + if(rva_desc_len != 5) { /* only support track or album labeled ones */ + return 0; + } + + if(synched_size < rva_desc_len + 1 + 4) return -1; /* at least 4 bytes after zero-terminated label */ + + uint8_t *rva_data = rva_desc + rva_desc_len + 1; + + uint8_t vol_type = rva_data[0]; + + if(vol_type != 1) return 0; + + int16_t volume_adjust = (int16_t)(((int16_t)rva_data[1] << 8) | rva_data[2]); /* this is little-endian safe :) */ + uint8_t peak_bits = rva_data[3]; + uint16_t peak_val = 0; + + if(peak_bits == 16 && synched_size >= rva_desc_len + 1 + 6) { + peak_val = (uint16_t)((rva_data[4] << 8) | rva_data[5]); + } + + if (!strcasecmp (rva_desc, "album")) { + pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMGAIN, (float)volume_adjust / 512.0); + if(peak_val) pl_set_item_replaygain (it, DDB_REPLAYGAIN_ALBUMPEAK, (float)peak_val / 32767.0); /* NOTE: this is a guess based on mp3gain 1.5.2 written tags */ + } + else if (!strcasecmp (rva_desc, "track")) { + pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKGAIN, (float)volume_adjust / 512.0); + if(peak_val) pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, (float)peak_val / 32767.0); + } + + return 0; +} + int junk_id3v2_load_txx (int version_major, playItem_t *it, uint8_t *readptr, int synched_size) { char *txx = convstr_id3v2 (version_major, *readptr, readptr+1, synched_size-1); @@ -3301,6 +3343,15 @@ junk_id3v2_read_full (playItem_t *it, DB_id3v2_tag_t *tag_store, DB_FILE *fp) { /*int res = */junk_load_comm_frame (version_major, it, readptr, synched_size); } + else if (it && !strcmp (frameid, "RVA2")) { + if (synched_size < 5) { + trace ("RVA2 frame is too short, skipped\n"); + readptr += sz; // bad tag + continue; + } + + /*int res = */junk_id3v2_load_rva2(version_major, it, readptr, synched_size); + } else if (it && !strcmp (frameid, "TXXX")) { if (synched_size < 2) { trace ("TXXX frame is too short, skipped\n"); @@ -2711,7 +2711,7 @@ pl_format_time (float t, char *dur, int size) { } } else { - strcpy (dur, "-:--"); + strcpy (dur, "∞"); } } @@ -340,6 +340,8 @@ static DB_functions_t deadbeef_api = { .pl_get_meta_raw = (int (*) (DB_playItem_t *it, const char *key, char *val, int size))pl_get_meta_raw, .plt_get_meta = (int (*) (ddb_playlist_t *handle, const char *key, char *val, int size))plt_get_meta, .pl_meta_exists = (int (*) (DB_playItem_t *it, const char *key))pl_meta_exists, + // FIXME ******* devel branch only ******* + .audio_get_waveform_data = audio_get_waveform_data, }; DB_functions_t *deadbeef = &deadbeef_api; diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index 7ba418fc..5a5a9800 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -132,6 +132,9 @@ queue_add (const char *fname, const char *artist, const char *album, int img_siz for (cover_query_t *q = queue; q; q = q->next) { if (!strcasecmp (artist, q->artist) || !strcasecmp (album, q->album)) { deadbeef->mutex_unlock (mutex); + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return; // already in queue } } @@ -169,6 +172,9 @@ queue_pop (void) { if (queue->album) { free (queue->album); } + if (queue->callback) { + queue->callback (NULL, NULL, NULL, queue->user_data); + } free (queue); } queue = next; @@ -1133,6 +1139,7 @@ fetcher_thread (void *none) } if (param->callback) { param->callback (param->fname, param->artist, param->album, param->user_data); + param->callback = NULL; } } queue_pop (); @@ -1187,16 +1194,25 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz if (!*artist || !*album) { //give up + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return size == -1 ? strdup (get_default_cover ()) : NULL; } if (!deadbeef->is_local_file (fname)) { + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return size == -1 ? strdup (get_default_cover ()) : NULL; } make_cache_path (path, sizeof (path), album, artist, size); char *p = find_image (path); if (p) { + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return p; } @@ -1215,6 +1231,9 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz else { int res = copy_file (unscaled_path, path, size); if (!res) { + if (callback) { + callback (NULL, NULL, NULL, user_data); + } return strdup (path); } } diff --git a/plugins/gtkui/Makefile.am b/plugins/gtkui/Makefile.am index 279b555b..b756dbfe 100644 --- a/plugins/gtkui/Makefile.am +++ b/plugins/gtkui/Makefile.am @@ -35,12 +35,12 @@ GTKUI_SOURCES = gtkui.c gtkui.h\ tagwritersettings.c tagwritersettings.h\ wingeom.c wingeom.h\ pluginconf.h\ + widgets.c widgets.h\ ddbseekbar.h ddbequalizer.h ddbcellrenderertextmultiline.h\ ddbseekbar.c ddbequalizer.c ddbcellrenderertextmultiline.c\ + gtkuigl.c gtkuigl.h\ $(SM_SOURCES) - - sdkdir = $(pkgincludedir) sdk_HEADERS = gtkui_api.h @@ -85,7 +85,7 @@ ddb_gui_GTK2_la_LIBADD = $(LDADD) -L$(GTK_ROOT_216)/lib -lgtk-x11-2.0 -lgdk-x11- ddb_gui_GTK2_la_CFLAGS = -std=c99 -I$(GTK_ROOT_216)/include/gtk-2.0 -I$(GTK_ROOT_216)/lib/gtk-2.0/include -I$(GTK_ROOT_216)/include/atk-1.0 -I$(GTK_ROOT_216)/include/cairo -I$(GTK_ROOT_216)/include/pango-1.0 -I$(GTK_ROOT_216)/include -I$(GTK_ROOT_216)/include/glib-2.0 -I$(GTK_ROOT_216)/lib/glib-2.0/include $(SM_CFLAGS) else -ddb_gui_GTK2_la_LIBADD = $(LDADD) $(GTK2_DEPS_LIBS) $(SM_LIBADD) +ddb_gui_GTK2_la_LIBADD = $(LDADD) $(GTK2_DEPS_LIBS) $(SM_LIBADD) -lGLU ddb_gui_GTK2_la_CFLAGS = -std=c99 $(GTK2_DEPS_CFLAGS) $(SM_CFLAGS) endif diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index 74b92e8e..2ac935e8 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -44,6 +44,7 @@ #include "drawing.h" #include "eq.h" #include "wingeom.h" +#include "widgets.h" //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -284,9 +285,8 @@ on_select_all1_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->pl_select_all (); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST); - pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); + DdbListview *pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (pl) { ddb_listview_refresh (pl, DDB_REFRESH_LIST); } @@ -387,9 +387,6 @@ on_mainwin_key_press_event (GtkWidget *widget, deadbeef->conf_set_int ("playlist.current", pl); } } - else { - ddb_listview_handle_keypress (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), event->keyval, event->state); - } return FALSE; } @@ -870,6 +867,8 @@ void on_toggle_column_headers_activate (GtkMenuItem *menuitem, gpointer user_data) { + // FIXME! + return; GtkWidget *playlist = lookup_widget (mainwin, "playlist"); if (playlist) { if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) { @@ -936,6 +935,8 @@ void on_toggle_tabs (GtkMenuItem *menuitem, gpointer user_data) { + // FIXME! + return; GtkWidget *ts = lookup_widget (mainwin, "tabstrip"); if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) { deadbeef->conf_set_int ("gtkui.tabs.visible", 0); @@ -981,9 +982,8 @@ on_deselect_all1_activate (GtkMenuItem *menuitem, it = next; } deadbeef->pl_unlock (); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST); - pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); + DdbListview *pl = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); if (pl) { ddb_listview_refresh (pl, DDB_REFRESH_LIST); } @@ -1008,8 +1008,7 @@ on_invert_selection1_activate (GtkMenuItem *menuitem, it = next; } deadbeef->pl_unlock (); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_LIST); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1121,7 +1120,7 @@ void on_jump_to_current_track1_activate (GtkMenuItem *menuitem, gpointer user_data) { - gtkui_focus_on_playing_track (); + deadbeef->sendmessage (DB_EV_TRACKFOCUSCURRENT, 0, 0, 0); } static GtkWidget *translatorswindow; @@ -1175,9 +1174,7 @@ on_sort_by_title_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%t", DDB_SORT_ASCENDING); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1189,9 +1186,7 @@ on_sort_by_track_nr_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%n", DDB_SORT_ASCENDING); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1203,9 +1198,7 @@ on_sort_by_album_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%b", DDB_SORT_ASCENDING); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1217,9 +1210,7 @@ on_sort_by_artist_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, "%a", DDB_SORT_ASCENDING); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1246,9 +1237,7 @@ on_sort_by_random_activate (GtkMenuItem *menuitem, deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -1282,12 +1271,18 @@ on_sort_by_custom_activate (GtkMenuItem *menuitem, deadbeef->plt_sort (plt, PL_MAIN, -1, fmt, order == 0 ? DDB_SORT_ASCENDING : DDB_SORT_DESCENDING); deadbeef->plt_unref (plt); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_clear_sort (pl); - ddb_listview_refresh (pl, DDB_REFRESH_LIST | DDB_LIST_CHANGED); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } gtk_widget_destroy (dlg); dlg = NULL; } +void +on_design_mode1_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + gboolean act = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)); + w_set_design_mode (act ? 1 : 0); +} + diff --git a/plugins/gtkui/callbacks.h b/plugins/gtkui/callbacks.h index 05f150e9..c0442b2e 100644 --- a/plugins/gtkui/callbacks.h +++ b/plugins/gtkui/callbacks.h @@ -1161,6 +1161,10 @@ on_convert8to16_toggled (GtkToggleButton *togglebutton, gpointer user_data); void +on_design_mode1_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void on_reset_autostop_toggled (GtkToggleButton *togglebutton, gpointer user_data); diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index e0451f5a..a2f1119a 100644 --- a/plugins/gtkui/coverart.c +++ b/plugins/gtkui/coverart.c @@ -45,6 +45,8 @@ typedef struct { typedef struct load_query_s { char *fname; int width; + void (*callback) (void *user_data); + void *user_data; struct load_query_s *next; } load_query_t; @@ -57,7 +59,7 @@ load_query_t *queue; load_query_t *tail; static void -queue_add (const char *fname, int width) { +queue_add (const char *fname, int width, void (*callback) (void *user_data), void *user_data) { deadbeef->mutex_lock (mutex); load_query_t *q; for (q = queue; q; q = q->next) { @@ -70,6 +72,8 @@ queue_add (const char *fname, int width) { memset (q, 0, sizeof (load_query_t)); q->fname = strdup (fname); q->width = width; + q->callback = callback; + q->user_data = user_data; if (tail) { tail->next = q; tail = q; @@ -108,7 +112,7 @@ loading_thread (void *none) { for (;;) { trace ("covercache: waiting for signal\n"); deadbeef->cond_wait (cond, mutex); - trace ("covercache: signal received\n"); + trace ("covercache: signal received (terminate=%d, queue=%p)\n", terminate, queue); deadbeef->mutex_unlock (mutex); while (!terminate && queue) { int cache_min = 0; @@ -144,6 +148,7 @@ loading_thread (void *none) { if (stat (queue->fname, &stat_buf) < 0) { trace ("failed to stat file %s\n", queue->fname); } + trace ("covercache: caching pixbuf for %s\n", queue->fname); GdkPixbuf *pixbuf = NULL; GError *error = NULL; pixbuf = gdk_pixbuf_new_from_file_at_scale (queue->fname, queue->width, queue->width, TRUE, &error); @@ -182,8 +187,12 @@ loading_thread (void *none) { struct stat stat_buf; deadbeef->mutex_unlock (mutex); } + + if (queue->callback) { + queue->callback (queue->user_data); + } queue_pop (); - g_idle_add (redraw_playlist_cb, NULL); + //g_idle_add (redraw_playlist_cb, NULL); } if (terminate) { break; @@ -191,20 +200,30 @@ loading_thread (void *none) { } } -void +typedef struct { + int width; + void (*callback)(void *user_data); + void *user_data; +} cover_avail_info_t; + +static void cover_avail_callback (const char *fname, const char *artist, const char *album, void *user_data) { + if (!fname) { + free (user_data); + return; + } + cover_avail_info_t *dt = user_data; // means requested image is now in disk cache // load it into main memory - GdkPixbuf *pb = get_cover_art (fname, artist, album, (intptr_t)user_data); + GdkPixbuf *pb = get_cover_art_callb (fname, artist, album, dt->width, dt->callback, dt->user_data); if (pb) { g_object_unref (pb); - // already in cache, redraw - g_idle_add (redraw_playlist_cb, NULL); } + free (dt); } static GdkPixbuf * -get_pixbuf (const char *fname, int width) { +get_pixbuf (const char *fname, int width, void (*callback)(void *user_data), void *user_data) { int requested_width = width; // find in cache deadbeef->mutex_lock (mutex); @@ -232,18 +251,45 @@ get_pixbuf (const char *fname, int width) { } #endif deadbeef->mutex_unlock (mutex); - queue_add (fname, width); + queue_add (fname, width, callback, user_data); return NULL; } +static void +redraw_playlist (void *user_data) { + g_idle_add (redraw_playlist_cb, NULL); +} + GdkPixbuf * get_cover_art (const char *fname, const char *artist, const char *album, int width) { if (!coverart_plugin) { return NULL; } - char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, (void *)(intptr_t)width); + cover_avail_info_t *dt = malloc (sizeof (cover_avail_info_t)); + dt->width = width; + dt->callback = redraw_playlist; + dt->user_data = NULL; + char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, (void*)dt); + if (image_fname) { + GdkPixbuf *pb = get_pixbuf (image_fname, width, redraw_playlist, NULL); + free (image_fname); + return pb; + } + return NULL; +} + +GdkPixbuf * +get_cover_art_callb (const char *fname, const char *artist, const char *album, int width, void (*callback) (void *user_data), void *user_data) { + if (!coverart_plugin) { + return NULL; + } + cover_avail_info_t *dt = malloc (sizeof (cover_avail_info_t)); + dt->width = width; + dt->callback = callback; + dt->user_data = user_data; + char *image_fname = coverart_plugin->get_album_art (fname, artist, album, -1, cover_avail_callback, dt); if (image_fname) { - GdkPixbuf *pb = get_pixbuf (image_fname, width); + GdkPixbuf *pb = get_pixbuf (image_fname, width, callback, user_data); free (image_fname); return pb; } diff --git a/plugins/gtkui/coverart.h b/plugins/gtkui/coverart.h index 490f62e1..a4914b6f 100644 --- a/plugins/gtkui/coverart.h +++ b/plugins/gtkui/coverart.h @@ -32,6 +32,10 @@ GdkPixbuf * get_cover_art (const char *fname, const char *artist, const char *album, int width); +GdkPixbuf * +get_cover_art_callb (const char *fname, const char *artist, const char *album, int width, void +(*cover_avail_callback) (void *user_data), void *user_data); + void coverart_reset_queue (void); diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.c b/plugins/gtkui/ddbcellrenderertextmultiline.c index f7e72cf6..81f77fce 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.c +++ b/plugins/gtkui/ddbcellrenderertextmultiline.c @@ -1,6 +1,3 @@ -/* ddbcellrenderertextmultiline.c generated by valac 0.14.0, the Vala compiler - * generated from ddbcellrenderertextmultiline.vala, do not modify */ - /* DeaDBeeF - ultimate music player for GNU/Linux systems with X11 Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> @@ -48,7 +45,7 @@ static gpointer ddb_cell_editable_text_view_parent_class = NULL; static GtkCellEditableIface* ddb_cell_editable_text_view_gtk_cell_editable_parent_iface = NULL; static gpointer ddb_cell_renderer_text_multiline_parent_class = NULL; -GType ddb_cell_editable_text_view_get_type (void) G_GNUC_CONST; +GType ddb_cell_editable_text_view_get_type (void); enum { DDB_CELL_EDITABLE_TEXT_VIEW_DUMMY_PROPERTY }; @@ -57,7 +54,7 @@ static void ddb_cell_editable_text_view_real_start_editing (GtkCellEditable* bas DdbCellEditableTextView* ddb_cell_editable_text_view_new (void); DdbCellEditableTextView* ddb_cell_editable_text_view_construct (GType object_type); static void ddb_cell_editable_text_view_finalize (GObject* obj); -GType ddb_cell_renderer_text_multiline_get_type (void) G_GNUC_CONST; +GType ddb_cell_renderer_text_multiline_get_type (void); #define DDB_CELL_RENDERER_TEXT_MULTILINE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DDB_TYPE_CELL_RENDERER_TEXT_MULTILINE, DdbCellRendererTextMultilinePrivate)) #define DDB_CELL_EDITABLE_TEXT_VIEW_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DDB_TYPE_CELL_EDITABLE_TEXT_VIEW, DdbCellEditableTextViewPrivate)) enum { @@ -122,12 +119,12 @@ static gboolean ddb_cell_editable_text_view_real_key_press_event (GtkWidget* bas return result; } - static void ddb_cell_editable_text_view_real_start_editing (GtkCellEditable* base, GdkEvent* event) { DdbCellEditableTextView * self; self = (DdbCellEditableTextView*) base; } + #if GTK_CHECK_VERSION(2,20,0) static void ddb_cell_editable_text_view_real_editing_canceled (GtkCellRenderer* base) { } @@ -217,6 +214,10 @@ static void ddb_cell_editable_text_view_class_init (DdbCellEditableTextViewClass g_object_class_install_property (gobject_class, PROP_EDITING_CANCELED, obj_properties[PROP_EDITING_CANCELED]); } +void ddb_cell_editable_text_view_start_editing (DdbCellEditableTextView* self, GdkEvent* event) { + g_return_if_fail (self != NULL); + g_return_if_fail (event != NULL); +} static void ddb_cell_editable_text_view_gtk_cell_editable_interface_init (GtkCellEditableIface * iface) { ddb_cell_editable_text_view_gtk_cell_editable_parent_iface = g_type_interface_peek_parent (iface); @@ -307,6 +308,7 @@ static void ddb_cell_renderer_text_multiline_gtk_cell_renderer_text_editing_done g_signal_emit_by_name ((GtkCellRendererText*) _tmp14_, "edited", _tmp16_, new_text); _g_free0 (new_text); _g_object_unref0 (buf); + _g_free0 (new_text); } diff --git a/plugins/gtkui/ddbcellrenderertextmultiline.h b/plugins/gtkui/ddbcellrenderertextmultiline.h index bdfec5ec..622a19a6 100644 --- a/plugins/gtkui/ddbcellrenderertextmultiline.h +++ b/plugins/gtkui/ddbcellrenderertextmultiline.h @@ -1,6 +1,3 @@ -/* ddbcellrenderertextmultiline.h generated by valac 0.14.0, the Vala compiler, do not modify */ - - #ifndef __DDBCELLRENDERERTEXTMULTILINE_H__ #define __DDBCELLRENDERERTEXTMULTILINE_H__ @@ -8,6 +5,7 @@ #include <gtk/gtk.h> #include <stdlib.h> #include <string.h> +#include <gdk/gdk.h> G_BEGIN_DECLS @@ -54,10 +52,11 @@ struct _DdbCellRendererTextMultilineClass { }; -GType ddb_cell_editable_text_view_get_type (void) G_GNUC_CONST; +GType ddb_cell_editable_text_view_get_type (void); +void ddb_cell_editable_text_view_start_editing (DdbCellEditableTextView* self, GdkEvent* event); DdbCellEditableTextView* ddb_cell_editable_text_view_new (void); DdbCellEditableTextView* ddb_cell_editable_text_view_construct (GType object_type); -GType ddb_cell_renderer_text_multiline_get_type (void) G_GNUC_CONST; +GType ddb_cell_renderer_text_multiline_get_type (void); DdbCellRendererTextMultiline* ddb_cell_renderer_text_multiline_new (void); DdbCellRendererTextMultiline* ddb_cell_renderer_text_multiline_construct (GType object_type); diff --git a/plugins/gtkui/ddbequalizer.c b/plugins/gtkui/ddbequalizer.c index 05f42547..02946aea 100644 --- a/plugins/gtkui/ddbequalizer.c +++ b/plugins/gtkui/ddbequalizer.c @@ -44,7 +44,6 @@ typedef struct _DdbEqualizerClass DdbEqualizerClass; typedef struct _DdbEqualizerPrivate DdbEqualizerPrivate; #define _gdk_cursor_unref0(var) ((var == NULL) ? NULL : (var = (gdk_cursor_unref (var), NULL))) #define _g_free0(var) (var = (g_free (var), NULL)) -#define _pango_font_description_free0(var) ((var == NULL) ? NULL : (var = (pango_font_description_free (var), NULL))) #define _g_object_unref0(var) ((var == NULL) ? NULL : (var = (g_object_unref (var), NULL))) #define _cairo_destroy0(var) ((var == NULL) ? NULL : (var = (cairo_destroy (var), NULL))) @@ -73,7 +72,7 @@ struct _DdbEqualizerPrivate { static gpointer ddb_equalizer_parent_class = NULL; -GType ddb_equalizer_get_type (void) G_GNUC_CONST; +GType ddb_equalizer_get_type (void); #define DDB_EQUALIZER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DDB_TYPE_EQUALIZER, DdbEqualizerPrivate)) enum { DDB_EQUALIZER_DUMMY_PROPERTY diff --git a/plugins/gtkui/ddbequalizer.h b/plugins/gtkui/ddbequalizer.h index c9e49988..02df70af 100644 --- a/plugins/gtkui/ddbequalizer.h +++ b/plugins/gtkui/ddbequalizer.h @@ -1,6 +1,3 @@ -/* ddbequalizer.h generated by valac 0.14.0, the Vala compiler, do not modify */ - - #ifndef __DDBEQUALIZER_H__ #define __DDBEQUALIZER_H__ diff --git a/plugins/gtkui/ddblistview.c b/plugins/gtkui/ddblistview.c index 19493806..557da26b 100644 --- a/plugins/gtkui/ddblistview.c +++ b/plugins/gtkui/ddblistview.c @@ -275,6 +275,14 @@ ddb_listview_motion_notify_event (GtkWidget *widget, GdkEventMotion *event, gpointer user_data); +gboolean +ddb_listview_list_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data); + +gboolean +ddb_listview_list_focus_in_event (GtkWidget *widget, GdkEvent *event, gpointer user_data); + +gboolean +ddb_listview_list_focus_out_event (GtkWidget *widget, GdkEvent *event, gpointer user_data); static void ddb_listview_class_init(DdbListviewClass *class) @@ -370,7 +378,9 @@ ddb_listview_init(DdbListview *listview) listview->list = gtk_drawing_area_new (); gtk_widget_show (listview->list); gtk_box_pack_start (GTK_BOX (vbox), listview->list, TRUE, TRUE, 0); - int events = GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK; + gtk_widget_set_can_focus (listview->list, TRUE); + gtk_widget_set_can_default (listview->list, TRUE); + int events = GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_FOCUS_CHANGE_MASK; #if GTK_CHECK_VERSION(3,0,0) events |= GDK_SCROLL_MASK; #endif @@ -413,7 +423,7 @@ ddb_listview_init(DdbListview *listview) g_signal_connect ((gpointer) listview->header, "motion_notify_event", G_CALLBACK (ddb_listview_header_motion_notify_event), NULL); - g_signal_connect ((gpointer) listview->header, "button_press_event", + g_signal_connect_after ((gpointer) listview->header, "button_press_event", G_CALLBACK (ddb_listview_header_button_press_event), NULL); g_signal_connect ((gpointer) listview->header, "button_release_event", @@ -431,7 +441,7 @@ ddb_listview_init(DdbListview *listview) g_signal_connect ((gpointer) listview->list, "realize", G_CALLBACK (ddb_listview_list_realize), NULL); - g_signal_connect ((gpointer) listview->list, "button_press_event", + g_signal_connect_after ((gpointer) listview->list, "button_press_event", G_CALLBACK (ddb_listview_list_button_press_event), NULL); g_signal_connect ((gpointer) listview->list, "scroll_event", @@ -470,6 +480,10 @@ ddb_listview_init(DdbListview *listview) g_signal_connect ((gpointer) listview->hscrollbar, "value_changed", G_CALLBACK (ddb_listview_hscroll_value_changed), NULL); + + g_signal_connect ((gpointer)listview->list, "key_press_event", G_CALLBACK (ddb_listview_list_key_press_event), NULL); + g_signal_connect ((gpointer)listview->list, "focus_in_event", G_CALLBACK (ddb_listview_list_focus_in_event), NULL); + g_signal_connect ((gpointer)listview->list, "focus_out_event", G_CALLBACK (ddb_listview_list_focus_out_event), NULL); } GtkWidget * ddb_listview_new() @@ -1271,7 +1285,7 @@ ddb_listview_list_render_row_background (DdbListview *ps, cairo_t *cr, DdbListvi if (gtk_widget_get_style (treeview)->depth == -1) { return; // drawing was called too early } - GTK_WIDGET_SET_FLAGS (GTK_WIDGET (treeview), GTK_HAS_FOCUS); + gtk_widget_set_can_focus (GTK_WIDGET (treeview), TRUE); #endif } int sel = it && ps->binding->is_selected (it); @@ -1473,6 +1487,7 @@ ddb_listview_click_selection (DdbListview *ps, int ex, int ey, DdbListviewGroup UNREF (it); } deadbeef->pl_unlock (); + deadbeef->sendmessage (DB_EV_SELCHANGED, 0, deadbeef->plt_get_curr_idx (), PL_MAIN); } // {{{ expected behaviour for mouse1 without modifiers: @@ -2580,7 +2595,7 @@ ddb_listview_header_button_press_event (GtkWidget *widget, } ps->prev_header_x = -1; ps->last_header_motion_ev = -1; - return FALSE; + return TRUE; } gboolean @@ -2730,6 +2745,7 @@ ddb_listview_list_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { + gtk_widget_grab_focus (widget); DdbListview *ps = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); if (event->button == 1) { ddb_listview_list_mouse1_pressed (ps, event->state, event->x, event->y, event->type); @@ -2771,7 +2787,7 @@ ddb_listview_list_button_press_event (GtkWidget *widget, UNREF (it); } } - return FALSE; + return TRUE; } gboolean @@ -3138,3 +3154,23 @@ ddb_listview_clear_sort (DdbListview *listview) { } gtk_widget_queue_draw (listview->header); } + +gboolean +ddb_listview_list_key_press_event (GtkWidget *widget, GdkEventKey *event, gpointer user_data) { + DdbListview *listview = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); + ddb_listview_handle_keypress (listview, event->keyval, event->state); + return FALSE; + +} + +gboolean +ddb_listview_list_focus_in_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { + gtk_widget_set_can_focus (widget, TRUE); + return FALSE; +} + +gboolean +ddb_listview_list_focus_out_event (GtkWidget *widget, GdkEvent *event, gpointer user_data) { + gtk_widget_set_can_focus (widget, FALSE); + return FALSE; +} diff --git a/plugins/gtkui/ddbseekbar.c b/plugins/gtkui/ddbseekbar.c index bcb8b7fe..6f0d01fe 100644 --- a/plugins/gtkui/ddbseekbar.c +++ b/plugins/gtkui/ddbseekbar.c @@ -48,7 +48,7 @@ struct _DdbSeekbarClass { static gpointer ddb_seekbar_parent_class = NULL; -GType ddb_seekbar_get_type (void) G_GNUC_CONST; +GType ddb_seekbar_get_type (void); enum { DDB_SEEKBAR_DUMMY_PROPERTY }; @@ -148,6 +148,26 @@ static gboolean ddb_seekbar_real_motion_notify_event (GtkWidget* base, GdkEventM _tmp0_ = *event; _tmp1_ = on_seekbar_motion_notify_event ((GtkWidget*) self, &_tmp0_); result = _tmp1_; + + DB_playItem_t *trk = deadbeef->streamer_get_playing_track (); + if (trk) { + GtkAllocation a; + gtk_widget_get_allocation (base, &a); + float time = (event->x - a.x) * deadbeef->pl_get_item_duration (trk) / (a.width); + if (time < 0) { + time = 0; + } + deadbeef->pl_item_unref (trk); + char s[1000]; + int hr = time/360; + int mn = (time-hr*360)/60; + int sc = time-hr*360-mn*60; + snprintf (s, sizeof (s), "%02d:%02d:%02d", hr, mn, sc); + + printf ("set tooltip text %s\n", s); + gtk_widget_set_tooltip_text (base, s); + gtk_widget_trigger_tooltip_query (base); + } return result; } @@ -164,8 +184,8 @@ static gboolean ddb_seekbar_real_configure_event (GtkWidget* base, GdkEventConfi DdbSeekbar* ddb_seekbar_construct (GType object_type) { - DdbSeekbar * self = NULL; - self = (DdbSeekbar*) gtk_widget_new (object_type, NULL); + DdbSeekbar * self; + self = g_object_newv (object_type, 0, NULL); return self; } @@ -206,6 +226,7 @@ static void ddb_seekbar_class_init (DdbSeekbarClass * klass) { static void ddb_seekbar_instance_init (DdbSeekbar * self) { gtk_widget_set_has_window ((GtkWidget*) self, FALSE); + gtk_widget_set_has_tooltip ((GtkWidget*) self, TRUE); } diff --git a/plugins/gtkui/ddbseekbar.h b/plugins/gtkui/ddbseekbar.h index 71b753c0..ee455087 100644 --- a/plugins/gtkui/ddbseekbar.h +++ b/plugins/gtkui/ddbseekbar.h @@ -1,6 +1,3 @@ -/* ddbseekbar.h generated by valac 0.14.0, the Vala compiler, do not modify */ - - #ifndef __DDBSEEKBAR_H__ #define __DDBSEEKBAR_H__ @@ -31,7 +28,7 @@ struct _DdbSeekbarClass { }; -GType ddb_seekbar_get_type (void) G_GNUC_CONST; +GType ddb_seekbar_get_type (void); DdbSeekbar* ddb_seekbar_new (void); DdbSeekbar* ddb_seekbar_construct (GType object_type); diff --git a/plugins/gtkui/ddbtabstrip.c b/plugins/gtkui/ddbtabstrip.c index b1b18b3d..f367b0f8 100644 --- a/plugins/gtkui/ddbtabstrip.c +++ b/plugins/gtkui/ddbtabstrip.c @@ -26,7 +26,7 @@ #include "gtkui.h" #include "interface.h" #include "support.h" -#include "ddblistview.h" +#include "mainplaylist.h" #define GLADE_HOOKUP_OBJECT(component,widget,name) \ g_object_set_data_full (G_OBJECT (component), name, \ @@ -280,7 +280,7 @@ on_tabstrip_drag_data_received (GtkWidget *widget, memcpy (mem, ptr, len); mem[len] = 0; // we don't pass control structure, but there's only one drag-drop view currently - ps->binding->external_drag_n_drop (NULL, mem, len); + gtkui_receive_fm_drop (NULL, mem, len); } else if (target_type == 1) { uint32_t *d= (uint32_t *)ptr; @@ -289,7 +289,7 @@ on_tabstrip_drag_data_received (GtkWidget *widget, int length = (len/4)-1; ddb_playlist_t *p = deadbeef->plt_get_for_idx (plt); if (p) { - ps->binding->drag_n_drop (NULL, p, d, length, gdk_drag_context_get_selected_action (drag_context) == GDK_ACTION_COPY ? 1 : 0); + main_drag_n_drop (NULL, p, d, length, gdk_drag_context_get_selected_action (drag_context) == GDK_ACTION_COPY ? 1 : 0); deadbeef->plt_unref (p); } } @@ -301,16 +301,16 @@ on_tabstrip_drag_leave (GtkWidget *widget, GdkDragContext *drag_context, guint time) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_list_drag_leave (pl->list, drag_context, time, NULL); +// DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// ddb_listview_list_drag_leave (pl->list, drag_context, time, NULL); } void on_tabstrip_drag_end (GtkWidget *widget, GdkDragContext *drag_context) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_list_drag_end (pl->list, drag_context, NULL); +// DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// ddb_listview_list_drag_end (pl->list, drag_context, NULL); } GtkWidget * ddb_tabstrip_new() { @@ -866,8 +866,6 @@ on_remove_playlist1_activate (GtkMenuItem *menuitem, { if (tab_clicked != -1) { deadbeef->plt_remove (tab_clicked); - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); int playlist = deadbeef->plt_get_curr_idx (); deadbeef->conf_set_int ("playlist.current", playlist); @@ -1141,7 +1139,7 @@ on_tabstrip_button_press_event(GtkWidget *widget, ts->scroll_direction = -1; ts->scroll_timer = g_timeout_add (300, tabstrip_scroll_cb, ts); } - return FALSE; + return TRUE; } else if (event->x >= a.width - arrow_widget_width) { if (event->type == GDK_BUTTON_PRESS) { @@ -1149,7 +1147,7 @@ on_tabstrip_button_press_event(GtkWidget *widget, ts->scroll_direction = 1; ts->scroll_timer = g_timeout_add (300, tabstrip_scroll_cb, ts); } - return FALSE; + return TRUE; } } if (tab_clicked != -1) { @@ -1162,9 +1160,9 @@ on_tabstrip_button_press_event(GtkWidget *widget, if (playlist != -1) { gtkui_playlist_set_curr (playlist); } - return FALSE; + return TRUE; } - return FALSE; + return TRUE; } // adjust scroll if clicked tab spans border @@ -1200,21 +1198,19 @@ on_tabstrip_button_press_event(GtkWidget *widget, if (playlist != -1) { gtkui_playlist_set_curr (playlist); } - return FALSE; + return TRUE; } else if (deadbeef->conf_get_int ("gtkui.mmb_delete_playlist", 1)) { if (tab_clicked != -1) { deadbeef->plt_remove (tab_clicked); // force invalidation of playlist cache - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); int playlist = deadbeef->plt_get_curr_idx (); deadbeef->conf_set_int ("playlist.current", playlist); } } } - return FALSE; + return TRUE; } diff --git a/plugins/gtkui/deadbeef.glade b/plugins/gtkui/deadbeef.glade index 18f7b6e9..907a35be 100644 --- a/plugins/gtkui/deadbeef.glade +++ b/plugins/gtkui/deadbeef.glade @@ -453,6 +453,16 @@ <signal name="activate" handler="on_toggle_eq" last_modification_time="Sat, 20 Mar 2010 12:28:50 GMT"/> </widget> </child> + + <child> + <widget class="GtkCheckMenuItem" id="design_mode1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Design mode</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_design_mode1_activate" last_modification_time="Fri, 17 Jun 2011 19:10:50 GMT"/> + </widget> + </child> </widget> </child> </widget> @@ -938,37 +948,6 @@ </child> <child> - <widget class="Custom" id="tabstrip"> - <property name="height_request">24</property> - <property name="visible">True</property> - <property name="creation_function">create_tabstrip_widget</property> - <property name="int1">0</property> - <property name="int2">0</property> - <property name="last_modification_time">Thu, 18 Feb 2010 18:05:36 GMT</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="Custom" id="playlist"> - <property name="visible">True</property> - <property name="creation_function">create_ddb_listview_widget</property> - <property name="int1">0</property> - <property name="int2">0</property> - <property name="last_modification_time">Sat, 13 Feb 2010 20:26:03 GMT</property> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> <widget class="GtkVBox" id="plugins_bottom_vbox"> <property name="visible">True</property> <property name="homogeneous">False</property> @@ -980,8 +959,8 @@ </widget> <packing> <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> + <property name="expand">True</property> + <property name="fill">True</property> </packing> </child> diff --git a/plugins/gtkui/fileman.c b/plugins/gtkui/fileman.c index bd52b4ab..72a5dfa8 100644 --- a/plugins/gtkui/fileman.c +++ b/plugins/gtkui/fileman.c @@ -111,17 +111,15 @@ open_files_worker (void *data) { gtkpl_add_files (lst); deadbeef->pl_save_all (); deadbeef->conf_save (); - gtkui_playlist_changed (); - extern GtkWidget *mainwin; - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_set_cursor (pl, 0); + deadbeef->pl_set_cursor (PL_MAIN, 0); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); deadbeef->sendmessage (DB_EV_PLAY_CURRENT, 0, 1, 0); } void gtkui_open_files (struct _GSList *lst) { deadbeef->pl_clear (); - playlist_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); intptr_t tid = deadbeef->thread_start (open_files_worker, lst); deadbeef->thread_detach (tid); @@ -171,13 +169,14 @@ strcopy_special (char *dest, const char *src, int len) { static gboolean set_dnd_cursor_idle (gpointer data) { - DdbListview *listview = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); if (!data) { - ddb_listview_set_cursor (listview, -1); + deadbeef->pl_set_cursor (PL_MAIN, -1); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); return FALSE; } int cursor = deadbeef->pl_get_idx_of (DB_PLAYITEM (data)); - ddb_listview_set_cursor (listview, cursor); + deadbeef->pl_set_cursor (PL_MAIN, cursor); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); return FALSE; } @@ -189,7 +188,6 @@ gtkpl_add_fm_dropped_files (DB_playItem_t *drop_before, char *ptr, int length) { deadbeef->plt_unref (plt); return; } - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); DdbListviewIter first = NULL; DdbListviewIter after = NULL; diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index b5286536..ff0a1bf2 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -29,7 +29,6 @@ #include "../../gettext.h" #include "gtkui.h" #include "ddblistview.h" -#include "mainplaylist.h" #include "search.h" #include "progress.h" #include "interface.h" @@ -47,6 +46,8 @@ #include "pluginconf.h" #include "gtkui_api.h" #include "wingeom.h" +#include "widgets.h" +#include "X11/Xlib.h" #ifdef EGG_SM_CLIENT_BACKEND_XSMP #include "smclient/eggsmclient.h" #endif @@ -351,7 +352,7 @@ activate_cb (gpointer nothing) { } void -redraw_queued_tracks (DdbListview *pl, int list) { +redraw_queued_tracks (DdbListview *pl) { DB_playItem_t *it; int idx = 0; deadbeef->pl_lock (); @@ -366,14 +367,14 @@ redraw_queued_tracks (DdbListview *pl, int list) { deadbeef->pl_unlock (); } -static gboolean -redraw_queued_tracks_cb (gpointer nothing) { +gboolean +redraw_queued_tracks_cb (gpointer plt) { + DdbListview *list = plt; int iconified = gdk_window_get_state(gtk_widget_get_window(mainwin)) & GDK_WINDOW_STATE_ICONIFIED; if (!gtk_widget_get_visible (mainwin) || iconified) { return FALSE; } - redraw_queued_tracks (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), PL_MAIN); - redraw_queued_tracks (DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")), PL_SEARCH); + redraw_queued_tracks (list); return FALSE; } @@ -390,7 +391,12 @@ gtkpl_songchanged_wrapper (DB_playItem_t *from, DB_playItem_t *to) { } g_idle_add (update_win_title_idle, ft); g_idle_add (redraw_seekbar_cb, NULL); - g_idle_add (redraw_queued_tracks_cb, NULL); + if (searchwin && gtk_widget_get_window (searchwin)) { + int iconified = gdk_window_get_state(gtk_widget_get_window (searchwin)) & GDK_WINDOW_STATE_ICONIFIED; + if (gtk_widget_get_visible (searchwin) && !iconified) { + g_idle_add (redraw_queued_tracks_cb, DDB_LISTVIEW (lookup_widget (searchwin, "searchlist"))); + } + } } void @@ -429,8 +435,8 @@ trackinfochanged_wrapper (DdbListview *playlist, DB_playItem_t *track, int iter) void gtkui_trackinfochanged (DB_playItem_t *track) { - GtkWidget *playlist = lookup_widget (mainwin, "playlist"); - trackinfochanged_wrapper (DDB_LISTVIEW (playlist), track, PL_MAIN); +// GtkWidget *playlist = lookup_widget (mainwin, "playlist"); +// trackinfochanged_wrapper (DDB_LISTVIEW (playlist), track, PL_MAIN); if (searchwin && gtk_widget_get_visible (searchwin)) { GtkWidget *search = lookup_widget (searchwin, "searchlist"); @@ -455,22 +461,10 @@ trackinfochanged_cb (gpointer data) { return FALSE; } -static gboolean -paused_cb (gpointer nothing) { - DB_playItem_t *curr = deadbeef->streamer_get_playing_track (); - if (curr) { - int idx = deadbeef->pl_get_idx_of (curr); - GtkWidget *playlist = lookup_widget (mainwin, "playlist"); - ddb_listview_draw_row (DDB_LISTVIEW (playlist), idx, (DdbListviewIter)curr); - deadbeef->pl_item_unref (curr); - } - return FALSE; -} - void playlist_refresh (void) { - DdbListview *ps = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); +// DdbListview *ps = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// ddb_listview_refresh (ps, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); search_refresh (); } @@ -487,26 +481,6 @@ gtkui_playlist_changed (void) { static gboolean playlistswitch_cb (gpointer none) { - GtkWidget *tabstrip = lookup_widget (mainwin, "tabstrip"); - int curr = deadbeef->plt_get_curr_idx (); - char conf[100]; - snprintf (conf, sizeof (conf), "playlist.scroll.%d", curr); - int scroll = deadbeef->conf_get_int (conf, 0); - snprintf (conf, sizeof (conf), "playlist.cursor.%d", curr); - int cursor = deadbeef->conf_get_int (conf, -1); - ddb_tabstrip_refresh (DDB_TABSTRIP (tabstrip)); - DdbListview *listview = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - deadbeef->pl_set_cursor (PL_MAIN, cursor); - if (cursor != -1) { - DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (cursor, PL_MAIN); - if (it) { - deadbeef->pl_set_selected (it, 1); - deadbeef->pl_item_unref (it); - } - } - - ddb_listview_refresh (listview, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); - ddb_listview_set_vscroll (listview, scroll); search_refresh (); return FALSE; } @@ -840,40 +814,6 @@ on_add_location_activate (GtkMenuItem *menuitem, gtk_widget_destroy (dlg); } -static void -songchanged (DdbListview *ps, DB_playItem_t *from, DB_playItem_t *to) { - int to_idx = -1; - if (!ddb_listview_is_scrolling (ps) && to) { - int cursor_follows_playback = deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 0); - int scroll_follows_playback = deadbeef->conf_get_int ("playlist.scroll.followplayback", 0); - int plt = deadbeef->streamer_get_current_playlist (); - if (plt != -1) { - if (cursor_follows_playback && plt != deadbeef->plt_get_curr_idx ()) { - deadbeef->plt_set_curr_idx (plt); - } - to_idx = deadbeef->pl_get_idx_of (to); - if (to_idx != -1) { - if (cursor_follows_playback) { - ddb_listview_set_cursor_noscroll (ps, to_idx); - } - if (scroll_follows_playback && plt == deadbeef->plt_get_curr_idx ()) { - ddb_listview_scroll_to (ps, to_idx); - } - } - } - } - - if (from) { - int idx = deadbeef->pl_get_idx_of (from); - if (idx != -1) { - ddb_listview_draw_row (ps, idx, from); - } - } - if (to && to_idx != -1) { - ddb_listview_draw_row (ps, to_idx, to); - } -} - static gboolean update_win_title_idle (gpointer data) { struct fromto_t *ft = (struct fromto_t *)data; @@ -897,8 +837,6 @@ update_win_title_idle (gpointer data) { gtkui_set_titlebar (NULL); } } - // update playlist view - songchanged (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), from, to); if (from) { deadbeef->pl_item_unref (from); } @@ -956,11 +894,11 @@ volumebar_redraw (void) { gdk_window_invalidate_rect (gtk_widget_get_window (volumebar), NULL, FALSE); } -void -tabstrip_redraw (void) { - GtkWidget *ts = lookup_widget (mainwin, "tabstrip"); - ddb_tabstrip_refresh (DDB_TABSTRIP (ts)); -} +//void +//tabstrip_redraw (void) { +// GtkWidget *ts = lookup_widget (mainwin, "tabstrip"); +// ddb_tabstrip_refresh (DDB_TABSTRIP (ts)); +//} static int gtk_initialized = 0; static gint refresh_timeout = 0; @@ -985,6 +923,16 @@ gtkui_setup_gui_refresh (void) { refresh_timeout = g_timeout_add (tm, gtkui_on_frameupdate, NULL); } +static void +send_messages_to_widgets (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + send_messages_to_widgets (c, id, ctx, p1, p2); + } + if (w->message) { + w->message (w, id, ctx, p1, p2); + } +} + gboolean add_mainmenu_actions_cb (void *data) { add_mainmenu_actions (); @@ -993,6 +941,10 @@ add_mainmenu_actions_cb (void *data) { int gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + ddb_gtkui_widget_t *rootwidget = w_get_rootwidget (); + if (rootwidget) { + send_messages_to_widgets (rootwidget, id, ctx, p1, p2); + } switch (id) { case DB_EV_ACTIVATED: g_idle_add (activate_cb, NULL); @@ -1012,9 +964,9 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { g_idle_add (trackinfochanged_cb, ev->track); } break; - case DB_EV_PAUSED: - g_idle_add (paused_cb, NULL); - break; +// case DB_EV_PAUSED: +// g_idle_add (paused_cb, NULL); +// break; case DB_EV_PLAYLISTCHANGED: gtkui_playlist_changed (); break; @@ -1062,12 +1014,20 @@ smclient_save_state (EggSMClient *client, const char *state_dir, gpointer user_d static gboolean unlock_playlist_columns_cb (void *ctx) { - ddb_listview_lock_columns (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), 0); +// ddb_listview_lock_columns (DDB_LISTVIEW (lookup_widget (mainwin, "playlist")), 0); return FALSE; } void gtkui_thread (void *ctx) { + XInitThreads (); // gtkglext/xcb doesn't work without this + // let's start some gtk + g_thread_init (NULL); +// add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); + add_pixmap_directory (deadbeef->get_pixmap_dir ()); + gdk_threads_init (); + gdk_threads_enter (); + int argc = 2; const char **argv = alloca (sizeof (char *) * argc); argv[0] = "deadbeef"; @@ -1109,7 +1069,45 @@ gtkui_thread (void *ctx) { gtk_init (&argc, (char ***)&argv); + // register widget types + w_reg_widget ("tabbed_playlist", _("Playlist with tabs"), w_tabbed_playlist_create); + w_reg_widget ("box", NULL, w_box_create); + w_reg_widget ("vsplitter", _("Splitter (top and bottom)"), w_vsplitter_create); + w_reg_widget ("hsplitter", _("Splitter (left and right)"), w_hsplitter_create); + w_reg_widget ("placeholder", NULL, w_placeholder_create); + w_reg_widget ("tabs", _("Tabs"), w_tabs_create); + w_reg_widget ("tabstrip", _("Playlist tabs"), w_tabstrip_create); + w_reg_widget ("playlist", _("Playlist"), w_playlist_create); + w_reg_widget ("selproperties", _("Selection properties"), w_selproperties_create); + w_reg_widget ("coverart", _("Album art display"), w_coverart_create); + w_reg_widget ("scope", _("Scope"), w_scope_create); + w_reg_widget ("spectrum", _("Spectrum"), w_spectrum_create); + mainwin = create_mainwin (); + + // construct mainwindow widgets + { + + w_init (); + ddb_gtkui_widget_t *rootwidget = w_get_rootwidget (); + gtk_widget_show (rootwidget->widget); + gtk_box_pack_start (GTK_BOX(lookup_widget(mainwin, "plugins_bottom_vbox")), rootwidget->widget, TRUE, TRUE, 0); + + // load layout + char layout[4000]; + deadbeef->conf_get_str ("gtkui.layout", "tabbed_playlist { }", layout, sizeof (layout)); + + ddb_gtkui_widget_t *w = NULL; + w_create_from_string (layout, &w); + if (!w) { + ddb_gtkui_widget_t *plt = w_create ("tabbed_playlist"); + w_append (rootwidget, plt); + gtk_widget_show (plt->widget); + } + else { + w_append (rootwidget, w); + } + } #if GTK_CHECK_VERSION(3,0,0) gtk_widget_set_events (GTK_WIDGET (mainwin), gtk_widget_get_events (GTK_WIDGET (mainwin)) | GDK_SCROLL_MASK); #endif @@ -1155,16 +1153,13 @@ gtkui_thread (void *ctx) { searchwin = create_searchwin (); gtk_window_set_transient_for (GTK_WINDOW (searchwin), GTK_WINDOW (mainwin)); - DdbListview *main_playlist = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - main_playlist_init (GTK_WIDGET (main_playlist)); - +// DdbListview *main_playlist = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); +// main_playlist_init (GTK_WIDGET (main_playlist)); if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (header_mi), TRUE); - ddb_listview_show_header (main_playlist, 1); } else { gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (header_mi), FALSE); - ddb_listview_show_header (main_playlist, 0); } DdbListview *search_playlist = DDB_LISTVIEW (lookup_widget (searchwin, "searchlist")); @@ -1188,6 +1183,8 @@ gtkui_thread (void *ctx) { gtk_main (); + w_free (); + if (refresh_timeout) { g_source_remove (refresh_timeout); refresh_timeout = 0; @@ -1278,24 +1275,6 @@ gtkui_plt_load (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname, in } void -gtkui_focus_on_playing_track (void) { - DB_playItem_t *it = deadbeef->streamer_get_playing_track (); - if (it) { - int plt = deadbeef->streamer_get_current_playlist (); - if (plt != deadbeef->plt_get_curr_idx ()) { - deadbeef->plt_set_curr_idx (plt); - } - int idx = deadbeef->pl_get_idx_of (it); - if (idx != -1) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_scroll_to (pl, idx); - ddb_listview_set_cursor (pl, idx); - } - deadbeef->pl_item_unref (it); - } -} - -void gtkui_playlist_set_curr (int playlist) { deadbeef->plt_set_curr_idx (playlist); deadbeef->conf_set_int ("playlist.current", playlist); @@ -1406,7 +1385,7 @@ gtkui_stop (void) { deadbeef->thread_join (gtk_tid); trace ("gtk thread finished\n"); gtk_tid = 0; - main_playlist_free (); + //main_playlist_free (); trace ("gtkui_stop completed\n"); return 0; } @@ -1482,4 +1461,15 @@ static ddb_gtkui_t plugin = { .gui.plugin.message = gtkui_message, .gui.run_dialog = gtkui_run_dialog_root, .get_mainwin = gtkui_get_mainwin, + .w_reg_widget = w_reg_widget, + .w_unreg_widget = w_unreg_widget, + .w_is_registered = w_is_registered, + .w_get_rootwidget = w_get_rootwidget, + .w_create = w_create, + .w_set_name = w_set_name, + .w_destroy = w_destroy, + .w_append = w_append, + .w_replace = w_replace, + .w_remove = w_remove, + .api_version = GTKUI_API_VERSION, }; diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h index 0f764901..56f57e8d 100644 --- a/plugins/gtkui/gtkui.h +++ b/plugins/gtkui/gtkui.h @@ -111,8 +111,8 @@ on_seekbar_motion_notify_event (GtkWidget *widget, void volumebar_redraw (void); -void -tabstrip_redraw (void); +//void +//tabstrip_redraw (void); void gtkui_playlist_changed (void); @@ -136,9 +136,6 @@ extern int (*gtkui_original_plt_add_dir) (ddb_playlist_t *plt, const char *dirna extern int (*gtkui_original_plt_add_file) (ddb_playlist_t *plt, const char *fname, int (*cb)(DB_playItem_t *it, void *data), void *user_data); void -gtkui_focus_on_playing_track (void); - -void gtkui_playlist_set_curr (int playlist); void @@ -150,6 +147,9 @@ gtkui_get_curr_playlist_mod (void); void gtkui_trackinfochanged (DB_playItem_t *it); +gboolean +redraw_queued_tracks_cb (gpointer plt); + extern DB_playItem_t * (*gtkui_original_plt_load) (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data); #endif diff --git a/plugins/gtkui/gtkui_api.h b/plugins/gtkui/gtkui_api.h index 7fa7944f..30bf7090 100644 --- a/plugins/gtkui/gtkui_api.h +++ b/plugins/gtkui/gtkui_api.h @@ -25,9 +25,105 @@ #ifndef __GTKUI_API_H #define __GTKUI_API_H +#define GTKUI_API_VERSION 1 // for compile-time checking + +typedef struct ddb_gtkui_widget_s { + const char *type; + char *name; + + struct ddb_gtkui_widget_s *parent; + + GtkWidget *widget; + + uint32_t flags; + + // all the functions here are overloads, so they are not mandatory + // they can be implemented to add custom code to normal widget code + // they can be NULL if you don't need them, or you can set them to + // standard functions (more below) + + // this function will be called after the widget is visible and needs to + // [re]initialize itself + // e.g. splitter widget sets the grip position in the init + void (*init) (struct ddb_gtkui_widget_s *container); + + // save your custom parameters in the string using strncat + // for example, if you need to write width and height: + // strncat (s, "100 200", sz); + void (*save) (struct ddb_gtkui_widget_s *w, char *s, int sz); + + // this is to read custom widget parameters, e.g. width and height + // you will be passed a string looking like "100 200 {" + // you will need to read params, and return the new pointer, normally it + // should be pointing to the "{" + const char *(*load) (struct ddb_gtkui_widget_s *w, const char *s); + + // custom destructor code + void (*destroy) (struct ddb_gtkui_widget_s *w); + + // custom append code + // if left NULL, appending will not be supported + // you should use standard w_container_add if your widget is derived from + // GTK_CONTAINER + void (*append) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child); + + // custom remove code + // you should use w_container_remove if your widget is derived from + // GTK_CONTAINER + void (*remove) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child); + + // custom replace code + // default replace will call remove;destroy;append + // but you can override if you need smarter behaviour + // look at the splitter and tabs implementation for more details + void (*replace) (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s *child, struct ddb_gtkui_widget_s *newchild); + + // implement this if you want to handle deadbeef broadcast messages/events + int (*message) (struct ddb_gtkui_widget_s *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2); + + // this will be called to setup the menu widget in design mode + void (*initmenu) (struct ddb_gtkui_widget_s *w, GtkWidget *menu); + + // you shouldn't touch this list normally, the system takes care of it + struct ddb_gtkui_widget_s *children; + struct ddb_gtkui_widget_s *next; // points to next widget in the same container +} ddb_gtkui_widget_t; + typedef struct { DB_gui_t gui; + int api_version; + + // returns main window ptr GtkWidget * (*get_mainwin) (void); -} ddb_gtkui_t; + // register the new widget + void (*w_reg_widget) (const char *type, const char *title, ddb_gtkui_widget_t *(*create_func) (void)); + + // unregister the widget + void (*w_unreg_widget) (const char *type); + + // returns 1 if a widget of the specified is registered + int (*w_is_registered) (const char *type); + + // returns toplevel widget + ddb_gtkui_widget_t * (*w_get_rootwidget) (void); + + // create a widget oof specified type + ddb_gtkui_widget_t * (*w_create) (const char *type); + + // set widget name + void (*w_set_name) (ddb_gtkui_widget_t *w, const char *name); + + // destroy the widget + void (*w_destroy) (ddb_gtkui_widget_t *w); + + // append the widget to container + void (*w_append) (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); + + // replace existing child widget with another widget + void (*w_replace) (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to); + + // remove the widget from its container + void (*w_remove) (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); +} ddb_gtkui_t; #endif diff --git a/plugins/gtkui/gtkuigl.c b/plugins/gtkui/gtkuigl.c new file mode 100644 index 00000000..db2a5667 --- /dev/null +++ b/plugins/gtkui/gtkuigl.c @@ -0,0 +1,52 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2012 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include "../../deadbeef.h" +#include "gtkui.h" +#include "support.h" +#include "gtkuigl.h" + +static int gl_initialized; +static int gl_init_state; +//PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; + +int +gtkui_gl_init (void) { + if (gl_initialized) { + return gl_init_state; + } + gl_initialized = 1; + int argc = 1; + const char **argv = alloca (sizeof (char *) * argc); + argv[0] = "deadbeef"; + gboolean success = gdk_gl_init_check (&argc, (char ***)&argv); + if (!success) { + fprintf (stderr, "gdk_gl_init_check failed\n"); + gl_init_state = -1; + return -1; + } +// glXSwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) glXGetProcAddressARB((const GLubyte*)"glXSwapIntervalSGI"); + fprintf (stderr, "gdk_gl_init_check success\n"); + gl_init_state = 0; + return 0; +} + +void +gtkui_gl_free (void) { + // ??? +} diff --git a/plugins/gtkui/gtkuigl.h b/plugins/gtkui/gtkuigl.h new file mode 100644 index 00000000..ddba54dd --- /dev/null +++ b/plugins/gtkui/gtkuigl.h @@ -0,0 +1,36 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2012 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __GTKUIGL_H +#define __GTKUIGL_H + +#include <gtk/gtkgl.h> +#include <GL/gl.h> +#include <GL/glu.h> +//#include <GL/glx.h> +//#include <GL/glxext.h> +// +//#ifndef GLX_SGI_swap_control +//typedef int ( * PFNGLXSWAPINTERVALSGIPROC) (int interval); +//#endif +//extern PFNGLXSWAPINTERVALSGIPROC glXSwapIntervalSGI; + +int +gtkui_gl_init (void); + +#endif diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index 8c38adf7..eff27824 100644 --- a/plugins/gtkui/interface.c +++ b/plugins/gtkui/interface.c @@ -79,6 +79,7 @@ create_mainwin (void) GtkWidget *view_headers; GtkWidget *view_tabs; GtkWidget *view_eq; + GtkWidget *design_mode1; GtkWidget *Playback; GtkWidget *Playback_menu; GtkWidget *Order; @@ -126,8 +127,6 @@ create_mainwin (void) GtkWidget *image5; GtkWidget *seekbar; GtkWidget *volumebar; - GtkWidget *tabstrip; - GtkWidget *playlist; GtkWidget *plugins_bottom_vbox; GtkWidget *statusbar; GtkAccelGroup *accel_group; @@ -352,6 +351,10 @@ create_mainwin (void) gtk_widget_show (view_eq); gtk_container_add (GTK_CONTAINER (View_menu), view_eq); + design_mode1 = gtk_check_menu_item_new_with_mnemonic (_("Design mode")); + gtk_widget_show (design_mode1); + gtk_container_add (GTK_CONTAINER (View_menu), design_mode1); + Playback = gtk_menu_item_new_with_mnemonic (_("_Playback")); gtk_widget_show (Playback); gtk_container_add (GTK_CONTAINER (menubar1), Playback); @@ -592,22 +595,9 @@ create_mainwin (void) gtk_widget_set_can_focus(volumebar, FALSE); gtk_widget_set_can_default(volumebar, FALSE); - tabstrip = create_tabstrip_widget ("tabstrip", "", "", 0, 0); - gtk_widget_show (tabstrip); - gtk_box_pack_start (GTK_BOX (vbox1), tabstrip, FALSE, TRUE, 0); - gtk_widget_set_size_request (tabstrip, -1, 24); - gtk_widget_set_can_focus(tabstrip, FALSE); - gtk_widget_set_can_default(tabstrip, FALSE); - - playlist = create_ddb_listview_widget ("playlist", "", "", 0, 0); - gtk_widget_show (playlist); - gtk_box_pack_start (GTK_BOX (vbox1), playlist, TRUE, TRUE, 0); - gtk_widget_set_can_focus(playlist, FALSE); - gtk_widget_set_can_default(playlist, FALSE); - plugins_bottom_vbox = gtk_vbox_new (FALSE, 0); gtk_widget_show (plugins_bottom_vbox); - gtk_box_pack_start (GTK_BOX (vbox1), plugins_bottom_vbox, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox1), plugins_bottom_vbox, TRUE, TRUE, 0); statusbar = gtk_statusbar_new (); gtk_widget_show (statusbar); @@ -721,6 +711,9 @@ create_mainwin (void) g_signal_connect ((gpointer) view_eq, "activate", G_CALLBACK (on_toggle_eq), NULL); + g_signal_connect ((gpointer) design_mode1, "activate", + G_CALLBACK (on_design_mode1_activate), + NULL); g_signal_connect ((gpointer) order_linear, "activate", G_CALLBACK (on_order_linear_activate), NULL); @@ -839,6 +832,7 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, view_headers, "view_headers"); GLADE_HOOKUP_OBJECT (mainwin, view_tabs, "view_tabs"); GLADE_HOOKUP_OBJECT (mainwin, view_eq, "view_eq"); + GLADE_HOOKUP_OBJECT (mainwin, design_mode1, "design_mode1"); GLADE_HOOKUP_OBJECT (mainwin, Playback, "Playback"); GLADE_HOOKUP_OBJECT (mainwin, Playback_menu, "Playback_menu"); GLADE_HOOKUP_OBJECT (mainwin, Order, "Order"); @@ -884,8 +878,6 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, image5, "image5"); GLADE_HOOKUP_OBJECT (mainwin, seekbar, "seekbar"); GLADE_HOOKUP_OBJECT (mainwin, volumebar, "volumebar"); - GLADE_HOOKUP_OBJECT (mainwin, tabstrip, "tabstrip"); - GLADE_HOOKUP_OBJECT (mainwin, playlist, "playlist"); GLADE_HOOKUP_OBJECT (mainwin, plugins_bottom_vbox, "plugins_bottom_vbox"); GLADE_HOOKUP_OBJECT (mainwin, statusbar, "statusbar"); diff --git a/plugins/gtkui/mainplaylist.c b/plugins/gtkui/mainplaylist.c index f056d2f4..b3a7f7d2 100644 --- a/plugins/gtkui/mainplaylist.c +++ b/plugins/gtkui/mainplaylist.c @@ -128,8 +128,8 @@ void main_external_drag_n_drop (DdbListviewIter before, char *mem, int length) { gboolean playlist_tooltip_handler (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip, gpointer unused) { - GtkWidget *pl = lookup_widget (mainwin, "playlist"); - DB_playItem_t *it = (DB_playItem_t *)ddb_listview_get_iter_from_coord (DDB_LISTVIEW (pl), 0, y); + DdbListview *pl = DDB_LISTVIEW (g_object_get_data (G_OBJECT (widget), "owner")); + DB_playItem_t *it = (DB_playItem_t *)ddb_listview_get_iter_from_coord (pl, 0, y); if (it) { deadbeef->pl_lock (); gtk_tooltip_set_text (tooltip, deadbeef->pl_find_meta (it, ":URI")); @@ -161,6 +161,7 @@ void main_selection_changed (DdbListviewIter it, int idx) { else { ddb_listview_draw_row (search, search_get_idx ((DB_playItem_t *)it), it); } + deadbeef->sendmessage (DB_EV_SELCHANGED, 0, deadbeef->plt_get_curr_idx (), PL_MAIN); } void main_draw_group_title (DdbListview *listview, cairo_t *drawable, DdbListviewIter it, int x, int y, int width, int height) { @@ -256,6 +257,14 @@ main_vscroll_changed (int pos) { deadbeef->conf_set_int (conf, pos); } +void +main_header_context_menu (DdbListview *ps, int column) { + GtkWidget *menu = create_headermenu (1); + set_last_playlist_cm (ps); // playlist ptr for context menu + set_active_column_cm (column); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time()); +} + DdbListviewBinding main_binding = { // rows .count = main_get_count, @@ -292,7 +301,7 @@ DdbListviewBinding main_binding = { // callbacks .handle_doubleclick = main_handle_doubleclick, .selection_changed = main_selection_changed, - .header_context_menu = header_context_menu, + .header_context_menu = main_header_context_menu, .list_context_menu = list_context_menu, .delete_selected = main_delete_selected, .vscroll_changed = main_vscroll_changed, @@ -334,8 +343,9 @@ main_playlist_init (GtkWidget *widget) { GValue value = {0, }; g_value_init (&value, G_TYPE_BOOLEAN); g_value_set_boolean (&value, TRUE); - g_object_set_property (G_OBJECT (widget), "has-tooltip", &value); - g_signal_connect (G_OBJECT (widget), "query-tooltip", G_CALLBACK (playlist_tooltip_handler), NULL); + DdbListview *pl = DDB_LISTVIEW (widget); + g_object_set_property (G_OBJECT (pl->list), "has-tooltip", &value); + g_signal_connect (G_OBJECT (pl->list), "query-tooltip", G_CALLBACK (playlist_tooltip_handler), NULL); } deadbeef->conf_lock (); strncpy (group_by_str, deadbeef->conf_get_str_fast ("playlist.group_by", ""), sizeof (group_by_str)); @@ -352,9 +362,6 @@ main_playlist_free (void) { void main_refresh (void) { - if (mainwin && gtk_widget_get_visible (mainwin)) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - ddb_listview_refresh (pl, DDB_REFRESH_VSCROLL | DDB_REFRESH_LIST); - } + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } diff --git a/plugins/gtkui/mainplaylist.h b/plugins/gtkui/mainplaylist.h index 34cc67bc..1b25ca6b 100644 --- a/plugins/gtkui/mainplaylist.h +++ b/plugins/gtkui/mainplaylist.h @@ -19,6 +19,8 @@ #ifndef __MAINPLAYLIST_H #define __MAINPLAYLIST_H +#include "ddblistview.h" + void main_playlist_init (GtkWidget *widget); @@ -31,4 +33,7 @@ main_refresh (void); int main_get_idx (DdbListviewIter it); +void +main_drag_n_drop (DdbListviewIter before, DdbPlaylistHandle from_playlist, uint32_t *indices, int length, int copy); + #endif diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c index aa1b0f8b..a0117545 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -303,8 +303,7 @@ on_clear1_activate (GtkMenuItem *menuitem, { deadbeef->pl_clear (); deadbeef->pl_save_all (); - main_refresh (); - search_refresh (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -313,8 +312,7 @@ on_remove1_activate (GtkMenuItem *menuitem, { int cursor = deadbeef->pl_delete_selected (); deadbeef->pl_save_all (); - main_refresh (); - search_redraw (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } @@ -322,11 +320,9 @@ void on_crop1_activate (GtkMenuItem *menuitem, gpointer user_data) { - DdbListview *pl = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); deadbeef->pl_crop_selected (); deadbeef->pl_save_all (); - main_refresh (); - search_redraw (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -335,8 +331,7 @@ on_remove2_activate (GtkMenuItem *menuitem, { int cursor = deadbeef->pl_delete_selected (); deadbeef->pl_save_all (); - main_refresh (); - search_redraw (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -374,8 +369,7 @@ on_remove_from_disk_activate (GtkMenuItem *menuitem, deadbeef->pl_save_all (); deadbeef->pl_unlock (); - main_refresh (); - search_redraw (); + deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); } void @@ -761,6 +755,16 @@ static DdbListview *last_playlist; static int active_column; void +set_last_playlist_cm (DdbListview *pl) { + last_playlist = pl; +} + +void +set_active_column_cm (int col) { + active_column = col; +} + +void append_column_from_textdef (DdbListview *listview, const uint8_t *def) { // syntax: "title" "format" id width alignright char token[MAX_TOKEN]; @@ -1096,14 +1100,6 @@ create_headermenu (int groupby) } void -header_context_menu (DdbListview *ps, int column) { - GtkWidget *menu = create_headermenu (GTK_WIDGET (ps) == lookup_widget (mainwin, "playlist") ? 1 : 0); - last_playlist = ps; - active_column = column; - gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time()); -} - -void add_column_helper (DdbListview *listview, const char *title, int width, int id, const char *format, int align_right) { if (!format) { format = ""; diff --git a/plugins/gtkui/plcommon.h b/plugins/gtkui/plcommon.h index 3c24f00f..f5c04e35 100644 --- a/plugins/gtkui/plcommon.h +++ b/plugins/gtkui/plcommon.h @@ -41,12 +41,18 @@ void list_context_menu (DdbListview *listview, DdbListviewIter it, int idx); void -header_context_menu (DdbListview *ps, int column); - -void append_column_from_textdef (DdbListview *listview, const uint8_t *def); void add_column_helper (DdbListview *listview, const char *title, int width, int id, const char *format, int align_right); +GtkWidget* +create_headermenu (int groupby); + +void +set_last_playlist_cm (DdbListview *pl); + +void +set_active_column_cm (int col); + #endif // __PLCOLUMNS_H diff --git a/plugins/gtkui/prefwin.c b/plugins/gtkui/prefwin.c index ddecb632..546759a0 100644 --- a/plugins/gtkui/prefwin.c +++ b/plugins/gtkui/prefwin.c @@ -912,8 +912,9 @@ on_tabstrip_light_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_light", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } @@ -928,8 +929,9 @@ on_tabstrip_mid_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_mid", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } @@ -944,8 +946,9 @@ on_tabstrip_dark_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_dark", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void @@ -959,8 +962,9 @@ on_tabstrip_base_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_base", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void @@ -974,8 +978,9 @@ on_tabstrip_text_color_set (GtkColorButton *colorbutton, deadbeef->conf_set_str ("gtkui.color.tabstrip_text", str); deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void @@ -1136,8 +1141,9 @@ on_override_tabstrip_colors_toggled (GtkToggleButton *togglebutton, deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); prefwin_init_theme_colors (); - redraw_headers (); - tabstrip_redraw (); +// redraw_headers (); +// tabstrip_redraw (); + gtk_widget_queue_draw (mainwin); } void diff --git a/plugins/gtkui/search.c b/plugins/gtkui/search.c index 432c7022..6634f12f 100644 --- a/plugins/gtkui/search.c +++ b/plugins/gtkui/search.c @@ -341,13 +341,7 @@ void search_handle_doubleclick (DdbListview *listview, DdbListviewIter iter, int } void search_selection_changed (DdbListviewIter it, int idx) { - DdbListview *main = DDB_LISTVIEW (lookup_widget (mainwin, "playlist")); - if (idx == -1) { - ddb_listview_refresh (main, DDB_REFRESH_LIST); - } - else { - ddb_listview_draw_row (main, main_get_idx ((DB_playItem_t *)it), it); - } + deadbeef->sendmessage (DB_EV_SELCHANGED, 0, 0, 0); } void @@ -357,6 +351,14 @@ search_delete_selected (void) { search_refresh (); } +void +search_header_context_menu (DdbListview *ps, int column) { + GtkWidget *menu = create_headermenu (0); + set_last_playlist_cm (ps); // playlist ptr for context menu + set_active_column_cm (column); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, ps, 3, gtk_get_current_event_time()); +} + DdbListviewBinding search_binding = { // rows .count = search_get_count, @@ -393,7 +395,7 @@ DdbListviewBinding search_binding = { // callbacks .handle_doubleclick = search_handle_doubleclick, .selection_changed = search_selection_changed, - .header_context_menu = header_context_menu, + .header_context_menu = search_header_context_menu, .list_context_menu = list_context_menu, .delete_selected = search_delete_selected, .modification_idx = gtkui_get_curr_playlist_mod, diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 3b35273b..36d3d2c4 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -52,8 +52,8 @@ static int numtracks; static GtkWidget *progressdlg; static int progress_aborted; -static int -build_key_list (const char ***pkeys, int props) { +int +build_key_list (const char ***pkeys, int props, DB_playItem_t **tracks, int numtracks) { int sz = 20; const char **keys = malloc (sizeof (const char *) * sz); if (!keys) { @@ -100,7 +100,7 @@ equals_ptr (const char *a, const char *b) { } static int -get_field_value (char *out, int size, const char *key, const char *(*getter)(DB_playItem_t *it, const char *key), int (*equals)(const char *a, const char *b)) { +get_field_value (char *out, int size, const char *key, const char *(*getter)(DB_playItem_t *it, const char *key), int (*equals)(const char *a, const char *b), DB_playItem_t **tracks, int numtracks) { int multiple = 0; *out = 0; if (numtracks == 0) { @@ -274,13 +274,13 @@ static const char *hc_props[] = { }; void -add_field (GtkListStore *store, const char *key, const char *title, int is_prop) { +add_field (GtkListStore *store, const char *key, const char *title, int is_prop, DB_playItem_t **tracks, int numtracks) { // get value to edit const char *mult = is_prop ? "" : _("[Multiple values] "); char val[1000]; size_t ml = strlen (mult); memcpy (val, mult, ml+1); - int n = get_field_value (val + ml, sizeof (val) - ml, key, deadbeef->pl_find_meta_raw, equals_ptr); + int n = get_field_value (val + ml, sizeof (val) - ml, key, deadbeef->pl_find_meta_raw, equals_ptr, tracks, numtracks); GtkTreeIter iter; gtk_list_store_append (store, &iter); @@ -304,23 +304,20 @@ add_field (GtkListStore *store, const char *key, const char *title, int is_prop) } void -trkproperties_fill_metadata (void) { - if (!trackproperties) { +trkproperties_fill_meta (GtkListStore *store, DB_playItem_t **tracks, int numtracks) { + gtk_list_store_clear (store); + if (!tracks) { return; } - trkproperties_modified = 0; - gtk_list_store_clear (store); - gtk_list_store_clear (propstore); - deadbeef->pl_lock (); const char **keys = NULL; - int nkeys = build_key_list (&keys, 0); + int nkeys = build_key_list (&keys, 0, tracks, numtracks); int k; // add "standard" fields for (int i = 0; types[i]; i += 2) { - add_field (store, types[i], _(types[i+1]), 0); + add_field (store, types[i], _(types[i+1]), 0, tracks, numtracks); } // add all other fields @@ -339,19 +336,31 @@ trkproperties_fill_metadata (void) { if (!types[i]) { snprintf (title, sizeof (title), "<%s>", keys[k]); } - add_field (store, keys[k], title, 0); + add_field (store, keys[k], title, 0, tracks, numtracks); } if (keys) { free (keys); } +} + +void +trkproperties_fill_metadata (void) { + if (!trackproperties) { + return; + } + trkproperties_modified = 0; + deadbeef->pl_lock (); + + trkproperties_fill_meta (store, tracks, numtracks); + gtk_list_store_clear (propstore); // hardcoded properties for (int i = 0; hc_props[i]; i += 2) { - add_field (propstore, hc_props[i], _(hc_props[i+1]), 1); + add_field (propstore, hc_props[i], _(hc_props[i+1]), 1, tracks, numtracks); } // properties - keys = NULL; - nkeys = build_key_list (&keys, 1); + const char **keys = NULL; + int nkeys = build_key_list (&keys, 1, tracks, numtracks); for (int k = 0; k < nkeys; k++) { int i; for (i = 0; hc_props[i]; i += 2) { @@ -364,7 +373,7 @@ trkproperties_fill_metadata (void) { } char title[1000]; snprintf (title, sizeof (title), "<%s>", keys[k]+1); - add_field (propstore, keys[k], title, 1); + add_field (propstore, keys[k], title, 1, tracks, numtracks); } if (keys) { free (keys); diff --git a/plugins/gtkui/trkproperties.h b/plugins/gtkui/trkproperties.h index 8b2b6311..ef1e530c 100644 --- a/plugins/gtkui/trkproperties.h +++ b/plugins/gtkui/trkproperties.h @@ -19,6 +19,8 @@ #ifndef __TRKPROPERTIES_H #define __TRKPROPERTIES_H +#include "../../deadbeef.h" + struct DB_playItem_s; void @@ -30,4 +32,10 @@ trkproperties_destroy (void); void trkproperties_fill_metadata (void); +int +build_key_list (const char ***pkeys, int props, DB_playItem_t **tracks, int numtracks); + +void +trkproperties_fill_meta (GtkListStore *store, DB_playItem_t **tracks, int numtracks); + #endif diff --git a/plugins/gtkui/widgets.c b/plugins/gtkui/widgets.c new file mode 100644 index 00000000..79ec9c66 --- /dev/null +++ b/plugins/gtkui/widgets.c @@ -0,0 +1,2225 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <math.h> +#include "gtkui.h" +#include "widgets.h" +#include "ddbtabstrip.h" +#include "ddblistview.h" +#include "mainplaylist.h" +#include "../../gettext.h" +#include "parser.h" +#include "trkproperties.h" +#include "coverart.h" +#include "gtkuigl.h" + +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) + +typedef struct w_creator_s { + const char *type; + const char *title; // set to NULL to avoid exposing this widget type to user + ddb_gtkui_widget_t *(*create_func) (void); + struct w_creator_s *next; +} w_creator_t; + +static w_creator_t *w_creators; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *box; // hack to support splitter locking, can be a vbox or a hbox + int position; + int locked; +} w_splitter_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_box_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_tabstrip_t; + +typedef struct { + ddb_gtkui_widget_t base; + DdbTabStrip *tabstrip; + DdbListview *list; +} w_tabbed_playlist_t; + +typedef struct { + ddb_gtkui_widget_t base; + DdbListview *list; +} w_playlist_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; +} w_placeholder_t; + +typedef struct { + ddb_gtkui_widget_t base; + int clicked_page; +} w_tabs_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *tree; +} w_selproperties_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; +} w_coverart_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; + guint drawtimer; + GdkGLContext *glcontext; +} w_scope_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *drawarea; + guint drawtimer; + GdkGLContext *glcontext; +} w_spectrum_t; + +static int design_mode; +static ddb_gtkui_widget_t *rootwidget; + +//// common functions + +void +w_init (void) { + rootwidget = w_create ("box"); +} + +void +w_free (void) { + w_save (); + w_creator_t *next = NULL; + for (w_creator_t *cr = w_creators; cr; cr = next) { + next = cr->next; + free (cr); + } + w_creators = NULL; +} + +ddb_gtkui_widget_t * +w_get_rootwidget (void) { + return rootwidget; +} + +static void +set_design_mode (ddb_gtkui_widget_t *w) { + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + set_design_mode (c); + } +} + +void +w_set_design_mode (int active) { + design_mode = active; + set_design_mode (rootwidget); +} + +void +w_append (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + child->parent = cont; + if (!cont->children) { + cont->children = child; + } + else { + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next) { + if (!c->next) { + c->next = child; + break; + } + } + } + + if (cont->append) { + cont->append (cont, child); + } + if (child->init) { + child->init (child); + } +} + +void +w_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + if (cont->remove) { + cont->remove (cont, child); + } + child->widget = NULL; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next) { + if (c == child) { + if (prev) { + prev->next = c->next; + } + else { + cont->children = c->next; + } + break; + } + prev = c; + } + child->parent = NULL; +} + +gboolean +w_init_cb (void *data) { + ddb_gtkui_widget_t *w = data; + if (w->init) { + w->init (w); + } + return FALSE; +} + +void +w_replace (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to) { + if (w->replace) { + w->replace (w, from, to); + if (to->init) { + g_idle_add (w_init_cb, to); + } + } + else { + w_remove (w, from); + w_destroy (from); + w_append (w, to); + } +} + +const char * +w_create_from_string (const char *s, ddb_gtkui_widget_t **parent) { + char t[MAX_TOKEN]; + s = gettoken (s, t); + if (!s) { + return NULL; + } + ddb_gtkui_widget_t *w = w_create (t); + // nuke all default children + while (w->children) { + w_remove (w, w->children); + } + + // name + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + if (t[0]) { + w_set_name (w, t); + } + + // load widget params + if (w->load) { + s = w->load (w, s); + if (!s) { + w_destroy (w); + return NULL; + } + } + + // { + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + if (strcmp (t, "{")) { + w_destroy (w); + return NULL; + } + + const char *back = s; + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + for (;;) { + if (!strcmp (t, "}")) { + break; + } + + s = w_create_from_string (back, &w); + if (!s) { + w_destroy (w); + return NULL; + } + + back = s; + s = gettoken (s, t); + if (!s) { + w_destroy (w); + return NULL; + } + } + + if (*parent) { + w_append (*parent, w); + } + else { + *parent = w; + } + return s; +} + +static ddb_gtkui_widget_t *current_widget; +static int hidden = 0; + +static gboolean +w_draw_event (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + if (hidden && user_data == current_widget) { + cairo_set_source_rgb (cr, 0.17f, 0, 0.83f); + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + + if (!gtk_widget_get_has_window (widget)) { + cairo_reset_clip (cr); + cairo_rectangle (cr, allocation.x, allocation.y, allocation.width, allocation.height); + } + else { + cairo_reset_clip (cr); + cairo_rectangle (cr, 0, 0, allocation.width, allocation.height); + } + cairo_fill (cr); + } + return FALSE; +} + +gboolean +w_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = w_draw_event (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +static char paste_buffer[1000]; + +void +save_widget_to_string (char *str, int sz, ddb_gtkui_widget_t *w) { + strcat (str, w->type); + strcat (str, " \""); + strcat (str, w->name ? w->name : ""); + strcat (str, "\""); + if (w->save) { + w->save (w, str, sz); + } + strcat (str, " {"); + for (ddb_gtkui_widget_t *c = w->children; c; c = c->next) { + save_widget_to_string (str, sz, c); + } + strcat (str, "} "); +} + +void +w_save (void) { + char buf[4000] = ""; + save_widget_to_string (buf, sizeof (buf), rootwidget->children); + deadbeef->conf_set_str ("gtkui.layout", buf); + deadbeef->conf_save (); +} + +static void +on_replace_activate (GtkMenuItem *menuitem, gpointer user_data) { + for (w_creator_t *cr = w_creators; cr; cr = cr->next) { + if (cr->type == user_data) { + w_replace (current_widget->parent, current_widget, w_create (user_data)); + } + } + w_save (); +} + +static void +on_delete_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!strcmp (current_widget->type, "placeholder")) { + return; + } + if (parent->replace) { + parent->replace (parent, current_widget, w_create ("placeholder")); + } + else { + w_remove (parent, current_widget); + w_destroy (current_widget); + current_widget = w_create ("placeholder"); + w_append (parent, current_widget); + } + w_save (); +} + +static void +on_cut_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!strcmp (current_widget->type, "placeholder")) { + return; + } + // save hierarchy to string + // FIXME: use real clipboard + paste_buffer[0] = 0; + save_widget_to_string (paste_buffer, sizeof (paste_buffer), current_widget); + + if (parent->replace) { + parent->replace (parent, current_widget, w_create ("placeholder")); + } + else { + w_remove (parent, current_widget); + w_destroy (current_widget); + current_widget = w_create ("placeholder"); + w_append (parent, current_widget); + } + w_save (); +} + +static void +on_copy_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!strcmp (current_widget->type, "placeholder")) { + return; + } + // save hierarchy to string + // FIXME: use real clipboard + paste_buffer[0] = 0; + save_widget_to_string (paste_buffer, sizeof (paste_buffer), current_widget); +} + +static void +on_paste_activate (GtkMenuItem *menuitem, gpointer user_data) { + ddb_gtkui_widget_t *parent = current_widget->parent; + if (!paste_buffer[0]) { + return; + } + ddb_gtkui_widget_t *w = NULL; + w_create_from_string (paste_buffer, &w); + if (parent->replace) { + parent->replace (parent, current_widget, w); + } + else { + w_remove (parent, current_widget); + w_destroy (current_widget); + current_widget = w; + w_append (parent, current_widget); + } + w_save (); +} + +void +hide_widget (GtkWidget *widget, gpointer data) { + gtk_widget_hide (widget); +} + +void +show_widget (GtkWidget *widget, gpointer data) { + gtk_widget_show (widget); +} + +void +w_menu_deactivate (GtkMenuShell *menushell, gpointer user_data) { + hidden = 0; + ddb_gtkui_widget_t *w = user_data; + if (GTK_IS_CONTAINER (w->widget)) { + gtk_container_foreach (GTK_CONTAINER (w->widget), show_widget, NULL); + } + gtk_widget_set_app_paintable (w->widget, FALSE); + gtk_widget_queue_draw (w->widget); +} + +gboolean +w_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { + if (!design_mode || event->button != 3) { + return FALSE; + } + + current_widget = user_data; + widget = current_widget->widget; + hidden = 1; + if (GTK_IS_CONTAINER (widget)) { + gtk_container_foreach (GTK_CONTAINER (widget), hide_widget, NULL); + } + gtk_widget_set_app_paintable (widget, TRUE); + gtk_widget_queue_draw (((ddb_gtkui_widget_t *)user_data)->widget); + GtkWidget *menu; + GtkWidget *submenu; + GtkWidget *item; + menu = gtk_menu_new (); + if (strcmp (current_widget->type, "placeholder")) { + item = gtk_menu_item_new_with_mnemonic (_("Replace with...")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + } + else { + item = gtk_menu_item_new_with_mnemonic (_("Insert...")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + } + + submenu = gtk_menu_new (); + gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu); + + for (w_creator_t *cr = w_creators; cr; cr = cr->next) { + if (cr->title) { + item = gtk_menu_item_new_with_mnemonic (cr->title); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (submenu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_replace_activate), + (void *)cr->type); + } + } + + if (strcmp (current_widget->type, "placeholder")) { + item = gtk_menu_item_new_with_mnemonic (_("Delete")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_delete_activate), + NULL); + + item = gtk_menu_item_new_with_mnemonic (_("Cut")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_cut_activate), + NULL); + + item = gtk_menu_item_new_with_mnemonic (_("Copy")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_copy_activate), + NULL); + } + item = gtk_menu_item_new_with_mnemonic ("Paste"); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_paste_activate), + NULL); + + if (current_widget->initmenu) { + current_widget->initmenu (current_widget, menu); + } + + g_signal_connect ((gpointer) menu, "deactivate", G_CALLBACK (w_menu_deactivate), user_data); + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, 0, gtk_get_current_event_time()); + return TRUE; +} + +static void +w_override_signals (GtkWidget *widget, gpointer user_data) { + g_signal_connect ((gpointer) widget, "button_press_event", G_CALLBACK (w_button_press_event), user_data); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) widget, "expose_event", G_CALLBACK (w_expose_event), user_data); +#else + g_signal_connect ((gpointer) widget, "draw", G_CALLBACK (w_draw_event), user_data); +#endif + if (GTK_IS_CONTAINER (widget)) { + gtk_container_forall (GTK_CONTAINER (widget), w_override_signals, user_data); + } +} + +void +w_reg_widget (const char *type, const char *title, ddb_gtkui_widget_t *(*create_func) (void)) { + w_creator_t *c; + for (c = w_creators; c; c = c->next) { + if (!strcmp (c->type, type)) { + fprintf (stderr, "gtkui w_reg_widget: widget type %s already registered\n", type); + return; + } + } + c = malloc (sizeof (w_creator_t)); + memset (c, 0, sizeof (w_creator_t)); + c->type = type; + c->title = title; + c->create_func = create_func; + c->next = w_creators; + w_creators = c; +} + +void +w_unreg_widget (const char *type) { + w_creator_t *c, *prev = NULL; + for (c = w_creators; c; c = c->next) { + if (!strcmp (c->type, type)) { + if (prev) { + prev->next = c->next; + } + else { + w_creators = c->next; + } + free (c); + return; + } + prev = c; + } + fprintf (stderr, "gtkui w_unreg_widget: widget type %s is not registered\n", type); +} + +int +w_is_registered (const char *type) { + // FIXME + return 0; +} + +ddb_gtkui_widget_t * +w_create (const char *type) { + for (w_creator_t *c = w_creators; c; c = c->next) { + if (!strcmp (c->type, type)) { + ddb_gtkui_widget_t *w = c->create_func (); + w->type = c->type; + + return w; + } + } + return NULL; +} + +void +w_set_name (ddb_gtkui_widget_t *w, const char *name) { + if (w->name) { + free (w->name); + w->name = NULL; + } + if (name) { + w->name = strdup (name); + } +} + +void +w_destroy (ddb_gtkui_widget_t *w) { + if (w->destroy) { + w->destroy (w); + } + if (w->widget) { + gtk_widget_destroy (w->widget); + } + if (w->name) { + free (w->name); + } + free (w); +} + +///// gtk_container convenience functions +void +w_container_add (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *container = NULL; + container = cont->widget; + gtk_container_add (GTK_CONTAINER (container), child->widget); + gtk_widget_show (child->widget); +} + +void +w_container_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *container = NULL; + container = cont->widget; + gtk_container_remove (GTK_CONTAINER (container), child->widget); + +} + +////// placeholder widget +gboolean +w_placeholder_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + cairo_set_source_rgb (cr, 255, 0, 0); + cairo_surface_t *checker; + cairo_t *cr2; + + checker = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 12, 12); + cr2 = cairo_create (checker); + + cairo_set_source_rgb (cr2, 0.5, 0.5 ,0.5); + cairo_paint (cr2); + cairo_set_source_rgb (cr2, 0, 0, 0); + cairo_move_to (cr2, 0, 0); + cairo_line_to (cr2, 12, 12); + cairo_move_to (cr2, 1, 12); + cairo_line_to (cr2, 12, 1); + cairo_set_line_width (cr2, 1); + cairo_set_antialias (cr2, CAIRO_ANTIALIAS_NONE); + cairo_stroke (cr2); + cairo_fill (cr2); + cairo_destroy (cr2); + + cairo_set_source_surface (cr, checker, 0, 0); + cairo_pattern_t *pt = cairo_get_source(cr); + cairo_pattern_set_extend (pt, CAIRO_EXTEND_REPEAT); + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + cairo_rectangle (cr, 0, 0, a.width, a.height); + cairo_paint (cr); + cairo_surface_destroy (checker); + return FALSE; +} + +gboolean +w_placeholder_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = w_placeholder_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +ddb_gtkui_widget_t * +w_placeholder_create (void) { + w_placeholder_t *w = malloc (sizeof (w_placeholder_t)); + memset (w, 0, sizeof (w_placeholder_t)); + + w->base.widget = gtk_event_box_new (); + w->drawarea = gtk_drawing_area_new (); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); + +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (w_placeholder_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (w_placeholder_draw), w); +#endif + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t*)w; +} + +// common splitter funcs +const char * +w_splitter_load (struct ddb_gtkui_widget_s *w, const char *s) { + char t[MAX_TOKEN]; + s = gettoken (s, t); + if (!s) { + return NULL; + } + ((w_splitter_t *)w)->position = atoi (t); + s = gettoken (s, t); + if (!s) { + return NULL; + } + ((w_splitter_t *)w)->locked = atoi (t); + return s; +} + +void +w_splitter_save (struct ddb_gtkui_widget_s *w, char *s, int sz) { + int pos = ((w_splitter_t *)w)->box ? ((w_splitter_t *)w)->position : gtk_paned_get_position (GTK_PANED(w->widget)); + char spos[10]; + snprintf (spos, sizeof (spos), " %d %d", pos, ((w_splitter_t *)w)->locked); + strncat (s, spos, sz); +} + +void +w_splitter_add (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *child) { + w_container_add (w, child); + if (((w_splitter_t *)w)->locked) { + if (child == w->children) { + if (GTK_IS_VBOX (((w_splitter_t *)w)->box)) { + gtk_widget_set_size_request (child->widget, -1, ((w_splitter_t *)w)->position); + } + else { + gtk_widget_set_size_request (child->widget, ((w_splitter_t *)w)->position, -1); + } + } + } + else { + gtk_paned_set_position (GTK_PANED(w->widget), ((w_splitter_t *)w)->position); + } +} + +void +w_splitter_init_signals (w_splitter_t *w) { +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); +#else + g_signal_connect ((gpointer) w->base.widget, "draw", G_CALLBACK (w_draw_event), w); +#endif + g_signal_connect ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (w_button_press_event), w); +} + +void +w_splitter_lock (w_splitter_t *w) { + // we can't change GtkPaned behavior, so convert to vbox for now + if (w->locked) { + return; + } + w->locked = 1; + + int vert = w->base.type == "vsplitter"; + + GtkWidget *box = vert ? gtk_vbox_new (FALSE, 6) : gtk_hbox_new (FALSE, 6); + gtk_widget_show (box); + + w->position = gtk_paned_get_position (GTK_PANED (w->base.widget)); + + GtkAllocation a; + gtk_widget_get_allocation (w->base.widget, &a); + + GtkWidget *c1 = gtk_paned_get_child1 (GTK_PANED (w->base.widget)); + g_object_ref (c1); + GtkWidget *c2 = gtk_paned_get_child2 (GTK_PANED (w->base.widget)); + g_object_ref (c2); + gtk_container_remove (GTK_CONTAINER (w->base.widget), c1); + gtk_container_remove (GTK_CONTAINER (w->base.widget), c2); + + gtk_box_pack_start (GTK_BOX (box), c1, FALSE, FALSE, 0); + gtk_widget_set_size_request (c1, vert ? -1 : w->position, vert ? w->position : -1); + gtk_box_pack_end (GTK_BOX (box), c2, TRUE, TRUE, 0); + + ddb_gtkui_widget_t *parent = w->base.parent; + if (w->base.parent) { + gtk_container_remove (GTK_CONTAINER (parent->widget), w->base.widget); + } + GtkWidget *eventbox = gtk_event_box_new (); + gtk_widget_show (eventbox); + gtk_container_add (GTK_CONTAINER (eventbox), box); + w->base.widget = eventbox; + w->box = box; + if (w->base.parent) { + gtk_container_add (GTK_CONTAINER (parent->widget), w->base.widget); + } + w_splitter_init_signals (w); +} + +void +w_splitter_unlock (w_splitter_t *w) { + if (!w->locked) { + return; + } + w->locked = 0; + + int vert = w->base.type == "vsplitter"; + // convert back to vpaned + GtkWidget *paned = vert ? gtk_vpaned_new () : gtk_hpaned_new (); + gtk_widget_show (paned); + + GList *lst = gtk_container_get_children (GTK_CONTAINER (w->box)); + + GtkWidget *c1 = lst->data; + g_object_ref (c1); + GtkWidget *c2 = lst->next->data; + g_object_ref (c2); + gtk_container_remove (GTK_CONTAINER (w->box), c1); + gtk_container_remove (GTK_CONTAINER (w->box), c2); + + gtk_container_add (GTK_CONTAINER (paned), c1); + gtk_container_add (GTK_CONTAINER (paned), c2); + gtk_paned_set_position (GTK_PANED (paned), w->position); + + ddb_gtkui_widget_t *parent = w->base.parent; + if (w->base.parent) { + gtk_container_remove (GTK_CONTAINER (parent->widget), w->base.widget); + } + w->base.widget = paned; + w->box = NULL; + if (w->base.parent) { + gtk_container_add (GTK_CONTAINER (parent->widget), w->base.widget); + } + w_splitter_init_signals (w); +} + +void +on_splitter_lock_movement_toggled (GtkCheckMenuItem *checkmenuitem, gpointer user_data) { + if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (checkmenuitem))) { + w_splitter_lock (user_data); + } + else { + w_splitter_unlock (user_data); + } +} + +void +w_splitter_initmenu (struct ddb_gtkui_widget_s *w, GtkWidget *menu) { + GtkWidget *item; + item = gtk_check_menu_item_new_with_mnemonic (_("Lock movement")); + gtk_widget_show (item); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), ((w_splitter_t *)w)->locked); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "toggled", + G_CALLBACK (on_splitter_lock_movement_toggled), + w); +} + +void +w_splitter_replace (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child, ddb_gtkui_widget_t *newchild) { + int ntab = 0; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = cont->children; c; c = c->next, ntab++) { + if (c == child) { + newchild->next = c->next; + if (prev) { + prev->next = newchild; + } + else { + cont->children = newchild; + } + newchild->parent = cont; + GtkWidget *container = ((w_splitter_t *)cont)->locked ? ((w_splitter_t *)cont)->box : cont->widget; + gtk_container_remove (GTK_CONTAINER(container), c->widget); + c->widget = NULL; + w_destroy (c); + gtk_widget_show (newchild->widget); + if (((w_splitter_t *)cont)->locked) { + if (ntab == 0) { + gtk_box_pack_start (GTK_BOX (container), newchild->widget, TRUE, TRUE, 0); + } + else { + gtk_box_pack_end (GTK_BOX (container), newchild->widget, TRUE, TRUE, 0); + } + } + else { + if (ntab == 0) { + gtk_paned_add1 (GTK_PANED (container), newchild->widget); + } + else { + gtk_paned_add2 (GTK_PANED (container), newchild->widget); + } + } + break; + } + prev = c; + } +} + +void +w_splitter_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *container = NULL; + w_splitter_t *w = (w_splitter_t *)cont; + if (w->locked) { + container = w->box; + } + else { + container = cont->widget; + } + gtk_container_remove (GTK_CONTAINER (container), child->widget); +} + +////// vsplitter widget +void +w_vsplitter_init (ddb_gtkui_widget_t *base) { + w_splitter_t *w = (w_splitter_t *)base; + int pos = ((w_splitter_t *)w)->position; // prevent lock/unlock from overwriting position + if (w->locked && !w->box) { + w->locked = 0; + w_splitter_lock (w); + } + else if (!w->locked && w->box) { + w->locked = 1; + w_splitter_unlock (w); + } + if (pos == -1) { + GtkAllocation a; + gtk_widget_get_allocation (w->base.widget, &a); + pos = a.height/2; + } + w->position = pos; + if (!w->box) { + gtk_widget_set_size_request (w->base.children->widget, -1, -1); + gtk_paned_set_position (GTK_PANED(w->base.widget), pos); + } + else { + gtk_widget_set_size_request (w->base.children->widget, -1, w->position); + } +} + +ddb_gtkui_widget_t * +w_vsplitter_create (void) { + w_splitter_t *w = malloc (sizeof (w_splitter_t)); + memset (w, 0, sizeof (w_splitter_t)); + w->position = -1; + w->base.widget = gtk_vpaned_new (); + w->base.append = w_splitter_add; + w->base.remove = w_splitter_remove; + w->base.replace = w_splitter_replace; + w->base.load = w_splitter_load; + w->base.save = w_splitter_save; + w->base.init = w_vsplitter_init; + w->base.initmenu = w_splitter_initmenu; + + ddb_gtkui_widget_t *ph1, *ph2; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + + w_append ((ddb_gtkui_widget_t*)w, ph1); + w_append ((ddb_gtkui_widget_t*)w, ph2); + + w_splitter_init_signals (w); + + return (ddb_gtkui_widget_t*)w; +} + +////// hsplitter widget +void +w_hsplitter_init (ddb_gtkui_widget_t *base) { + w_splitter_t *w = (w_splitter_t *)base; + int pos = ((w_splitter_t *)w)->position; // prevent lock/unlock from overwriting position + if (w->locked && !w->box) { + w->locked = 0; + w_splitter_lock (w); + } + else if (!w->locked && w->box) { + w->locked = 1; + w_splitter_unlock (w); + } + if (pos == -1) { + GtkAllocation a; + gtk_widget_get_allocation (w->base.widget, &a); + pos = a.width/2; + } + w->position = pos; + if (!w->box) { + gtk_widget_set_size_request (w->base.children->widget, -1, -1); + gtk_paned_set_position (GTK_PANED(w->base.widget), pos); + } + else { + gtk_widget_set_size_request (w->base.children->widget, w->position, -1); + } +} + +ddb_gtkui_widget_t * +w_hsplitter_create (void) { + w_splitter_t *w = malloc (sizeof (w_splitter_t)); + memset (w, 0, sizeof (w_splitter_t)); + w->position = -1; + w->base.widget = gtk_hpaned_new (); + w->base.append = w_splitter_add; + w->base.remove = w_splitter_remove; + w->base.replace = w_splitter_replace; + w->base.load = w_splitter_load; + w->base.save = w_splitter_save; + w->base.init = w_hsplitter_init; + w->base.initmenu = w_splitter_initmenu; + + ddb_gtkui_widget_t *ph1, *ph2; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + + w_append ((ddb_gtkui_widget_t*)w, ph1); + w_append ((ddb_gtkui_widget_t*)w, ph2); + + w_splitter_init_signals (w); + + return (ddb_gtkui_widget_t*)w; +} + +///// tabs widget +static gboolean +tab_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data); + +static void +on_remove_tab_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + + int i = 0; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++) { + if (i == w->clicked_page) { + w_remove ((ddb_gtkui_widget_t *)w, c); + return; + } + } +} + +static void +on_add_tab_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + + ddb_gtkui_widget_t *ph; + ph = w_create ("placeholder"); + w_append ((ddb_gtkui_widget_t*)w, ph); + + int i = 0; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++); + w->clicked_page = i-1; + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), w->clicked_page); + +} + +static void +on_move_tab_left_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + if (w->clicked_page <= 0) { + return; + } + + // remove and save widget + int i = 0; + ddb_gtkui_widget_t *newchild = NULL; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++) { + if (i == w->clicked_page) { + char buf[4000] = ""; + save_widget_to_string (buf, sizeof (buf), c); + w_create_from_string (buf, &newchild); + + w_remove ((ddb_gtkui_widget_t *)w, c); + break; + } + } + if (!newchild) { + return; + } + + // add new child at new position + i = 0; + prev = NULL; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++) { + if (i == w->clicked_page-1) { + if (prev) { + newchild->next = prev->next; + prev->next = newchild; + } + else { + newchild->next = w->base.children; + w->base.children = newchild; + } + GtkWidget *eventbox = gtk_event_box_new (); + GtkWidget *label = gtk_label_new (newchild->type); + gtk_widget_show (eventbox); + g_object_set_data (G_OBJECT (eventbox), "owner", w); + g_signal_connect ((gpointer) eventbox, "button_press_event", G_CALLBACK (tab_button_press_event), newchild->widget); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_widget_show (newchild->widget); + + gtk_notebook_insert_page (GTK_NOTEBOOK (w->base.widget), newchild->widget, eventbox, w->clicked_page-1); + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), w->clicked_page-1); + w->clicked_page--; + break; + } + prev = c; + } +} + +static void +on_move_tab_right_activate (GtkMenuItem *menuitem, gpointer user_data) { + w_tabs_t *w = user_data; + + int i = 0; + for (ddb_gtkui_widget_t *c = w->base.children; c; c = c->next, i++); + if (w->clicked_page >= i) + return; + + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), ++w->clicked_page); + on_move_tab_left_activate (menuitem, user_data); + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), ++w->clicked_page); +} + +static gboolean +tab_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_data) { + if (event->button != 3) { + return FALSE; + } + // user_data is child widget + if (design_mode) { + w_tabs_t *w = (w_tabs_t *)g_object_get_data (G_OBJECT (widget), "owner"); + GtkWidget *menu; + GtkWidget *item; + menu = gtk_menu_new (); + + item = gtk_menu_item_new_with_mnemonic (_("Move tab left")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_move_tab_left_activate), + w); + + item = gtk_menu_item_new_with_mnemonic (_("Move tab right")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_move_tab_right_activate), + w); + + item = gtk_menu_item_new_with_mnemonic (_("Remove tab")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_remove_tab_activate), + w); + + item = gtk_menu_item_new_with_mnemonic (_("Rename tab")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + + w->clicked_page = gtk_notebook_page_num (GTK_NOTEBOOK (w->base.widget), user_data); + gtk_notebook_set_current_page (GTK_NOTEBOOK (w->base.widget), w->clicked_page); + + gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, widget, 0, gtk_get_current_event_time()); + return TRUE; + } + return FALSE; +} + +void +w_tabs_add (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child) { + GtkWidget *eventbox = gtk_event_box_new (); + GtkWidget *label = gtk_label_new (child->type); + gtk_widget_show (eventbox); + g_object_set_data (G_OBJECT (eventbox), "owner", cont); + g_signal_connect ((gpointer) eventbox, "button_press_event", G_CALLBACK (tab_button_press_event), child->widget); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_widget_show (child->widget); + gtk_notebook_append_page (GTK_NOTEBOOK (cont->widget), child->widget, eventbox); +} + +void +w_tabs_replace (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child, ddb_gtkui_widget_t *newchild) { + int ntab = 0; + ddb_gtkui_widget_t *prev = NULL; + for (ddb_gtkui_widget_t *c = cont->children; c; prev = c, c = c->next, ntab++) { + if (c == child) { + newchild->next = c->next; + if (prev) { + prev->next = newchild; + } + else { + cont->children = newchild; + } + newchild->parent = cont; + gtk_notebook_remove_page (GTK_NOTEBOOK(cont->widget), ntab); + c->widget = NULL; + w_destroy (c); + GtkWidget *eventbox = gtk_event_box_new (); + GtkWidget *label = gtk_label_new (newchild->type); + gtk_widget_show (eventbox); + g_object_set_data (G_OBJECT (eventbox), "owner", cont); + g_signal_connect ((gpointer) eventbox, "button_press_event", G_CALLBACK (tab_button_press_event), newchild->widget); + gtk_widget_show (label); + gtk_container_add (GTK_CONTAINER (eventbox), label); + gtk_widget_show (newchild->widget); + int pos = gtk_notebook_insert_page (GTK_NOTEBOOK (cont->widget), newchild->widget, eventbox, ntab); + gtk_notebook_set_current_page (GTK_NOTEBOOK (cont->widget), pos); + break; + } + } +} + +void +w_tabs_initmenu (struct ddb_gtkui_widget_s *w, GtkWidget *menu) { + GtkWidget *item; + item = gtk_menu_item_new_with_mnemonic (_("Add new tab")); + gtk_widget_show (item); + gtk_container_add (GTK_CONTAINER (menu), item); + g_signal_connect ((gpointer) item, "activate", + G_CALLBACK (on_add_tab_activate), + w); +} + +ddb_gtkui_widget_t * +w_tabs_create (void) { + w_tabs_t *w = malloc (sizeof (w_tabs_t)); + memset (w, 0, sizeof (w_tabs_t)); + w->base.widget = gtk_notebook_new (); + w->base.append = w_tabs_add; + w->base.remove = w_container_remove; + w->base.replace = w_tabs_replace; + w->base.initmenu = w_tabs_initmenu; + + ddb_gtkui_widget_t *ph1, *ph2, *ph3; + ph1 = w_create ("placeholder"); + ph2 = w_create ("placeholder"); + ph3 = w_create ("placeholder"); + +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect ((gpointer) w->base.widget, "expose_event", G_CALLBACK (w_expose_event), w); +#else + g_signal_connect ((gpointer) w->base.widget, "draw", G_CALLBACK (w_draw_event), w); +#endif + g_signal_connect ((gpointer) w->base.widget, "button_press_event", G_CALLBACK (w_button_press_event), w); + + w_append ((ddb_gtkui_widget_t*)w, ph1); + w_append ((ddb_gtkui_widget_t*)w, ph2); + w_append ((ddb_gtkui_widget_t*)w, ph3); + + return (ddb_gtkui_widget_t*)w; +} + +//// box widget +//// this widget should not be exposed to user, it is used as a top level +//// container (rootwidget) + +ddb_gtkui_widget_t * +w_box_create (void) { + w_box_t *w = malloc (sizeof (w_box_t)); + memset (w, 0, sizeof (w_box_t)); + w->base.widget = gtk_vbox_new (FALSE, 0); + w->base.append = w_container_add; + w->base.remove = w_container_remove; + + return (ddb_gtkui_widget_t*)w; +} + +//// tabstrip widget + +ddb_gtkui_widget_t * +w_tabstrip_create (void) { + w_tabstrip_t *w = malloc (sizeof (w_tabstrip_t)); + memset (w, 0, sizeof (w_tabstrip_t)); + w->base.widget = gtk_event_box_new (); + GtkWidget *ts = ddb_tabstrip_new (); + gtk_widget_show (ts); + gtk_container_add (GTK_CONTAINER (w->base.widget), ts); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t*)w; +} + +//// tabbed playlist widget + +typedef struct { + ddb_gtkui_widget_t *w; + DB_playItem_t *trk; +} w_trackdata_t; + +static gboolean +tabbed_trackinfochanged_cb (gpointer p) { + w_trackdata_t *d = p; + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)d->w; + ddb_playlist_t *plt = deadbeef->plt_get_curr (); + if (plt) { + int idx = deadbeef->plt_get_item_idx (plt, (DB_playItem_t *)d->trk, PL_MAIN); + if (idx != -1) { + ddb_listview_draw_row (tp->list, idx, (DdbListviewIter)d->trk); + } + deadbeef->plt_unref (plt); + } + if (d->trk) { + deadbeef->pl_item_unref (d->trk); + } + free (d); + return FALSE; +} + +static gboolean +trackinfochanged_cb (gpointer data) { + w_trackdata_t *d = data; + w_playlist_t *p = (w_playlist_t *)d->w; + ddb_playlist_t *plt = deadbeef->plt_get_curr (); + if (plt) { + int idx = deadbeef->plt_get_item_idx (plt, (DB_playItem_t *)d->trk, PL_MAIN); + if (idx != -1) { + ddb_listview_draw_row (DDB_LISTVIEW (p->list), idx, (DdbListviewIter)d->trk); + } + deadbeef->plt_unref (plt); + } + if (d->trk) { + deadbeef->pl_item_unref (d->trk); + } + free (d); + return FALSE; +} + +static gboolean +tabbed_paused_cb (gpointer p) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)p; + DB_playItem_t *curr = deadbeef->streamer_get_playing_track (); + if (curr) { + int idx = deadbeef->pl_get_idx_of (curr); + ddb_listview_draw_row (tp->list, idx, (DdbListviewIter)curr); + deadbeef->pl_item_unref (curr); + } + return FALSE; +} + +static gboolean +paused_cb (gpointer data) { + w_playlist_t *p = (w_playlist_t *)data; + DB_playItem_t *curr = deadbeef->streamer_get_playing_track (); + if (curr) { + int idx = deadbeef->pl_get_idx_of (curr); + ddb_listview_draw_row (p->list, idx, (DdbListviewIter)curr); + deadbeef->pl_item_unref (curr); + } + return FALSE; +} + +static gboolean +tabbed_refresh_cb (gpointer p) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)p; + ddb_listview_clear_sort (tp->list); + ddb_listview_refresh (tp->list, DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + return FALSE; +} + +static gboolean +refresh_cb (gpointer data) { + w_playlist_t *p = (w_playlist_t *)data; + ddb_listview_clear_sort (DDB_LISTVIEW (p->list)); + ddb_listview_refresh (DDB_LISTVIEW (p->list), DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + return FALSE; +} + +static gboolean +tabbed_playlistswitch_cb (gpointer p) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)p; + int curr = deadbeef->plt_get_curr_idx (); + char conf[100]; + snprintf (conf, sizeof (conf), "playlist.scroll.%d", curr); + int scroll = deadbeef->conf_get_int (conf, 0); + snprintf (conf, sizeof (conf), "playlist.cursor.%d", curr); + int cursor = deadbeef->conf_get_int (conf, -1); + ddb_tabstrip_refresh (tp->tabstrip); + deadbeef->pl_set_cursor (PL_MAIN, cursor); + if (cursor != -1) { + DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (cursor, PL_MAIN); + if (it) { + deadbeef->pl_set_selected (it, 1); + deadbeef->pl_item_unref (it); + } + } + + ddb_listview_refresh (tp->list, DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + ddb_listview_set_vscroll (tp->list, scroll); + return FALSE; +} + +static gboolean +playlistswitch_cb (gpointer data) { + w_playlist_t *p = (w_playlist_t *)data; + int curr = deadbeef->plt_get_curr_idx (); + char conf[100]; + snprintf (conf, sizeof (conf), "playlist.scroll.%d", curr); + int scroll = deadbeef->conf_get_int (conf, 0); + snprintf (conf, sizeof (conf), "playlist.cursor.%d", curr); + int cursor = deadbeef->conf_get_int (conf, -1); + deadbeef->pl_set_cursor (PL_MAIN, cursor); + if (cursor != -1) { + DB_playItem_t *it = deadbeef->pl_get_for_idx_and_iter (cursor, PL_MAIN); + if (it) { + deadbeef->pl_set_selected (it, 1); + deadbeef->pl_item_unref (it); + } + } + + ddb_listview_refresh (DDB_LISTVIEW (p->list), DDB_LIST_CHANGED | DDB_REFRESH_LIST | DDB_REFRESH_VSCROLL); + ddb_listview_set_vscroll (DDB_LISTVIEW (p->list), scroll); + return FALSE; +} + +struct fromto_t { + ddb_gtkui_widget_t *w; + DB_playItem_t *from; + DB_playItem_t *to; +}; + +static gboolean +tabbed_songchanged_cb (gpointer p) { + struct fromto_t *ft = p; + DB_playItem_t *from = ft->from; + DB_playItem_t *to = ft->to; + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)ft->w; + int to_idx = -1; + if (!ddb_listview_is_scrolling (tp->list) && to) { + int cursor_follows_playback = deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 0); + int scroll_follows_playback = deadbeef->conf_get_int ("playlist.scroll.followplayback", 0); + int plt = deadbeef->streamer_get_current_playlist (); + if (plt != -1) { + if (cursor_follows_playback && plt != deadbeef->plt_get_curr_idx ()) { + deadbeef->plt_set_curr_idx (plt); + } + to_idx = deadbeef->pl_get_idx_of (to); + if (to_idx != -1) { + if (cursor_follows_playback) { + ddb_listview_set_cursor_noscroll (tp->list, to_idx); + } + if (scroll_follows_playback && plt == deadbeef->plt_get_curr_idx ()) { + ddb_listview_scroll_to (tp->list, to_idx); + } + } + } + } + + if (from) { + int idx = deadbeef->pl_get_idx_of (from); + if (idx != -1) { + ddb_listview_draw_row (tp->list, idx, from); + } + } + if (to && to_idx != -1) { + ddb_listview_draw_row (tp->list, to_idx, to); + } + if (ft->from) { + deadbeef->pl_item_unref (ft->from); + } + if (ft->to) { + deadbeef->pl_item_unref (ft->to); + } + free (ft); + return FALSE; +} + +static gboolean +songchanged_cb (gpointer data) { + struct fromto_t *ft = data; + DB_playItem_t *from = ft->from; + DB_playItem_t *to = ft->to; + w_playlist_t *p = (w_playlist_t *)ft->w; + int to_idx = -1; + if (!ddb_listview_is_scrolling (DDB_LISTVIEW (p->list)) && to) { + int cursor_follows_playback = deadbeef->conf_get_int ("playlist.scroll.cursorfollowplayback", 0); + int scroll_follows_playback = deadbeef->conf_get_int ("playlist.scroll.followplayback", 0); + int plt = deadbeef->streamer_get_current_playlist (); + if (plt != -1) { + if (cursor_follows_playback && plt != deadbeef->plt_get_curr_idx ()) { + deadbeef->plt_set_curr_idx (plt); + } + to_idx = deadbeef->pl_get_idx_of (to); + if (to_idx != -1) { + if (cursor_follows_playback) { + ddb_listview_set_cursor_noscroll (DDB_LISTVIEW (p->list), to_idx); + } + if (scroll_follows_playback && plt == deadbeef->plt_get_curr_idx ()) { + ddb_listview_scroll_to (DDB_LISTVIEW (p->list), to_idx); + } + } + } + } + + if (from) { + int idx = deadbeef->pl_get_idx_of (from); + if (idx != -1) { + ddb_listview_draw_row (DDB_LISTVIEW (p->list), idx, from); + } + } + if (to && to_idx != -1) { + ddb_listview_draw_row (DDB_LISTVIEW (p->list), to_idx, to); + } + if (ft->from) { + deadbeef->pl_item_unref (ft->from); + } + if (ft->to) { + deadbeef->pl_item_unref (ft->to); + } + free (ft); + return FALSE; +} + +static gboolean +tabbed_trackfocus_cb (gpointer p) { + w_tabbed_playlist_t *tp = p; + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (it) { + int idx = deadbeef->pl_get_idx_of (it); + if (idx != -1) { + ddb_listview_scroll_to (tp->list, idx); + ddb_listview_set_cursor (tp->list, idx); + } + deadbeef->pl_item_unref (it); + } + + return FALSE; +} + +static gboolean +trackfocus_cb (gpointer p) { + w_playlist_t *tp = p; + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (it) { + int idx = deadbeef->pl_get_idx_of (it); + if (idx != -1) { + ddb_listview_scroll_to (tp->list, idx); + ddb_listview_set_cursor (tp->list, idx); + } + deadbeef->pl_item_unref (it); + } + + return FALSE; +} + +static int +w_tabbed_playlist_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)w; + switch (id) { + case DB_EV_SONGCHANGED: + g_idle_add (redraw_queued_tracks_cb, tp->list); + ddb_event_trackchange_t *ev = (ddb_event_trackchange_t *)ctx; + struct fromto_t *ft = malloc (sizeof (struct fromto_t)); + ft->from = ev->from; + ft->to = ev->to; + if (ft->from) { + deadbeef->pl_item_ref (ft->from); + } + if (ft->to) { + deadbeef->pl_item_ref (ft->to); + } + ft->w = w; + g_idle_add (tabbed_songchanged_cb, ft); + break; + case DB_EV_TRACKINFOCHANGED: + { + ddb_event_track_t *ev = (ddb_event_track_t *)ctx; + if (ev->track) { + deadbeef->pl_item_ref (ev->track); + } + w_trackdata_t *d = malloc (sizeof (w_trackdata_t)); + memset (d, 0, sizeof (w_trackdata_t)); + d->w = w; + d->trk = ev->track; + g_idle_add (tabbed_trackinfochanged_cb, d); + } + break; + case DB_EV_PAUSED: + g_idle_add (tabbed_paused_cb, w); + break; + case DB_EV_PLAYLISTCHANGED: + g_idle_add (tabbed_refresh_cb, w); + break; + case DB_EV_PLAYLISTSWITCHED: + g_idle_add (tabbed_playlistswitch_cb, w); + break; + case DB_EV_TRACKFOCUSCURRENT: + g_idle_add (tabbed_trackfocus_cb, w); + break; + } + return 0; +} + +static int +w_playlist_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_playlist_t *p = (w_playlist_t *)w; + switch (id) { + case DB_EV_SONGCHANGED: + g_idle_add (redraw_queued_tracks_cb, p->list); + ddb_event_trackchange_t *ev = (ddb_event_trackchange_t *)ctx; + struct fromto_t *ft = malloc (sizeof (struct fromto_t)); + ft->from = ev->from; + ft->to = ev->to; + if (ft->from) { + deadbeef->pl_item_ref (ft->from); + } + if (ft->to) { + deadbeef->pl_item_ref (ft->to); + } + ft->w = w; + g_idle_add (songchanged_cb, ft); + break; + case DB_EV_TRACKINFOCHANGED: + { + ddb_event_track_t *ev = (ddb_event_track_t *)ctx; + if (ev->track) { + deadbeef->pl_item_ref (ev->track); + } + w_trackdata_t *d = malloc (sizeof (w_trackdata_t)); + memset (d, 0, sizeof (w_trackdata_t)); + d->w = w; + d->trk = ev->track; + g_idle_add (trackinfochanged_cb, d); + } + break; + case DB_EV_PAUSED: + g_idle_add (paused_cb, w); + break; + case DB_EV_PLAYLISTCHANGED: + g_idle_add (refresh_cb, w); + break; + case DB_EV_PLAYLISTSWITCHED: + g_idle_add (playlistswitch_cb, w); + break; + case DB_EV_TRACKFOCUSCURRENT: + g_idle_add (trackfocus_cb, w); + break; + } + return 0; +} +ddb_gtkui_widget_t * +w_tabbed_playlist_create (void) { + w_tabbed_playlist_t *w = malloc (sizeof (w_tabbed_playlist_t)); + memset (w, 0, sizeof (w_tabbed_playlist_t)); + + GtkWidget *vbox = gtk_vbox_new (FALSE, 0); + w->base.widget = vbox; + gtk_widget_show (vbox); + + GtkWidget *tabstrip = ddb_tabstrip_new (); + w->tabstrip = (DdbTabStrip *)tabstrip; + gtk_widget_show (tabstrip); + GtkWidget *list = ddb_listview_new (); + w->list = (DdbListview *)list; + gtk_widget_show (list); + GtkWidget *frame = gtk_frame_new (NULL); + gtk_widget_show (frame); + + gtk_box_pack_start (GTK_BOX (vbox), tabstrip, FALSE, TRUE, 0); + gtk_widget_set_size_request (tabstrip, -1, 24); + gtk_widget_set_can_focus (tabstrip, FALSE); + gtk_widget_set_can_default (tabstrip, FALSE); + + gtk_box_pack_start (GTK_BOX (vbox), frame, TRUE, TRUE, 0); + gtk_container_set_border_width (GTK_CONTAINER (frame), 1); + + gtk_container_add (GTK_CONTAINER (frame), list); + main_playlist_init (list); + if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { + ddb_listview_show_header (w->list, 1); + } + else { + ddb_listview_show_header (w->list, 0); + } + +// gtk_container_forall (GTK_CONTAINER (w->base.widget), w_override_signals, w); + w_override_signals (w->base.widget, w); + + w->base.message = w_tabbed_playlist_message; + return (ddb_gtkui_widget_t*)w; +} + +///// playlist widget + +ddb_gtkui_widget_t * +w_playlist_create (void) { + w_playlist_t *w = malloc (sizeof (w_playlist_t)); + memset (w, 0, sizeof (w_playlist_t)); + w->base.widget = gtk_event_box_new (); + w->list = DDB_LISTVIEW (ddb_listview_new ()); + gtk_widget_show (GTK_WIDGET (w->list)); + main_playlist_init (GTK_WIDGET (w->list)); + if (deadbeef->conf_get_int ("gtkui.headers.visible", 1)) { + ddb_listview_show_header (DDB_LISTVIEW (w->list), 1); + } + else { + ddb_listview_show_header (DDB_LISTVIEW (w->list), 0); + } + + gtk_container_add (GTK_CONTAINER (w->base.widget), GTK_WIDGET (w->list)); + w_override_signals (w->base.widget, w); + w->base.message = w_playlist_message; + return (ddb_gtkui_widget_t*)w; +} + +////// selection properties widget + +gboolean +fill_selproperties_cb (gpointer data) { + w_selproperties_t *w = data; + DB_playItem_t **tracks = NULL; + int numtracks = 0; + deadbeef->pl_lock (); + int nsel = deadbeef->pl_getselcount (); + if (0 < nsel) { + tracks = malloc (sizeof (DB_playItem_t *) * nsel); + if (tracks) { + int n = 0; + DB_playItem_t *it = deadbeef->pl_get_first (PL_MAIN); + while (it) { + if (deadbeef->pl_is_selected (it)) { + assert (n < nsel); + deadbeef->pl_item_ref (it); + tracks[n++] = it; + } + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + numtracks = nsel; + } + else { + deadbeef->pl_unlock (); + return FALSE; + } + } + GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (w->tree))); + trkproperties_fill_meta (store, tracks, numtracks); + if (tracks) { + for (int i = 0; i < numtracks; i++) { + deadbeef->pl_item_unref (tracks[i]); + } + free (tracks); + tracks = NULL; + numtracks = 0; + } + deadbeef->pl_unlock (); + return FALSE; +} + +static int +selproperties_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_tabbed_playlist_t *tp = (w_tabbed_playlist_t *)w; + switch (id) { + case DB_EV_PLAYLISTCHANGED: + case DB_EV_SELCHANGED: + { + g_idle_add (fill_selproperties_cb, w); + } + break; + } + return 0; +} + +ddb_gtkui_widget_t * +w_selproperties_create (void) { + w_selproperties_t *w = malloc (sizeof (w_selproperties_t)); + memset (w, 0, sizeof (w_selproperties_t)); + + w->base.widget = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (w->base.widget), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + w->tree = gtk_tree_view_new (); + gtk_widget_show (w->tree); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->tree); + w->base.message = selproperties_message; + + GtkListStore *store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT); + gtk_tree_view_set_model (GTK_TREE_VIEW (w->tree), GTK_TREE_MODEL (store)); + gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (w->tree), TRUE); + + GtkCellRenderer *rend1 = gtk_cell_renderer_text_new (); + GtkCellRenderer *rend2 = gtk_cell_renderer_text_new (); + GtkTreeViewColumn *col1 = gtk_tree_view_column_new_with_attributes (_("Key"), rend1, "text", 0, NULL); + gtk_tree_view_column_set_resizable (col1, TRUE); + GtkTreeViewColumn *col2 = gtk_tree_view_column_new_with_attributes (_("Value"), rend2, "text", 1, NULL); + gtk_tree_view_column_set_resizable (col2, TRUE); + gtk_tree_view_append_column (GTK_TREE_VIEW (w->tree), col1); + gtk_tree_view_append_column (GTK_TREE_VIEW (w->tree), col2); + GtkCellRenderer *rend_propkey = gtk_cell_renderer_text_new (); + GtkCellRenderer *rend_propvalue = gtk_cell_renderer_text_new (); + gtk_tree_view_set_headers_clickable (GTK_TREE_VIEW (w->tree), TRUE); + w_override_signals (w->base.widget, w); + + return (ddb_gtkui_widget_t *)w; +} + +///// cover art display +void +coverart_avail_callback (void *user_data) { + w_coverart_t *w = user_data; + gtk_widget_queue_draw (w->drawarea); +} + +static gboolean +coverart_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (!it) { + return FALSE; + } + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + int width = a.width; + int height = a.height; + const char *album = deadbeef->pl_find_meta (it, "album"); + const char *artist = deadbeef->pl_find_meta (it, "artist"); + if (!album || !*album) { + album = deadbeef->pl_find_meta (it, "title"); + } + GdkPixbuf *pixbuf = get_cover_art_callb (deadbeef->pl_find_meta ((it), ":URI"), artist, album, min(width,height), coverart_avail_callback, user_data); + if (pixbuf) { + int pw = gdk_pixbuf_get_width (pixbuf); + int ph = gdk_pixbuf_get_height (pixbuf); + gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0); + cairo_rectangle (cr, 0, 0, pw, ph); + cairo_fill (cr); +// gdk_draw_pixbuf (gtk_widget_get_window (widget), widget->style->white_gc, pixbuf, 0, 0, a.width/2-pw/2, a.height/2-ph/2, pw, ph, GDK_RGB_DITHER_NONE, 0, 0); + g_object_unref (pixbuf); + } + deadbeef->pl_item_unref (it); + return TRUE; +} + +static gboolean +coverart_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = coverart_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +static gboolean +coverart_redraw_cb (void *user_data) { + w_coverart_t *w = user_data; + gtk_widget_queue_draw (w->drawarea); + return FALSE; +} + +static int +coverart_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + w_coverart_t *ca = (w_coverart_t *)w; + switch (id) { + case DB_EV_TRACKINFOCHANGED: + { + ddb_event_track_t *ev = (ddb_event_track_t *)ctx; + DB_playItem_t *it = deadbeef->streamer_get_playing_track (); + if (it == ev->track) { + g_idle_add (coverart_redraw_cb, w); + } + if (it) { + deadbeef->pl_item_unref (it); + } + } + break; + } + return 0; +} + +ddb_gtkui_widget_t * +w_coverart_create (void) { + w_coverart_t *w = malloc (sizeof (w_coverart_t)); + memset (w, 0, sizeof (w_coverart_t)); + + w->base.widget = gtk_event_box_new (); + w->base.message = coverart_message; + w->drawarea = gtk_drawing_area_new (); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (coverart_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (coverart_draw), w); +#endif + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t *)w; +} + +///// scope vis +void +w_scope_destroy (ddb_gtkui_widget_t *w) { + w_scope_t *s = (w_scope_t *)w; + if (s->drawtimer) { + g_source_remove (s->drawtimer); + s->drawtimer = 0; + } + if (s->glcontext) { + gdk_gl_context_destroy (s->glcontext); + s->glcontext = NULL; + } +} + +gboolean +w_scope_draw_cb (void *data) { + w_scope_t *s = data; + gtk_widget_queue_draw (s->drawarea); + return TRUE; +} + +gboolean +scope_draw (GtkWidget *widget, cairo_t *cr, gpointer user_data) { + ddb_waveformat_t fmt; + float data[DDB_AUDIO_MEMORY_FRAMES]; + deadbeef->audio_get_waveform_data (DDB_AUDIO_WAVEFORM, data); + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE); + cairo_set_line_width (cr, 1); + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + + float incr = (float)DDB_AUDIO_MEMORY_FRAMES / a.width; + float pos = 0; + for (int x = 0; x < a.width; x++, pos += incr) { + float s = data[(int)pos]; + cairo_line_to (cr, x, s * a.height/2 / 0x7fff + a.height/2); + } + cairo_stroke (cr); + + return FALSE; +} + +gboolean +scope_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + w_scope_t *w = user_data; + float data[DDB_AUDIO_MEMORY_FRAMES]; + deadbeef->audio_get_waveform_data (DDB_AUDIO_WAVEFORM, data); + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + + GdkGLDrawable *d = gtk_widget_get_gl_drawable (widget); + gdk_gl_drawable_gl_begin (d, w->glcontext); + // if (glXSwapIntervalSGI) { + // glXSwapIntervalSGI (1); + // } + + glClear (GL_COLOR_BUFFER_BIT); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + gluOrtho2D(0,a.width,a.height,0); + glMatrixMode (GL_MODELVIEW); + glViewport (0, 0, a.width, a.height); + +#if 0 + // vsync test + static int box = 0; + static int speed = 5; + if (box > a.width-50) { + box = a.width-50; + speed = -5; + } + else if (box < 0) { + box = 0; + speed = 5; + } + box += speed; + + glBegin (GL_QUADS); + glVertex2f (box, 0); + glVertex2f (box+50, 0); + glVertex2f (box+50, 50); + glVertex2f (box, 50); + glEnd (); +#endif + glBegin (GL_LINE_STRIP); + + short *samples = (short *)data; + + float incr = a.width / (float)DDB_AUDIO_MEMORY_FRAMES; + int pos = 0; + for (float x = 0; x < a.width && pos < DDB_AUDIO_MEMORY_FRAMES; x += incr, pos ++) { + float s = data[(int)pos]; + glVertex2f (x, s * a.height/2 + a.height/2); + } + + glEnd(); + gdk_gl_drawable_swap_buffers (d); + + gdk_gl_drawable_gl_end (d); + + return FALSE; + + cairo_t *cr = gdk_cairo_create (gtk_widget_get_window (widget)); + gboolean res = scope_draw (widget, cr, user_data); + cairo_destroy (cr); + return res; +} + +void +w_scope_init (ddb_gtkui_widget_t *w) { + w_scope_t *s = (w_scope_t *)w; + if (s->drawtimer) { + g_source_remove (s->drawtimer); + s->drawtimer = 0; + } + if (!gtkui_gl_init ()) { + s->drawtimer = g_timeout_add (33, w_scope_draw_cb, w); + } +} + +void +scope_realize (GtkWidget *widget, gpointer data) { + w_scope_t *w = data; + w->glcontext = gtk_widget_create_gl_context (w->drawarea, NULL, TRUE, GDK_GL_RGBA_TYPE); +} + +ddb_gtkui_widget_t * +w_scope_create (void) { + w_scope_t *w = malloc (sizeof (w_scope_t)); + memset (w, 0, sizeof (w_scope_t)); + + w->base.widget = gtk_event_box_new (); + w->base.init = w_scope_init; + w->base.destroy = w_scope_destroy; + w->drawarea = gtk_drawing_area_new (); + int attrlist[] = {GDK_GL_ATTRIB_LIST_NONE}; + GdkGLConfig *conf = gdk_gl_config_new_by_mode ((GdkGLConfigMode)(GDK_GL_MODE_RGB | + GDK_GL_MODE_DOUBLE)); + gboolean cap = gtk_widget_set_gl_capability (w->drawarea, conf, NULL, TRUE, GDK_GL_RGBA_TYPE); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (scope_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (scope_draw), w); +#endif + g_signal_connect_after (G_OBJECT (w->drawarea), "realize", G_CALLBACK (scope_realize), w); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t *)w; +} + +///// spectrum vis +void +w_spectrum_destroy (ddb_gtkui_widget_t *w) { + w_spectrum_t *s = (w_spectrum_t *)w; + if (s->drawtimer) { + g_source_remove (s->drawtimer); + s->drawtimer = 0; + } + if (s->glcontext) { + gdk_gl_context_destroy (s->glcontext); + s->glcontext = NULL; + } +} + +gboolean +w_spectrum_draw_cb (void *data) { + w_spectrum_t *s = data; + gtk_widget_queue_draw (s->drawarea); + return TRUE; +} + +// spectrum analyzer based on cairo-spectrum from audacious +// Copyright (c) 2011 William Pitcock <nenolod@dereferenced.org> +#define MAX_BANDS 256 +#define VIS_DELAY 1 +#define VIS_DELAY_PEAK 10 +#define VIS_FALLOFF 3 +#define VIS_FALLOFF_PEAK 1 +#define BAND_WIDTH 5 +static float xscale[MAX_BANDS + 1]; +static int bars[MAX_BANDS + 1]; +static int delay[MAX_BANDS + 1]; +static int peaks[MAX_BANDS + 1]; +static int delay_peak[MAX_BANDS + 1]; + +static void calculate_bands(int bands) +{ + int i; + + for (i = 0; i < bands; i++) + xscale[i] = powf(257., ((float) i / (float) bands)) - 1; +} + + +gboolean +spectrum_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data) { + w_spectrum_t *w = user_data; + float data[DDB_AUDIO_MEMORY_FRAMES]; + float *freq = data; + deadbeef->audio_get_waveform_data (DDB_AUDIO_FREQ, data); + + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + + int width, height, bands; + bands = a.width/BAND_WIDTH; + bands = CLAMP(bands, 4, MAX_BANDS); + width = a.width; + height = a.height; + calculate_bands(bands); + + for (int i = 0; i < bands; i ++) + { + int a = ceil (xscale[i]); + int b = floor (xscale[i + 1]); + float n = 0; + + if (b < a) + n += freq[b] * (xscale[i + 1] - xscale[i]); + else + { + if (a > 0) + n += freq[a - 1] * (a - xscale[i]); + for (; a < b; a ++) + n += freq[a]; + if (b < 256) + n += freq[b] * (xscale[i + 1] - b); + } + + /* 40 dB range */ + int x = 20 * log10 (n * 100); + x = CLAMP (x, 0, 40); + + bars[i] -= MAX (0, VIS_FALLOFF - delay[i]); + peaks[i] -= MAX (0, VIS_FALLOFF_PEAK - delay_peak[i]);; + + if (delay[i]) + delay[i]--; + if (delay_peak[i]) + delay_peak[i]--; + + if (x > bars[i]) + { + bars[i] = x; + delay[i] = VIS_DELAY; + } + if (x > peaks[i]) { + peaks[i] = x; + delay_peak[i] = VIS_DELAY_PEAK; + } + } + + GdkGLDrawable *d = gtk_widget_get_gl_drawable (widget); + gdk_gl_drawable_gl_begin (d, w->glcontext); + + glClear (GL_COLOR_BUFFER_BIT); + glMatrixMode (GL_PROJECTION); + glLoadIdentity (); + gluOrtho2D(0,a.width,a.height,0); + glMatrixMode (GL_MODELVIEW); + glViewport (0, 0, a.width, a.height); + + glBegin (GL_QUADS); + float base_s = (height / 40.f); + + for (gint i = 0; i <= bands; i++) + { + gint x = ((width / bands) * i) + 2; + int y = a.height - bars[i] * base_s; + glColor3f (0, 0.5, 1); + glVertex2f (x + 1, y); + glVertex2f (x + 1 + (width / bands) - 1, y); + glVertex2f (x + 1 + (width / bands) - 1, a.height); + glVertex2f (x + 1, a.height); + + // peak + glColor3f (1, 1, 1); + y = a.height - peaks[i] * base_s; + glVertex2f (x + 1, y); + glVertex2f (x + 1 + (width / bands) - 1, y); + glVertex2f (x + 1 + (width / bands) - 1, y+1); + glVertex2f (x + 1, y+1); + } + glEnd(); + gdk_gl_drawable_swap_buffers (d); + + gdk_gl_drawable_gl_end (d); + + return FALSE; +} + +void +w_spectrum_init (ddb_gtkui_widget_t *w) { + w_spectrum_t *s = (w_spectrum_t *)w; + gtkui_gl_init (); + if (s->drawtimer) { + g_source_remove (s->drawtimer); + s->drawtimer = 0; + } + if (!gtkui_gl_init ()) { + s->drawtimer = g_timeout_add (33, w_spectrum_draw_cb, w); + } +} + +void +spectrum_realize (GtkWidget *widget, gpointer data) { + w_spectrum_t *w = data; + w->glcontext = gtk_widget_create_gl_context (w->drawarea, NULL, TRUE, GDK_GL_RGBA_TYPE); +} + +ddb_gtkui_widget_t * +w_spectrum_create (void) { + w_spectrum_t *w = malloc (sizeof (w_spectrum_t)); + memset (w, 0, sizeof (w_spectrum_t)); + + w->base.widget = gtk_event_box_new (); + w->base.init = w_spectrum_init; + w->base.destroy = w_spectrum_destroy; + w->drawarea = gtk_drawing_area_new (); + int attrlist[] = {GDK_GL_ATTRIB_LIST_NONE}; + GdkGLConfig *conf = gdk_gl_config_new_by_mode ((GdkGLConfigMode)(GDK_GL_MODE_RGB | + GDK_GL_MODE_DOUBLE)); + gboolean cap = gtk_widget_set_gl_capability (w->drawarea, conf, NULL, TRUE, GDK_GL_RGBA_TYPE); + gtk_widget_show (w->drawarea); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); +#if !GTK_CHECK_VERSION(3,0,0) + g_signal_connect_after ((gpointer) w->drawarea, "expose_event", G_CALLBACK (spectrum_expose_event), w); +#else + g_signal_connect_after ((gpointer) w->drawarea, "draw", G_CALLBACK (spectrum_draw), w); +#endif + g_signal_connect_after (G_OBJECT (w->drawarea), "realize", G_CALLBACK (spectrum_realize), w); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t *)w; +} + diff --git a/plugins/gtkui/widgets.h b/plugins/gtkui/widgets.h new file mode 100644 index 00000000..56a3672d --- /dev/null +++ b/plugins/gtkui/widgets.h @@ -0,0 +1,105 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#ifndef __WIDGETS_H +#define __WIDGETS_H + +#include "gtkui_api.h" + +void +w_init (void); + +void +w_free (void); + +void +w_save (void); + +ddb_gtkui_widget_t * +w_get_rootwidget (void); + +void +w_set_design_mode (int active); + +void +w_reg_widget (const char *type, const char *title, ddb_gtkui_widget_t *(*create_func) (void)); + +void +w_unreg_widget (const char *type); + +int +w_is_registered (const char *type); + +ddb_gtkui_widget_t * +w_create (const char *type); + +void +w_set_name (ddb_gtkui_widget_t *w, const char *name); + +const char * +w_create_from_string (const char *s, ddb_gtkui_widget_t **parent); + +void +w_destroy (ddb_gtkui_widget_t *w); + +void +w_append (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); + +void +w_replace (ddb_gtkui_widget_t *w, ddb_gtkui_widget_t *from, ddb_gtkui_widget_t *to); + +void +w_remove (ddb_gtkui_widget_t *cont, ddb_gtkui_widget_t *child); + +ddb_gtkui_widget_t * +w_hsplitter_create (void); + +ddb_gtkui_widget_t * +w_vsplitter_create (void); + +ddb_gtkui_widget_t * +w_box_create (void); + +ddb_gtkui_widget_t * +w_tabstrip_create (void); + +ddb_gtkui_widget_t * +w_tabbed_playlist_create (void); + +ddb_gtkui_widget_t * +w_playlist_create (void); + +ddb_gtkui_widget_t * +w_placeholder_create (void); + +ddb_gtkui_widget_t * +w_tabs_create (void); + +ddb_gtkui_widget_t * +w_selproperties_create (void); + +ddb_gtkui_widget_t * +w_coverart_create (void); + +ddb_gtkui_widget_t * +w_scope_create (void); + +ddb_gtkui_widget_t * +w_spectrum_create (void); + +#endif diff --git a/plugins/medialib/Makefile.am b/plugins/medialib/Makefile.am new file mode 100644 index 00000000..0406cac9 --- /dev/null +++ b/plugins/medialib/Makefile.am @@ -0,0 +1,8 @@ +if HAVE_MEDIALIB +pkglib_LTLIBRARIES = medialib.la +medialib_la_SOURCES = medialib.c +medialib_la_LDFLAGS = -module + +medialib_la_LIBADD = $(LDADD) +AM_CFLAGS = $(CFLAGS) -std=c99 -fPIC +endif diff --git a/plugins/medialib/medialib.c b/plugins/medialib/medialib.c new file mode 100644 index 00000000..8a957156 --- /dev/null +++ b/plugins/medialib/medialib.c @@ -0,0 +1,256 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include <sys/time.h> +#include <string.h> +#include "../../deadbeef.h" + +DB_functions_t *deadbeef; + +typedef struct ml_string_s { + const char *text; + struct ml_string_s *next; +} ml_string_t; + +typedef struct ml_entry_s { + const char *file; + const char *title; + int subtrack; + ml_string_t *artist; + ml_string_t *album; + ml_string_t *genre; + ml_string_t *folder; + struct ml_entry_s *next; +} ml_entry_t; + +typedef struct { + ml_entry_t *tracks; + + // index tables + ml_string_t *album; + ml_string_t *artist; + ml_string_t *genre; + ml_string_t *folder; +} ml_db_t; + +#define REG_COL(col)\ +ml_string_t *\ +ml_reg_##col (ml_db_t *db, const char *col) {\ + ml_string_t *s;\ + int release = 0;\ + if (!col) {\ + col = deadbeef->metacache_add_string ("Unknown");\ + release = 1;\ + }\ + for (s = db->col; s; s = s->next) {\ + if (s->text == col) {\ + if (release) {\ + deadbeef->metacache_unref (col);\ + }\ + return s;\ + }\ + }\ + if (!release) {\ + deadbeef->metacache_ref (col);\ + }\ + s = malloc (sizeof (ml_string_t));\ + memset (s, 0, sizeof (ml_string_t));\ + s->text = col;\ + s->next = db->col;\ + db->col = s;\ + return s;\ +} + +REG_COL(album); +REG_COL(artist); +REG_COL(genre); +REG_COL(folder); + +DB_playItem_t *(*plt_insert_dir) (ddb_playlist_t *plt, DB_playItem_t *after, const char *dirname, int *pabort, int (*cb)(DB_playItem_t *it, void *data), void *user_data); + +uintptr_t tid; +int scanner_terminate; + +static int +add_file_info_cb (DB_playItem_t *it, void *data) { +// fprintf (stderr, "added %s \r", deadbeef->pl_find_meta (it, ":URI")); + return 0; +} + +static void +scanner_thread (void *none) { + return; + // create invisible playlist + ddb_playlist_t *plt = deadbeef->plt_alloc ("scanner"); + + struct timeval tm1; + gettimeofday (&tm1, NULL); + + plt_insert_dir (plt, NULL, "/backup/mus/en", &scanner_terminate, add_file_info_cb, NULL); + + struct timeval tm2; + gettimeofday (&tm2, NULL); + int ms = (tm2.tv_sec*1000+tm2.tv_usec/1000) - (tm1.tv_sec*1000+tm1.tv_usec/1000); + fprintf (stderr, "initial scan time: %f seconds (%d tracks)\n", ms / 1000.f, deadbeef->plt_get_item_count (plt, PL_MAIN)); + + fprintf (stderr, "building index...\n"); + gettimeofday (&tm1, NULL); + ml_db_t db; + memset (&db, 0, sizeof (db)); + + ml_entry_t *tail = NULL; + + DB_playItem_t *it = deadbeef->plt_get_first (plt, PL_MAIN); + while (it) { + + ml_entry_t *en = malloc (sizeof (ml_entry_t)); + memset (en, 0, sizeof (ml_entry_t)); + + const char *uri = deadbeef->pl_find_meta (it, ":URI"); + const char *title = deadbeef->pl_find_meta (it, "title"); + const char *artist = deadbeef->pl_find_meta (it, "artist"); + const char *album = deadbeef->pl_find_meta (it, "album"); + const char *genre = deadbeef->pl_find_meta (it, "genre"); + + ml_string_t *alb = ml_reg_album (&db, album); + ml_string_t *art = ml_reg_artist (&db, artist); + ml_string_t *gnr = ml_reg_genre (&db, genre); + + char *fn = strrchr (uri, '/'); + ml_string_t *fld = NULL; + if (fn) { + char folder[fn-uri+1]; + memcpy (folder, uri, fn-uri); + folder[fn-uri] = 0; + const char *s = deadbeef->metacache_add_string (folder); + fld = ml_reg_folder (&db, s); + deadbeef->metacache_unref (s); // there should be at least 1 ref left, so it's safe + } + + deadbeef->metacache_ref (uri); + en->file = uri; + deadbeef->metacache_ref (title); + if (deadbeef->pl_get_item_flags (it) & DDB_IS_SUBTRACK) { + en->subtrack = deadbeef->pl_find_meta_int (it, ":TRACKNUM", -1); + } + else { + en->subtrack = -1; + } + en->title = title; + en->artist = art; + en->album = alb; + en->genre = gnr; + en->folder = fld; + + if (tail) { + tail->next = en; + tail = en; + } + else { + tail = db.tracks = en; + } + + DB_playItem_t *next = deadbeef->pl_get_next (it, PL_MAIN); + deadbeef->pl_item_unref (it); + it = next; + } + ms = (tm2.tv_sec*1000+tm2.tv_usec/1000) - (tm1.tv_sec*1000+tm1.tv_usec/1000); + + int nalb = 0; + int nart = 0; + int ngnr = 0; + int nfld = 0; + ml_string_t *s; + for (s = db.album; s; s = s->next, nalb++); + for (s = db.artist; s; s = s->next, nart++); + for (s = db.genre; s; s = s->next, ngnr++); + for (s = db.folder; s; s = s->next, nfld++); + + fprintf (stderr, "index build time: %f seconds (%d albums, %d artists, %d genres, %d folders)\n", ms / 1000.f, nalb, nart, ngnr, nfld); + + deadbeef->plt_free (plt); +} + +static int +ml_connect (void) { + tid = deadbeef->thread_start_low_priority (scanner_thread, NULL); + return 0; +} + +static int +ml_stop (void) { + if (tid) { + scanner_terminate = 1; + deadbeef->thread_join (tid); + tid = 0; + } + + return 0; +} + +static int +ml_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + return 0; +} + +typedef struct { + DB_misc_t plugin; +} ddb_medialib_plugin_t; + +// define plugin interface +static ddb_medialib_plugin_t plugin = { + .plugin.plugin.api_vmajor = 1, + .plugin.plugin.api_vminor = 0, + .plugin.plugin.version_major = 0, + .plugin.plugin.version_minor = 1, + .plugin.plugin.type = DB_PLUGIN_MISC, + .plugin.plugin.id = "medialib", + .plugin.plugin.name = "Media Library", + .plugin.plugin.descr = "Scans disk for music files and manages them as database", + .plugin.plugin.copyright = + "Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This program is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU General Public License\n" + "as published by the Free Software Foundation; either version 2\n" + "of the License, or (at your option) any later version.\n" + "\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program; if not, write to the Free Software\n" + "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" + , + .plugin.plugin.website = "http://deadbeef.sf.net", + .plugin.plugin.connect = ml_connect, + .plugin.plugin.stop = ml_stop, +// .plugin.plugin.configdialog = settings_dlg, + .plugin.plugin.message = ml_message, +}; + +DB_plugin_t * +medialib_load (DB_functions_t *api) { + deadbeef = api; + + // hack: we need original function without overrides + plt_insert_dir = deadbeef->plt_insert_dir; + return DB_PLUGIN (&plugin); +} diff --git a/plugins/supereq/Makefile.am b/plugins/supereq/Makefile.am index af5572c6..0096e4ab 100644 --- a/plugins/supereq/Makefile.am +++ b/plugins/supereq/Makefile.am @@ -3,47 +3,6 @@ supereqdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = supereq.la supereq_la_SOURCES = supereq.c Equ.cpp Equ.h Fftsg_fl.c paramlist.hpp -#nsfft-1.00/simd/SIMDBaseUndiff.c\ -#nsfft-1.00/simd/SIMDBase.c\ -#nsfft-1.00/dft/DFT.c\ -#nsfft-1.00/dft/DFTUndiff.c\ -#nsfft-1.00/simd/SIMDBase.h\ -#nsfft-1.00/simd/SIMDBaseUndiff.h\ -#nsfft-1.00/dft/DFTUndiff.h\ -#nsfft-1.00/dft/DFT.h\ -#shibatch_rdft.c - -#ffmpeg_fft/libavutil/mem.c\ -#ffmpeg_fft/libavutil/mathematics.c\ -#ffmpeg_fft/libavutil/rational.c\ -#ffmpeg_fft/libavutil/intfloat_readwrite.c\ -#ffmpeg_fft/libavcodec/dct.c\ -#ffmpeg_fft/libavcodec/avfft.c\ -#ffmpeg_fft/libavcodec/fft.c\ -#ffmpeg_fft/libavcodec/dct32.c\ -#ffmpeg_fft/libavcodec/rdft.c\ -#ffmpeg_fft/libavutil/intfloat_readwrite.h\ -#ffmpeg_fft/libavutil/avutil.h\ -#ffmpeg_fft/libavutil/common.h\ -#ffmpeg_fft/libavutil/attributes.h\ -#ffmpeg_fft/libavutil/mem.h\ -#ffmpeg_fft/libavutil/avconfig.h\ -#ffmpeg_fft/libavutil/mathematics.h\ -#ffmpeg_fft/libavutil/rational.h\ -#ffmpeg_fft/publik.h\ -#ffmpeg_fft/ffmpeg_fft.h\ -#ffmpeg_fft/libavcodec/dct32.h\ -#ffmpeg_fft/libavcodec/fft.h\ -#ffmpeg_fft/libavcodec/avfft.h\ -#ffmpeg_fft/config.h\ -#ff_rdft.c - -#AM_CFLAGS = $(CFLAGS) -I ffmpeg_fft -I ffmpeg_fft/libavcodec -I ffmpeg_fft/libavutil -std=c99 -#AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -I ffmpeg_fft -I ffmpeg_fft/libavcodec -I ffmpeg_fft/libavutil - -#AM_CFLAGS = $(CFLAGS) -I nsfft-1.00/dft -I nsfft-1.00/simd -std=c99 -msse -DENABLE_SSE_FLOAT -DUSE_SHIBATCH -#AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -I nsfft-1.00/dft -I nsfft-1.00/simd -msse -DENABLE_SSE_FLOAT -DUSE_SHIBATCH - AM_CFLAGS = $(CFLAGS) -std=c99 -DUSE_OOURA AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -DUSE_OOURA diff --git a/scripts/quickinstall.sh b/scripts/quickinstall.sh index ca09088b..41cc412e 100755 --- a/scripts/quickinstall.sh +++ b/scripts/quickinstall.sh @@ -45,5 +45,6 @@ cp ./plugins/converter/.libs/converter_gtk2.so /usr/local/lib/deadbeef/ cp ./plugins/converter/.libs/converter_gtk3.so /usr/local/lib/deadbeef/ cp ./plugins/soundtouch/ddb_soundtouch.so /usr/local/lib/deadbeef/ cp ./plugins/vfs_zip/.libs/vfs_zip.so /usr/local/lib/deadbeef/ +cp ./plugins/medialib/.libs/medialib.so /usr/local/lib/deadbeef/ cp ./plugins/mono2stereo/.libs/ddb_mono2stereo.so /usr/local/lib/deadbeef/ cp ./plugins/alac/.libs/alac.so /usr/local/lib/deadbeef/ @@ -99,6 +99,7 @@ static char streambuffer[STREAM_BUFFER_SIZE]; static int bytes_until_next_song = 0; static uintptr_t mutex; static uintptr_t decodemutex; +static uintptr_t audio_mem_mutex; static int nextsong = -1; static int nextsong_pstate = -1; static int badsong = -1; @@ -128,6 +129,10 @@ static int streamer_buffering; // to allow interruption of stall file requests static DB_FILE *streamer_file; +// for vis plugins +static float freq_data[DDB_AUDIO_MEMORY_FRAMES]; +static float audio_data[DDB_AUDIO_MEMORY_FRAMES]; + #if DETECT_PL_LOCK_RC volatile pthread_t streamer_lock_tid = 0; #endif @@ -1747,6 +1752,7 @@ streamer_init (void) { #endif mutex = mutex_create (); decodemutex = mutex_create (); + audio_mem_mutex = mutex_create (); ringbuf_init (&streamer_ringbuf, streambuffer, STREAM_BUFFER_SIZE); @@ -1788,6 +1794,8 @@ streamer_free (void) { decodemutex = 0; mutex_free (mutex); mutex = 0; + mutex_free (audio_mem_mutex); + audio_mem_mutex = 0; streamer_dsp_chain_save(); @@ -1994,6 +2002,16 @@ streamer_read_async (char *bytes, int size) { return bytesread; } +void rdft(int, int, float *, int *, float *); +static void do_fft(int n,float *x) +{ + static int ipsize = 0,wsize=0; + static int ip[18]; + static float w[256]; + + rdft(n,1,x,ip,w); +} + int streamer_read (char *bytes, int size) { #if 0 @@ -2056,6 +2074,34 @@ streamer_read (char *bytes, int size) { printf ("streamer_read took %d ms\n", ms); #endif + mutex_lock (audio_mem_mutex); + int in_frame_size = (output->fmt.bps >> 3) * output->fmt.channels; + int in_frames = sz / in_frame_size; + ddb_waveformat_t out_fmt = { + .bps = 32, + .channels = 1, + .samplerate = output->fmt.samplerate, + .channelmask = DDB_SPEAKER_FRONT_LEFT, + .is_float = 1, + .is_bigendian = 0 + }; + if (in_frames < DDB_AUDIO_MEMORY_FRAMES) { + memmove (audio_data, audio_data + in_frames, (DDB_AUDIO_MEMORY_FRAMES-in_frames)*sizeof (float)); + pcm_convert (&output->fmt, bytes, &out_fmt, (char *)(audio_data + DDB_AUDIO_MEMORY_FRAMES - in_frames), sz); + } + else { + pcm_convert (&output->fmt, bytes + sz - DDB_AUDIO_MEMORY_FRAMES * in_frame_size, &out_fmt, (char *)audio_data, DDB_AUDIO_MEMORY_FRAMES * in_frame_size); + } + + memcpy (freq_data, audio_data, sizeof (audio_data)); + int N = DDB_AUDIO_MEMORY_FRAMES; + do_fft(N, freq_data); + for (int n = 0; n < N / 2 - 1; n ++) + freq_data[n] = 2 * fabs (freq_data[1 + n]) / N; + freq_data[N / 2 - 1] = fabs(freq_data[N / 2]) / N; + + mutex_unlock (audio_mem_mutex); + if (!output->has_volume) { char *stream = bytes; int bytesread = sz; @@ -2310,6 +2356,15 @@ streamer_notify_order_changed (int prev_order, int new_order) { } void +audio_get_waveform_data (int type, float *data) { + if (!audio_mem_mutex) { + return; + } + mutex_lock (audio_mem_mutex); + memcpy (data, type == DDB_AUDIO_WAVEFORM ? audio_data : freq_data, sizeof (audio_data)); + mutex_unlock (audio_mem_mutex); + +void streamer_set_streamer_playlist (playlist_t *plt) { if (streamer_playlist) { plt_unref (streamer_playlist); @@ -140,6 +140,9 @@ void streamer_notify_order_changed (int prev_order, int new_order); void +audio_get_waveform_data (int type, float *data); + +void streamer_set_streamer_playlist (playlist_t *plt); #endif // __STREAMER_H diff --git a/tools/pluginfo/pluginfo.c b/tools/pluginfo/pluginfo.c index d1e4f4b9..d1a05d52 100644 --- a/tools/pluginfo/pluginfo.c +++ b/tools/pluginfo/pluginfo.c @@ -67,7 +67,18 @@ main (int argc, char *argv[]) { printf ("version=\"%d.%d\"\n", plug->version_major, plug->version_minor); printf ("id=\"%s\"\n", plug->id); printf ("name=\"%s\"\n", plug->name); - printf ("descr=\"%s\"\n", plug->descr); + printf ("descr=\""); + const char *c; + for (c = plug->descr; *c; c++) { + if (*c == '"') { + printf ("\\\""); + } + else { + printf ("%c", *c); + } + } + + printf ("\"\n"); printf ("website=\"%s\"\n", plug->website); dlclose (handle); |