diff options
Diffstat (limited to 'plugins')
82 files changed, 2596 insertions, 2265 deletions
diff --git a/plugins/aac/COPYING b/plugins/aac/COPYING new file mode 100644 index 00000000..a23602c3 --- /dev/null +++ b/plugins/aac/COPYING @@ -0,0 +1,25 @@ +AAC DeaDBeeF Player Plugin +Copyright (c) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +libmp4ff (modified) +Code from MP4FF is copyright (c) Nero AG, www.nero.com +deadbeef-related modifications (c) 2009-2014 Alexey Yakovenko + +Relies on libfaad2 +Code from FAAD2 is copyright (c) Nero AG, www.nero.com + + +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. + diff --git a/plugins/aac/Makefile.am b/plugins/aac/Makefile.am index ffd7b76e..1669db2f 100644 --- a/plugins/aac/Makefile.am +++ b/plugins/aac/Makefile.am @@ -6,6 +6,6 @@ aac_la_SOURCES = aac.c aac_parser.c aac_parser.h aac_la_LDFLAGS = -module -avoid-version aac_la_LIBADD = $(LDADD) $(FAAD2_LIBS) ../libmp4ff/libmp4ff.a -AM_CFLAGS = $(CFLAGS) -std=c99 -DUSE_MP4FF -DUSE_TAGGING -I@top_srcdir@/plugins/libmp4ff +AM_CFLAGS = -I@top_srcdir@/plugins/libmp4ff $(CFLAGS) $(FAAD2_CFLAGS) -std=c99 -DUSE_MP4FF -DUSE_TAGGING endif diff --git a/plugins/aac/aac.c b/plugins/aac/aac.c index a711621b..8796da83 100644 --- a/plugins/aac/aac.c +++ b/plugins/aac/aac.c @@ -482,6 +482,10 @@ aac_init (DB_fileinfo_t *_info, DB_playItem_t *it) { info->endsample = totalsamples-1; } } + if (_info->fmt.channels == 7) { + _info->fmt.channels = 8; + } + trace ("totalsamples: %d, endsample: %d, samples-from-duration: %d, samplerate %d, channels %d\n", totalsamples-1, info->endsample, (int)deadbeef->pl_get_item_duration (it)*44100, _info->fmt.samplerate, _info->fmt.channels); for (int i = 0; i < _info->fmt.channels; i++) { @@ -555,44 +559,45 @@ aac_read (DB_fileinfo_t *_info, char *bytes, int size) { int i, j; if (info->remap[0] == -1) { // build remap mtx + memset (info->remap, -1, sizeof (info->remap)); // FIXME: should build channelmask 1st; then remap based on channelmask for (i = 0; i < _info->fmt.channels; i++) { switch (info->frame_info.channel_position[i]) { case FRONT_CHANNEL_CENTER: - trace ("FC->%d\n", i); + trace ("FC->%d %d\n", i, 2); info->remap[2] = i; break; case FRONT_CHANNEL_LEFT: - trace ("FL->%d\n", i); + trace ("FL->%d %d\n", i, 0); info->remap[0] = i; break; case FRONT_CHANNEL_RIGHT: - trace ("FR->%d\n", i); + trace ("FR->%d %d\n", i, 1); info->remap[1] = i; break; case SIDE_CHANNEL_LEFT: - trace ("SL->%d\n", i); + trace ("SL->%d %d\n", i, 6); info->remap[6] = i; break; case SIDE_CHANNEL_RIGHT: - trace ("SR->%d\n", i); + trace ("SR->%d %d\n", i, 7); info->remap[7] = i; break; case BACK_CHANNEL_LEFT: - trace ("RL->%d\n", i); + trace ("RL->%d %d\n", i, 4); info->remap[4] = i; break; case BACK_CHANNEL_RIGHT: - trace ("RR->%d\n", i); + trace ("RR->%d %d\n", i, 5); info->remap[5] = i; break; case BACK_CHANNEL_CENTER: - trace ("BC->%d\n", i); + trace ("BC->%d %d\n", i, 8); info->remap[8] = i; break; case LFE_CHANNEL: - trace ("LFE->%d\n", i); + trace ("LFE->%d %d\n", i, 3); info->remap[3] = i; break; default: @@ -615,7 +620,12 @@ aac_read (DB_fileinfo_t *_info, char *bytes, int size) { for (i = 0; i < n; i++) { for (j = 0; j < _info->fmt.channels; j++) { - ((int16_t *)bytes)[j] = ((int16_t *)src)[info->remap[j]]; + if (info->remap[j] == -1) { + ((int16_t *)bytes)[j] = 0; + } + else { + ((int16_t *)bytes)[j] = ((int16_t *)src)[info->remap[j]]; + } } src += samplesize; bytes += samplesize; @@ -842,6 +852,7 @@ static const char *metainfo[] = { "copyright", "copyright", "totaltracks", "numtracks", "tool", "tool", + "MusicBrainz Track Id", "musicbrainz_trackid", NULL }; diff --git a/plugins/adplug/COPYING b/plugins/adplug/COPYING new file mode 100644 index 00000000..8dcb9556 --- /dev/null +++ b/plugins/adplug/COPYING @@ -0,0 +1,40 @@ +ADPLUG DeaDBeeF Player Plugin +Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. + + + +adplug (modified) +Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al. +deadbeef-related modifications (c) 2009-2014 Alexey Yakovenko + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/plugins/adplug/adplug-db.cpp b/plugins/adplug/adplug-db.cpp index 45cc6421..4561b217 100644 --- a/plugins/adplug/adplug-db.cpp +++ b/plugins/adplug/adplug-db.cpp @@ -1,20 +1,24 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + DeaDBeeF ADPLUG plugin + Copyright (C) 2009-2014 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. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ #include <stdio.h> diff --git a/plugins/adplug/plugin.c b/plugins/adplug/plugin.c index 5ad940a4..61143d5f 100644 --- a/plugins/adplug/plugin.c +++ b/plugins/adplug/plugin.c @@ -1,20 +1,24 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + DeaDBeeF ADPLUG plugin + Copyright (C) 2009-2014 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. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ // this is a decoder plugin skeleton @@ -59,25 +63,46 @@ DB_decoder_t adplug_plugin = { .plugin.name = "Adplug player", .plugin.descr = "Adplug player (ADLIB OPL2/OPL3 emulator)", .plugin.copyright = - "Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net>\n" - "\n" - "Uses modified AdPlug library\n" - "Copyright (C) 1999 - 2010 Simon Peter, et al.\n" - "http://adplug.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" + "ADPLUG DeaDBeeF Player Plugin\n" + "Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This software is provided 'as-is', without any express or implied\n" + "warranty. In no event will the authors be held liable for any damages\n" + "arising from the use of this software.\n" + "\n" + "Permission is granted to anyone to use this software for any purpose,\n" + "including commercial applications, and to alter it and redistribute it\n" + "freely, subject to the following restrictions:\n" + "\n" + "1. The origin of this software must not be misrepresented; you must not\n" + " claim that you wrote the original software. If you use this software\n" + " in a product, an acknowledgment in the product documentation would be\n" + " appreciated but is not required.\n" + "\n" + "2. Altered source versions must be plainly marked as such, and must not be\n" + " misrepresented as being the original software.\n" + "\n" + "3. This notice may not be removed or altered from any source distribution.\n" + "\n" + "\n" + "\n" + "adplug (modified)\n" + "Copyright (C) 1999 - 2006 Simon Peter, <dn.tlp@gmx.net>, et al.\n" + "deadbeef-related modifications (c) 2009-2014 Alexey Yakovenko\n" + "\n" + "This library is free software; you can redistribute it and/or\n" + "modify it under the terms of the GNU Lesser General Public\n" + "License as published by the Free Software Foundation; either\n" + "version 2.1 of the License, or (at your option) any later version.\n" + "\n" + "This library 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 GNU\n" + "Lesser General Public License for more details.\n" + "\n" + "You should have received a copy of the GNU Lesser General Public\n" + "License along with this library; if not, write to the Free Software\n" + "Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n" , .plugin.website = "http://deadbeef.sf.net", .plugin.start = adplug_start, diff --git a/plugins/alac/COPYING b/plugins/alac/COPYING new file mode 100644 index 00000000..ed09298c --- /dev/null +++ b/plugins/alac/COPYING @@ -0,0 +1,68 @@ +ALAC plugin for deadbeef +Copyright (C) 2012-2013 Alexey Yakovenko <waker@users.sourceforge.net> + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + + +ALAC (Apple Lossless Audio Codec) decoder +Copyright (c) 2005 David Hammerton +All rights reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + + +libmp4ff (modified) +Code from MP4FF is copyright (c) Nero AG, www.nero.com +deadbeef-related modifications (c) 2009-2014 Alexey Yakovenko + +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. diff --git a/plugins/alac/alac_plugin.c b/plugins/alac/alac_plugin.c index 160821f2..4291099f 100644 --- a/plugins/alac/alac_plugin.c +++ b/plugins/alac/alac_plugin.c @@ -357,6 +357,7 @@ static const char *metainfo[] = { "copyright", "copyright", "totaltracks", "numtracks", "tool", "tool", + "MusicBrainz Track Id", "musicbrainz_trackid", NULL }; diff --git a/plugins/alsa/COPYING b/plugins/alsa/COPYING new file mode 100644 index 00000000..8317a4be --- /dev/null +++ b/plugins/alsa/COPYING @@ -0,0 +1,17 @@ +DeaDBeeF ALSA plugin +Copyright (C) 2009-2014 Alexey Yakovenko et al. + +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. + diff --git a/plugins/ao/license.txt b/plugins/ao/COPYING index b700befa..070fb661 100644 --- a/plugins/ao/license.txt +++ b/plugins/ao/COPYING @@ -1,3 +1,22 @@ +AudioOverload DeaDBeeF plugin +Copyright (C) 2009-2014 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. + + + Here is a list of the licenses each file in this package are under, and their text. The upshot is this: if you create anything from this SDK and include any MAME licensed components diff --git a/plugins/ao/Makefile.am b/plugins/ao/Makefile.am index 55638c11..2d124b27 100644 --- a/plugins/ao/Makefile.am +++ b/plugins/ao/Makefile.am @@ -22,6 +22,6 @@ ddb_ao_la_LDFLAGS = -module -avoid-version $(ZLIB_LIBS) EXTRA_DIST=eng_psf/peops/reverb.c eng_psf/peops/adsr.c eng_psf/peops/registers.c eng_psf/peops/dma.c eng_psf/peops2/spu2.c eng_psf/peops2/reverb2.c eng_psf/peops2/adsr2.c eng_dsf/arm7memil.c eng_dsf/aicalfo.c eng_ssf/scsplfo.c ddb_ao_la_LIBADD = $(LDADD) -AM_CFLAGS = $(CFLAGS) -Wall -DPATH_MAX=1024 -DHAS_PSXCPU=1 -I.. -Ieng_ssf -Ieng_qsf -Ieng_dsf -lm -fPIC +AM_CFLAGS = $(CFLAGS) $(ZLIB_CFLAGS) -Wall -DPATH_MAX=1024 -DHAS_PSXCPU=1 -I.. -Ieng_ssf -Ieng_qsf -Ieng_dsf -lm -fPIC endif diff --git a/plugins/artwork/COPYING b/plugins/artwork/COPYING new file mode 100644 index 00000000..f88ac121 --- /dev/null +++ b/plugins/artwork/COPYING @@ -0,0 +1,20 @@ +DeaDBeeF Album Art plugin +Copyright (C) 2009-2014 Viktor Semykin, Alexey Yakovenko et al. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/plugins/artwork/Makefile.am b/plugins/artwork/Makefile.am index 5948af9b..9fb8bea8 100644 --- a/plugins/artwork/Makefile.am +++ b/plugins/artwork/Makefile.am @@ -1,7 +1,13 @@ if HAVE_ARTWORK artworkdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = artwork.la -artwork_la_SOURCES = artwork.c artwork.h albumartorg.c albumartorg.h lastfm.c lastfm.h wos.c wos.h escape.c escape.h + +if ARTWORK_USE_VFS_CURL +artwork_net_cflags = -DUSE_VFS_CURL +artwork_net_sources = albumartorg.c albumartorg.h lastfm.c lastfm.h wos.c wos.h escape.c escape.h +endif + +artwork_la_SOURCES = artwork.c artwork.h $(artwork_net_sources) artwork_la_LDFLAGS = -module -avoid-version @@ -15,9 +21,9 @@ endif if HAVE_FLAC FLAC_DEPS=$(FLAC_LIBS) -FLAC_CFLAGS=-DUSE_METAFLAC +flac_clags=-DUSE_METAFLAC $(FLAC_CFLAGS) endif -AM_CFLAGS = $(CFLAGS) $(ARTWORK_CFLAGS) $(FLAC_CFLAGS) -std=c99 +AM_CFLAGS = $(CFLAGS) $(ARTWORK_CFLAGS) $(flac_cflags) $(artwork_net_cflags) -std=c99 artwork_la_LIBADD = $(LDADD) $(ARTWORK_DEPS) $(FLAC_DEPS) endif diff --git a/plugins/artwork/albumartorg.c b/plugins/artwork/albumartorg.c index 1b21f3f0..c15a17ad 100644 --- a/plugins/artwork/albumartorg.c +++ b/plugins/artwork/albumartorg.c @@ -1,21 +1,27 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Album Art plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> Copyright (C) 2009-2013 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. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ + #ifdef HAVE_CONFIG_H # include <config.h> #endif diff --git a/plugins/artwork/albumartorg.h b/plugins/artwork/albumartorg.h index 46ade53f..dc062541 100644 --- a/plugins/artwork/albumartorg.h +++ b/plugins/artwork/albumartorg.h @@ -1,21 +1,27 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Album Art plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> Copyright (C) 2009-2013 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. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ + #ifndef __ALBUMARTORG_H #define __ALBUMARTORG_H diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index 44aa68dc..fa1a3f3b 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -1,3 +1,27 @@ +/* + Album Art plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> + Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ + #ifdef HAVE_CONFIG_H # include "../../config.h" #endif @@ -22,9 +46,11 @@ #include <assert.h> #include "../../deadbeef.h" #include "artwork.h" +#ifdef USE_VFS_CURL #include "lastfm.h" #include "albumartorg.h" #include "wos.h" +#endif #ifdef USE_IMLIB2 #include <Imlib2.h> @@ -85,18 +111,42 @@ static intptr_t tid; static int artwork_enable_embedded; static int artwork_enable_local; +#ifdef USE_VFS_CURL static int artwork_enable_lfm; static int artwork_enable_aao; static int artwork_enable_wos; +#endif static time_t artwork_reset_time; static char artwork_filemask[200]; static const char *get_default_cover (void) { - return default_cover; +return default_cover; +} + +static int +esc_char (char c) { + if (c < 1 + || (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || c == ' ' + || c == '_' + || c == '-') { + return c; + } + return '_'; } int make_cache_dir_path (char *path, int size, const char *artist, int img_size) { + char esc_artist[PATH_MAX]; + int i; + + for (i = 0; artist[i]; i++) { + esc_artist[i] = esc_char (artist[i]); + } + esc_artist[i] = 0; + const char *cache = getenv ("XDG_CACHE_HOME"); int sz; @@ -108,7 +158,7 @@ make_cache_dir_path (char *path, int size, const char *artist, int img_size) { } path += sz; - sz += snprintf (path, size-sz, "%s", artist); + sz += snprintf (path, size-sz, "%s", esc_artist); for (char *p = path; *p; p++) { if (*p == '/') { *p = '_'; @@ -117,13 +167,50 @@ make_cache_dir_path (char *path, int size, const char *artist, int img_size) { return sz; } -void -make_cache_path (char *path, int size, const char *album, const char *artist, int img_size) { +int +make_cache_path2 (char *path, int size, const char *fname, const char *album, const char *artist, int img_size) { + if (!album) { + album = ""; + } + if (!artist) { + artist = ""; + } + + if (*album && !(*artist)) { + artist = album; + } + + if (!*artist || !*album) + { + if (fname) { + // album=escape(path), artist=uknown + artist = "Unknown artist"; + album = fname; + } + else { + trace ("artist or album is empty, give up\n"); + *path = 0; + return -1; + } + } + char *p = path; + char esc_album[PATH_MAX]; + const char *palbum = album; + size_t l = strlen (album); + if (l > 200) { + palbum = album + l - 200; + } + int i; + for (i = 0; palbum[i]; i++) { + esc_album[i] = esc_char (palbum[i]); + } + esc_album[i] = 0; + int sz = make_cache_dir_path (path, size, artist, img_size); size -= sz; path += sz; - sz = snprintf (path, size, "/%s.jpg", album); + sz = snprintf (path, size, "/%s.jpg", esc_album); for (char *p = path+1; *p; p++) { if (*p == '/') { *p = '_'; @@ -132,6 +219,11 @@ make_cache_path (char *path, int size, const char *album, const char *artist, in } void +make_cache_path (char *path, int size, const char *album, const char *artist, int img_size) { + make_cache_path2 (path, size, NULL, album, artist, img_size); +} + +void queue_add (const char *fname, const char *artist, const char *album, int img_size, artwork_callback callback, void *user_data) { if (!artist) { artist = ""; @@ -849,7 +941,7 @@ fetcher_thread (void *none) trace ("fetching cover for %s %s\n", param->album, param->artist); char cache_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); + make_cache_path2 (cache_path, sizeof (cache_path), param->fname, param->album, param->artist, -1); int got_pic = 0; if (deadbeef->is_local_file (param->fname)) { @@ -884,7 +976,7 @@ fetcher_thread (void *none) trace ("artwork: corrupted id3v2 APIC frame\n"); continue; } - if (strcasecmp (data, "image/jpeg") && strcasecmp (data, "image/png")) { + if (strcasecmp (data, "image/jpeg") && strcasecmp (data, "image/png") && strcasecmp (data, "image/gif")) { trace ("artwork: unsupported mime type: %s\n", data); continue; } @@ -973,7 +1065,7 @@ fetcher_thread (void *none) trace ("found apev2 cover art of %d bytes (%s)\n", sz, ext); char tmp_path[1024]; char cache_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); + make_cache_path2 (cache_path, sizeof (cache_path), param->fname, param->album, param->artist, -1); trace ("will write apev2 cover art into %s\n", cache_path); snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); FILE *out = fopen (tmp_path, "w+b"); @@ -1042,7 +1134,7 @@ fetcher_thread (void *none) trace ("found flac cover art of %d bytes (%s)\n", pic->data_length, pic->description); char tmp_path[1024]; char cache_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); + make_cache_path2 (cache_path, sizeof (cache_path), param->fname, param->album, param->artist, -1); trace ("will write flac cover art into %s\n", cache_path); snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); FILE *out = fopen (tmp_path, "w+b"); @@ -1118,7 +1210,7 @@ fetcher_thread (void *none) strcat (path, files[0]->d_name); char cache_path[1024]; char tmp_path[1024]; - make_cache_path (cache_path, sizeof (cache_path), param->album, param->artist, -1); + make_cache_path2 (cache_path, sizeof (cache_path), param->fname, param->album, param->artist, -1); snprintf (tmp_path, sizeof (tmp_path), "%s.part", cache_path); copy_file (path, tmp_path, -1); int err = rename (tmp_path, cache_path); @@ -1136,6 +1228,7 @@ fetcher_thread (void *none) } } +#ifdef USE_VFS_CURL if (!got_pic) { if (artwork_enable_wos) { @@ -1164,6 +1257,7 @@ fetcher_thread (void *none) got_pic = 1; } } +#endif if (got_pic) { trace ("downloaded art for %s %s\n", param->album, param->artist); @@ -1176,7 +1270,7 @@ fetcher_thread (void *none) continue; } char scaled_path[1024]; - make_cache_path (scaled_path, sizeof (scaled_path), param->album, param->artist, param->size); + make_cache_path2 (scaled_path, sizeof (scaled_path), param->fname, param->album, param->artist, param->size); copy_file (cache_path, scaled_path, param->size); } for (int i = 0; i < param->numcb; i++) { @@ -1227,23 +1321,6 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz { char path [1024]; - if (!album) { - album = ""; - } - if (!artist) { - artist = ""; - } - - if (!*artist || !*album) - { - trace ("artist or album is empty, give up\n"); - //give up - if (callback) { - callback (NULL, NULL, NULL, user_data); - } - return NULL; - } - if (!deadbeef->is_local_file (fname)) { if (callback) { callback (NULL, NULL, NULL, user_data); @@ -1251,7 +1328,7 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz return NULL; } - make_cache_path (path, sizeof (path), album, artist, size); + make_cache_path2 (path, sizeof (path), fname, album, artist, size); char *p = find_image (path); if (p) { if (callback) { @@ -1263,7 +1340,7 @@ get_album_art (const char *fname, const char *artist, const char *album, int siz if (size != -1) { // check if we have unscaled image char unscaled_path[1024]; - make_cache_path (unscaled_path, sizeof (unscaled_path), album, artist, -1); + make_cache_path2 (unscaled_path, sizeof (unscaled_path), fname, album, artist, -1); p = find_image (unscaled_path); if (p) { free (p); @@ -1358,25 +1435,31 @@ static int artwork_configchanged (void) { int new_artwork_enable_embedded = deadbeef->conf_get_int ("artwork.enable_embedded", 1); int new_artwork_enable_local = deadbeef->conf_get_int ("artwork.enable_localfolder", 1); +#ifdef USE_VFS_CURL int new_artwork_enable_lfm = deadbeef->conf_get_int ("artwork.enable_lastfm", 0); int new_artwork_enable_aao = deadbeef->conf_get_int ("artwork.enable_albumartorg", 0); int new_artwork_enable_wos = deadbeef->conf_get_int ("artwork.enable_wos", 0); +#endif char new_artwork_filemask[200]; deadbeef->conf_get_str ("artwork.filemask", DEFAULT_FILEMASK, new_artwork_filemask, sizeof (new_artwork_filemask)); if (new_artwork_enable_embedded != artwork_enable_embedded || new_artwork_enable_local != artwork_enable_local +#ifdef USE_VFS_CURL || new_artwork_enable_lfm != artwork_enable_lfm || new_artwork_enable_aao != artwork_enable_aao || new_artwork_enable_wos != artwork_enable_wos +#endif || strcmp (new_artwork_filemask, artwork_filemask)) { trace ("artwork config changed, invalidating cache...\n"); artwork_enable_embedded = new_artwork_enable_embedded; artwork_enable_local = new_artwork_enable_local; +#ifdef USE_VFS_CURL artwork_enable_lfm = new_artwork_enable_lfm; artwork_enable_aao = new_artwork_enable_aao; artwork_enable_wos = new_artwork_enable_wos; +#endif artwork_reset_time = time (NULL); strcpy (artwork_filemask, new_artwork_filemask); deadbeef->conf_set_int64 ("artwork.cache_reset_time", artwork_reset_time); @@ -1413,8 +1496,11 @@ artwork_plugin_start (void) artwork_enable_embedded = deadbeef->conf_get_int ("artwork.enable_embedded", 1); artwork_enable_local = deadbeef->conf_get_int ("artwork.enable_localfolder", 1); +#ifdef USE_VFS_CURL artwork_enable_lfm = deadbeef->conf_get_int ("artwork.enable_lastfm", 0); artwork_enable_aao = deadbeef->conf_get_int ("artwork.enable_albumartorg", 0); + artwork_enable_wos = deadbeef->conf_get_int ("artwork.enable_wos", 0); +#endif artwork_reset_time = deadbeef->conf_get_int64 ("artwork.cache_reset_time", 0); deadbeef->conf_get_str ("artwork.filemask", DEFAULT_FILEMASK, artwork_filemask, sizeof (artwork_filemask)); @@ -1471,9 +1557,11 @@ static const char settings_dlg[] = "property \"Fetch from embedded tags\" checkbox artwork.enable_embedded 1;\n" "property \"Fetch from local folder\" checkbox artwork.enable_localfolder 1;\n" "property \"Local cover file mask\" entry artwork.filemask \"" DEFAULT_FILEMASK "\";\n" +#ifdef USE_VFS_CURL "property \"Fetch from last.fm\" checkbox artwork.enable_lastfm 0;\n" "property \"Fetch from albumart.org\" checkbox artwork.enable_albumartorg 0;\n" "property \"Fetch from worldofspectrum.org (AY only)\" checkbox artwork.enable_wos 0;\n" +#endif "property \"Scale artwork towards longer side\" checkbox artwork.scale_towards_longer 1;\n" ; @@ -1482,28 +1570,33 @@ static DB_artwork_plugin_t plugin = { .plugin.plugin.api_vmajor = 1, .plugin.plugin.api_vminor = 0, .plugin.plugin.version_major = 1, - .plugin.plugin.version_minor = 1, + .plugin.plugin.version_minor = 2, .plugin.plugin.type = DB_PLUGIN_MISC, .plugin.plugin.id = "artwork", .plugin.plugin.name = "Album Artwork", .plugin.plugin.descr = "Loads album artwork either from local directories or from internet", .plugin.plugin.copyright = - "Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Album Art plugin for DeaDBeeF\n" "Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com>\n" + "Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "\n" + "This software is provided 'as-is', without any express or implied\n" + "warranty. In no event will the authors be held liable for any damages\n" + "arising from the use of this software.\n" + "\n" + "Permission is granted to anyone to use this software for any purpose,\n" + "including commercial applications, and to alter it and redistribute it\n" + "freely, subject to the following restrictions:\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" + "1. The origin of this software must not be misrepresented; you must not\n" + " claim that you wrote the original software. If you use this software\n" + " in a product, an acknowledgment in the product documentation would be\n" + " appreciated but is not required.\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" + "2. Altered source versions must be plainly marked as such, and must not be\n" + " misrepresented as being the original software.\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" + "3. This notice may not be removed or altered from any source distribution.\n" , .plugin.plugin.website = "http://deadbeef.sf.net", .plugin.plugin.start = artwork_plugin_start, @@ -1515,4 +1608,5 @@ static DB_artwork_plugin_t plugin = { .get_default_cover = get_default_cover, .get_album_art_sync = get_album_art_sync, .make_cache_path = make_cache_path, + .make_cache_path2 = make_cache_path2, }; diff --git a/plugins/artwork/artwork.h b/plugins/artwork/artwork.h index 9d8a2a7d..850937e1 100644 --- a/plugins/artwork/artwork.h +++ b/plugins/artwork/artwork.h @@ -1,3 +1,26 @@ +/* + Album Art plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> + Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ #ifndef __ARTWORK_H #define __ARTWORK_H @@ -24,6 +47,9 @@ typedef struct { // creates full path string for cache storage void (*make_cache_path) (char *path, int size, const char *album, const char *artist, int img_size); + + // creates full path string for cache storage + int (*make_cache_path2) (char *path, int size, const char *fname, const char *album, const char *artist, int img_size); } DB_artwork_plugin_t; #endif /*__ARTWORK_H*/ diff --git a/plugins/artwork/lastfm.c b/plugins/artwork/lastfm.c index 5b5cedd4..83bda9cb 100644 --- a/plugins/artwork/lastfm.c +++ b/plugins/artwork/lastfm.c @@ -1,3 +1,26 @@ +/* + Album Art plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> + Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ #ifdef HAVE_CONFIG_H # include <config.h> #endif @@ -22,11 +45,17 @@ int fetch_from_lastfm (const char *artist, const char *album, const char *dest) { char url [1024]; - char *artist_url = uri_escape (artist, 0); - char *album_url = uri_escape (album, 0); + char *artist_url = NULL; + char *album_url = NULL; + +retry: + artist_url = uri_escape (artist, 0); + album_url = uri_escape (album, 0); snprintf (url, sizeof (url), BASE_URL "?method=album.getinfo&api_key=" API_KEY "&artist=%s&album=%s", artist_url, album_url); free (artist_url); + artist_url = NULL; free (album_url); + album_url = NULL; DB_FILE *fp = deadbeef->fopen (url); if (!fp) { @@ -55,6 +84,11 @@ fetch_from_lastfm (const char *artist, const char *album, const char *dest) char *end = strstr (img, "</image>"); if (!end || end == img) { + if (artist != album) { + artist = album; + goto retry; + } + trace ("fetch_from_lastfm: bad xml (or image not found) from %s\n", url); return -1; } diff --git a/plugins/artwork/lastfm.h b/plugins/artwork/lastfm.h index b11ca370..e6fc82ba 100644 --- a/plugins/artwork/lastfm.h +++ b/plugins/artwork/lastfm.h @@ -1,20 +1,25 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Album Art plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> Copyright (C) 2009-2013 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. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ #ifndef __LASTFM_H #define __LASTFM_H diff --git a/plugins/cdda/COPYING b/plugins/cdda/COPYING new file mode 100644 index 00000000..cf6220be --- /dev/null +++ b/plugins/cdda/COPYING @@ -0,0 +1,55 @@ +CD audio plugin for DeaDBeeF +Copyright (C) 2009-2014 Alexey Yakovenko, Viktor Semykin + +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, see <http://www.gnu.org/licenses/>. + + +libcddb +Copyright (C) 2003, 2004, 2005 Kris Verbeeck <airborne@advalvas.be> + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This library 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 +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with this library; if not, write to the +Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. + + + +libcdio +Copyright (C) 2003, 2004, 2005, 2007, 2008, 2009 Rocky Bernstein <rocky@gnu.org> + +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 3 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, see <http://www.gnu.org/licenses/>. + + + diff --git a/plugins/converter/COPYING b/plugins/converter/COPYING new file mode 100644 index 00000000..0950706e --- /dev/null +++ b/plugins/converter/COPYING @@ -0,0 +1,17 @@ +Converter plugin for DeaDBeeF Player + +Copyright (C) 2009-2013 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. diff --git a/plugins/converter/Makefile.am b/plugins/converter/Makefile.am index f34ec5fc..cc379437 100644 --- a/plugins/converter/Makefile.am +++ b/plugins/converter/Makefile.am @@ -22,7 +22,8 @@ convdata_DATA = \ presets/MusePack.txt\ presets/Ogg_Vorbis_(-q_5).txt\ presets/TTA.txt\ - presets/WavPack.txt + presets/WavPack.txt\ + presets/ALAC.txt EXTRA_DIST = $(convdata_DATA) converter.glade @@ -37,7 +38,7 @@ converter_gtk2_la_LDFLAGS = -module -avoid-version if STATICLINK GTK_ROOT=@top_srcdir@/$(LIB)/gtk-2.12.12/usr -converter_gtk2_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib $(GTK_ROOT)/lib/libgtk-x11-2.0.la $(GTK_ROOT)/lib/libgdk-x11-2.0.la $(GTK_ROOT)/lib/libpangoft2-1.0.la $(GTK_ROOT)/lib/libpangocairo-1.0.la $(GTK_ROOT)/lib/libgdk_pixbuf-2.0.la -lm $(GTK_ROOT)/lib/libcairo.la $(GTK_ROOT)/lib/libpango-1.0.la $(GTK_ROOT)/lib/libgobject-2.0.la $(GTK_ROOT)/lib/libgmodule-2.0.la $(GTK_ROOT)/lib/libgthread-2.0.la -lrt $(GTK_ROOT)/lib/libglib-2.0.la +converter_gtk2_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lrt converter_gtk2_la_CFLAGS = -std=c99 -I $(GTK_ROOT)/include -I $(GTK_ROOT)/lib/gtk-2.0/include -I $(GTK_ROOT)/include/glib-2.0 -I $(GTK_ROOT)/include/gtk-2.0 -I $(GTK_ROOT)/include/cairo -I $(GTK_ROOT)/lib/glib-2.0/include/ -I $(GTK_ROOT)/include/pango-1.0 -I $(GTK_ROOT)/include/atk-1.0 -DULTRA_COMPATIBLE=1 else @@ -49,17 +50,8 @@ endif if HAVE_GTK3 converter_gtk3_la_SOURCES = convgui.c interface.c support.c callbacks.h converter.h interface.h support.h converter_gtk3_la_LDFLAGS = -module -avoid-version -if STATICLINK -GTK_ROOT_300=@top_srcdir@/$(LIB)/gtk-3.0.0 - -converter_gtk3_la_LIBADD = $(LDADD) -L$(GTK_ROOT_300)/lib -lgtk-3 -lgdk-3 -latk-1.0 -lgio-2.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo-gobject -lpango-1.0 -lcairo -lgobject-2.0 -lglib-2.0 -lfreetype $(SM_LIBADD) - -converter_gtk3_la_CFLAGS = -std=c99 -I$(GTK_ROOT_300)/include/gtk-3.0 -I$(GTK_ROOT_300)/include/pango-1.0 -I$(GTK_ROOT_300)/include/gio-unix-2.0/ -I$(GTK_ROOT_300)/include/atk-1.0 -I$(GTK_ROOT_300)/include/cairo -I$(GTK_ROOT_300)/include/gdk-pixbuf-2.0 -I$(GTK_ROOT_300)/include/freetype2 -I$(GTK_ROOT_300)/include/glib-2.0 -I$(GTK_ROOT_300)/lib/glib-2.0/include $(SM_CFLAGS) - -else converter_gtk3_la_LIBADD = $(LDADD) $(GTK3_DEPS_LIBS) converter_gtk3_la_CFLAGS = -std=c99 $(GTK3_DEPS_CFLAGS) endif -endif endif diff --git a/plugins/converter/converter.c b/plugins/converter/converter.c index 17be3036..04faaae3 100644 --- a/plugins/converter/converter.c +++ b/plugins/converter/converter.c @@ -1158,20 +1158,18 @@ error: if (encoder_preset->tag_oggvorbis) { // find flac decoder plugin DB_decoder_t **plugs = deadbeef->plug_get_decoder_list (); - DB_decoder_t *ogg = NULL; + int res = -1; for (int i = 0; plugs[i]; i++) { - if (!strcmp (plugs[i]->plugin.id, "stdogg")) { - ogg = plugs[i]; - break; + if (!strcmp (plugs[i]->plugin.id, "stdogg") + || !strcmp (plugs[i]->plugin.id, "stdopus")) { + res = plugs[i]->write_metadata (out_it); + if (!res) { + break; + } } } - if (!ogg) { - fprintf (stderr, "converter: ogg plugin not found, cannot write ogg metadata\n"); - } - else { - if (0 != ogg->write_metadata (out_it)) { - fprintf (stderr, "converter: failed to write ogg metadata, not an ogg file?\n"); - } + if (res) { + fprintf (stderr, "converter: failed to write ogg metadata, not an ogg file?\n"); } } if (out_it) { diff --git a/plugins/converter/presets/ALAC.txt b/plugins/converter/presets/ALAC.txt new file mode 100644 index 00000000..3ede71f3 --- /dev/null +++ b/plugins/converter/presets/ALAC.txt @@ -0,0 +1,10 @@ +title ALAC +ext m4a +encoder ffmpeg -i %i -acodec alac %o +method 1 +id3v2_version 0 +tag_id3v2 0 +tag_id3v1 0 +tag_apev2 0 +tag_flac 0 +tag_oggvorbis 0 diff --git a/plugins/coreaudio/COPYING b/plugins/coreaudio/COPYING new file mode 100644 index 00000000..fb7c6b6c --- /dev/null +++ b/plugins/coreaudio/COPYING @@ -0,0 +1,17 @@ +CoreAudio Output Deadbeef Plugin +Copyright (c) 2011-2013 Carlos Nunes <carloslnunes@gmail.com> + +This plugin is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2 of the License, or (at your option) any later version. + +This plugin 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + diff --git a/plugins/dca/COPYING b/plugins/dca/COPYING index d511905c..77d7529a 100644 --- a/plugins/dca/COPYING +++ b/plugins/dca/COPYING @@ -1,339 +1,20 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - 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. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. +DCA Plugin for DeaDBeeF Player +Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +libdca (C) Gildas Bazin et al. +deadbeef-related modifications (C) Alexey Yakovenko +see AUTHORS for more details + +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. diff --git a/plugins/dsp_libsrc/COPYING b/plugins/dsp_libsrc/COPYING new file mode 100644 index 00000000..641f9446 --- /dev/null +++ b/plugins/dsp_libsrc/COPYING @@ -0,0 +1,20 @@ +resampling plugin for DeaDBeeF Player +Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +libsamplerate +Copyright (C) 2002-2008 Erik de Castro Lopo <erikd@mega-nerd.com> + +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. + diff --git a/plugins/dumb/COPYING b/plugins/dumb/COPYING new file mode 100644 index 00000000..3621ccac --- /dev/null +++ b/plugins/dumb/COPYING @@ -0,0 +1,89 @@ +DUMB plugin for DeaDBeeF Player +Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. + + + +modified DUMB 0.9.3 +Dynamic Universal Music Bibliotheque, Version 0.9.3 + +Copyright (C) 2001-2005 Ben Davis, Robert J Ohannessian and Julien Cugniere + +This software is provided 'as-is', without any express or implied warranty. +In no event shall the authors be held liable for any damages arising from the +use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim + that you wrote the original software. If you use this software in a + product, you are requested to acknowledge its use in the product + documentation, along with details on where to get an unmodified version of + this software, but this is not a strict requirement. + + [Note that the above point asks for a link to DUMB, not just a mention. + Googling for DUMB doesn't help much! The URL is "http://dumb.sf.net/".] + + [The link was originally strictly required. This was changed for two + reasons. Firstly, if many projects request an acknowledgement, the list of + acknowledgements can become quite unmanageable. Secondly, DUMB was placing + a restriction on the code using it, preventing people from using the GNU + General Public Licence which disallows any such restrictions. See + http://www.gnu.org/philosophy/bsd.html for more information on this + subject. However, if DUMB plays a significant part in your project, we do + urge you to acknowledge its use.] + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed from or altered in any source distribution. + +4. If you are using the Program in someone else's bedroom on any Monday at + 3:05 pm, you are not allowed to modify the Program for ten minutes. [This + clause provided by Inphernic; every licence should contain at least one + clause, the reasoning behind which is far from obvious.] + +5. Users who wish to use DUMB for the specific purpose of playing music are + required to feed their dog on every full moon (if deemed appropriate). + [This clause provided by Allefant, who couldn't remember what Inphernic's + clause was.] + +6. No clause in this licence shall prevent this software from being depended + upon by a product licensed under the GNU General Public Licence. If such a + clause is deemed to exist, Debian, then it shall be respected in spirit as + far as possible and all other clauses shall continue to apply in full + force. + +We regret that we cannot provide any warranty, not even the implied warranty +of merchantability or fitness for a particular purpose. + +Some files generated or copied by automake, autoconf and friends are +available in an extra download. These fall under separate licences but are +all free to distribute. Please check their licences as necessary. + + + +Uses code from kode54's foobar2000 plugin, http://kode54.foobar2000.org/ + + + +deadbeef-related modifications (C) Alexey Yakovenko diff --git a/plugins/dumb/cdumb.c b/plugins/dumb/cdumb.c index dfd9ec50..017b850b 100644 --- a/plugins/dumb/cdumb.c +++ b/plugins/dumb/cdumb.c @@ -1,19 +1,24 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + DUMB Plugin for DeaDBeeF Player + Copyright (C) 2009-2014 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 software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. - 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. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ // based on fb2k dumb plugin from http://kode54.foobar2000.org @@ -121,6 +126,8 @@ cdumb_startrenderer (DB_fileinfo_t *_info) { dumb_it_set_resampling_quality (itsr, q); dumb_it_set_xm_speed_zero_callback (itsr, &dumb_it_callback_terminate, NULL); dumb_it_set_global_volume_zero_callback (itsr, &dumb_it_callback_terminate, NULL); + + dumb_it_sr_set_global_volume (itsr, deadbeef->conf_get_int ("dumb.globalvolume", 64)); return 0; } @@ -884,6 +891,7 @@ cgme_stop (void) { static const char settings_dlg[] = "property \"Resampling quality (0..2, higher is better)\" entry dumb.resampling_quality 2;\n" "property \"8-bit output (default is 16)\" checkbox dumb.8bitoutput 0;\n" + "property \"Internal DUMB volume (0..128)\" spinbtn[0,128,16] dumb.globalvolume 64;\n" ; // define plugin interface @@ -897,25 +905,30 @@ static DB_decoder_t plugin = { .plugin.name = "DUMB module player", .plugin.descr = "module player based on DUMB library", .plugin.copyright = - "Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "DUMB Plugin for DeaDBeeF Player\n" + "Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net>\n" "\n" "Uses a fork of DUMB (Dynamic Universal Music Bibliotheque), Version 0.9.3\n" "Copyright (C) 2001-2005 Ben Davis, Robert J Ohannessian and Julien Cugniere\n" "Uses code from kode54's foobar2000 plugin, http://kode54.foobar2000.org/\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" + "This software is provided 'as-is', without any express or implied\n" + "warranty. In no event will the authors be held liable for any damages\n" + "arising from the use of this software.\n" + "\n" + "Permission is granted to anyone to use this software for any purpose,\n" + "including commercial applications, and to alter it and redistribute it\n" + "freely, subject to the following restrictions:\n" + "\n" + "1. The origin of this software must not be misrepresented; you must not\n" + " claim that you wrote the original software. If you use this software\n" + " in a product, an acknowledgment in the product documentation would be\n" + " appreciated but is not required.\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" + "2. Altered source versions must be plainly marked as such, and must not be\n" + " misrepresented as being the original software.\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" + "3. This notice may not be removed or altered from any source distribution.\n" , .plugin.website = "http://deadbeef.sf.net", .plugin.start = cgme_start, diff --git a/plugins/ffap/COPYING b/plugins/ffap/COPYING new file mode 100644 index 00000000..a85ee5d0 --- /dev/null +++ b/plugins/ffap/COPYING @@ -0,0 +1,18 @@ +APE / Monkey's Audio plugin for DeaDBeeF +Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> +based on apedec from FFMpeg Copyright (c) 2007 Benjamin Zores <ben@geexbox.org> +based upon libdemac from Dave Chapman. + +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. diff --git a/plugins/ffmpeg/COPYING b/plugins/ffmpeg/COPYING new file mode 100644 index 00000000..c07664c4 --- /dev/null +++ b/plugins/ffmpeg/COPYING @@ -0,0 +1,18 @@ +FFMPEG plugin for DeaDBeeF +Copyright (C) 2009-2014 Alexey Yakovenko et al. +Copyright (C) FFMPEG Developers + +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. + diff --git a/plugins/ffmpeg/ffmpeg.c b/plugins/ffmpeg/ffmpeg.c index efedb7bd..1cc6564a 100644 --- a/plugins/ffmpeg/ffmpeg.c +++ b/plugins/ffmpeg/ffmpeg.c @@ -442,10 +442,9 @@ ffmpeg_read (DB_fileinfo_t *_info, char *bytes, int size) { if (info->pkt.duration > 0) { AVRational *time_base = &info->fctx->streams[info->stream_id]->time_base; float sec = (float)info->pkt.duration * time_base->num / time_base->den; - int bitrate = info->pkt.size/sec; + int bitrate = info->pkt.size * 8 / sec; if (bitrate > 0) { - // FIXME: seems like duration translation is wrong - deadbeef->streamer_set_bitrate (bitrate / 100); + deadbeef->streamer_set_bitrate (bitrate / 1000); } } diff --git a/plugins/flac/COPYING b/plugins/flac/COPYING new file mode 100644 index 00000000..b8147af8 --- /dev/null +++ b/plugins/flac/COPYING @@ -0,0 +1,93 @@ +FLAC plugin for DeaDBeeF +Copyright (C) 2009-2014 Alexey Yakovenko et al. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the DeaDBeeF Player nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +libFLAC - Free Lossless Audio Codec library +Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +libogg +Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the Xiph.org Foundation nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/plugins/flac/Makefile.am b/plugins/flac/Makefile.am index 51efb662..49eecb7f 100644 --- a/plugins/flac/Makefile.am +++ b/plugins/flac/Makefile.am @@ -5,5 +5,5 @@ flac_la_SOURCES = flac.c flac_la_LDFLAGS = -module -avoid-version flac_la_LIBADD = $(LDADD) $(FLAC_LIBS) -AM_CFLAGS = $(CFLAGS) -std=c99 +AM_CFLAGS = $(CFLAGS) $(FLAC_CFLAGS) -std=c99 endif diff --git a/plugins/flac/flac.c b/plugins/flac/flac.c index 96d89b8f..9a823b9e 100644 --- a/plugins/flac/flac.c +++ b/plugins/flac/flac.c @@ -2,18 +2,32 @@ DeaDBeeF - ultimate music player for GNU/Linux systems with X11 Copyright (C) 2009-2013 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, see <http://www.gnu.org/licenses/>. + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the DeaDBeeF Player nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include <string.h> #include <stdio.h> @@ -1141,21 +1155,36 @@ static DB_decoder_t plugin = { .plugin.name = "FLAC decoder", .plugin.descr = "FLAC decoder using libFLAC", .plugin.copyright = - "Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net>\n" + "Copyright (C) 2009-2013 Alexey Yakovenko et al.\n" + "Uses libFLAC (C) Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson\n" + "Uses libogg Copyright (c) 2002, Xiph.org Foundation\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" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted provided that the following conditions\n" + "are met:\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" + "- Redistributions of source code must retain the above copyright\n" + "notice, this list of conditions and the following disclaimer.\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" + "- Redistributions in binary form must reproduce the above copyright\n" + "notice, this list of conditions and the following disclaimer in the\n" + "documentation and/or other materials provided with the distribution.\n" + "\n" + "- Neither the name of the DeaDBeeF Player nor the names of its\n" + "contributors may be used to endorse or promote products derived from\n" + "this software without specific prior written permission.\n" + "\n" + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" + "``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" + "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" + "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR\n" + "CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n" + "EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n" + "PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n" + "PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n" + "LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n" + "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n" + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" , .plugin.website = "http://deadbeef.sf.net", .open = cflac_open, diff --git a/plugins/gme/COPYING b/plugins/gme/COPYING new file mode 100644 index 00000000..445d1b98 --- /dev/null +++ b/plugins/gme/COPYING @@ -0,0 +1,40 @@ +Game_Music_Emu plugin for DeaDBeeF +Copyright (C) 2009-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. + + + +Game_Music_Emu (modified) +Copyright (C) 2003-2009 Shay Green. +DeaDBeeF-related modifications (C) Alexey Yakovenko. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/plugins/gme/cgme.c b/plugins/gme/cgme.c index a6452034..557ec13f 100644 --- a/plugins/gme/cgme.c +++ b/plugins/gme/cgme.c @@ -1,20 +1,29 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + GameMusicEmu plugin for DeaDBeeF + Copyright (C) 2009-2014 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 software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. - 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. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ + +#ifdef HAVE_CONFIG_H +# include "../../config.h" +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -22,20 +31,30 @@ #include "gme/gme.h" #include <zlib.h> #include "../../deadbeef.h" +#if HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif +#if HAVE_SYS_SYSLIMITS_H +#include <sys/syslimits.h> +#endif //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) +// how big vgz can be? +#define MAX_REMOTE_GZ_FILE 0x100000 + static DB_decoder_t plugin; static DB_functions_t *deadbeef; static int conf_fadeout = 10; static int conf_loopcount = 2; +static int chip_voices = 0xff; +static int chip_voices_changed = 0; typedef struct { DB_fileinfo_t info; Music_Emu *emu; int reallength; - uint32_t cgme_voicemask; float duration; // of current song } gme_fileinfo_t; @@ -48,14 +67,38 @@ cgme_open (uint32_t hint) { static int read_gzfile (const char *fname, char **buffer, int *size) { - FILE *fp = fopen (fname, "rb"); + int fd = -1; + DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { - trace ("failed to fopen %s\n", fname); + trace ("gme read_gzfile: failed to fopen %s\n", fname); return -1; } - fseek (fp, 0, SEEK_END); - size_t sz = ftell (fp); - fclose (fp); + + int64_t sz = deadbeef->fgetlength (fp); + if (fp->vfs && fp->vfs->plugin.id && strcmp (fp->vfs->plugin.id, "vfs_stdio") && sz > 0 && sz <= MAX_REMOTE_GZ_FILE) { + trace ("gme read_gzfile: reading %s of size %lld and writing to temp file\n", fname, sz); + char buffer[sz]; + if (sz == deadbeef->fread (buffer, 1, sz, fp)) { + const char *tmp = getenv ("TMPDIR"); + if (!tmp) { + tmp = "/tmp"; + } + char nm[PATH_MAX]; + snprintf (nm, sizeof (nm), "%s/ddbgmeXXXXXX.vgz", tmp); + fd = mkstemps (nm, 4); + if (fd == -1 || sz != write (fd, buffer, sz)) { + trace ("gme read_gzfile: failed to write temp file\n"); + if (fd != -1) { + close (fd); + } + } + if (fd != -1) { + lseek (fd, 0, SEEK_SET); + } + } + } + + deadbeef->fclose (fp); sz *= 2; int readsize = sz; @@ -64,7 +107,7 @@ read_gzfile (const char *fname, char **buffer, int *size) { return -1; } - gzFile gz = gzopen (fname, "rb"); + gzFile gz = fd == -1 ? gzopen (fname, "rb") : gzdopen (fd, "r"); if (!gz) { trace ("failed to gzopen %s\n", fname); return -1; @@ -77,6 +120,7 @@ read_gzfile (const char *fname, char **buffer, int *size) { if (nb < 0) { free (*buffer); trace ("failed to gzread from %s\n", fname); + gzclose (gz); return -1; } if (nb > 0) { @@ -150,7 +194,8 @@ cgme_init (DB_fileinfo_t *_info, DB_playItem_t *it) { trace ("failed with error %d\n", res); return -1; } - gme_mute_voices (info->emu, info->cgme_voicemask); + chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff); + gme_mute_voices (info->emu, chip_voices^0xff); gme_start_track (info->emu, deadbeef->pl_find_meta_int (it, ":TRACKNUM", 0)); #ifdef GME_VERSION_055 @@ -194,6 +239,13 @@ cgme_read (DB_fileinfo_t *_info, char *bytes, int size) { // DON'T ajust size, buffer must always be po2 //size = t * (float)info->samplerate * 4; } + + if (chip_voices_changed) { + chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff); + chip_voices_changed = 0; + gme_mute_voices (info->emu, chip_voices^0xff); + } + if (gme_play (info->emu, size/2, (short*)bytes)) { return 0; } @@ -406,25 +458,6 @@ static const char * exts[]= "ay","gbs","gym","hes","kss","nsf","nsfe","sap","spc","vgm","vgz",NULL }; -#if 0 -static int -cgme_numvoices (void) { - if (!emu) { - return 0; - } - return gme_voice_count (emu); -} - -static void -cgme_mutevoice (int voice, int mute) { - cgme_voicemask &= ~ (1<<voice); - cgme_voicemask |= ((mute ? 1 : 0) << voice); - if (emu) { - gme_mute_voices (emu, cgme_voicemask); - } -} -#endif - static int cgme_start (void) { conf_fadeout = deadbeef->conf_get_int ("gme.fadeout", 10); @@ -443,6 +476,9 @@ cgme_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { case DB_EV_CONFIGCHANGED: conf_fadeout = deadbeef->conf_get_int ("gme.fadeout", 10); conf_loopcount = deadbeef->conf_get_int ("gme.loopcount", 2); + if (chip_voices != deadbeef->conf_get_int ("chip.voices", 0xff)) { + chip_voices_changed = 1; + } break; } return 0; diff --git a/plugins/gtkui/COPYING b/plugins/gtkui/COPYING new file mode 100644 index 00000000..5e62520a --- /dev/null +++ b/plugins/gtkui/COPYING @@ -0,0 +1,16 @@ +GTK2/3 UI plugin for DeaDBeeF player +Copyright (C) 2009-2014 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, see <http://www.gnu.org/licenses/>. + diff --git a/plugins/gtkui/Makefile.am b/plugins/gtkui/Makefile.am index f7fb1203..d7f6fcc9 100644 --- a/plugins/gtkui/Makefile.am +++ b/plugins/gtkui/Makefile.am @@ -154,17 +154,9 @@ GTKUI_SOURCES_GTK2 = $(GTKUI_SOURCES) ddb_gui_GTK2_la_SOURCES = $(GTKUI_SOURCES_GTK2) ddb_gui_GTK2_la_LDFLAGS = -module -avoid-version -if STATICLINK -GTK_ROOT_216=@top_srcdir@/$(LIB)/gtk-2.16.0 - -ddb_gui_GTK2_la_LIBADD = $(LDADD) -L$(GTK_ROOT_216)/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgio-2.0 -lpangoft2-1.0 -lgdk_pixbuf-2.0 -lpangocairo-1.0 -lcairo -lpango-1.0 -lfreetype -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lrt -lglib-2.0 $(SM_LIBADD) ../libparser/libparser.a $(GTKGLEXT2_LIBS) - -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) $(GTKGLEXT_CFLAGS_GTK2) -else ddb_gui_GTK2_la_LIBADD = $(LDADD) $(GTK2_DEPS_LIBS) $(SM_LIBADD) ../libparser/libparser.a $(GTKGLEXT2_LIBS) ddb_gui_GTK2_la_CFLAGS = -std=c99 $(GTK2_DEPS_CFLAGS) $(SM_CFLAGS) $(GTKGLEXT_CFLAGS_GTK2) -DDDB_WARN_DEPRECATED=1 -endif endif @@ -233,21 +225,12 @@ GTKGLEXT_CFLAGS_GTK3 = -I@top_srcdir@/plugins/gtkui/gtkglext-gtk3 -I@top_srcdir@ ddb_gui_GTK3_la_LDFLAGS = -module -avoid-version -if STATICLINK -ddb_gui_GTK3_la_SOURCES = $(GTKUI_SOURCES_GTK3) -GTK_ROOT_300=@top_srcdir@/$(LIB)/gtk-3.0.0 - -ddb_gui_GTK3_la_LIBADD = $(LDADD) -L$(GTK_ROOT_300)/lib -lgtk-3 -lgdk-3 -latk-1.0 -lgio-2.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo-gobject -lpango-1.0 -lcairo -lgobject-2.0 -lgthread-2.0 -lglib-2.0 -lfreetype $(SM_LIBADD) ../libparser/libparser.a $(GTKGLEXT3_LIBS) - -ddb_gui_GTK3_la_CFLAGS = -std=c99 -I$(GTK_ROOT_300)/include/gtk-3.0 -I$(GTK_ROOT_300)/include/pango-1.0 -I$(GTK_ROOT_300)/include/gio-unix-2.0/ -I$(GTK_ROOT_300)/include/atk-1.0 -I$(GTK_ROOT_300)/include/cairo -I$(GTK_ROOT_300)/include/gdk-pixbuf-2.0 -I$(GTK_ROOT_300)/include/freetype2 -I$(GTK_ROOT_300)/include/glib-2.0 -I$(GTK_ROOT_300)/lib/glib-2.0/include $(SM_CFLAGS) $(GTKGLEXT_CFLAGS_GTK3) -else ddb_gui_GTK3_la_SOURCES = $(GTKUI_SOURCES_GTK3) ddb_gui_GTK3_la_LIBADD = $(LDADD) $(GTK3_DEPS_LIBS) $(SM_LIBADD) ../libparser/libparser.a $(GTKGLEXT3_LIBS) ddb_gui_GTK3_la_CFLAGS = -std=c99 $(GTK3_DEPS_CFLAGS) $(SM_CFLAGS) $(GTKGLEXT_CFLAGS_GTK3) ddb_gui_GTK3_la_OBJCFLAGS = $(GTK3_DEPS_CFLAGS) $(SM_CFLAGS) $(GTKGLEXT_CFLAGS_GTK3) endif -endif # fallback lib if STATICLINK @@ -256,7 +239,7 @@ GTK_ROOT=@top_srcdir@/$(LIB)/gtk-2.12.12/usr ddb_gui_GTK2_fallback_la_SOURCES = $(GTKUI_SOURCES_GTK2) ddb_gui_GTK2_fallback_la_LDFLAGS = -module -avoid-version -ddb_gui_GTK2_fallback_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib $(GTK_ROOT)/lib/libgtk-x11-2.0.la $(GTK_ROOT)/lib/libgdk-x11-2.0.la $(GTK_ROOT)/lib/libpangoft2-1.0.la $(GTK_ROOT)/lib/libpangocairo-1.0.la $(GTK_ROOT)/lib/libgdk_pixbuf-2.0.la -lm $(GTK_ROOT)/lib/libcairo.la $(GTK_ROOT)/lib/libpango-1.0.la $(GTK_ROOT)/lib/libgobject-2.0.la $(GTK_ROOT)/lib/libgmodule-2.0.la $(GTK_ROOT)/lib/libgthread-2.0.la -lrt $(GTK_ROOT)/lib/libglib-2.0.la $(SM_LIBADD) ../libparser/libparser.a $(GTKGLEXT2_LIBS) +ddb_gui_GTK2_fallback_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lgthread-2.0 -lrt -lglib-2.0 $(SM_LIBADD) ../libparser/libparser.a $(GTKGLEXT2_LIBS) ddb_gui_GTK2_fallback_la_CFLAGS = -std=c99 -I $(GTK_ROOT)/include -I $(GTK_ROOT)/lib/gtk-2.0/include -I $(GTK_ROOT)/include/glib-2.0 -I $(GTK_ROOT)/include/gtk-2.0 -I $(GTK_ROOT)/include/cairo -I $(GTK_ROOT)/lib/glib-2.0/include/ -I $(GTK_ROOT)/include/pango-1.0 -I $(GTK_ROOT)/include/atk-1.0 $(SM_CFLAGS) $(GTKGLEXT_CFLAGS_GTK2) diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index 5a696bc9..51de7a9d 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -257,6 +257,7 @@ on_loop_all_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->conf_set_int ("playback.loop", 0); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } @@ -265,6 +266,7 @@ on_loop_single_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->conf_set_int ("playback.loop", 2); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } @@ -273,6 +275,7 @@ on_loop_disable_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->conf_set_int ("playback.loop", 1); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } void @@ -311,6 +314,7 @@ on_scroll_follows_playback_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->conf_set_int ("playlist.scroll.followplayback", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } @@ -454,7 +458,7 @@ on_toggle_status_bar_activate (GtkMenuItem *menuitem, gtk_widget_show (sb); } } - deadbeef->conf_save (); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } void @@ -462,6 +466,15 @@ on_stop_after_current_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->conf_set_int ("playlist.stop_after_current", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); +} + +void +on_stop_after_album_activate (GtkMenuItem *menuitem, + gpointer user_data) +{ + deadbeef->conf_set_int ("playlist.stop_after_album", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } void @@ -469,6 +482,7 @@ on_cursor_follows_playback_activate (GtkMenuItem *menuitem, gpointer user_data) { deadbeef->conf_set_int ("playlist.scroll.cursorfollowplayback", gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } GtkWidget* @@ -515,7 +529,7 @@ on_toggle_eq (GtkMenuItem *menuitem, deadbeef->conf_set_int ("gtkui.eq.visible", 1); eq_window_show (); } - deadbeef->conf_save (); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } diff --git a/plugins/gtkui/callbacks.h b/plugins/gtkui/callbacks.h index b38d5f2e..bbd33f0e 100644 --- a/plugins/gtkui/callbacks.h +++ b/plugins/gtkui/callbacks.h @@ -1308,3 +1308,11 @@ on_auto_size_columns_toggled (GtkToggleButton *togglebutton, void on_searchentry_activate (GtkEntry *entry, gpointer user_data); + +void +on_stop_after_album_activate (GtkMenuItem *menuitem, + gpointer user_data); + +void +on_reset_autostopalbum_toggled (GtkToggleButton *togglebutton, + gpointer user_data); diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index ecbb1277..f7d78696 100644 --- a/plugins/gtkui/coverart.c +++ b/plugins/gtkui/coverart.c @@ -272,7 +272,7 @@ get_cover_art_callb (const char *fname, const char *artist, const char *album, i if (width == -1) { char path[2048]; - coverart_plugin->make_cache_path (path, sizeof (path), album, artist, -1); + coverart_plugin->make_cache_path2 (path, sizeof (path), fname, album, artist, -1); deadbeef->mutex_lock (mutex); int i_largest = -1; int size_largest = -1; diff --git a/plugins/gtkui/ddblistview.c b/plugins/gtkui/ddblistview.c index 8cff479e..465117ff 100644 --- a/plugins/gtkui/ddblistview.c +++ b/plugins/gtkui/ddblistview.c @@ -1795,7 +1795,7 @@ ddb_listview_list_mousemove (DdbListview *ps, GdkEventMotion *ev, int ex, int ey deadbeef->pl_lock (); if (ps->dragwait) { GtkWidget *widget = ps->list; - if (gtk_drag_check_threshold (widget, ps->lastpos[0], ex, ps->lastpos[1], ey)) { + if (gtk_drag_check_threshold (widget, ps->lastpos[0], ps->lastpos[1], ex, ey)) { ps->dragwait = 0; ps->drag_source_playlist = deadbeef->plt_get_curr_idx (); GtkTargetEntry entry = { diff --git a/plugins/gtkui/ddbseekbar.c b/plugins/gtkui/ddbseekbar.c index 31740a29..0dbb7594 100644 --- a/plugins/gtkui/ddbseekbar.c +++ b/plugins/gtkui/ddbseekbar.c @@ -52,7 +52,6 @@ static gboolean ddb_seekbar_real_button_press_event (GtkWidget* base, GdkEventBu static gboolean ddb_seekbar_real_button_release_event (GtkWidget* base, GdkEventButton* event); static gboolean ddb_seekbar_real_motion_notify_event (GtkWidget* base, GdkEventMotion* event); static gboolean ddb_seekbar_real_configure_event (GtkWidget* base, GdkEventConfigure* event); -DdbSeekbar* ddb_seekbar_new (void); DdbSeekbar* ddb_seekbar_construct (GType object_type); static GObject * ddb_seekbar_constructor (GType type, guint n_construct_properties, GObjectConstructParam * construct_properties); @@ -158,8 +157,8 @@ DdbSeekbar* ddb_seekbar_construct (GType object_type) { } -DdbSeekbar* ddb_seekbar_new (void) { - return ddb_seekbar_construct (DDB_TYPE_SEEKBAR); +GtkWidget* ddb_seekbar_new (void) { + return GTK_WIDGET (ddb_seekbar_construct (DDB_TYPE_SEEKBAR)); } @@ -331,7 +330,7 @@ seekbar_draw (GtkWidget *widget, cairo_t *cr) { cairo_fill (cr); cairo_reset_clip (cr); - if ((self->seekbar_moving || self->seekbar_moved > 0.0) && trk) { + if (!gtkui_disable_seekbar_overlay && (self->seekbar_moving || self->seekbar_moved > 0.0) && trk) { float time = 0; float dur = deadbeef->pl_get_item_duration (trk); @@ -439,23 +438,65 @@ on_seekbar_button_release_event (GtkWidget *widget, self->seekbar_moved = 1.0; DB_playItem_t *trk = deadbeef->streamer_get_playing_track (); if (trk) { - GtkAllocation a; - gtk_widget_get_allocation (widget, &a); - float time = (event->x - a.x) * deadbeef->pl_get_item_duration (trk) / (a.width); - if (time < 0) { - time = 0; + if (deadbeef->pl_get_item_duration (trk) >= 0) { + GtkAllocation a; + gtk_widget_get_allocation (widget, &a); + float time = (event->x - a.x) * deadbeef->pl_get_item_duration (trk) / (a.width); + if (time < 0) { + time = 0; + } + deadbeef->sendmessage (DB_EV_SEEK, 0, time * 1000, 0); } - deadbeef->sendmessage (DB_EV_SEEK, 0, time * 1000, 0); deadbeef->pl_item_unref (trk); } gtk_widget_queue_draw (widget); return FALSE; } -void -seekbar_redraw (void) { - GtkWidget *widget = lookup_widget (mainwin, "seekbar"); - gtk_widget_queue_draw (widget); +static gboolean +on_evbox_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); } +static gboolean +on_evbox_button_release_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +static gboolean +on_evbox_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +static gboolean +on_evbox_scroll_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) { + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +void +ddb_seekbar_init_signals (DdbSeekbar *sb, GtkWidget *evbox) { + g_signal_connect ((gpointer) evbox, "button_press_event", + G_CALLBACK (on_evbox_button_press_event), + sb); + g_signal_connect ((gpointer) evbox, "button_release_event", + G_CALLBACK (on_evbox_button_release_event), + sb); + g_signal_connect ((gpointer) evbox, "scroll_event", + G_CALLBACK (on_evbox_scroll_event), + sb); + g_signal_connect ((gpointer) evbox, "motion_notify_event", + G_CALLBACK (on_evbox_motion_notify_event), + sb); +} diff --git a/plugins/gtkui/ddbseekbar.h b/plugins/gtkui/ddbseekbar.h index 7ce90052..b4e470bc 100644 --- a/plugins/gtkui/ddbseekbar.h +++ b/plugins/gtkui/ddbseekbar.h @@ -34,10 +34,14 @@ struct _DdbSeekbarClass { }; -GType ddb_seekbar_get_type (void); -DdbSeekbar* ddb_seekbar_new (void); -DdbSeekbar* ddb_seekbar_construct (GType object_type); +GType +ddb_seekbar_get_type (void); +GtkWidget* +ddb_seekbar_new (void); + +void +ddb_seekbar_init_signals (DdbSeekbar *sb, GtkWidget *evbox); G_END_DECLS diff --git a/plugins/gtkui/ddbvolumebar.c b/plugins/gtkui/ddbvolumebar.c index e56ca03c..dfb3de1f 100644 --- a/plugins/gtkui/ddbvolumebar.c +++ b/plugins/gtkui/ddbvolumebar.c @@ -266,17 +266,6 @@ on_volumebar_button_release_event (GtkWidget *widget, return FALSE; } -void -volumebar_notify_changed (void) { - GtkWidget *widget = lookup_widget (mainwin, "volumebar"); - gtk_widget_queue_draw (widget); - char s[100]; - int db = deadbeef->volume_get_db (); - snprintf (s, sizeof (s), "%s%ddB", db < 0 ? "" : "+", db); - gtk_widget_set_tooltip_text (widget, s); - gtk_widget_trigger_tooltip_query (widget); -} - gboolean on_volumebar_scroll_event (GtkWidget *widget, GdkEventScroll *event) @@ -296,7 +285,7 @@ on_volumebar_scroll_event (GtkWidget *widget, vol = -range; } deadbeef->volume_set_db (vol); - GtkWidget *volumebar = lookup_widget (mainwin, "volumebar"); + GtkWidget *volumebar = DDB_VOLUMEBAR (widget); gtk_widget_queue_draw (widget); char s[100]; int db = deadbeef->volume_get_db (); @@ -311,3 +300,51 @@ on_volumebar_configure_event (GtkWidget *widget, GdkEventConfigure *event) { gtkui_init_theme_colors (); return FALSE; } + +static gboolean +on_evbox_button_press_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +static gboolean +on_evbox_button_release_event (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +static gboolean +on_evbox_motion_notify_event (GtkWidget *widget, + GdkEventMotion *event, + gpointer user_data) +{ + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +static gboolean +on_evbox_scroll_event (GtkWidget *widget, + GdkEvent *event, + gpointer user_data) { + return gtk_widget_event (GTK_WIDGET (user_data), (GdkEvent *)event); +} + +void +ddb_volumebar_init_signals (DdbVolumeBar *vb, GtkWidget *evbox) { + g_signal_connect ((gpointer) evbox, "button_press_event", + G_CALLBACK (on_evbox_button_press_event), + vb); + g_signal_connect ((gpointer) evbox, "button_release_event", + G_CALLBACK (on_evbox_button_release_event), + vb); + g_signal_connect ((gpointer) evbox, "scroll_event", + G_CALLBACK (on_evbox_scroll_event), + vb); + g_signal_connect ((gpointer) evbox, "motion_notify_event", + G_CALLBACK (on_evbox_motion_notify_event), + vb); +} + diff --git a/plugins/gtkui/ddbvolumebar.h b/plugins/gtkui/ddbvolumebar.h index e8ef0a2d..d560a5cd 100644 --- a/plugins/gtkui/ddbvolumebar.h +++ b/plugins/gtkui/ddbvolumebar.h @@ -48,6 +48,9 @@ struct _DdbVolumeBarClass { GType ddb_volumebar_get_type(void) G_GNUC_CONST; GtkWidget * ddb_volumebar_new(void); +void +ddb_volumebar_init_signals (DdbVolumeBar *vb, GtkWidget *evbox); + G_END_DECLS #endif // __DDBVOLUMEBAR_H diff --git a/plugins/gtkui/deadbeef.glade b/plugins/gtkui/deadbeef.glade index 98f85ffc..b75b4589 100644 --- a/plugins/gtkui/deadbeef.glade +++ b/plugins/gtkui/deadbeef.glade @@ -582,6 +582,16 @@ </widget> </child> + <child> + <widget class="GtkCheckMenuItem" id="stop_after_album"> + <property name="visible">True</property> + <property name="label" translatable="yes">Stop after current album</property> + <property name="use_underline">True</property> + <property name="active">False</property> + <signal name="activate" handler="on_stop_after_album_activate" last_modification_time="Thu, 01 May 2014 15:14:26 GMT"/> + </widget> + </child> + <child> <widget class="GtkSeparatorMenuItem" id="separator11"> <property name="visible">True</property> @@ -724,194 +734,6 @@ </child> <child> - <widget class="GtkHBox" id="hbox2"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> - - <child> - <widget class="GtkHBox" id="hbox3"> - <property name="visible">True</property> - <property name="homogeneous">False</property> - <property name="spacing">0</property> - - <child> - <widget class="GtkButton" id="stopbtn"> - <property name="visible">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_stopbtn_clicked" last_modification_time="Sun, 05 Jul 2009 10:48:41 GMT"/> - - <child> - <widget class="GtkImage" id="image128"> - <property name="visible">True</property> - <property name="stock">gtk-media-stop</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkButton" id="playbtn"> - <property name="visible">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_playbtn_clicked" last_modification_time="Sun, 05 Jul 2009 10:48:53 GMT"/> - - <child> - <widget class="GtkImage" id="image2"> - <property name="visible">True</property> - <property name="stock">gtk-media-play</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkButton" id="pausebtn"> - <property name="visible">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_pausebtn_clicked" last_modification_time="Sun, 05 Jul 2009 10:49:01 GMT"/> - - <child> - <widget class="GtkImage" id="image3"> - <property name="visible">True</property> - <property name="stock">gtk-media-pause</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkButton" id="prevbtn"> - <property name="visible">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_prevbtn_clicked" last_modification_time="Sun, 05 Jul 2009 10:49:08 GMT"/> - - <child> - <widget class="GtkImage" id="image4"> - <property name="visible">True</property> - <property name="stock">gtk-media-previous</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> - <widget class="GtkButton" id="nextbtn"> - <property name="visible">True</property> - <property name="relief">GTK_RELIEF_NONE</property> - <property name="focus_on_click">True</property> - <signal name="clicked" handler="on_nextbtn_clicked" last_modification_time="Sun, 05 Jul 2009 10:49:12 GMT"/> - - <child> - <widget class="GtkImage" id="image5"> - <property name="visible">True</property> - <property name="stock">gtk-media-next</property> - <property name="icon_size">4</property> - <property name="xalign">0.5</property> - <property name="yalign">0.5</property> - <property name="xpad">0</property> - <property name="ypad">0</property> - </widget> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="Custom" id="seekbar"> - <property name="width_request">20</property> - <property name="visible">True</property> - <property name="creation_function">create_seekbar</property> - <property name="int1">0</property> - <property name="int2">0</property> - <property name="last_modification_time">Fri, 09 Apr 2010 19:51:15 GMT</property> - </widget> - <packing> - <property name="padding">2</property> - <property name="expand">True</property> - <property name="fill">True</property> - </packing> - </child> - - <child> - <widget class="Custom" id="volumebar"> - <property name="width_request">70</property> - <property name="visible">True</property> - <property name="creation_function">create_volumebar_widget</property> - <property name="int1">0</property> - <property name="int2">0</property> - <property name="last_modification_time">Thu, 18 Feb 2010 20:13:13 GMT</property> - </widget> - <packing> - <property name="padding">2</property> - <property name="expand">False</property> - <property name="fill">True</property> - </packing> - </child> - </widget> - <packing> - <property name="padding">0</property> - <property name="expand">False</property> - <property name="fill">False</property> - </packing> - </child> - - <child> <widget class="GtkVBox" id="plugins_bottom_vbox"> <property name="visible">True</property> <property name="homogeneous">False</property> @@ -3037,6 +2859,26 @@ Album</property> <property name="fill">False</property> </packing> </child> + + <child> + <widget class="GtkCheckButton" id="reset_autostopalbum"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Auto-reset "Stop after current album"</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="active">False</property> + <property name="inconsistent">False</property> + <property name="draw_indicator">True</property> + <signal name="toggled" handler="on_reset_autostopalbum_toggled" last_modification_time="Thu, 01 May 2014 15:30:01 GMT"/> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> </widget> <packing> <property name="tab_expand">False</property> diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index bb89b17d..5d2dedeb 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -81,6 +81,8 @@ GtkWidget *theme_button; static int gtkui_accept_messages = 0; +static gint refresh_timeout = 0; + int fileadded_listener_id; int fileadd_beginend_listener_id; // overriden API methods @@ -99,6 +101,9 @@ int gtkui_groups_pinned; int gtkui_is_retina = 0; #endif +int gtkui_unicode_playstate = 0; +int gtkui_disable_seekbar_overlay = 0; + #define TRAY_ICON "deadbeef_tray_icon" // that must be called before gtk_init @@ -111,7 +116,7 @@ gtkpl_init (void) { gtk_box_pack_start (GTK_BOX (vbox1), theme_treeview, FALSE, FALSE, 0); gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (theme_treeview), TRUE); - theme_button = lookup_widget (mainwin, "stopbtn"); + theme_button = mainwin;//lookup_widget (mainwin, "stopbtn"); } void @@ -131,8 +136,6 @@ struct fromto_t { static gboolean update_win_title_idle (gpointer data); -static gboolean -redraw_seekbar_cb (gpointer nothing); // update status bar and window title static int sb_context_id = -1; @@ -149,7 +152,6 @@ update_songinfo (gpointer ctx) { } DB_output_t *output = deadbeef->get_output (); char sbtext_new[512] = "-"; - float songpos = last_songpos; float pl_totaltime = deadbeef->pl_get_totaltime (); int daystotal = (int)pl_totaltime / (3600*24); @@ -175,7 +177,6 @@ update_songinfo (gpointer ctx) { if (!output || (output->state () == OUTPUT_STATE_STOPPED || !track || !c)) { snprintf (sbtext_new, sizeof (sbtext_new), _("Stopped | %d tracks | %s total playtime"), deadbeef->pl_getcount (PL_MAIN), totaltime_str); - songpos = 0; } else { float playpos = deadbeef->streamer_get_playpos (); @@ -195,7 +196,6 @@ update_songinfo (gpointer ctx) { } int samplerate = c->fmt.samplerate; int bitspersample = c->fmt.bps; - songpos = playpos; // codec_unlock (); char t[100]; @@ -240,18 +240,6 @@ update_songinfo (gpointer ctx) { gtk_statusbar_push (sb, sb_context_id, sb_text); } - if (mainwin) { - GtkWidget *widget = lookup_widget (mainwin, "seekbar"); - // translate volume to seekbar pixels - songpos /= duration; - GtkAllocation a; - gtk_widget_get_allocation (widget, &a); - songpos *= a.width; - if (fabs (songpos - last_songpos) > 0.01) { - gtk_widget_queue_draw (widget); - last_songpos = songpos; - } - } if (track) { deadbeef->pl_item_unref (track); } @@ -289,17 +277,6 @@ on_trayicon_scroll_event (GtkWidget *widget, vol = deadbeef->volume_get_min_db (); } deadbeef->volume_set_db (vol); - volumebar_redraw (); - - //Update volume bar tooltip - if (mainwin) { - GtkWidget *volumebar = lookup_widget (mainwin, "volumebar"); - char s[100]; - int db = vol; - snprintf (s, sizeof (s), "%s%ddB", db < 0 ? "" : "+", db); - gtk_widget_set_tooltip_text (volumebar, s); - gtk_widget_trigger_tooltip_query (volumebar); - } #if 0 char str[100]; @@ -414,7 +391,6 @@ gtkpl_songchanged_wrapper (DB_playItem_t *from, DB_playItem_t *to) { deadbeef->pl_item_ref (to); } g_idle_add (update_win_title_idle, ft); - g_idle_add (redraw_seekbar_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) { @@ -508,13 +484,6 @@ gtkui_on_frameupdate (gpointer data) { } static gboolean -gtkui_volumechanged_cb (gpointer ctx) { - GtkWidget *volumebar = lookup_widget (mainwin, "volumebar"); - gdk_window_invalidate_rect (gtk_widget_get_window (volumebar), NULL, FALSE); - return FALSE; -} - -static gboolean gtkui_update_status_icon (gpointer unused) { int hide_tray_icon = deadbeef->conf_get_int ("gtkui.hide_tray_icon", 0); if (hide_tray_icon && !trayicon) { @@ -589,6 +558,19 @@ gtkui_get_curr_playlist_mod (void) { return res; } +void +gtkui_setup_gui_refresh (void) { + int tm = 1000/gtkui_get_gui_refresh_rate (); + + if (refresh_timeout) { + g_source_remove (refresh_timeout); + refresh_timeout = 0; + } + + refresh_timeout = g_timeout_add (tm, gtkui_on_frameupdate, NULL); +} + + static gboolean gtkui_on_configchanged (void *data) { // order and looping @@ -614,15 +596,28 @@ gtkui_on_configchanged (void *data) { int stop_after_current = deadbeef->conf_get_int ("playlist.stop_after_current", 0); gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "stop_after_current")), stop_after_current ? TRUE : FALSE); + // stop after current album + int stop_after_album = deadbeef->conf_get_int ("playlist.stop_after_album", 0); + gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (lookup_widget (mainwin, "stop_after_album")), stop_after_album ? TRUE : FALSE); + // embolden current track gtkui_embolden_current_track = deadbeef->conf_get_int ("gtkui.embolden_current_track", 0); // pin groups gtkui_groups_pinned = deadbeef->conf_get_int ("playlist.pin.groups", 0); + // play state images + gtkui_unicode_playstate = deadbeef->conf_get_int ("gtkui.unicode_playstate", 0); + + // seekbar overlay + gtkui_disable_seekbar_overlay = deadbeef->conf_get_int ("gtkui.disable_seekbar_overlay", 0); + // tray icon gtkui_update_status_icon (NULL); + // statusbar refresh + gtkui_setup_gui_refresh (); + return FALSE; } @@ -690,16 +685,6 @@ update_win_title_idle (gpointer data) { return FALSE; } -static gboolean -redraw_seekbar_cb (gpointer nothing) { - int iconified = gdk_window_get_state(gtk_widget_get_window(mainwin)) & GDK_WINDOW_STATE_ICONIFIED; - if (!gtk_widget_get_visible (mainwin) || iconified) { - return FALSE; - } - seekbar_redraw (); - return FALSE; -} - int gtkui_add_new_playlist (void) { int cnt = deadbeef->plt_get_count (); @@ -732,22 +717,8 @@ gtkui_add_new_playlist (void) { return -1; } -void -volumebar_redraw (void) { - GtkWidget *volumebar = lookup_widget (mainwin, "volumebar"); - 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)); -//} - -static gint refresh_timeout = 0; - -void -gtkui_setup_gui_refresh (void) { +int +gtkui_get_gui_refresh_rate () { int fps = deadbeef->conf_get_int ("gtkui.refresh_rate", 10); if (fps < 1) { fps = 1; @@ -755,15 +726,7 @@ gtkui_setup_gui_refresh (void) { else if (fps > 30) { fps = 30; } - - int tm = 1000/fps; - - if (refresh_timeout) { - g_source_remove (refresh_timeout); - refresh_timeout = 0; - } - - refresh_timeout = g_timeout_add (tm, gtkui_on_frameupdate, NULL); + return fps; } static void @@ -834,9 +797,6 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { case DB_EV_PLAYLISTCHANGED: g_idle_add (playlistchanged_cb, NULL); break; - case DB_EV_VOLUMECHANGED: - g_idle_add (gtkui_volumechanged_cb, NULL); - break; case DB_EV_CONFIGCHANGED: g_idle_add (gtkui_on_configchanged, NULL); break; @@ -856,6 +816,8 @@ gtkui_message (uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { return 0; } +static const char gtkui_def_layout[] = "vbox expand=\"0 1\" fill=\"1 1\" homogeneous=0 {hbox expand=\"0 1 0\" fill=\"1 1 1\" homogeneous=0 {playtb {} seekbar {} volumebar {} } tabbed_playlist hideheaders=0 {} } "; + static void init_widget_layout (void) { w_init (); @@ -864,8 +826,27 @@ init_widget_layout (void) { gtk_box_pack_start (GTK_BOX(lookup_widget(mainwin, "plugins_bottom_vbox")), rootwidget->widget, TRUE, TRUE, 0); // load layout + // config var name is defined in DDB_GTKUI_CONF_LAYOUT + // gtkui.layout: 0.6.0 and 0.6.1 + // gtkui.layout.major.minor.point: later versions + char layout[20000]; - deadbeef->conf_get_str ("gtkui.layout", "tabbed_playlist \"\" { }", layout, sizeof (layout)); + deadbeef->conf_get_str (DDB_GTKUI_CONF_LAYOUT, "-", layout, sizeof (layout)); + if (!strcmp (layout, "-")) { + // upgrade from 0.6.0 to 0.6.2 + char layout_060[20000]; + deadbeef->conf_get_str ("gtkui.layout", "-", layout_060, sizeof (layout_060)); + if (!strcmp (layout_060, "-")) { + // new setup + strcpy (layout, gtkui_def_layout); + } + else { + // upgrade with top bar + snprintf (layout, sizeof (layout), "vbox expand=\"0 1\" fill=\"1 1\" homogeneous=0 {hbox expand=\"0 1 0\" fill=\"1 1 1\" homogeneous=0 {playtb {} seekbar {} volumebar {} } %s }", layout_060); + deadbeef->conf_set_str (DDB_GTKUI_CONF_LAYOUT, layout); + deadbeef->conf_save (); + } + } ddb_gtkui_widget_t *w = NULL; w_create_from_string (layout, &w); @@ -902,7 +883,7 @@ gtkui_connect_cb (void *none) { DB_plugin_t **plugins = deadbeef->plug_get_list (); for (int i = 0; plugins[i]; i++) { DB_plugin_t *p = plugins[i]; - if (p->id && !strcmp (p->id, "artwork")) { + if (p->id && !strcmp (p->id, "artwork") && p->version_major == 1 && p->version_minor >= 2) { trace ("gtkui: found cover-art loader plugin\n"); coverart_plugin = (DB_artwork_plugin_t *)p; break; @@ -987,6 +968,10 @@ gtkui_thread (void *ctx) { w_reg_widget (_("HBox"), 0, w_hbox_create, "hbox", NULL); w_reg_widget (_("VBox"), 0, w_vbox_create, "vbox", NULL); w_reg_widget (_("Button"), 0, w_button_create, "button", NULL); + w_reg_widget (_("Seekbar"), 0, w_seekbar_create, "seekbar", NULL); + w_reg_widget (_("Playback controls"), 0, w_playtb_create, "playtb", NULL); + w_reg_widget (_("Volume bar"), 0, w_volumebar_create, "volumebar", NULL); + w_reg_widget (_("Chiptune voices"), 0, w_ctvoices_create, "ctvoices", NULL); mainwin = create_mainwin (); @@ -1079,8 +1064,6 @@ gtkui_thread (void *ctx) { init_widget_layout (); - gtkui_setup_gui_refresh (); - char fmt[500]; char str[600]; deadbeef->conf_get_str ("gtkui.titlebar_stopped", "DeaDBeeF-%V", fmt, sizeof (fmt)); @@ -1605,10 +1588,11 @@ ddb_gui_GTK3_load (DB_functions_t *api) { static const char settings_dlg[] = "property \"Ask confirmation to delete files from disk\" checkbox gtkui.delete_files_ask 1;\n" "property \"Status icon volume control sensitivity\" entry gtkui.tray_volume_sensitivity 1;\n" -// "property \"Show volume in dB (percentage otherwise)\" entry gtkui.show_gain_in_db 1\n" "property \"Custom status icon\" entry gtkui.custom_tray_icon \"" TRAY_ICON "\" ;\n" "property \"Run gtk_init with --sync (debug mode)\" checkbox gtkui.sync 0;\n" "property \"Add separators between plugin context menu items\" checkbox gtkui.action_separators 0;\n" + "property \"Use unicode chars instead of images for track state\" checkbox gtkui.unicode_playstate 0;\n" + "property \"Disable seekbar overlay text\" checkbox gtkui.disable_seekbar_overlay 0;\n" ; // define plugin interface diff --git a/plugins/gtkui/gtkui.h b/plugins/gtkui/gtkui.h index abd71af7..5ed95c8c 100644 --- a/plugins/gtkui/gtkui.h +++ b/plugins/gtkui/gtkui.h @@ -36,6 +36,8 @@ extern GtkWidget *mainwin; extern GtkWidget *searchwin; extern int gtkui_embolden_current_track; extern int gtkui_is_retina; +extern int gtkui_unicode_playstate; +extern int gtkui_disable_seekbar_overlay; struct _GSList; @@ -94,9 +96,6 @@ int gtkui_add_new_playlist (void); void -seekbar_redraw (void); - -void seekbar_draw (GtkWidget *widget, cairo_t *cr); gboolean @@ -112,9 +111,6 @@ on_seekbar_motion_notify_event (GtkWidget *widget, GdkEventMotion *event); void -volumebar_redraw (void); - -void gtkui_set_titlebar (DB_playItem_t *it); gboolean @@ -129,9 +125,6 @@ gtkui_set_progress_text_idle (gpointer data); void gtkui_playlist_set_curr (int playlist); -void -gtkui_setup_gui_refresh (); - int gtkui_get_curr_playlist_mod (void); @@ -162,4 +155,7 @@ gtkui_quit (void); void gtkui_run_preferences_dlg (void); +int +gtkui_get_gui_refresh_rate (); + #endif diff --git a/plugins/gtkui/gtkui_api.h b/plugins/gtkui/gtkui_api.h index c8057339..a3dd95d2 100644 --- a/plugins/gtkui/gtkui_api.h +++ b/plugins/gtkui/gtkui_api.h @@ -44,7 +44,10 @@ #endif #define DDB_GTKUI_API_VERSION_MAJOR 2 -#define DDB_GTKUI_API_VERSION_MINOR 0 +#define DDB_GTKUI_API_VERSION_MINOR 1 + +// added in API 2.1 +#define DDB_GTKUI_CONF_LAYOUT "gtkui.layout.0.6.2" // this flag tells that the widget should be added to h/vboxes with expand=FALSE #define DDB_GTKUI_WIDGET_FLAG_NON_EXPANDABLE 0x00000001 diff --git a/plugins/gtkui/hotkeys.c b/plugins/gtkui/hotkeys.c index 7111703f..e8bffef9 100644 --- a/plugins/gtkui/hotkeys.c +++ b/plugins/gtkui/hotkeys.c @@ -929,6 +929,7 @@ gtkui_set_default_hotkeys (void) { deadbeef->conf_set_str ("hotkey.key29", "v 0 0 stop"); deadbeef->conf_set_str ("hotkey.key30", "b 0 0 next"); deadbeef->conf_set_str ("hotkey.key31", "n 0 0 playback_random"); + deadbeef->conf_set_str ("hotkey.key32", "\"Ctrl k\" 0 0 toggle_stop_after_album"); deadbeef->conf_save (); } diff --git a/plugins/gtkui/interface.c b/plugins/gtkui/interface.c index b712cba2..5550996c 100644 --- a/plugins/gtkui/interface.c +++ b/plugins/gtkui/interface.c @@ -96,6 +96,7 @@ create_mainwin (void) GtkWidget *scroll_follows_playback; GtkWidget *cursor_follows_playback; GtkWidget *stop_after_current; + GtkWidget *stop_after_album; GtkWidget *separator11; GtkWidget *jump_to_current_track1; GtkWidget *Help; @@ -111,20 +112,6 @@ create_mainwin (void) GtkWidget *image654; GtkWidget *translators1; GtkWidget *image655; - GtkWidget *hbox2; - GtkWidget *hbox3; - GtkWidget *stopbtn; - GtkWidget *image128; - GtkWidget *playbtn; - GtkWidget *image2; - GtkWidget *pausebtn; - GtkWidget *image3; - GtkWidget *prevbtn; - GtkWidget *image4; - GtkWidget *nextbtn; - GtkWidget *image5; - GtkWidget *seekbar; - GtkWidget *volumebar; GtkWidget *plugins_bottom_vbox; GtkWidget *statusbar; @@ -400,6 +387,10 @@ create_mainwin (void) gtk_widget_show (stop_after_current); gtk_container_add (GTK_CONTAINER (Playback_menu), stop_after_current); + stop_after_album = gtk_check_menu_item_new_with_mnemonic (_("Stop after current album")); + gtk_widget_show (stop_after_album); + gtk_container_add (GTK_CONTAINER (Playback_menu), stop_after_album); + separator11 = gtk_separator_menu_item_new (); gtk_widget_show (separator11); gtk_container_add (GTK_CONTAINER (Playback_menu), separator11); @@ -462,78 +453,6 @@ create_mainwin (void) gtk_widget_show (image655); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (translators1), image655); - hbox2 = gtk_hbox_new (FALSE, 0); - gtk_widget_show (hbox2); - gtk_box_pack_start (GTK_BOX (vbox1), hbox2, FALSE, FALSE, 0); - - hbox3 = gtk_hbox_new (FALSE, 0); - gtk_widget_show (hbox3); - gtk_box_pack_start (GTK_BOX (hbox2), hbox3, FALSE, TRUE, 0); - - stopbtn = gtk_button_new (); - gtk_widget_show (stopbtn); - gtk_box_pack_start (GTK_BOX (hbox3), stopbtn, FALSE, FALSE, 0); - gtk_widget_set_can_focus(stopbtn, FALSE); - gtk_button_set_relief (GTK_BUTTON (stopbtn), GTK_RELIEF_NONE); - - image128 = gtk_image_new_from_stock ("gtk-media-stop", GTK_ICON_SIZE_BUTTON); - gtk_widget_show (image128); - gtk_container_add (GTK_CONTAINER (stopbtn), image128); - - playbtn = gtk_button_new (); - gtk_widget_show (playbtn); - gtk_box_pack_start (GTK_BOX (hbox3), playbtn, FALSE, FALSE, 0); - gtk_widget_set_can_focus(playbtn, FALSE); - gtk_button_set_relief (GTK_BUTTON (playbtn), GTK_RELIEF_NONE); - - image2 = gtk_image_new_from_stock ("gtk-media-play", GTK_ICON_SIZE_BUTTON); - gtk_widget_show (image2); - gtk_container_add (GTK_CONTAINER (playbtn), image2); - - pausebtn = gtk_button_new (); - gtk_widget_show (pausebtn); - gtk_box_pack_start (GTK_BOX (hbox3), pausebtn, FALSE, FALSE, 0); - gtk_widget_set_can_focus(pausebtn, FALSE); - gtk_button_set_relief (GTK_BUTTON (pausebtn), GTK_RELIEF_NONE); - - image3 = gtk_image_new_from_stock ("gtk-media-pause", GTK_ICON_SIZE_BUTTON); - gtk_widget_show (image3); - gtk_container_add (GTK_CONTAINER (pausebtn), image3); - - prevbtn = gtk_button_new (); - gtk_widget_show (prevbtn); - gtk_box_pack_start (GTK_BOX (hbox3), prevbtn, FALSE, FALSE, 0); - gtk_widget_set_can_focus(prevbtn, FALSE); - gtk_button_set_relief (GTK_BUTTON (prevbtn), GTK_RELIEF_NONE); - - image4 = gtk_image_new_from_stock ("gtk-media-previous", GTK_ICON_SIZE_BUTTON); - gtk_widget_show (image4); - gtk_container_add (GTK_CONTAINER (prevbtn), image4); - - nextbtn = gtk_button_new (); - gtk_widget_show (nextbtn); - gtk_box_pack_start (GTK_BOX (hbox3), nextbtn, FALSE, FALSE, 0); - gtk_widget_set_can_focus(nextbtn, FALSE); - gtk_button_set_relief (GTK_BUTTON (nextbtn), GTK_RELIEF_NONE); - - image5 = gtk_image_new_from_stock ("gtk-media-next", GTK_ICON_SIZE_BUTTON); - gtk_widget_show (image5); - gtk_container_add (GTK_CONTAINER (nextbtn), image5); - - seekbar = create_seekbar ("seekbar", "", "", 0, 0); - gtk_widget_show (seekbar); - gtk_box_pack_start (GTK_BOX (hbox2), seekbar, TRUE, TRUE, 2); - gtk_widget_set_size_request (seekbar, 20, -1); - gtk_widget_set_can_focus(seekbar, FALSE); - gtk_widget_set_can_default(seekbar, FALSE); - - volumebar = create_volumebar_widget ("volumebar", "", "", 0, 0); - gtk_widget_show (volumebar); - gtk_box_pack_start (GTK_BOX (hbox2), volumebar, FALSE, TRUE, 2); - gtk_widget_set_size_request (volumebar, 70, -1); - gtk_widget_set_can_focus(volumebar, FALSE); - gtk_widget_set_can_default(volumebar, 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, TRUE, TRUE, 0); @@ -677,6 +596,9 @@ create_mainwin (void) g_signal_connect ((gpointer) stop_after_current, "activate", G_CALLBACK (on_stop_after_current_activate), NULL); + g_signal_connect ((gpointer) stop_after_album, "activate", + G_CALLBACK (on_stop_after_album_activate), + NULL); g_signal_connect ((gpointer) jump_to_current_track1, "activate", G_CALLBACK (on_jump_to_current_track1_activate), NULL); @@ -698,21 +620,6 @@ create_mainwin (void) g_signal_connect ((gpointer) translators1, "activate", G_CALLBACK (on_translators1_activate), NULL); - g_signal_connect ((gpointer) stopbtn, "clicked", - G_CALLBACK (on_stopbtn_clicked), - NULL); - g_signal_connect ((gpointer) playbtn, "clicked", - G_CALLBACK (on_playbtn_clicked), - NULL); - g_signal_connect ((gpointer) pausebtn, "clicked", - G_CALLBACK (on_pausebtn_clicked), - NULL); - g_signal_connect ((gpointer) prevbtn, "clicked", - G_CALLBACK (on_prevbtn_clicked), - NULL); - g_signal_connect ((gpointer) nextbtn, "clicked", - G_CALLBACK (on_nextbtn_clicked), - NULL); /* Store pointers to all widgets, for use by lookup_widget(). */ GLADE_HOOKUP_OBJECT_NO_REF (mainwin, mainwin, "mainwin"); @@ -780,6 +687,7 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, scroll_follows_playback, "scroll_follows_playback"); GLADE_HOOKUP_OBJECT (mainwin, cursor_follows_playback, "cursor_follows_playback"); GLADE_HOOKUP_OBJECT (mainwin, stop_after_current, "stop_after_current"); + GLADE_HOOKUP_OBJECT (mainwin, stop_after_album, "stop_after_album"); GLADE_HOOKUP_OBJECT (mainwin, separator11, "separator11"); GLADE_HOOKUP_OBJECT (mainwin, jump_to_current_track1, "jump_to_current_track1"); GLADE_HOOKUP_OBJECT (mainwin, Help, "Help"); @@ -795,20 +703,6 @@ create_mainwin (void) GLADE_HOOKUP_OBJECT (mainwin, image654, "image654"); GLADE_HOOKUP_OBJECT (mainwin, translators1, "translators1"); GLADE_HOOKUP_OBJECT (mainwin, image655, "image655"); - GLADE_HOOKUP_OBJECT (mainwin, hbox2, "hbox2"); - GLADE_HOOKUP_OBJECT (mainwin, hbox3, "hbox3"); - GLADE_HOOKUP_OBJECT (mainwin, stopbtn, "stopbtn"); - GLADE_HOOKUP_OBJECT (mainwin, image128, "image128"); - GLADE_HOOKUP_OBJECT (mainwin, playbtn, "playbtn"); - GLADE_HOOKUP_OBJECT (mainwin, image2, "image2"); - GLADE_HOOKUP_OBJECT (mainwin, pausebtn, "pausebtn"); - GLADE_HOOKUP_OBJECT (mainwin, image3, "image3"); - GLADE_HOOKUP_OBJECT (mainwin, prevbtn, "prevbtn"); - GLADE_HOOKUP_OBJECT (mainwin, image4, "image4"); - GLADE_HOOKUP_OBJECT (mainwin, nextbtn, "nextbtn"); - GLADE_HOOKUP_OBJECT (mainwin, image5, "image5"); - GLADE_HOOKUP_OBJECT (mainwin, seekbar, "seekbar"); - GLADE_HOOKUP_OBJECT (mainwin, volumebar, "volumebar"); GLADE_HOOKUP_OBJECT (mainwin, plugins_bottom_vbox, "plugins_bottom_vbox"); GLADE_HOOKUP_OBJECT (mainwin, statusbar, "statusbar"); @@ -1630,6 +1524,7 @@ create_prefwin (void) GtkWidget *resume_last_session; GtkWidget *ignore_archives; GtkWidget *reset_autostop; + GtkWidget *reset_autostopalbum; GtkWidget *label39; GtkWidget *vbox29; GtkWidget *hbox80; @@ -1958,6 +1853,10 @@ create_prefwin (void) gtk_widget_show (reset_autostop); gtk_box_pack_start (GTK_BOX (vbox8), reset_autostop, FALSE, FALSE, 0); + reset_autostopalbum = gtk_check_button_new_with_mnemonic (_("Auto-reset \"Stop after current album\"")); + gtk_widget_show (reset_autostopalbum); + gtk_box_pack_start (GTK_BOX (vbox8), reset_autostopalbum, FALSE, FALSE, 0); + label39 = gtk_label_new (_("Playback")); gtk_widget_show (label39); gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), 1), label39); @@ -2776,6 +2675,9 @@ create_prefwin (void) g_signal_connect ((gpointer) reset_autostop, "toggled", G_CALLBACK (on_reset_autostop_toggled), NULL); + g_signal_connect ((gpointer) reset_autostopalbum, "toggled", + G_CALLBACK (on_reset_autostopalbum_toggled), + NULL); g_signal_connect ((gpointer) dsp_add, "clicked", G_CALLBACK (on_dsp_add_clicked), NULL); @@ -2987,6 +2889,7 @@ create_prefwin (void) GLADE_HOOKUP_OBJECT (prefwin, resume_last_session, "resume_last_session"); GLADE_HOOKUP_OBJECT (prefwin, ignore_archives, "ignore_archives"); GLADE_HOOKUP_OBJECT (prefwin, reset_autostop, "reset_autostop"); + GLADE_HOOKUP_OBJECT (prefwin, reset_autostopalbum, "reset_autostopalbum"); GLADE_HOOKUP_OBJECT (prefwin, label39, "label39"); GLADE_HOOKUP_OBJECT (prefwin, vbox29, "vbox29"); GLADE_HOOKUP_OBJECT (prefwin, hbox80, "hbox80"); diff --git a/plugins/gtkui/mainplaylist.c b/plugins/gtkui/mainplaylist.c index 926e6cbb..fc781a39 100644 --- a/plugins/gtkui/mainplaylist.c +++ b/plugins/gtkui/mainplaylist.c @@ -349,7 +349,7 @@ main_playlist_init (GtkWidget *widget) { DB_conf_item_t *col = deadbeef->conf_find ("playlist.column.", NULL); if (!col) { // create default set of columns - add_column_helper (listview, _("Playing"), 50, DB_COLUMN_PLAYING, NULL, 0); + add_column_helper (listview, "♫", 50, DB_COLUMN_PLAYING, NULL, 0); add_column_helper (listview, _("Artist / Album"), 150, -1, "%a - %b", 0); add_column_helper (listview, _("Track No"), 50, -1, "%n", 1); add_column_helper (listview, _("Title"), 150, -1, "%t", 0); diff --git a/plugins/gtkui/plcommon.c b/plugins/gtkui/plcommon.c index 9e3e3cdc..12229d0b 100644 --- a/plugins/gtkui/plcommon.c +++ b/plugins/gtkui/plcommon.c @@ -278,7 +278,7 @@ void draw_column_data (DdbListview *listview, cairo_t *cr, DdbListviewIter it, D } } } - else if (it && it == playing_track && cinf->id == DB_COLUMN_PLAYING) { + else if (!gtkui_unicode_playstate && it && it == playing_track && cinf->id == DB_COLUMN_PLAYING) { int paused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED; int buffering = !deadbeef->streamer_ok_to_read (-1); GdkPixbuf *pixbuf; @@ -296,15 +296,30 @@ void draw_column_data (DdbListview *listview, cairo_t *cr, DdbListviewIter it, D cairo_fill (cr); } else if (it) { - char text[1024]; - deadbeef->pl_format_title (it, -1, text, sizeof (text), cinf->id, cinf->format); - char *lb = strchr (text, '\r'); - if (lb) { - *lb = 0; + char text[1024] = ""; + if (it == playing_track && cinf->id == DB_COLUMN_PLAYING) { + int paused = deadbeef->get_output ()->state () == OUTPUT_STATE_PAUSED; + int buffering = !deadbeef->streamer_ok_to_read (-1); + if (paused) { + strcpy (text, "||"); + } + else if (!buffering) { + strcpy (text, "â–º"); + } + else { + strcpy (text, "⋯⋯⋯"); + } } - lb = strchr (text, '\n'); - if (lb) { - *lb = 0; + else { + deadbeef->pl_format_title (it, -1, text, sizeof (text), cinf->id, cinf->format); + char *lb = strchr (text, '\r'); + if (lb) { + *lb = 0; + } + lb = strchr (text, '\n'); + if (lb) { + *lb = 0; + } } GdkColor *color = NULL; if (theming) { @@ -332,7 +347,7 @@ void draw_column_data (DdbListview *listview, cairo_t *cr, DdbListviewIter it, D draw_init_font_bold (&listview->listctx); } if (calign_right) { - draw_text (&listview->listctx, x+5, y + 3, cwidth-10, 1, text); + draw_text (&listview->listctx, x + 5, y + 3, cwidth-10, 1, text); } else { draw_text (&listview->listctx, x + 5, y + 3, cwidth-10, 0, text); diff --git a/plugins/gtkui/prefwin.c b/plugins/gtkui/prefwin.c index 6034feef..00b68fbc 100644 --- a/plugins/gtkui/prefwin.c +++ b/plugins/gtkui/prefwin.c @@ -215,6 +215,9 @@ gtkui_run_preferences_dlg (void) { // reset autostop gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "reset_autostop")), deadbeef->conf_get_int ("playlist.stop_after_current_reset", 0)); + // reset album autostop + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lookup_widget (w, "reset_autostopalbum")), deadbeef->conf_get_int ("playlist.stop_after_album_reset", 0)); + // titlebar text gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "titlebar_format_playing")), deadbeef->conf_get_str_fast ("gtkui.titlebar_playing", "%a - %t - DeaDBeeF-%V")); gtk_entry_set_text (GTK_ENTRY (lookup_widget (w, "titlebar_format_stopped")), deadbeef->conf_get_str_fast ("gtkui.titlebar_stopped", "DeaDBeeF-%V")); @@ -773,8 +776,6 @@ on_override_bar_colors_toggled (GtkToggleButton *togglebutton, deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); gtkui_init_theme_colors (); prefwin_init_theme_colors (); - seekbar_redraw (); - volumebar_redraw (); eq_redraw (); } @@ -1052,7 +1053,7 @@ on_gui_fps_value_changed (GtkRange *range, { int val = gtk_range_get_value (range); deadbeef->conf_set_int ("gtkui.refresh_rate", val); - gtkui_setup_gui_refresh (); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); } void @@ -1070,6 +1071,13 @@ on_reset_autostop_toggled (GtkToggleButton *togglebutton, deadbeef->conf_set_int ("playlist.stop_after_current_reset", gtk_toggle_button_get_active (togglebutton)); } +void +on_reset_autostopalbum_toggled (GtkToggleButton *togglebutton, + gpointer user_data) +{ + deadbeef->conf_set_int ("playlist.stop_after_album_reset", gtk_toggle_button_get_active (togglebutton)); +} + void diff --git a/plugins/gtkui/trkproperties.c b/plugins/gtkui/trkproperties.c index 8ab5981f..ffeeaf90 100644 --- a/plugins/gtkui/trkproperties.c +++ b/plugins/gtkui/trkproperties.c @@ -243,8 +243,18 @@ on_metadata_edited (GtkCellRendererText *renderer, gchar *path, gchar *new_text, GtkListStore *store = GTK_LIST_STORE (user_data); GtkTreePath *treepath = gtk_tree_path_new_from_string (path); GtkTreeIter iter; - gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, treepath); + + if (!treepath) { + return; + } + + gboolean valid = gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, treepath); gtk_tree_path_free (treepath); + + if (!valid) { + return; + } + GValue value = {0,}; GValue mult = {0,}; gtk_tree_model_get_value (GTK_TREE_MODEL (store), &iter, 1, &value); @@ -428,7 +438,7 @@ show_track_properties_dlg (int ctx) { tracks = malloc (sizeof (DB_playItem_t *) * num); if (!tracks) { - fprintf (stderr, "gtkui: failed to alloc %d bytes to store selected tracks\n", num * sizeof (void *)); + fprintf (stderr, "gtkui: failed to alloc %d bytes to store selected tracks\n", (int)(num * sizeof (void *))); deadbeef->pl_unlock (); return; } diff --git a/plugins/gtkui/widgets.c b/plugins/gtkui/widgets.c index 70f60fac..2f9ff7af 100644 --- a/plugins/gtkui/widgets.c +++ b/plugins/gtkui/widgets.c @@ -37,8 +37,11 @@ #include "namedicons.h" #include "hotkeys.h" // for building action treeview #include "../../strdupa.h" -#include "../../optmath.h" +#include "../../fastftoi.h" #include "actions.h" +#include "ddbseekbar.h" +#include "ddbvolumebar.h" +#include "callbacks.h" //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) @@ -193,6 +196,27 @@ typedef struct { unsigned use_textcolor : 1; } w_button_t; +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *seekbar; + gint timer; + float last_songpos; +} w_seekbar_t; + +typedef struct { + ddb_gtkui_widget_t base; +} w_playtb_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *volumebar; +} w_volumebar_t; + +typedef struct { + ddb_gtkui_widget_t base; + GtkWidget *voices[8]; +} w_ctvoices_t; + static int design_mode; static ddb_gtkui_widget_t *rootwidget; @@ -455,7 +479,7 @@ void w_save (void) { char buf[20000] = ""; save_widget_to_string (buf, sizeof (buf), rootwidget->children); - deadbeef->conf_set_str ("gtkui.layout", buf); + deadbeef->conf_set_str (DDB_GTKUI_CONF_LAYOUT, buf); deadbeef->conf_save (); } @@ -562,12 +586,15 @@ show_widget (GtkWidget *widget, gpointer data) { gtk_widget_show (widget); } +static GtkRequisition prev_req; + 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_size_request (w->widget, prev_req.width, prev_req.height); } gtk_widget_set_app_paintable (w->widget, FALSE); gtk_widget_queue_draw (w->widget); @@ -582,9 +609,12 @@ w_button_press_event (GtkWidget *widget, GdkEventButton *event, gpointer user_da current_widget = user_data; widget = current_widget->widget; hidden = 1; + if (GTK_IS_CONTAINER (widget)) { // hide all children + gtk_widget_size_request (widget, &prev_req); gtk_container_foreach (GTK_CONTAINER (widget), hide_widget, NULL); + gtk_widget_set_size_request (widget, prev_req.width, prev_req.height); } gtk_widget_set_app_paintable (widget, TRUE); @@ -857,7 +887,7 @@ w_placeholder_create (void) { w->base.widget = gtk_event_box_new (); w->drawarea = gtk_drawing_area_new (); - gtk_widget_set_size_request (w->drawarea, 20, 20); + gtk_widget_set_size_request (w->base.widget, 20, 20); gtk_widget_show (w->drawarea); gtk_container_add (GTK_CONTAINER (w->base.widget), w->drawarea); @@ -1884,11 +1914,11 @@ w_tabbed_playlist_create (void) { w->tabstrip = DDB_TABSTRIP (tabstrip); gtk_widget_show (tabstrip); GtkWidget *list = ddb_listview_new (); + gtk_widget_set_size_request (vbox, 100, 100); w->plt.list = (DdbListview *)list; gtk_widget_show (list); 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); @@ -1896,7 +1926,6 @@ w_tabbed_playlist_create (void) { main_playlist_init (list); -// gtk_container_forall (GTK_CONTAINER (w->base.widget), w_override_signals, w); w_override_signals (w->plt.base.widget, w); w->plt.base.message = w_tabbed_playlist_message; @@ -1912,6 +1941,7 @@ w_playlist_create (void) { w->base.widget = gtk_event_box_new (); w->list = DDB_LISTVIEW (ddb_listview_new ()); + gtk_widget_set_size_request (GTK_WIDGET (w->base.widget), 100, 100); w->base.save = w_playlist_save; w->base.load = w_playlist_load; w->base.init = w_playlist_init; @@ -2920,8 +2950,6 @@ w_hvbox_replace (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s return; } - w_remove (container, c); - w_destroy (c); if (prev) { prev->next = newchild; } @@ -2930,6 +2958,8 @@ w_hvbox_replace (struct ddb_gtkui_widget_s *container, struct ddb_gtkui_widget_s } newchild->next = c->next; newchild->parent = container; + w_remove (container, c); + w_destroy (c); gtk_box_pack_start (GTK_BOX (b->box), newchild->widget, TRUE, TRUE, 0); gtk_widget_show (newchild->widget); @@ -3445,3 +3475,263 @@ w_button_create (void) { w_override_signals (w->base.widget, w); return (ddb_gtkui_widget_t *)w; } + +// seekbar +static gboolean +redraw_seekbar_cb (gpointer data) { + w_seekbar_t *w = data; + int iconified = gdk_window_get_state(gtk_widget_get_window(mainwin)) & GDK_WINDOW_STATE_ICONIFIED; + if (!gtk_widget_get_visible (mainwin) || iconified) { + return FALSE; + } + gtk_widget_queue_draw (w->seekbar); + return FALSE; +} + +static gboolean +seekbar_frameupdate (gpointer data) { + w_seekbar_t *w = data; + DB_output_t *output = deadbeef->get_output (); + DB_playItem_t *track = deadbeef->streamer_get_playing_track (); + DB_fileinfo_t *c = deadbeef->streamer_get_current_fileinfo (); // FIXME: might crash streamer + float songpos = w->last_songpos; + float duration = track ? deadbeef->pl_get_item_duration (track) : -1; + if (!output || (output->state () == OUTPUT_STATE_STOPPED || !track || !c)) { + songpos = 0; + } + else { + songpos = deadbeef->streamer_get_playpos (); + } + // translate pos to seekbar pixels + songpos /= duration; + GtkAllocation a; + gtk_widget_get_allocation (w->seekbar, &a); + songpos *= a.width; + if (fabs (songpos - w->last_songpos) > 0.01) { + gtk_widget_queue_draw (w->seekbar); + w->last_songpos = songpos; + } + if (track) { + deadbeef->pl_item_unref (track); + } + return TRUE; +} + +static void +w_seekbar_init (ddb_gtkui_widget_t *base) { + w_seekbar_t *w = (w_seekbar_t *)base; + if (w->timer) { + g_source_remove (w->timer); + w->timer = 0; + } + + w->timer = g_timeout_add (1000/gtkui_get_gui_refresh_rate (), seekbar_frameupdate, w); +} + +static int +w_seekbar_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + switch (id) { + case DB_EV_SONGCHANGED: + g_idle_add (redraw_seekbar_cb, w); + break; + case DB_EV_CONFIGCHANGED: + w_seekbar_init (w); + break; + } + return 0; +} + +static void +w_seekbar_destroy (ddb_gtkui_widget_t *wbase) { + w_seekbar_t *w = (w_seekbar_t *)wbase; + if (w->timer) { + g_source_remove (w->timer); + w->timer = 0; + } +} + +ddb_gtkui_widget_t * +w_seekbar_create (void) { + w_seekbar_t *w = malloc (sizeof (w_seekbar_t)); + memset (w, 0, sizeof (w_seekbar_t)); + w->base.widget = gtk_event_box_new (); + w->base.message = w_seekbar_message; + w->base.destroy = w_seekbar_destroy; + w->base.init = w_seekbar_init; + w->seekbar = ddb_seekbar_new (); + gtk_widget_set_size_request (w->base.widget, 20, 16); + w->last_songpos = -1; + ddb_seekbar_init_signals (DDB_SEEKBAR (w->seekbar), w->base.widget); + gtk_widget_show (w->seekbar); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->seekbar); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t*)w; +} + +// play toolbar +ddb_gtkui_widget_t * +w_playtb_create (void) { + w_playtb_t *w = malloc (sizeof (w_playtb_t)); + memset (w, 0, sizeof (w_playtb_t)); + w->base.widget = gtk_hbox_new (FALSE, 0); + w->base.flags = DDB_GTKUI_WIDGET_FLAG_NON_EXPANDABLE; + gtk_widget_show (w->base.widget); + + GtkWidget *stopbtn; + GtkWidget *image128; + GtkWidget *playbtn; + GtkWidget *image2; + GtkWidget *pausebtn; + GtkWidget *image3; + GtkWidget *prevbtn; + GtkWidget *image4; + GtkWidget *nextbtn; + GtkWidget *image5; + + + stopbtn = gtk_button_new (); + gtk_widget_show (stopbtn); + gtk_box_pack_start (GTK_BOX (w->base.widget), stopbtn, FALSE, FALSE, 0); + gtk_widget_set_can_focus(stopbtn, FALSE); + gtk_button_set_relief (GTK_BUTTON (stopbtn), GTK_RELIEF_NONE); + + image128 = gtk_image_new_from_stock ("gtk-media-stop", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image128); + gtk_container_add (GTK_CONTAINER (stopbtn), image128); + + playbtn = gtk_button_new (); + gtk_widget_show (playbtn); + gtk_box_pack_start (GTK_BOX (w->base.widget), playbtn, FALSE, FALSE, 0); + gtk_widget_set_can_focus(playbtn, FALSE); + gtk_button_set_relief (GTK_BUTTON (playbtn), GTK_RELIEF_NONE); + + image2 = gtk_image_new_from_stock ("gtk-media-play", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image2); + gtk_container_add (GTK_CONTAINER (playbtn), image2); + + pausebtn = gtk_button_new (); + gtk_widget_show (pausebtn); + gtk_box_pack_start (GTK_BOX (w->base.widget), pausebtn, FALSE, FALSE, 0); + gtk_widget_set_can_focus(pausebtn, FALSE); + gtk_button_set_relief (GTK_BUTTON (pausebtn), GTK_RELIEF_NONE); + + image3 = gtk_image_new_from_stock ("gtk-media-pause", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image3); + gtk_container_add (GTK_CONTAINER (pausebtn), image3); + + prevbtn = gtk_button_new (); + gtk_widget_show (prevbtn); + gtk_box_pack_start (GTK_BOX (w->base.widget), prevbtn, FALSE, FALSE, 0); + gtk_widget_set_can_focus(prevbtn, FALSE); + gtk_button_set_relief (GTK_BUTTON (prevbtn), GTK_RELIEF_NONE); + + image4 = gtk_image_new_from_stock ("gtk-media-previous", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image4); + gtk_container_add (GTK_CONTAINER (prevbtn), image4); + + nextbtn = gtk_button_new (); + gtk_widget_show (nextbtn); + gtk_box_pack_start (GTK_BOX (w->base.widget), nextbtn, FALSE, FALSE, 0); + gtk_widget_set_can_focus(nextbtn, FALSE); + gtk_button_set_relief (GTK_BUTTON (nextbtn), GTK_RELIEF_NONE); + + image5 = gtk_image_new_from_stock ("gtk-media-next", GTK_ICON_SIZE_BUTTON); + gtk_widget_show (image5); + gtk_container_add (GTK_CONTAINER (nextbtn), image5); + w_override_signals (w->base.widget, w); + + g_signal_connect ((gpointer) stopbtn, "clicked", + G_CALLBACK (on_stopbtn_clicked), + NULL); + g_signal_connect ((gpointer) playbtn, "clicked", + G_CALLBACK (on_playbtn_clicked), + NULL); + g_signal_connect ((gpointer) pausebtn, "clicked", + G_CALLBACK (on_pausebtn_clicked), + NULL); + g_signal_connect ((gpointer) prevbtn, "clicked", + G_CALLBACK (on_prevbtn_clicked), + NULL); + g_signal_connect ((gpointer) nextbtn, "clicked", + G_CALLBACK (on_nextbtn_clicked), + NULL); + return (ddb_gtkui_widget_t*)w; +} + +// volumebar +static gboolean +redraw_volumebar_cb (gpointer data) { + w_volumebar_t *w = data; + gtk_widget_queue_draw (w->volumebar); + char s[100]; + int db = deadbeef->volume_get_db (); + snprintf (s, sizeof (s), "%s%ddB", db < 0 ? "" : "+", db); + gtk_widget_set_tooltip_text (w->volumebar, s); + gtk_widget_trigger_tooltip_query (w->volumebar); + return FALSE; +} + +static int +w_volumebar_message (ddb_gtkui_widget_t *w, uint32_t id, uintptr_t ctx, uint32_t p1, uint32_t p2) { + switch (id) { + case DB_EV_CONFIGCHANGED: + case DB_EV_VOLUMECHANGED: + g_idle_add (redraw_volumebar_cb, w); + break; + } + return 0; +} + +ddb_gtkui_widget_t * +w_volumebar_create (void) { + w_volumebar_t *w = malloc (sizeof (w_volumebar_t)); + memset (w, 0, sizeof (w_volumebar_t)); + w->base.widget = gtk_event_box_new (); + w->base.message = w_volumebar_message; + w->volumebar = ddb_volumebar_new (); + ddb_volumebar_init_signals (DDB_VOLUMEBAR (w->volumebar), w->base.widget); + gtk_widget_show (w->volumebar); + gtk_widget_set_size_request (w->base.widget, 70, -1); + gtk_container_add (GTK_CONTAINER (w->base.widget), w->volumebar); + w_override_signals (w->base.widget, w); + return (ddb_gtkui_widget_t*)w; +} + +// chiptune voice ctl +static void +on_voice_toggled (GtkToggleButton *togglebutton, gpointer user_data) { + w_ctvoices_t *w = user_data; + int voices = 0; + for (int i = 0; i < 8; i++) { + int active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w->voices[i])); + voices |= active << i; + } + deadbeef->conf_set_int ("chip.voices", voices); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); +} + +ddb_gtkui_widget_t * +w_ctvoices_create (void) { + w_ctvoices_t *w = malloc (sizeof (w_ctvoices_t)); + memset (w, 0, sizeof (w_ctvoices_t)); + w->base.widget = gtk_event_box_new (); + GtkWidget *hbox = gtk_hbox_new (FALSE, 0); + gtk_widget_show (hbox); + gtk_container_add (GTK_CONTAINER (w->base.widget), hbox); + + GtkWidget *label = gtk_label_new_with_mnemonic (_("Voices:")); + gtk_widget_show (label); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + + int voices = deadbeef->conf_get_int ("chip.voices", 0xff); + for (int i = 0; i < 8; i++) { + w->voices[i] = gtk_check_button_new (); + gtk_widget_show (w->voices[i]); + gtk_box_pack_start (GTK_BOX (hbox), w->voices[i], FALSE, FALSE, 0); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w->voices[i]), voices & (1<<i)); + g_signal_connect ((gpointer) w->voices[i], "toggled", G_CALLBACK (on_voice_toggled), 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 index 964fb665..8521e25d 100644 --- a/plugins/gtkui/widgets.h +++ b/plugins/gtkui/widgets.h @@ -122,4 +122,16 @@ w_vbox_create (void); ddb_gtkui_widget_t * w_button_create (void); +ddb_gtkui_widget_t * +w_seekbar_create (void); + +ddb_gtkui_widget_t * +w_playtb_create (void); + +ddb_gtkui_widget_t * +w_volumebar_create (void); + +ddb_gtkui_widget_t * +w_ctvoices_create (void); + #endif diff --git a/plugins/hotkeys/COPYING b/plugins/hotkeys/COPYING new file mode 100644 index 00000000..197bfba0 --- /dev/null +++ b/plugins/hotkeys/COPYING @@ -0,0 +1,22 @@ +Hotkeys plugin for DeaDBeeF + +Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> +Copyright (C) 2012-2014 Alexey Yakovenko <waker@users.sourceforge.net> + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/plugins/hotkeys/Makefile.am b/plugins/hotkeys/Makefile.am index 5f5fb965..2fd1ad34 100644 --- a/plugins/hotkeys/Makefile.am +++ b/plugins/hotkeys/Makefile.am @@ -7,5 +7,5 @@ hotkeys_la_LDFLAGS = -module -avoid-version EXTRA_hotkeys_la_SOURCES = keysyms.inc hotkeys_la_LIBADD = $(LDADD) $(HOTKEYS_LIBS) -AM_CFLAGS = $(CFLAGS) -std=c99 +AM_CFLAGS = $(CFLAGS) $(HOTKEYS_CFLAGS) -std=c99 endif diff --git a/plugins/hotkeys/hotkeys.c b/plugins/hotkeys/hotkeys.c index c8d1fa67..f1deaac8 100644 --- a/plugins/hotkeys/hotkeys.c +++ b/plugins/hotkeys/hotkeys.c @@ -3,18 +3,23 @@ Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> Copyright (C) 2012-2013 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, see <http://www.gnu.org/licenses/>. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ #include <stdio.h> #include <stdlib.h> @@ -740,6 +745,15 @@ action_toggle_stop_after_current_cb (struct DB_plugin_action_s *action, int ctx) return 0; } +int +action_toggle_stop_after_album_cb (struct DB_plugin_action_s *action, int ctx) { + int var = deadbeef->conf_get_int ("playlist.stop_after_album", 0); + var = 1 - var; + deadbeef->conf_set_int ("playlist.stop_after_album", var); + deadbeef->sendmessage (DB_EV_CONFIGCHANGED, 0, 0, 0); + return 0; +} + static DB_plugin_action_t action_reload_metadata = { .title = "Reload Metadata", .name = "reload_metadata", @@ -1053,10 +1067,18 @@ static DB_plugin_action_t action_toggle_stop_after_current = { .next = &action_volume_down }; +static DB_plugin_action_t action_toggle_stop_after_album = { + .title = "Playback/Toggle Stop After Current Album", + .name = "toggle_stop_after_album", + .flags = DB_ACTION_COMMON, + .callback2 = action_toggle_stop_after_album_cb, + .next = &action_toggle_stop_after_current +}; + static DB_plugin_action_t * hotkeys_get_actions (DB_playItem_t *it) { - return &action_toggle_stop_after_current; + return &action_toggle_stop_after_album; } // define plugin interface diff --git a/plugins/hotkeys/hotkeys.h b/plugins/hotkeys/hotkeys.h index cfc12108..7c7171f3 100644 --- a/plugins/hotkeys/hotkeys.h +++ b/plugins/hotkeys/hotkeys.h @@ -1,21 +1,25 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2011 Alexey Yakovenko <waker@users.sourceforge.net> + Hotkeys plugin for DeaDBeeF + Copyright (C) 2009-2011 Viktor Semykin <thesame.ml@gmail.com> Copyright (C) 2012-2013 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. + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ #ifndef __HOTKEYS_H #define __HOTKEYS_H diff --git a/plugins/lastfm/lastfm.c b/plugins/lastfm/lastfm.c index 2750cfb2..a7443e1c 100644 --- a/plugins/lastfm/lastfm.c +++ b/plugins/lastfm/lastfm.c @@ -68,6 +68,7 @@ static char lfm_nowplaying[2048]; // packet for nowplaying, or "" typedef struct { DB_playItem_t *it; time_t started_timestamp; + float playtime; } subm_item_t; static subm_item_t lfm_subm_queue[LFM_SUBMISSION_QUEUE_SIZE]; @@ -278,7 +279,7 @@ auth (void) { end++; } if (end - p > sizeof (lfm_nowplaying_url)-1) { - trace ("scrobbler nowplaying url is too long:\n", lfm_reply); + trace ("scrobbler nowplaying url is too long %d:\n", (int)(end-p)); goto fail; } strncpy (lfm_nowplaying_url, p, end-p); @@ -299,7 +300,7 @@ auth (void) { end++; } if (end - p > sizeof (lfm_submission_url)-1) { - trace ("scrobbler submission url is too long:\n", lfm_reply); + trace ("scrobbler submission url is too long: %d\n", (int)(end-p)); goto fail; } strncpy (lfm_submission_url, p, end-p); @@ -322,7 +323,7 @@ fail: } static int -lfm_fetch_song_info (DB_playItem_t *song, char *a, char *t, char *b, float *l, char *n, char *m) { +lfm_fetch_song_info (DB_playItem_t *song, float playtime, char *a, char *t, char *b, float *l, char *n, char *m) { if (deadbeef->conf_get_int ("lastfm.prefer_album_artist", 0)) { if (!deadbeef->pl_get_meta (song, "band", a, META_FIELD_SIZE)) { if (!deadbeef->pl_get_meta (song, "album artist", a, META_FIELD_SIZE)) { @@ -352,10 +353,13 @@ lfm_fetch_song_info (DB_playItem_t *song, char *a, char *t, char *b, float *l, c *b = 0; } *l = deadbeef->pl_get_item_duration (song); + if (*l <= 0) { + *l = playtime; + } if (!deadbeef->pl_get_meta (song, "track", n, META_FIELD_SIZE)) { *n = 0; } - if (!deadbeef->pl_get_meta (song, "mbid", m, META_FIELD_SIZE)) { + if (!deadbeef->pl_get_meta (song, "musicbrainz_trackid", m, META_FIELD_SIZE)) { *m = 0; } return 0; @@ -433,7 +437,7 @@ lfm_add_keyvalue_uri_encoded (char **out, int *outl, const char *key, const char // subm is submission idx, or -1 for nowplaying // returns number of bytes added, or -1 static int -lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl, time_t started_timestamp) { +lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl, time_t started_timestamp, float playtime) { if (subm > 50) { trace ("lastfm: it's only allowed to send up to 50 submissions at once (got idx=%d)\n", subm); return -1; @@ -462,7 +466,7 @@ lfm_format_uri (int subm, DB_playItem_t *song, char *out, int outl, time_t start strcpy (km+1, ka+1); } - if (lfm_fetch_song_info (song, a, t, b, &l, n, m) == 0) { + if (lfm_fetch_song_info (song, playtime, a, t, b, &l, n, m) == 0) { // trace ("playtime: %f\nartist: %s\ntitle: %s\nalbum: %s\nduration: %f\ntracknum: %s\n---\n", song->playtime, a, t, b, l, n); } else { @@ -518,7 +522,7 @@ lastfm_songstarted (ddb_event_track_t *ev, uintptr_t data) { return 0; } deadbeef->mutex_lock (lfm_mutex); - if (lfm_format_uri (-1, ev->track, lfm_nowplaying, sizeof (lfm_nowplaying), ev->started_timestamp) < 0) { + if (lfm_format_uri (-1, ev->track, lfm_nowplaying, sizeof (lfm_nowplaying), ev->started_timestamp, 120) < 0) { lfm_nowplaying[0] = 0; } // trace ("%s\n", lfm_nowplaying); @@ -542,13 +546,14 @@ lastfm_songchanged (ddb_event_trackchange_t *ev, uintptr_t data) { trace ("lfm songfinished %s\n", deadbeef->pl_find_meta (ev->from, ":URI")); #if !LFM_IGNORE_RULES // check submission rules - // duration must be >= 30 sec - if (deadbeef->pl_get_item_duration (ev->from) < 30) { - trace ("track duration is %f seconds. not eligible for submission\n", deadbeef->pl_get_item_duration (ev->from)); + // duration/playtime must be >= 30 sec + float dur = deadbeef->pl_get_item_duration (ev->from); + if (dur < 30 && ev->playtime < 30) { + trace ("track duration is %f sec, playtime if %f sec. not eligible for submission\n", dur, ev->playtime); return 0; } - // must be played for >=240sec of half the total time - if (ev->playtime < 240 && ev->playtime < deadbeef->pl_get_item_duration (ev->from)/2) { + // must be played for >=240sec or half the total time + if (ev->playtime < 240 && ev->playtime < dur/2) { trace ("track playtime=%f seconds. not eligible for submission\n", ev->playtime); return 0; } @@ -568,6 +573,7 @@ lastfm_songchanged (ddb_event_trackchange_t *ev, uintptr_t data) { trace ("lfm: song is now in queue for submission\n"); lfm_subm_queue[i].it = ev->from; lfm_subm_queue[i].started_timestamp = ev->started_timestamp; + lfm_subm_queue[i].playtime = ev->playtime; deadbeef->pl_item_ref (ev->from); break; } @@ -634,7 +640,7 @@ lfm_send_submissions (void) { deadbeef->mutex_lock (lfm_mutex); for (i = 0; i < LFM_SUBMISSION_QUEUE_SIZE; i++) { if (lfm_subm_queue[i].it) { - res = lfm_format_uri (idx, lfm_subm_queue[i].it, r, len, lfm_subm_queue[i].started_timestamp); + res = lfm_format_uri (idx, lfm_subm_queue[i].it, r, len, lfm_subm_queue[i].started_timestamp, lfm_subm_queue[i].playtime); if (res < 0) { trace ("lfm: failed to format uri\n"); return; diff --git a/plugins/mpgmad/Makefile.am b/plugins/mpgmad/Makefile.am index 814b81e4..a4ae3aca 100644 --- a/plugins/mpgmad/Makefile.am +++ b/plugins/mpgmad/Makefile.am @@ -5,5 +5,5 @@ mpgmad_la_SOURCES = mpgmad.c mpgmad_la_LDFLAGS = -module -avoid-version mpgmad_la_LIBADD = $(LDADD) $(MAD_LIBS) -AM_CFLAGS = $(CFLAGS) -std=c99 -fPIC +AM_CFLAGS = $(CFLAGS) $(MAD_CFLAGS) -std=c99 -fPIC endif diff --git a/plugins/mpgmad/mpgmad.c b/plugins/mpgmad/mpgmad.c index b450ffbc..c5f67ef3 100644 --- a/plugins/mpgmad/mpgmad.c +++ b/plugins/mpgmad/mpgmad.c @@ -238,6 +238,8 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { } for (;;) { + uint32_t hdr; + uint8_t sync; // {{{ parse frame header, sync stream // mp3 files often have some garbage in the beginning // try to skip it if this is the case @@ -254,12 +256,18 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { break; // eof } - uint32_t hdr; - uint8_t sync = fb[0]; +retry_sync: + + sync = fb[0]; if (sync != 0xff) { // trace ("[1]frame %d didn't seek to frame end\n", nframe); lastframe_valid = 0; - continue; // not an mpeg frame + memmove (fb, fb+1, 3); + if (deadbeef->fread (fb+3, 1, 1, buffer->file) != 1) { + break; // eof + } + offs++; + goto retry_sync; // not an mpeg frame } else { // 2nd sync byte @@ -267,7 +275,12 @@ cmp3_scan_stream (buffer_t *buffer, int sample) { if ((sync >> 5) != 7) { // trace ("[2]frame %d didn't seek to frame end\n", nframe); lastframe_valid = 0; - continue; + memmove (fb, fb+1, 3); + if (deadbeef->fread (fb+3, 1, 1, buffer->file) != 1) { + break; // eof + } + offs++; + goto retry_sync; // not an mpeg frame } } // found frame @@ -839,14 +852,6 @@ cmp3_init (DB_fileinfo_t *_info, DB_playItem_t *it) { } else { deadbeef->fset_track (info->buffer.file, it); - int64_t len = deadbeef->fgetlength (info->buffer.file); - if (len > 0) { - deadbeef->pl_delete_all_meta (it); - int v2err = deadbeef->junk_id3v2_read (it, info->buffer.file); - if (v2err != 0) { - deadbeef->fseek (info->buffer.file, 0, SEEK_SET); - } - } deadbeef->pl_add_meta (it, "title", NULL); int res = cmp3_scan_stream (&info->buffer, 0); if (res < 0) { diff --git a/plugins/pltbrowser/Makefile.am b/plugins/pltbrowser/Makefile.am index ddd396c3..ffcc610e 100644 --- a/plugins/pltbrowser/Makefile.am +++ b/plugins/pltbrowser/Makefile.am @@ -20,7 +20,7 @@ pltbrowser_gtk2_la_LDFLAGS = -module -avoid-version if STATICLINK GTK_ROOT=@top_srcdir@/$(LIB)/gtk-2.12.12/usr -pltbrowser_gtk2_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib $(GTK_ROOT)/lib/libgtk-x11-2.0.la $(GTK_ROOT)/lib/libgdk-x11-2.0.la $(GTK_ROOT)/lib/libpangoft2-1.0.la $(GTK_ROOT)/lib/libpangocairo-1.0.la $(GTK_ROOT)/lib/libgdk_pixbuf-2.0.la -lm $(GTK_ROOT)/lib/libcairo.la $(GTK_ROOT)/lib/libpango-1.0.la $(GTK_ROOT)/lib/libgobject-2.0.la $(GTK_ROOT)/lib/libgmodule-2.0.la $(GTK_ROOT)/lib/libgthread-2.0.la -lrt $(GTK_ROOT)/lib/libglib-2.0.la +pltbrowser_gtk2_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lrt pltbrowser_gtk2_la_CFLAGS = -std=c99 -I $(GTK_ROOT)/include -I $(GTK_ROOT)/lib/gtk-2.0/include -I $(GTK_ROOT)/include/glib-2.0 -I $(GTK_ROOT)/include/gtk-2.0 -I $(GTK_ROOT)/include/cairo -I $(GTK_ROOT)/lib/glib-2.0/include/ -I $(GTK_ROOT)/include/pango-1.0 -I $(GTK_ROOT)/include/atk-1.0 -DULTRA_COMPATIBLE=1 else @@ -32,17 +32,8 @@ endif if HAVE_GTK3 pltbrowser_gtk3_la_SOURCES = pltbrowser.c pltbrowser_gtk3_la_LDFLAGS = -module -avoid-version -if STATICLINK -GTK_ROOT_300=@top_srcdir@/$(LIB)/gtk-3.0.0 - -pltbrowser_gtk3_la_LIBADD = $(LDADD) -L$(GTK_ROOT_300)/lib -lgtk-3 -lgdk-3 -latk-1.0 -lgio-2.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo-gobject -lpango-1.0 -lcairo -lgobject-2.0 -lglib-2.0 -lfreetype $(SM_LIBADD) - -pltbrowser_gtk3_la_CFLAGS = -std=c99 -I$(GTK_ROOT_300)/include/gtk-3.0 -I$(GTK_ROOT_300)/include/pango-1.0 -I$(GTK_ROOT_300)/include/gio-unix-2.0/ -I$(GTK_ROOT_300)/include/atk-1.0 -I$(GTK_ROOT_300)/include/cairo -I$(GTK_ROOT_300)/include/gdk-pixbuf-2.0 -I$(GTK_ROOT_300)/include/freetype2 -I$(GTK_ROOT_300)/include/glib-2.0 -I$(GTK_ROOT_300)/lib/glib-2.0/include $(SM_CFLAGS) - -else pltbrowser_gtk3_la_LIBADD = $(LDADD) $(GTK3_DEPS_LIBS) pltbrowser_gtk3_la_CFLAGS = -std=c99 $(GTK3_DEPS_CFLAGS) endif -endif endif diff --git a/plugins/shellexec/shellexec.c b/plugins/shellexec/shellexec.c index 473fc1a7..3894b6b4 100644 --- a/plugins/shellexec/shellexec.c +++ b/plugins/shellexec/shellexec.c @@ -88,6 +88,24 @@ static int shx_exec_track_cmd (Shx_action_t *action, DB_playItem_t *it) { return -1; } strcat (cmd, "&"); + + // replace \' with '"'"' + size_t l = strlen (cmd); + size_t remaining = _POSIX_ARG_MAX - l - 1; + for (int i = 0; cmd[i]; i++) { + if (cmd[i] == '\\' && cmd[i+1] == '\'' && remaining >= 3) { + memmove (&cmd[i+5], &cmd[i+2], l - i + 1 - 2); + memcpy (&cmd[i], "'\"'\"'", 5); + l += 3; + remaining -= 3; + i += 5; + } + else if (remaining < 3) { + fprintf (stderr, "shellexec: command is too long.\n"); + return -1; + } + } + trace ("%s\n", cmd); res = system (cmd); return 0; diff --git a/plugins/shellexecui/Makefile.am b/plugins/shellexecui/Makefile.am index ee15f428..22d84e57 100644 --- a/plugins/shellexecui/Makefile.am +++ b/plugins/shellexecui/Makefile.am @@ -22,7 +22,7 @@ shellexecui_gtk2_la_LDFLAGS = -module -avoid-version if STATICLINK GTK_ROOT=@top_srcdir@/$(LIB)/gtk-2.12.12/usr -shellexecui_gtk2_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib $(GTK_ROOT)/lib/libgtk-x11-2.0.la $(GTK_ROOT)/lib/libgdk-x11-2.0.la $(GTK_ROOT)/lib/libpangoft2-1.0.la $(GTK_ROOT)/lib/libpangocairo-1.0.la $(GTK_ROOT)/lib/libgdk_pixbuf-2.0.la -lm $(GTK_ROOT)/lib/libcairo.la $(GTK_ROOT)/lib/libpango-1.0.la $(GTK_ROOT)/lib/libgobject-2.0.la $(GTK_ROOT)/lib/libgmodule-2.0.la $(GTK_ROOT)/lib/libgthread-2.0.la -lrt $(GTK_ROOT)/lib/libglib-2.0.la +shellexecui_gtk2_la_LIBADD = $(LDADD) -L$(GTK_ROOT)/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0 -lrt shellexecui_gtk2_la_CFLAGS = -std=c99 -I $(GTK_ROOT)/include -I $(GTK_ROOT)/lib/gtk-2.0/include -I $(GTK_ROOT)/include/glib-2.0 -I $(GTK_ROOT)/include/gtk-2.0 -I $(GTK_ROOT)/include/cairo -I $(GTK_ROOT)/lib/glib-2.0/include/ -I $(GTK_ROOT)/include/pango-1.0 -I $(GTK_ROOT)/include/atk-1.0 -DULTRA_COMPATIBLE=1 else @@ -34,18 +34,9 @@ endif if HAVE_GTK3 shellexecui_gtk3_la_SOURCES = shellexecui.c interface.c support.c callbacks.c interface.h support.h callbacks.h shellexecui_gtk3_la_LDFLAGS = -module -avoid-version -if STATICLINK -GTK_ROOT_300=@top_srcdir@/$(LIB)/gtk-3.0.0 - -shellexecui_gtk3_la_LIBADD = $(LDADD) -L$(GTK_ROOT_300)/lib -lgtk-3 -lgdk-3 -latk-1.0 -lgio-2.0 -lpangocairo-1.0 -lgdk_pixbuf-2.0 -lcairo-gobject -lpango-1.0 -lcairo -lgobject-2.0 -lglib-2.0 -lfreetype $(SM_LIBADD) - -shellexecui_gtk3_la_CFLAGS = -std=c99 -I$(GTK_ROOT_300)/include/gtk-3.0 -I$(GTK_ROOT_300)/include/pango-1.0 -I$(GTK_ROOT_300)/include/gio-unix-2.0/ -I$(GTK_ROOT_300)/include/atk-1.0 -I$(GTK_ROOT_300)/include/cairo -I$(GTK_ROOT_300)/include/gdk-pixbuf-2.0 -I$(GTK_ROOT_300)/include/freetype2 -I$(GTK_ROOT_300)/include/glib-2.0 -I$(GTK_ROOT_300)/lib/glib-2.0/include $(SM_CFLAGS) - -else shellexecui_gtk3_la_LIBADD = $(LDADD) $(GTK3_DEPS_LIBS) shellexecui_gtk3_la_CFLAGS = -std=c99 $(GTK3_DEPS_CFLAGS) endif -endif endif diff --git a/plugins/sid/csid.cpp b/plugins/sid/csid.cpp index 5eb717b6..c4b2744b 100644 --- a/plugins/sid/csid.cpp +++ b/plugins/sid/csid.cpp @@ -48,8 +48,6 @@ typedef struct { float duration; // of the current song } sid_info_t; -static uint32_t csid_voicemask; - static inline void le_int16 (int16_t in, unsigned char *out) { char *pin = (char *)∈ @@ -78,6 +76,9 @@ static int sldb_loaded; static sldb_t *sldb; static int sldb_disable; +static int chip_voices = 0xff; +static int chip_voices_changed = 0; + static void sldb_load() { @@ -279,6 +280,20 @@ csid_open (uint32_t hints) { return _info; } +static void +csid_mute_voices (sid_info_t *info, int chip_voices) { + int maxsids = info->sidplay->info ().maxsids; + for (int k = 0; k < maxsids; k++) { + sidemu *emu = info->resid->getsidemu (k); + if (emu) { + for (int i = 0; i < 3; i++) { + bool mute = chip_voices & (1 << i) ? false : true; + emu->voice (i, mute ? 0x00 : 0xff, mute); + } + } + } +} + int csid_init (DB_fileinfo_t *_info, DB_playItem_t *it) { sid_info_t *info = (sid_info_t *)_info; @@ -328,16 +343,8 @@ csid_init (DB_fileinfo_t *_info, DB_playItem_t *it) { _info->fmt.channelmask = _info->fmt.channels == 1 ? DDB_SPEAKER_FRONT_LEFT : (DDB_SPEAKER_FRONT_LEFT | DDB_SPEAKER_FRONT_RIGHT); _info->readpos = 0; - int maxsids = info->sidplay->info ().maxsids; - for (int k = 0; k < maxsids; k++) { - sidemu *emu = info->resid->getsidemu (k); - if (emu) { - for (int i = 0; i < 3; i++) { - bool mute = csid_voicemask & (1 << i) ? true : false; - emu->voice (i, mute ? 0x00 : 0xff, mute); - } - } - } + chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff); + csid_mute_voices (info, chip_voices); return 0; } @@ -359,6 +366,12 @@ csid_read (DB_fileinfo_t *_info, char *bytes, int size) { return 0; } + if (chip_voices_changed) { + chip_voices = deadbeef->conf_get_int ("chip.voices", 0xff); + chip_voices_changed = 0; + csid_mute_voices (info, chip_voices); + } + int rd = info->sidplay->play (bytes, size); int samplesize = (_info->fmt.bps>>3) * _info->fmt.channels; @@ -549,32 +562,6 @@ csid_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { return after; } -#if 0 -int -csid_numvoices (DB_fileinfo_t *_info) { - return 3; -} - -void -csid_mutevoice (DB_fileinfo_t *_info, int voice, int mute) { - sid_info_t *info = (sid_info_t *)_info; - csid_voicemask &= ~ (1<<voice); - csid_voicemask |= ((mute ? 1 : 0) << voice); - if (info->resid) { - int maxsids = info->sidplay->info ().maxsids; - for (int k = 0; k < maxsids; k++) { - sidemu *emu = info->resid->getsidemu (k); - if (emu) { - for (int i = 0; i < 3; i++) { - bool mute = csid_voicemask & (1 << i) ? true : false; - emu->voice (i, mute ? 0x00 : 0xff, mute); - } - } - } - } -} -#endif - static int sid_configchanged (void) { int conf_hvsc_enable = deadbeef->conf_get_int ("hvsc_enable", 0); @@ -590,6 +577,10 @@ sid_configchanged (void) { sldb_loaded = 0; } + if (chip_voices != deadbeef->conf_get_int ("chip.voices", 0xff)) { + chip_voices_changed = 1; + } + return 0; } diff --git a/plugins/sndfile/Makefile.am b/plugins/sndfile/Makefile.am index d0116c64..10aca7ef 100644 --- a/plugins/sndfile/Makefile.am +++ b/plugins/sndfile/Makefile.am @@ -5,5 +5,5 @@ sndfile_la_SOURCES = sndfile.c sndfile_la_LDFLAGS = -module -avoid-version sndfile_la_LIBADD = $(LDADD) $(SNDFILE_LIBS) -AM_CFLAGS = $(CFLAGS) -std=c99 +AM_CFLAGS = $(CFLAGS) $(WAVPACK_CFLAGS) -std=c99 endif diff --git a/plugins/vfs_curl/vfs_curl.c b/plugins/vfs_curl/vfs_curl.c index c3ae2537..a9aaebc2 100644 --- a/plugins/vfs_curl/vfs_curl.c +++ b/plugins/vfs_curl/vfs_curl.c @@ -75,6 +75,9 @@ typedef struct { char http_err[CURL_ERROR_SIZE]; + float prev_playtime; + time_t started_timestamp; + // flags (bitfields to save some space) unsigned seektoend : 1; // indicates that next tell must return length unsigned gotheader : 1; // tells that all headers (including ICY) were processed (to start reading body) @@ -205,6 +208,14 @@ http_parse_shoutcast_meta (HTTP_FILE *fp, const char *meta, int size) { int songstarted = 0; char *tit = strstr (title, " - "); deadbeef->pl_lock (); + int emulate_trackchange = deadbeef->conf_get_int ("vfs_curl.emulate_trackchange", 0); + // create dummy track with previous meta + DB_playItem_t *from = NULL; + if (emulate_trackchange) { + from = deadbeef->pl_item_alloc (); + deadbeef->pl_items_copy_junk (fp->track, from, from); + } + if (tit) { *tit = 0; tit += 3; @@ -224,6 +235,7 @@ http_parse_shoutcast_meta (HTTP_FILE *fp, const char *meta, int size) { else { const char *orig_title = deadbeef->pl_find_meta (fp->track, "title"); if (!orig_title || strcasecmp (orig_title, title)) { + deadbeef->pl_delete_meta (fp->track, "artist"); vfs_curl_set_meta (fp->track, "title", title); songstarted = 1; } @@ -236,12 +248,34 @@ http_parse_shoutcast_meta (HTTP_FILE *fp, const char *meta, int size) { } deadbeef->sendmessage (DB_EV_PLAYLISTCHANGED, 0, 0, 0); if (songstarted) { + + float playpos = deadbeef->streamer_get_playpos (); + if (emulate_trackchange) + { + ddb_event_trackchange_t *ev = (ddb_event_trackchange_t *)deadbeef->event_alloc (DB_EV_SONGCHANGED); + + ev->from = from; + ev->to = fp->track; + ev->playtime = playpos - fp->prev_playtime; + ev->started_timestamp = fp->started_timestamp; + deadbeef->pl_item_ref (ev->from); + deadbeef->pl_item_ref (ev->to); + deadbeef->event_send ((ddb_event_t *)ev, 0, 0); + } + ddb_event_track_t *ev = (ddb_event_track_t *)deadbeef->event_alloc (DB_EV_SONGSTARTED); ev->track = fp->track; + fp->started_timestamp = time(NULL); + ev->started_timestamp = fp->started_timestamp; if (ev->track) { deadbeef->pl_item_ref (ev->track); } deadbeef->event_send ((ddb_event_t *)ev, 0, 0); + fp->prev_playtime = playpos; + } + if (from) { + deadbeef->pl_item_unref (from); + from = NULL; } } return 0; @@ -1089,7 +1123,10 @@ http_is_streaming (void) { return 1; } -// standard stdio vfs +static const char settings_dlg[] = + "property \"Emulate track change events (for scrobbling)\" checkbox vfs_curl.emulate_trackchange 0;\n" +; + static DB_vfs_t plugin = { .plugin.api_vmajor = 1, .plugin.api_vminor = 0, @@ -1119,6 +1156,7 @@ static DB_vfs_t plugin = { .plugin.website = "http://deadbeef.sf.net", .plugin.start = vfs_curl_start, .plugin.stop = vfs_curl_stop, + .plugin.configdialog = settings_dlg, .open = http_open, .set_track = http_set_track, .close = http_close, diff --git a/plugins/vfs_zip/Makefile.am b/plugins/vfs_zip/Makefile.am index a2394e11..579baf58 100644 --- a/plugins/vfs_zip/Makefile.am +++ b/plugins/vfs_zip/Makefile.am @@ -5,5 +5,5 @@ vfs_zip_la_SOURCES = vfs_zip.c vfs_zip_la_LDFLAGS = -module -avoid-version vfs_zip_la_LIBADD = $(LDADD) $(ZLIB_LIBS) $(ZIP_LIBS) -AM_CFLAGS = $(CFLAGS) -std=c99 +AM_CFLAGS = $(CFLAGS) $(ZLIB_CFLAGS) $(ZIP_CFLAGS) -std=c99 endif diff --git a/plugins/vfs_zip/vfs_zip.c b/plugins/vfs_zip/vfs_zip.c index 2f84dc31..087d8366 100644 --- a/plugins/vfs_zip/vfs_zip.c +++ b/plugins/vfs_zip/vfs_zip.c @@ -31,6 +31,8 @@ static DB_functions_t *deadbeef; static DB_vfs_t plugin; +#define ZIP_BUFFER_SIZE 8192 + typedef struct { DB_FILE file; struct zip* z; @@ -38,6 +40,10 @@ typedef struct { int64_t offset; int index; int64_t size; + + uint8_t buffer[ZIP_BUFFER_SIZE]; + int buffer_remaining; + int buffer_pos; } zip_file_t; static const char *scheme_names[] = { "zip://", NULL }; @@ -99,6 +105,7 @@ vfs_zip_open (const char *fname) { f->zf = zf; f->index = st.index; f->size = st.size; + trace ("vfs_zip: end open %s\n", fname); return (DB_FILE*)f; } @@ -118,14 +125,34 @@ vfs_zip_close (DB_FILE *f) { size_t vfs_zip_read (void *ptr, size_t size, size_t nmemb, DB_FILE *f) { zip_file_t *zf = (zip_file_t *)f; - ssize_t rb = zip_fread (zf->zf, ptr, size * nmemb); - zf->offset += rb; - return rb / size; +// printf ("read: %d\n", size*nmemb); + + int sz = size * nmemb; + while (sz) { + if (zf->buffer_remaining == 0) { + zf->buffer_pos = 0; + int rb = zip_fread (zf->zf, zf->buffer, ZIP_BUFFER_SIZE); + if (rb <= 0) { + break; + } + zf->buffer_remaining = rb; + } + int from_buf = min (sz, zf->buffer_remaining); + memcpy (ptr, zf->buffer+zf->buffer_pos, from_buf); + zf->buffer_remaining -= from_buf; + zf->buffer_pos += from_buf; + zf->offset += from_buf; + sz -= from_buf; + ptr += from_buf; + } + + return (size * nmemb - sz) / size; } int vfs_zip_seek (DB_FILE *f, int64_t offset, int whence) { zip_file_t *zf = (zip_file_t *)f; +// printf ("seek: %lld (%d)\n", offset, whence); if (whence == SEEK_CUR) { offset = zf->offset + offset; @@ -134,6 +161,34 @@ vfs_zip_seek (DB_FILE *f, int64_t offset, int whence) { offset = zf->size + offset; } + int64_t offs = offset - zf->offset; + if ((offs < 0 && -offs <= zf->buffer_pos) || (offs >= 0 && offs < zf->buffer_remaining)) { + if (offs != 0) { + //printf ("cache success\n"); + + //printf ("[before] absoffs: %lld, offs: %lld, rem: %d, pos: %d\n", offset, offs, zf->buffer_remaining, zf->buffer_pos); + + // test cases: + // fail: offs = -3, pos = 0, rem = 100 + // fail: offs = 10, pos = 95, rem = 5 + // succ: offs = -3, pos = 3, rem = 97 ----> pos = 0, rem=100 + // succ: offs = 10, pos = 0, rem = 100 ---> pos = 10, rem = 90 + + zf->buffer_pos += offs; + zf->buffer_remaining -= offs; + //printf ("[after] offs: %lld, rem: %d, pos: %d\n", offs, zf->buffer_remaining, zf->buffer_pos); + zf->offset = offset; + return 0; + } + else { +// printf ("cache double success\n"); + return 0; + } + } +// else { +// printf ("cache miss: abs_offs: %lld, offs: %lld, rem: %d, pos: %d\n", offset, offs, zf->buffer_remaining, zf->buffer_pos); +// } + if (offset < zf->offset) { // reopen zip_fclose (zf->zf); @@ -143,6 +198,8 @@ vfs_zip_seek (DB_FILE *f, int64_t offset, int whence) { } zf->offset = 0; } + zf->buffer_pos = 0; + zf->buffer_remaining = 0; char buf[4096]; int64_t n = offset - zf->offset; while (n > 0) { @@ -174,6 +231,7 @@ vfs_zip_rewind (DB_FILE *f) { zf->zf = zip_fopen_index (zf->z, zf->index, 0); assert (zf->zf); // FIXME: better error handling? zf->offset = 0; + zf->buffer_remaining = 0; } int64_t diff --git a/plugins/vorbis/COPYING b/plugins/vorbis/COPYING new file mode 100644 index 00000000..f076a6a0 --- /dev/null +++ b/plugins/vorbis/COPYING @@ -0,0 +1,55 @@ +OggVorbis plugin for DeaDBeeF +Copyright (C) 2009-2014 Alexey Yakovenko et al. + +vcedit.c - ogg tagging library +Copyright (C) 2014 Ian Nartowicz <deadbeef@nartowicz.co.uk> + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. + + + +Uses libogg,libvorbis Copyright (c) 2002, Xiph.org Foundation + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +- Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +- Neither the name of the DeaDBeeF Player nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/plugins/vorbis/i18n.h b/plugins/vorbis/i18n.h deleted file mode 100644 index 86248307..00000000 --- a/plugins/vorbis/i18n.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef VORBIS_TOOLS_I18N_H -#define VORBIS_TOOLS_I18N_H - -#ifdef ENABLE_NLS -#include <libintl.h> -#define _(X) gettext(X) -#else -#define _(X) (X) -#define textdomain(X) -#define bindtextdomain(X, Y) -#endif -#ifdef gettext_noop -#define N_(X) gettext_noop(X) -#else -#define N_(X) (X) -#endif - -#endif diff --git a/plugins/vorbis/vcedit.c b/plugins/vorbis/vcedit.c index 2300b3da..5709f89c 100644 --- a/plugins/vorbis/vcedit.c +++ b/plugins/vorbis/vcedit.c @@ -1,880 +1,285 @@ -/* This program is licensed under the GNU Library General Public License, version 2, - * a copy of which is included with this program (LICENCE.LGPL). - * - * (c) 2000-2001 Michael Smith <msmith@xiph.org> - * - * - * Comment editing backend, suitable for use by nice frontend interfaces. - * - * last modified: $Id: vcedit.c 16826 2010-01-27 04:16:24Z xiphmont $ - */ - -/* Handle muxed streams and the Vorbis renormalization without having - * to understand remuxing: - * Linked list of buffers (buffer_chain). Start a link and whenever - * you encounter an unknown page from the current stream (ie we found - * its bos in the bos section) push it onto the current buffer. Whenever - * you encounter the stream being renormalized create a new link in the - * chain. - * On writing, write the contents of the first link before every Vorbis - * page written, and move to the next link. Assuming the Vorbis pages - * in match vorbis pages out, the order of pages from different logical - * streams will be unchanged. - * Special case: header. After writing the vorbis headers, and before - * starting renormalization, flush accumulated links (takes care of - * situations where number of secondary vorbis header pages changes due - * to remuxing. Similarly flush links at the end of renormalization - * and before the start of the next chain is written. - * - */ +/* + This file is part of Deadbeef Player source code + http://deadbeef.sourceforge.net + + Ogg Vorbis plugin Ogg edit functions + + Copyright (C) 2014 Ian Nartowicz <deadbeef@nartowicz.co.uk> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +*/ #ifdef HAVE_CONFIG_H #include <config.h> #endif - #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <stdbool.h> #include <ogg/ogg.h> -#include <vorbis/codec.h> - +#include <deadbeef.h> #include "vcedit.h" -#include "vceditaux.h" -#include "i18n.h" - -#define CHUNKSIZE 4096 -#define BUFFERCHUNK CHUNKSIZE - -/* Helper function, shouldn't need to call directly */ -static int page_buffer_push(vcedit_buffer_chain *bufferlink, ogg_page *og) { - int result=0; - char *tmp; - vcedit_page_buffer *buffer; - - buffer = &bufferlink->buffer; - tmp = realloc(buffer->data, - buffer->data_len + og->header_len + og->body_len); - if(tmp) { - buffer->data = tmp; - memcpy(buffer->data + buffer->data_len, og->header, - og->header_len); - buffer->data_len += og->header_len; - memcpy(buffer->data + buffer->data_len, og->body, - og->body_len); - result = 1; - buffer->data_len += og->body_len; - } else { - result = -1; - } - - return result; -} - -/* Write and free the first link using callbacks */ -static int buffer_chain_writelink(vcedit_state *state, void *out) { - int result = 0; - vcedit_buffer_chain *tmpchain; - vcedit_page_buffer *tmpbuffer; - - tmpchain = state->sidebuf; - tmpbuffer = &tmpchain->buffer; - if(tmpbuffer->data_len) - { - if(state->write(tmpbuffer->data,1,tmpbuffer->data_len, out) != - (size_t) tmpbuffer->data_len) - result = -1; - else - result = 1; - } - - free(tmpbuffer->data); - state->sidebuf = tmpchain->next; - free(tmpchain); - return result; -} - - -static int buffer_chain_newlink(vcedit_state *state) { - int result = 1; - vcedit_buffer_chain *bufferlink; - - if(!state->sidebuf) { - state->sidebuf = malloc (sizeof *state->sidebuf); - if(state->sidebuf) { - bufferlink = state->sidebuf; - } else { - result = -1; - } - } else { - bufferlink=state->sidebuf; - while(bufferlink->next) { - bufferlink = bufferlink->next; - } - bufferlink->next = malloc (sizeof *bufferlink->next); - if(bufferlink->next) { - bufferlink = bufferlink->next; - } else { - result = -1; - } - } - - if(result > 0 ) { - bufferlink->next = 0; - bufferlink->buffer.data = 0; - bufferlink->buffer.data_len = 0; - } - else - state->lasterror = - _("Couldn't get enough memory for input buffering."); - - return result; -} - - -/* Push page onto the end of the buffer chain */ -static int buffer_chain_push(vcedit_state *state, ogg_page *og) { - /* If there is no sidebuffer yet we need to create one, otherwise - * traverse to the last buffer and push the new page onto it. */ - int result=1; - vcedit_buffer_chain *bufferlink; - if(!state->sidebuf) { - result = buffer_chain_newlink(state); - } - - if(result > 0) { - bufferlink = state->sidebuf; - while(bufferlink->next) { - bufferlink = bufferlink->next; - } - result = page_buffer_push(bufferlink, og); - } - - if(result < 0) - state->lasterror = - _("Couldn't get enough memory for input buffering."); - - return result; -} - - - -static int vcedit_supported_stream(vcedit_state *state, ogg_page *og) { - ogg_stream_state os; - vorbis_info vi; - vorbis_comment vc; - ogg_packet header; - int result = 0; - - ogg_stream_init(&os, ogg_page_serialno(og)); - vorbis_info_init(&vi); - vorbis_comment_init(&vc); - - if( !ogg_page_bos(og) ) - result = -1; - - if(result >= 0 && ogg_stream_pagein(&os, og) < 0) - { - state->lasterror = - _("Error reading first page of Ogg bitstream."); - result = -1; - } - - if(result >= 0 && ogg_stream_packetout(&os, &header) != 1) - { - state->lasterror = _("Error reading initial header packet."); - result = -1; - } - - if(result >= 0 && vorbis_synthesis_headerin(&vi, &vc, &header) >= 0) - { - result = 1; - } else { - /* Not vorbis, may eventually become a chain of checks (Speex, - * Theora), but for the moment return 0, bos scan will push - * the current page onto the buffer. - */ - } - - ogg_stream_clear(&os); - vorbis_info_clear(&vi); - vorbis_comment_clear(&vc); - return result; -} - - -static int vcedit_contains_serial (vcedit_state *state, int serialno) { - int result = 0; - size_t count; - for( count=0; count < state->serials.streams_len; count++ ) { - if ( *(state->serials.streams + count ) == serialno ) - result = 1; - } - - return result; -} +#define VORBISNAME "Vorbis" +#define TAGMAGIC "\3vorbis" +#define CODEMAGIC "\5vorbis" -static int vcedit_add_serial (vcedit_state *state, long serial) { - int result = 0; - long *tmp; - - - if( vcedit_contains_serial(state, serial) ) - { - result = 1; - } else { - tmp = realloc(state->serials.streams, - (state->serials.streams_len + 1) * sizeof *tmp); - if(tmp) { - state->serials.streams = tmp; - *(state->serials.streams + - state->serials.streams_len) = serial; - state->serials.streams_len += 1; - result = 1; - } else { - state->lasterror = - _("Couldn't get enough memory to register new stream serial number."); - result = -1; - } - } - return result; -} - - -/* For the benefit of the secondary header read only. Quietly creates - * newlinks and pushes pages onto the buffer in the right way */ -static int vcedit_target_pageout (vcedit_state *state, ogg_page *og) { - int result = 0; - int pageout_result; - pageout_result = ogg_sync_pageout(state->oy, og); - if(pageout_result > 0) - { - if(state->serial == ogg_page_serialno(og)) - result = buffer_chain_newlink(state); - else - result = buffer_chain_push(state, og); - } else if (pageout_result < 0) { - /* Vorbis comment traditionally ignores the not-synced - * error from pageout, so give it a different code. */ - result = -2; - } - return result; -} - +#define CHUNKSIZE 4096 +#define MAXOGGPAGE 65536 -/* (I'm paranoid about memset(x,0,len) not giving null pointers */ -vcedit_state *vcedit_new_state(void) { - vcedit_state *state = malloc(sizeof(vcedit_state)); - if(state) { - memset(state, 0, sizeof(vcedit_state)); - state->sidebuf = 0; - state->serials.streams = 0; - state->serials.streams_len = 0; - } - return state; +static const char *ogg_codec(ogg_page *og) +{ + typedef struct { + const char *magic; + const size_t length; + const char *codec; + } codec_t; + const codec_t codecs[] = { + {.codec = "Opus", .length = 19, .magic = "OpusHead"}, + {.codec = "Vorbis", .length = 30, .magic = "\1vorbis"}, + {.codec = "Flac", .length = 47, .magic = "\177FLAC"}, + {.codec = "Speex", .length = 80, .magic = "Speex "}, + {.codec = "Celt", .length = 80, .magic = "CELT"}, // obsolete + {.codec = "MIDI", .length = 13, .magic = "OggMIDI\0"}, + {.codec = "PCM", .length = 28, .magic = "PCM "}, + {.codec = "Theora", .length = 42, .magic = "\200theora"}, + {.codec = "Daala", .length = 38, .magic = "\200daala"}, + {.codec = "Dirac", .length = 5, .magic = "BBCD\0"}, + {.codec = "Skeleton", .length = 80, .magic = "fishead\0"}, + {.codec = "Kate", .length = 64, .magic = "\200kate\0\0\0"}, + {.codec = "CMML", .length = 29, .magic = "CMML\0\0\0\0"}, + {.codec = "YUV4MPEG", .length = 8, .magic = "YUV4Mpeg"}, + {.codec = "UVS", .length = 48, .magic = "UVS "}, + {.codec = "YUV", .length = 32, .magic = "\0YUV"}, + {.codec = "RGB", .length = 24, .magic = "\0RGB"}, + {.codec = "JNG", .length = 48, .magic = "\213JNG\r\n\032\n"}, + {.codec = "MNG", .length = 48, .magic = "\212MNG\r\n\032\n"}, + {.codec = "PNG", .length = 48, .magic = "\211PNG\r\n\032\n"}, + {.codec = "Spots", .length = 52, .magic = "SPOTS\0\0\0"}, + {.codec = NULL} + }; + + for (const codec_t *match = codecs; match->codec; match++) + if ((size_t)og->body_len >= match->length && !memcmp(og->body, match->magic, strlen(match->codec))) + return match->codec; + + return "unknown"; } -char *vcedit_error(vcedit_state *state) { - return state->lasterror; +static void _oggpack_chars(oggpack_buffer *opb, const char *s, size_t length) +{ + while (length--) + oggpack_write(opb, *s++, 8); } -vorbis_comment *vcedit_comments(vcedit_state *state) { - return state->vc; +static void _oggpack_string(oggpack_buffer *opb, const char *s) +{ + oggpack_write(opb, strlen(s), 32); + _oggpack_chars(opb, s, strlen(s)); } -static void vcedit_clear_internals(vcedit_state *state) { - char *tmp; - if(state->vc) { - vorbis_comment_clear(state->vc); - free(state->vc); - } - if(state->os) { - ogg_stream_clear(state->os); - free(state->os); - } - if(state->oy) { - ogg_sync_clear(state->oy); - free(state->oy); - } - if(state->serials.streams_len) { - free(state->serials.streams); - state->serials.streams_len = 0; - state->serials.streams = 0; - } - while(state->sidebuf) { - vcedit_buffer_chain *tmpbuffer; - tmpbuffer = state->sidebuf; - state->sidebuf = tmpbuffer->next; - free(tmpbuffer->buffer.data); - free(tmpbuffer); - } - if(state->vendor) - free(state->vendor); - if(state->mainbuf) - free(state->mainbuf); - if(state->bookbuf) - free(state->bookbuf); - if(state->vi) { - vorbis_info_clear(state->vi); - free(state->vi); +static int get_page(DB_FILE *in, ogg_sync_state *oy, ogg_page *og, char **lasterror) +{ + uint16_t chunks_left = MAXOGGPAGE / CHUNKSIZE; + while (ogg_sync_pageout(oy, og) != 1) { + char *buffer = ogg_sync_buffer(oy, CHUNKSIZE); + if (!in || !buffer || !chunks_left--) { + *lasterror = "can't find Ogg bitstream."; + return -1; + } + + const size_t bytes = in->vfs->read(buffer, 1, CHUNKSIZE, in); + if (!bytes) { + *lasterror = "unexpected EOF."; + return 0; + } + + ogg_sync_wrote(oy, bytes); } - tmp = state->lasterror; - memset(state, 0, sizeof(*state)); - state->lasterror = tmp; + return ogg_page_serialno(og); } -void vcedit_clear(vcedit_state *state) +static bool write_page(FILE *out, ogg_page *og) { - if(state) - { - vcedit_clear_internals(state); - free(state); - } + return fwrite(og->header, 1, og->header_len, out) != (size_t)og->header_len || + fwrite(og->body, 1, og->body_len, out) != (size_t)og->body_len; } -/* Next two functions pulled straight from libvorbis, apart from one change - * - we don't want to overwrite the vendor string. - */ -static void _v_writestring(oggpack_buffer *o,char *s, int len) +static int write_page_and_get_next(DB_FILE *in, FILE *out, ogg_sync_state *oy, ogg_page *og, char **lasterror) { - while(len--) - { - oggpack_write(o,*s++,8); - } -} + if (write_page(out, og)) + return -1; -static int _commentheader_out(vorbis_comment *vc, char *vendor, ogg_packet *op) -{ - oggpack_buffer opb; - - oggpack_writeinit(&opb); - - /* preamble */ - oggpack_write(&opb,0x03,8); - _v_writestring(&opb,"vorbis", 6); - - /* vendor */ - oggpack_write(&opb,strlen(vendor),32); - _v_writestring(&opb,vendor, strlen(vendor)); - - /* comments */ - oggpack_write(&opb,vc->comments,32); - if(vc->comments){ - int i; - for(i=0;i<vc->comments;i++){ - if(vc->user_comments[i]){ - oggpack_write(&opb,vc->comment_lengths[i],32); - _v_writestring(&opb,vc->user_comments[i], - vc->comment_lengths[i]); - }else{ - oggpack_write(&opb,0,32); - } - } - } - oggpack_write(&opb,1,1); - - op->packet = malloc(oggpack_bytes(&opb)); - memcpy(op->packet, opb.buffer, oggpack_bytes(&opb)); - - op->bytes=oggpack_bytes(&opb); - op->b_o_s=0; - op->e_o_s=0; - op->granulepos=0; - - oggpack_writeclear(&opb); - return 0; + return get_page(in, oy, og, lasterror); } -static int _blocksize(vcedit_state *s, ogg_packet *p) +static bool write_vorbis_tags(FILE *out, const int serial, const char *vendor, const size_t num_tags, char **tags, ogg_packet *codebooks) { - int this = vorbis_packet_blocksize(s->vi, p); - int ret = (this + s->prevW)/4; - - if(!s->prevW) - { - s->prevW = this; - return 0; - } - - s->prevW = this; - return ret; + oggpack_buffer opb; + oggpack_writeinit(&opb); + _oggpack_chars(&opb, TAGMAGIC, strlen(TAGMAGIC)); + _oggpack_string(&opb, vendor); + oggpack_write(&opb, num_tags, 32); + for (size_t i = 0; i < num_tags; i++) + _oggpack_string(&opb, tags[i]); + oggpack_write(&opb, 1, 1); + oggpack_writealign(&opb); + + + ogg_stream_state os; + ogg_stream_init(&os, serial); + os.b_o_s = 1; + os.pageno = 1; + ogg_packet op; + memset(&op, '\0', sizeof(op)); + op.packet = oggpack_get_buffer(&opb); + op.bytes = oggpack_bytes(&opb); + ogg_stream_packetin(&os, &op); + oggpack_writeclear(&opb); + ogg_stream_packetin(&os, codebooks); + + ogg_page og; + while (ogg_stream_flush(&os, &og)) + if (write_page(out, &og)) + return true; + + return ogg_stream_check(&os) || ogg_stream_clear(&os); } -static int _fetch_next_packet(vcedit_state *s, ogg_packet *p, ogg_page *page) +static int extract_codebooks(DB_FILE *in, ogg_sync_state *oy, ogg_page *og, int serial, ogg_packet *codebooks, char **lasterror) { - int result; - char *buffer; - int bytes; - int serialno; - - result = ogg_stream_packetout(s->os, p); - - if(result > 0) - return 1; - else { - while(1) { - if(s->eosin) - return 0; - - while(ogg_sync_pageout(s->oy, page) <= 0) - { - buffer = ogg_sync_buffer(s->oy, CHUNKSIZE); - bytes = s->read(buffer,1, CHUNKSIZE, s->in); - ogg_sync_wrote(s->oy, bytes); - if(bytes == 0) - return 0; - } - - serialno = ogg_page_serialno(page); - if(ogg_page_serialno(page) != s->serial) - { - if(vcedit_contains_serial(s, serialno)) { - result = buffer_chain_push(s, page); - if(result < 0) - return result; - } - else - { - s->eosin = 1; - s->extrapage = 1; - return 0; - } - } - else - { - ogg_stream_pagein(s->os, page); - result = buffer_chain_newlink(s); - if (result < 0) - return result; - - if(ogg_page_eos(page)) - s->eosin = 1; - } - result = ogg_stream_packetout(s->os, p); - if(result > 0) - return 1; - } - /* Here == trouble */ - return 0; - } -} + memset(codebooks, '\0', sizeof(codebooks)); + ogg_stream_state os; + if (ogg_stream_init(&os, serial)) { + *lasterror = "cannot init Ogg stream."; + return -1; + } + os.pageno = 1; + os.b_o_s = 1; + + ogg_packet op; + ogg_stream_pagein(&os, og); + do { + while (ogg_stream_packetpeek(&os, NULL) == 0) { + if ((serial = get_page(in, oy, og, lasterror)) <= 0) + goto cleanup; + fprintf(stderr, "Serial: %d, %ld bytes (%s)\n", serial, og->header_len + og->body_len, og->body); + if (ogg_stream_pagein(&os, og)) { + *lasterror = "failed to stream page for codebooks packet."; + goto cleanup; + } + } + } while (ogg_stream_packetout(&os, &op) != 1 || op.bytes < strlen(CODEMAGIC) || memcmp(op.packet, CODEMAGIC, strlen(CODEMAGIC))); + + if (!(codebooks->packet = malloc(op.bytes))) { + *lasterror = "cannot allocate codebooks packet."; + goto cleanup; + } + codebooks->bytes = op.bytes; + memcpy(codebooks->packet, op.packet, op.bytes); -int vcedit_open(vcedit_state *state, FILE *in) -{ - return vcedit_open_callbacks(state, (void *)in, - (vcedit_read_func)fread, (vcedit_write_func)fwrite); +cleanup: + ogg_stream_clear(&os); + + return codebooks->packet ? serial : -1; } -int vcedit_open_callbacks(vcedit_state *state, void *in, - vcedit_read_func read_func, vcedit_write_func write_func) +off_t vcedit_write_metadata(DB_FILE *in, const char *fname, int link, const char *vendor, const int num_tags, char **tags, char **lasterror) { - - char *buffer; - int bytes,i; - int chunks = 0; - int read_bos, test_supported, page_pending; - int have_vorbis; - ogg_packet *header; - ogg_packet header_main; - ogg_packet header_comments; - ogg_packet header_codebooks; - ogg_page og; - - state->in = in; - state->read = read_func; - state->write = write_func; - - state->oy = malloc(sizeof(ogg_sync_state)); - ogg_sync_init(state->oy); - - while(1) - { - buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); - bytes = state->read(buffer, 1, CHUNKSIZE, state->in); - - ogg_sync_wrote(state->oy, bytes); - - if(ogg_sync_pageout(state->oy, &og) == 1) - break; - - if(chunks++ >= 10) /* Bail if we don't find data in the first 40 kB */ - { - if(bytes<CHUNKSIZE) - state->lasterror = _("Input truncated or empty."); - else - state->lasterror = _("Input is not an Ogg bitstream."); - goto err; - } + off_t file_size = 0; + ogg_page og; + ogg_sync_state oy; + ogg_sync_init(&oy); + + char outname[PATH_MAX]; + snprintf(outname, PATH_MAX, "%s.temp", fname); + FILE *out = fopen(outname, "w+b"); + if (!out) { + *lasterror = "unable to open temporary file for writing."; + goto cleanup; + } + if (!in) { + *lasterror = "file not opened for reading."; + goto cleanup; } - /* BOS loop, starting with a loaded ogg page. */ - if(buffer_chain_newlink(state) < 0) - goto err; - - for( read_bos = 1, have_vorbis = 0 ; read_bos; ) - { - test_supported = vcedit_supported_stream(state, &og); - if(test_supported < 0) - { - goto err; - } - else if (test_supported == 0 || have_vorbis ) - { - if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) - goto err; - if( buffer_chain_push(state, &og) < 0) - goto err; - } - else if (test_supported > 0) - { - if(buffer_chain_newlink(state) < 0) - goto err; - state->serial = ogg_page_serialno(&og); - if(vcedit_add_serial ( state, ogg_page_serialno(&og)) < 0) - goto err; - - state->os = malloc(sizeof(ogg_stream_state)); - ogg_stream_init(state->os, state->serial); - - state->vi = malloc(sizeof(vorbis_info)); - vorbis_info_init(state->vi); - - state->vc = malloc(sizeof(vorbis_comment)); - vorbis_comment_init(state->vc); - - if(ogg_stream_pagein(state->os, &og) < 0) - { - state->lasterror = - _("Error reading first page of Ogg bitstream."); - goto err; - } - - if(ogg_stream_packetout(state->os, &header_main) != 1) - { - state->lasterror = - _("Error reading initial header packet."); - goto err; - } - - if(vorbis_synthesis_headerin(state->vi, state->vc, - &header_main) < 0) - { - state->lasterror = - _("Ogg bitstream does not contain Vorbis data."); - goto err; - } - have_vorbis = 1; - } - while(1) - { - buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); - bytes = state->read(buffer, 1, CHUNKSIZE, state->in); - - if(bytes == 0) - { - state->lasterror = - _("EOF before recognised stream."); - goto err; - } - - ogg_sync_wrote(state->oy, bytes); - - if(ogg_sync_pageout(state->oy, &og) == 1) - break; - } - if(!ogg_page_bos(&og)) { - read_bos = 0; - page_pending = 1; - } + /* Copy through pages until we reach the right info header */ + int codec_serial = 0; + int serial = get_page(in, &oy, &og, lasterror); + while (serial > 0 && !codec_serial) { + while (serial > 0 && !ogg_page_bos(&og)) + serial = write_page_and_get_next(in, out, &oy, &og, lasterror); + + while (serial > 0 && ogg_page_bos(&og)) { + if (link < 1 && !strcmp(ogg_codec(&og), VORBISNAME)) { + codec_serial = serial; + } + serial = write_page_and_get_next(in, out, &oy, &og, lasterror); + } + link--; } - if(!state->os) { - state->lasterror = _("Ogg bitstream does not contain a supported data-type."); - goto err; - } - - state->mainlen = header_main.bytes; - state->mainbuf = malloc(state->mainlen); - memcpy(state->mainbuf, header_main.packet, header_main.bytes); - - if(ogg_page_serialno(&og) == state->serial) - { - if(buffer_chain_newlink(state) < 0) - goto err; - } - - else - { - if(buffer_chain_push(state, &og) < 0) - goto err; - page_pending = 0; - } - - i = 0; - header = &header_comments; - while(i<2) { - while(i<2) { - int result; - if(!page_pending) - result = vcedit_target_pageout(state, &og); - else - { - result = 1; - page_pending = 0; - } - if(result == 0 || result == -2) break; /* Too little data so far */ - else if(result == -1) goto err; - else if(result == 1) - { - ogg_stream_pagein(state->os, &og); - while(i<2) - { - result = ogg_stream_packetout(state->os, header); - if(result == 0) break; - if(result == -1) - { - state->lasterror = _("Corrupt secondary header."); - goto err; - } - vorbis_synthesis_headerin(state->vi, state->vc, header); - if(i==1) - { - state->booklen = header->bytes; - state->bookbuf = malloc(state->booklen); - memcpy(state->bookbuf, header->packet, - header->bytes); - } - i++; - header = &header_codebooks; - } - } - } - - buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); - bytes = state->read(buffer, 1, CHUNKSIZE, state->in); - if(bytes == 0 && i < 2) - { - state->lasterror = _("EOF before end of Vorbis headers."); - goto err; - } - ogg_sync_wrote(state->oy, bytes); - } - - /* Copy the vendor tag */ - state->vendor = malloc(strlen(state->vc->vendor) +1); - strcpy(state->vendor, state->vc->vendor); - - /* Headers are done! */ - return 0; - -err: - vcedit_clear_internals(state); - return -1; -} + /* Copy additional pages up to our comment header */ + while (serial > 0 && serial != codec_serial) + serial = write_page_and_get_next(in, out, &oy, &og, lasterror); + if (serial <= 0) + goto cleanup; + + /* Add the codebook packet to our comment header and save it */ + ogg_packet codebooks; + if ((serial = extract_codebooks(in, &oy, &og, codec_serial, &codebooks, lasterror)) <= 0) + goto cleanup; + const bool write_tags = write_vorbis_tags(out, codec_serial, vendor, num_tags, tags, &codebooks); + ogg_packet_clear(&codebooks); + if (write_tags) { + *lasterror = "internal error writing Vorbis comment header packet."; + goto cleanup; + } -int vcedit_write(vcedit_state *state, void *out) -{ - ogg_stream_state streamout; - ogg_packet header_main; - ogg_packet header_comments; - ogg_packet header_codebooks; - - ogg_page ogout, ogin; - ogg_packet op; - ogg_int64_t granpos = 0; - int result; - char *buffer; - int bytes; - int needflush=0, needout=0; - - state->eosin = 0; - state->extrapage = 0; - - header_main.bytes = state->mainlen; - header_main.packet = state->mainbuf; - header_main.b_o_s = 1; - header_main.e_o_s = 0; - header_main.granulepos = 0; - - header_codebooks.bytes = state->booklen; - header_codebooks.packet = state->bookbuf; - header_codebooks.b_o_s = 0; - header_codebooks.e_o_s = 0; - header_codebooks.granulepos = 0; - - ogg_stream_init(&streamout, state->serial); - - _commentheader_out(state->vc, state->vendor, &header_comments); - - ogg_stream_packetin(&streamout, &header_main); - ogg_stream_packetin(&streamout, &header_comments); - ogg_stream_packetin(&streamout, &header_codebooks); - - while((result = ogg_stream_flush(&streamout, &ogout))) - { - if(state->sidebuf && buffer_chain_writelink(state, out) < 0) - goto cleanup; - if(state->write(ogout.header,1,ogout.header_len, out) != - (size_t) ogout.header_len) - goto cleanup; - if(state->write(ogout.body,1,ogout.body_len, out) != - (size_t) ogout.body_len) - goto cleanup; - } - - while(state->sidebuf) { - if(buffer_chain_writelink(state, out) < 0) - goto cleanup; - } - if(buffer_chain_newlink(state) < 0) - goto cleanup; - - while(_fetch_next_packet(state, &op, &ogin)) - { - int size; - size = _blocksize(state, &op); - granpos += size; - - if(needflush) - { - if(ogg_stream_flush(&streamout, &ogout)) - { - if(state->sidebuf && - buffer_chain_writelink(state, out) < 0) - goto cleanup; - if(state->write(ogout.header,1,ogout.header_len, - out) != (size_t) ogout.header_len) - goto cleanup; - if(state->write(ogout.body,1,ogout.body_len, - out) != (size_t) ogout.body_len) - goto cleanup; - } - } - else if(needout) - { - if(ogg_stream_pageout(&streamout, &ogout)) - { - if(state->sidebuf && - buffer_chain_writelink(state, out) < 0) - goto cleanup; - if(state->write(ogout.header,1,ogout.header_len, - out) != (size_t) ogout.header_len) - goto cleanup; - if(state->write(ogout.body,1,ogout.body_len, - out) != (size_t) ogout.body_len) - goto cleanup; - } - } - - needflush=needout=0; - - if(op.granulepos == -1) - { - op.granulepos = granpos; - ogg_stream_packetin(&streamout, &op); - } - else /* granulepos is set, validly. Use it, and force a flush to - account for shortened blocks (vcut) when appropriate */ - { - if(granpos > op.granulepos) - { - granpos = op.granulepos; - ogg_stream_packetin(&streamout, &op); - needflush=1; - } - else - { - ogg_stream_packetin(&streamout, &op); - needout=1; - } - } - } - - streamout.e_o_s = 1; - while(ogg_stream_flush(&streamout, &ogout)) - { - if(state->sidebuf && buffer_chain_writelink(state, out) < 0) - goto cleanup; - if(state->write(ogout.header,1,ogout.header_len, - out) != (size_t) ogout.header_len) - goto cleanup; - if(state->write(ogout.body,1,ogout.body_len, - out) != (size_t) ogout.body_len) - goto cleanup; - } - - if (state->extrapage) - { - /* This is the first page of a new chain, get rid of the - * sidebuffer */ - while(state->sidebuf) - if(buffer_chain_writelink(state, out) < 0) - goto cleanup; - if(state->write(ogin.header,1,ogin.header_len, - out) != (size_t) ogin.header_len) - goto cleanup; - if (state->write(ogin.body,1,ogin.body_len, out) != - (size_t) ogin.body_len) - goto cleanup; - } - - state->eosin=0; /* clear it, because not all paths to here do */ - while(!state->eosin) /* We reached eos, not eof */ - { - /* We copy the rest of the stream (other logical streams) - * through, a page at a time. */ - while(1) - { - result = ogg_sync_pageout(state->oy, &ogout); - if(result==0) - break; - if(result<0) - state->lasterror = _("Corrupt or missing data, continuing..."); - else - { - /* Don't bother going through the rest, we can just - * write the page out now */ - if(state->write(ogout.header,1,ogout.header_len, - out) != (size_t) ogout.header_len) { - goto cleanup; - } - if(state->write(ogout.body,1,ogout.body_len, out) != - (size_t) ogout.body_len) { - goto cleanup; - } - } - } - buffer = ogg_sync_buffer(state->oy, CHUNKSIZE); - bytes = state->read(buffer,1, CHUNKSIZE, state->in); - ogg_sync_wrote(state->oy, bytes); - if(bytes == 0) - { - state->eosin = 1; - break; - } - } + /* Blindly copy through the remaining pages */ + serial = get_page(in, &oy, &og, lasterror); + while (serial > 0) + serial = write_page_and_get_next(in, out, &oy, &og, lasterror); + if (serial < 0) + goto cleanup; + fseeko(out, 0, SEEK_END); + file_size = ftello(out); cleanup: - ogg_stream_clear(&streamout); + if (in) + in->vfs->close(in); - /* We don't ogg_packet_clear() this, because the memory was allocated in - _commentheader_out(), so we mirror that here */ - _ogg_free(header_comments.packet); + if (out) + fclose(out); - free(state->mainbuf); - free(state->bookbuf); - state->mainbuf = state->bookbuf = NULL; + ogg_sync_clear(&oy); - if(!state->eosin) - { - state->lasterror = - _("Error writing stream to output. " - "Output stream may be corrupted or truncated."); - return -1; - } + if (file_size <= 0) { + unlink(outname); + if (!*lasterror) + *lasterror = "error writing new file, changes backed out."; + return -1; + } - return 0; + rename(outname, fname); + return file_size; } diff --git a/plugins/vorbis/vcedit.h b/plugins/vorbis/vcedit.h index 173876f3..7eabe877 100644 --- a/plugins/vorbis/vcedit.h +++ b/plugins/vorbis/vcedit.h @@ -1,69 +1,35 @@ -/* This program is licensed under the GNU Library General Public License, version 2, - * a copy of which is included with this program (with filename LICENSE.LGPL). - * - * (c) 2000-2001 Michael Smith <msmith@xiph.org> - * - * VCEdit header. - * - * last modified: $ID:$ - */ +/* + This file is part of Deadbeef Player source code + http://deadbeef.sourceforge.net -#ifndef __VCEDIT_H -#define __VCEDIT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdio.h> -#include <ogg/ogg.h> -#include <vorbis/codec.h> + Ogg Vorbis plugin Ogg edit functions header -typedef size_t (*vcedit_read_func)(void *, size_t, size_t, void *); -typedef size_t (*vcedit_write_func)(const void *, size_t, size_t, void *); + Copyright (C) 2014 Ian Nartowicz <deadbeef@nartowicz.co.uk> -typedef struct { - long *streams; - size_t streams_len; -} vcedit_serial_nos; + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. -typedef struct { - ogg_sync_state *oy; - ogg_stream_state *os; + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: - vorbis_comment *vc; - vorbis_info *vi; + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. - vcedit_read_func read; - vcedit_write_func write; +*/ - void *in; - int serial; - vcedit_serial_nos serials; - unsigned char *mainbuf; - unsigned char *bookbuf; - int mainlen; - int booklen; - char *lasterror; - char *vendor; - int prevW; - int extrapage; - int eosin; - struct vcedit_buffer_chain *sidebuf; -} vcedit_state; +#ifndef __VCEDIT_H +#define __VCEDIT_H -extern vcedit_state * vcedit_new_state(void); -extern void vcedit_clear(vcedit_state *state); -extern vorbis_comment * vcedit_comments(vcedit_state *state); -extern int vcedit_open(vcedit_state *state, FILE *in); -extern int vcedit_open_callbacks(vcedit_state *state, void *in, - vcedit_read_func read_func, vcedit_write_func write_func); -extern int vcedit_write(vcedit_state *state, void *out); -extern char * vcedit_error(vcedit_state *state); +#define ALBUM_ART_KEY "METADATA_BLOCK_PICTURE" +#define ALBUM_ART_META "metadata_block_picture" -#ifdef __cplusplus -} -#endif +off_t vcedit_write_metadata(DB_FILE *in, const char *fname, int link, const char *vendor, const int num_tags, char **tags, char **lasterror); #endif /* __VCEDIT_H */ - diff --git a/plugins/vorbis/vceditaux.h b/plugins/vorbis/vceditaux.h deleted file mode 100644 index bb40eaeb..00000000 --- a/plugins/vorbis/vceditaux.h +++ /dev/null @@ -1,9 +0,0 @@ -typedef struct vcedit_page_buffer { - char *data; - size_t data_len; -} vcedit_page_buffer; - -typedef struct vcedit_buffer_chain { - struct vcedit_buffer_chain *next; - struct vcedit_page_buffer buffer; -} vcedit_buffer_chain; diff --git a/plugins/vorbis/vorbis.c b/plugins/vorbis/vorbis.c index 5851a95f..1bd4a382 100644 --- a/plugins/vorbis/vorbis.c +++ b/plugins/vorbis/vorbis.c @@ -1,20 +1,26 @@ /* - DeaDBeeF - ultimate music player for GNU/Linux systems with X11 - Copyright (C) 2009-2013 Alexey Yakovenko <waker@users.sourceforge.net> + DeaDBeeF -- the music player + Copyright (C) 2009-2014 Alexey Yakovenko and other contributors - 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 software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. - 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. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source distribution. */ + #ifdef HAVE_CONFIG_H # include <config.h> #endif @@ -26,6 +32,7 @@ #include <limits.h> #include <unistd.h> #include <math.h> +#include <stdbool.h> #include "../../deadbeef.h" #include "vcedit.h" @@ -35,6 +42,9 @@ //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) +#define RG_REFERENCE_LOUDNESS -1 +#define DELIMITER "\n - \n" + static DB_decoder_t plugin; static DB_functions_t *deadbeef; @@ -74,6 +84,53 @@ cvorbis_ftell (void *datasource) { return deadbeef->ftell (datasource); } +static const char +*gain_tag_name(const int tag_enum) +{ + switch(tag_enum) { + case DDB_REPLAYGAIN_ALBUMGAIN: + return "REPLAYGAIN_ALBUM_GAIN"; + case DDB_REPLAYGAIN_ALBUMPEAK: + return "REPLAYGAIN_ALBUM_PEAK"; + case DDB_REPLAYGAIN_TRACKGAIN: + return "REPLAYGAIN_TRACK_GAIN"; + case DDB_REPLAYGAIN_TRACKPEAK: + return "REPLAYGAIN_TRACK_PEAK"; + case RG_REFERENCE_LOUDNESS: + return "REPLAYGAIN_REFERENCE_LOUDNESS"; + default: + return NULL; + } +} + +static const char +*gain_meta_key(const int tag_enum) +{ + switch(tag_enum) { + case DDB_REPLAYGAIN_ALBUMGAIN: + return ":REPLAYGAIN_ALBUMGAIN"; + case DDB_REPLAYGAIN_ALBUMPEAK: + return ":REPLAYGAIN_ALBUMPEAK"; + case DDB_REPLAYGAIN_TRACKGAIN: + return ":REPLAYGAIN_TRACKGAIN"; + case DDB_REPLAYGAIN_TRACKPEAK: + return ":REPLAYGAIN_TRACKPEAK"; + case RG_REFERENCE_LOUDNESS: + return ":REPLAYGAIN_REFERENCE_LOUDNESS"; + default: + return NULL; + } +} + +static bool +is_special_tag(const char *tag) { + return !strcasecmp(tag, gain_tag_name(DDB_REPLAYGAIN_ALBUMGAIN)) || + !strcasecmp(tag, gain_tag_name(DDB_REPLAYGAIN_ALBUMPEAK)) || + !strcasecmp(tag, gain_tag_name(DDB_REPLAYGAIN_TRACKGAIN)) || + !strcasecmp(tag, gain_tag_name(DDB_REPLAYGAIN_TRACKPEAK)) || + !strcasecmp(tag, gain_tag_name(RG_REFERENCE_LOUDNESS)); +} + static const char *metainfo[] = { "ARTIST", "artist", "TITLE", "title", @@ -142,6 +199,9 @@ update_vorbis_comments (DB_playItem_t *it, vorbis_comment *vc, int refresh_playl else if (!strncasecmp (s, "replaygain_track_peak=", 22)) { deadbeef->pl_set_item_replaygain (it, DDB_REPLAYGAIN_TRACKPEAK, atof (s+22)); } + else if (!strncasecmp (s, "replaygain_reference_loudness=", 30)) { + deadbeef->pl_replace_meta(it, gain_meta_key(RG_REFERENCE_LOUDNESS), s+30); + } else { const char *p = s; while (*p && *p != '=') { @@ -324,7 +384,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { if (!info->info.file->vfs->is_streaming ()) { if (info->currentsample + size / samplesize > info->endsample) { size = (info->endsample - info->currentsample + 1) * samplesize; - trace ("size truncated to %d bytes, cursample=%d, info->endsample=%d, totalsamples=%d\n", size, info->currentsample, info->endsample, ov_pcm_total (&info->vorbis_file, -1)); + trace ("size truncated to %d bytes, cursample=%d, info->endsample=%d, totalsamples=%lld\n", size, info->currentsample, info->endsample, ov_pcm_total (&info->vorbis_file, -1)); if (size <= 0) { return 0; } @@ -350,7 +410,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { } // trace ("cvorbis_read %d bytes[2]\n", size); int initsize = size; - long ret; + int64_t ret; for (;;) { // read ogg @@ -360,7 +420,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { #endif if (_info->fmt.channels <= 2 || _info->fmt.channels == 4) { - ret=ov_read (&info->vorbis_file, bytes, size, endianess, 2, 1, &info->cur_bit_stream); + ret = (int64_t)ov_read (&info->vorbis_file, bytes, size, endianess, 2, 1, &info->cur_bit_stream); } else { int16_t temp[size/2]; @@ -368,13 +428,20 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { if (ret > 0) { // remap channels to wav format int idx = _info->fmt.channels - 3; - static int remap[4][6] = { + static int remap[6][8] = { {0,2,1}, {0,1,2,3}, // should not be used {0,2,1,3,4}, - {0,2,1,4,5,3} + {0,2,1,4,5,3}, + {0,2,1,4,5,6,3}, + {0,2,1,6,7,4,5,3} }; + if (_info->fmt.channels > 8) { + fprintf (stderr, "vorbis plugin doesn't support more than 8 channels\n"); + return -1; + } + int i, j; int16_t *src = temp; int n = ret / samplesize; @@ -389,7 +456,7 @@ cvorbis_read (DB_fileinfo_t *_info, char *bytes, int size) { if (ret <= 0) { if (ret < 0) { - trace ("ov_read returned %d\n", ret); + trace ("ov_read returned %lld\n", ret); switch (ret) { case OV_HOLE: trace ("OV_HOLE\n"); @@ -437,11 +504,11 @@ cvorbis_seek_sample (DB_fileinfo_t *_info, int sample) { trace ("vorbis: file is NULL on seek\n"); return -1; } - trace ("vorbis: seek to sample %d\n"); + trace ("vorbis: seek to sample %d\n", sample); sample += info->startsample; int res = ov_pcm_seek (&info->vorbis_file, sample); if (res != 0 && res != OV_ENOSEEK) { - trace ("vorbis: error %x seeking to sample %d\n", sample); + trace ("vorbis: error %x seeking to sample %d\n", res, sample); return -1; } int tell = ov_pcm_tell (&info->vorbis_file); @@ -584,7 +651,7 @@ cvorbis_read_metadata (DB_playItem_t *it) { DB_FILE *fp = NULL; OggVorbis_File vorbis_file; vorbis_info *vi = NULL; - + deadbeef->pl_lock (); fp = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); deadbeef->pl_unlock (); @@ -629,153 +696,127 @@ error: } -int -cvorbis_write_metadata (DB_playItem_t *it) { - vcedit_state *state = NULL; - vorbis_comment *vc = NULL; - FILE *fp = NULL; - FILE *out = NULL; - int err = -1; - char outname[PATH_MAX] = ""; - char fname[PATH_MAX]; - deadbeef->pl_get_meta (it, ":URI", fname, sizeof (fname)); - - struct field { - struct field *next; - int size; - uint8_t data[0]; - }; +static void +split_tag(vorbis_comment *tags, const char *key, const char *value) +{ + if (key && value) { + const char *p; + while (p = strstr(value, DELIMITER)) { + char v[p - value + 1]; + strncpy(v, value, p-value); + v[p-value] = 0; + vorbis_comment_add_tag(tags, key, v); + value = p + strlen(DELIMITER); + } - struct field *preserved_fields = NULL; + vorbis_comment_add_tag(tags, key, value); + } +} - state = vcedit_new_state (); - if (!state) { - trace ("cvorbis_write_metadata: vcedit_new_state failed\n"); - return -1; +static void +merge_gain_tag(DB_playItem_t *it, vorbis_comment *vc, vorbis_comment *tags, const int tag_enum, const char *pattern, const int min, const int max) +{ + const char *key = gain_tag_name(tag_enum); + + char *end; + const char *meta_value = deadbeef->pl_find_meta(it, key); + const float value = meta_value ? strtof(meta_value, &end) : 0; + if (meta_value && end != meta_value && value > min && value < max) { + char tag_value[10]; + sprintf(tag_value, pattern, value); + vorbis_comment_add_tag(tags, key, tag_value); + } + else { + const char *tag_value = vorbis_comment_query(vc, key, 0); + if (tag_value) + vorbis_comment_add_tag(tags, key, tag_value); } - fp = fopen (fname, "rb"); +} + +static const char +*map_tag(const char *key) +{ + for (int i = 0; metainfo[i]; i += 2) + if (!strcasecmp (metainfo[i+1], key)) + return metainfo[i]; + return key; +} + +static vorbis_comment +*create_tags_list(DB_playItem_t *it, const char *fname, vorbis_comment *tags) +{ + DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { - trace ("cvorbis_write_metadata: failed to read metadata from %s\n", fname); - goto error; + trace ("vorbis: failed to fopen %s\n", fname); + return NULL; } - if (vcedit_open (state, fp) != 0) { - trace ("cvorbis_write_metadata: vcedit_open failed, error: %s\n", vcedit_error (state)); - goto error; + ov_callbacks ovcb = { + .read_func = cvorbis_fread, + .seek_func = cvorbis_fseek, + .close_func = cvorbis_fclose, + .tell_func = cvorbis_ftell + }; + OggVorbis_File vorbis_file; + int err = ov_test_callbacks (fp, &vorbis_file, NULL, 0, ovcb); + if (err != 0) { + trace ("ov_test_callbacks returned %d\n", err); + deadbeef->fclose (fp); + return NULL; } - vc = vcedit_comments (state); + vorbis_comment *vc = ov_comment (&vorbis_file, -1); if (!vc) { - trace ("cvorbis_write_metadata: vcedit_comments failed, error: %s\n", vcedit_error (state)); - goto error; + trace ("ov_comment failed\n"); + ov_clear (&vorbis_file); + return NULL; } -#if 0 - // copy all unknown fields to separate buffer - for (int i = 0; i < vc->comments; i++) { - int m; - for (m = 0; metainfo[m]; m += 2) { - int l = strlen (metainfo[m]); - if (vc->comment_lengths[i] > l && !strncasecmp (vc->user_comments[i], metainfo[m], l) && vc->user_comments[i][l] == '=') { - break; - } - } - if (!metainfo[m]) { - trace ("preserved field: %s\n", vc->user_comments[i]); - // unknown field - struct field *f = malloc (sizeof (struct field) + vc->comment_lengths[i]); - memset (f, 0, sizeof (struct field)); - memcpy (f->data, vc->user_comments[i], vc->comment_lengths[i]); - f->size = vc->comment_lengths[i]; - f->next = preserved_fields; - preserved_fields = f; - } + vorbis_comment_init(tags); + if (!(tags->vendor = strdup(vc->vendor))) { + ov_clear (&vorbis_file); + trace("create_tags_list: cannot allocate tags list\n"); + return NULL; } -#endif - - vorbis_comment_clear(vc); - vorbis_comment_init(vc); - // add unknown/custom fields deadbeef->pl_lock (); - DB_metaInfo_t *m = deadbeef->pl_get_metadata_head (it); + merge_gain_tag(it, vc, tags, DDB_REPLAYGAIN_ALBUMGAIN, "%0.2f dB", -100, 100); + merge_gain_tag(it, vc, tags, DDB_REPLAYGAIN_ALBUMPEAK, "%0.8f", 0, 2); + merge_gain_tag(it, vc, tags, DDB_REPLAYGAIN_TRACKGAIN, "%0.2f dB", -100, 100); + merge_gain_tag(it, vc, tags, DDB_REPLAYGAIN_TRACKPEAK, "%0.8f", 0, 2); + merge_gain_tag(it, vc, tags, RG_REFERENCE_LOUDNESS, "%0.1f dB", 0, 128); + DB_metaInfo_t *m = deadbeef->pl_get_metadata_head(it); while (m) { - if (m->key[0] != ':') { - int i; - for (i = 0; metainfo[i]; i += 2) { - if (!strcasecmp (metainfo[i+1], m->key)) { - break; - } - } - const char *val = m->value; - if (val && *val) { - while (val) { - const char *next = strchr (val, '\n'); - int l; - if (next) { - l = next - val; - next++; - } - else { - l = strlen (val); - } - if (l > 0) { - char s[100+l+1]; - int n = snprintf (s, sizeof (s), "%s=", metainfo[i] ? metainfo[i] : m->key); - strncpy (s+n, val, l); - *(s+n+l) = 0; - vorbis_comment_add (vc, s); - } - val = next; - } - } - } + if (m->key[0] != ':' && m->key[0] != '!' && !is_special_tag(m->key)) + split_tag(tags, map_tag(m->key), (char *)m->value); m = m->next; } deadbeef->pl_unlock (); - // add preserved fields - for (struct field *f = preserved_fields; f; f = f->next) { - vorbis_comment_add (vc, f->data); - } - - snprintf (outname, sizeof (outname), "%s.temp.ogg", fname); + ov_clear (&vorbis_file); + return tags; +} - out = fopen (outname, "w+b"); - if (!out) { - trace ("cvorbis_write_metadata: failed to open %s for writing\n", outname); - goto error; - } +static int +cvorbis_write_metadata (DB_playItem_t *it) { + char fname[PATH_MAX]; + deadbeef->pl_get_meta (it, ":URI", fname, sizeof (fname)); - if (vcedit_write (state, out) < 0) { - trace ("cvorbis_write_metadata: failed to write tags to %s, error: %s\n", fname, vcedit_error (state)); - goto error; - } + vorbis_comment tags; + if (!create_tags_list(it, fname, &tags)) + return -1; - err = 0; -error: - if (fp) { - fclose (fp); - } - if (out) { - fclose (out); - } - if (state) { - vcedit_clear (state); - } - while (preserved_fields) { - struct field *next = preserved_fields->next; - free (preserved_fields); - preserved_fields = next; + char *vorbis_error = NULL; + const off_t file_size = vcedit_write_metadata (deadbeef->fopen(fname), fname, -1, tags.vendor, tags.comments, tags.user_comments, &vorbis_error); + vorbis_comment_clear(&tags); + if (file_size <= 0) { + trace ("cvorbis_write_metadata: failed to write tags to %s, error: %s\n", fname, vorbis_error); + return -1; } - if (!err) { - rename (outname, fname); - } - else if (out) { - unlink (outname); - } + deadbeef->pl_set_meta_int(it, ":FILE_SIZE", file_size); + return cvorbis_read_metadata(it); - return err; + return 0; } @@ -791,22 +832,62 @@ static DB_decoder_t plugin = { .plugin.id = "stdogg", .plugin.name = "OggVorbis decoder", .plugin.descr = "OggVorbis decoder using standard xiph.org libraries", - .plugin.copyright = - "Copyright (C) 2009-2013 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.copyright = + "OggVorbis plugin for DeaDBeeF\n" + "Copyright (C) 2009-2014 Alexey Yakovenko et al.\n" + "\n" + "vcedit.c\n" + "Ogg Vorbis plugin Ogg edit functions\n" + "\n" + "Copyright (C) 2014 Ian Nartowicz <deadbeef@nartowicz.co.uk>\n" + "This software is provided 'as-is', without any express or implied\n" + "warranty. In no event will the authors be held liable for any damages\n" + "arising from the use of this software.\n" + "\n" + "Permission is granted to anyone to use this software for any purpose,\n" + "including commercial applications, and to alter it and redistribute it\n" + "freely, subject to the following restrictions:\n" + "\n" + "1. The origin of this software must not be misrepresented; you must not\n" + " claim that you wrote the original software. If you use this software\n" + " in a product, an acknowledgment in the product documentation would be\n" + " appreciated but is not required.\n" + "\n" + "2. Altered source versions must be plainly marked as such, and must not be\n" + " misrepresented as being the original software.\n" + "\n" + "3. This notice may not be removed or altered from any source distribution.\n" + "\n" + "\n" + "\n" + "Uses libogg,libvorbis Copyright (c) 2002, Xiph.org Foundation\n" + "\n" + "Redistribution and use in source and binary forms, with or without\n" + "modification, are permitted provided that the following conditions\n" + "are met:\n" + "\n" + "- Redistributions of source code must retain the above copyright\n" + "notice, this list of conditions and the following disclaimer.\n" + "\n" + "- Redistributions in binary form must reproduce the above copyright\n" + "notice, this list of conditions and the following disclaimer in the\n" + "documentation and/or other materials provided with the distribution.\n" + "\n" + "- Neither the name of the DeaDBeeF Player nor the names of its\n" + "contributors may be used to endorse or promote products derived from\n" + "this software without specific prior written permission.\n" + "\n" + "THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n" + "``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n" + "LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n" + "A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR\n" + "CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n" + "EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n" + "PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n" + "PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n" + "LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n" + "NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n" + "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n" , .plugin.website = "http://deadbeef.sf.net", .plugin.start = vorbis_start, diff --git a/plugins/wavpack/Makefile.am b/plugins/wavpack/Makefile.am index 06100ea5..17651ee5 100644 --- a/plugins/wavpack/Makefile.am +++ b/plugins/wavpack/Makefile.am @@ -5,5 +5,5 @@ wavpack_la_SOURCES = wavpack.c wavpack_la_LDFLAGS = -module -avoid-version wavpack_la_LIBADD = $(LDADD) $(WAVPACK_LIBS) -AM_CFLAGS = $(CFLAGS) -std=c99 +AM_CFLAGS = $(CFLAGS) $(WAVPACK_CFLAGS) -std=c99 endif diff --git a/plugins/wma/asfheader.c b/plugins/wma/asfheader.c index db662103..9e12f12f 100644 --- a/plugins/wma/asfheader.c +++ b/plugins/wma/asfheader.c @@ -751,6 +751,9 @@ static int asf_parse_header(DB_FILE *fd, asf_waveformatex_t* wfx, DB_playItem_t if (type == 0) { // FIXME: custom fields -- after others work unsigned char *s = id3buf; asf_utf16LEdecode(fd, length, &id3buf, &id3buf_remaining); + if (!strcmp (utf8buf, "MusicBrainz/Track Id")) { + strcpy (utf8buf, "musicbrainz_trackid"); + } deadbeef->pl_append_meta (it, utf8buf, s); } else { |