aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS12
-rw-r--r--CMakeLists.txt4
-rw-r--r--COPYING340
-rw-r--r--ChangeLog93
-rw-r--r--ChangeLog.rus98
-rw-r--r--README63
-rw-r--r--README.RUS51
-rwxr-xr-xbin/qmmp8
-rwxr-xr-xclear_cmake.sh51
-rw-r--r--lib/CMakeLists.txt53
-rw-r--r--lib/buffer.h37
-rw-r--r--lib/constants.h30
-rw-r--r--lib/decoder.cpp270
-rw-r--r--lib/decoder.h158
-rw-r--r--lib/decoderfactory.h52
-rw-r--r--lib/equ/iir.c85
-rw-r--r--lib/equ/iir.h84
-rw-r--r--lib/equ/iir_cfs.c237
-rw-r--r--lib/equ/iir_cfs.h39
-rw-r--r--lib/equ/iir_fpu.c210
-rw-r--r--lib/equ/iir_fpu.h39
-rw-r--r--lib/fileinfo.cpp48
-rw-r--r--lib/fileinfo.h48
-rw-r--r--lib/filetag.h44
-rw-r--r--lib/iir.c85
-rw-r--r--lib/iir.h84
-rw-r--r--lib/iir_cfs.c235
-rw-r--r--lib/iir_cfs.h41
-rw-r--r--lib/iir_fpu.c210
-rw-r--r--lib/iir_fpu.h39
-rw-r--r--lib/lib.pro36
-rw-r--r--lib/output.cpp192
-rw-r--r--lib/output.h156
-rw-r--r--lib/outputfactory.h46
-rw-r--r--lib/qmmp/CMakeLists.txt3
-rw-r--r--lib/qmmp/Input/CMakeLists.txt41
-rw-r--r--lib/qmmp/Input/Input.pro26
-rw-r--r--lib/qmmp/Input/ffmpeg/CMakeLists.txt96
-rw-r--r--lib/qmmp/Input/ffmpeg/decoder_ffmpeg.cpp346
-rw-r--r--lib/qmmp/Input/ffmpeg/decoder_ffmpeg.h78
-rw-r--r--lib/qmmp/Input/ffmpeg/decoderffmpegfactory.cpp75
-rw-r--r--lib/qmmp/Input/ffmpeg/decoderffmpegfactory.h55
-rw-r--r--lib/qmmp/Input/ffmpeg/detailsdialog.cpp103
-rw-r--r--lib/qmmp/Input/ffmpeg/detailsdialog.h45
-rw-r--r--lib/qmmp/Input/ffmpeg/detailsdialog.ui332
-rw-r--r--lib/qmmp/Input/ffmpeg/ffmpeg.pro25
-rw-r--r--lib/qmmp/Input/ffmpeg/tag.cpp98
-rw-r--r--lib/qmmp/Input/ffmpeg/tag.h64
-rw-r--r--lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.qmbin0 -> 2221 bytes
-rw-r--r--lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts149
-rw-r--r--lib/qmmp/Input/ffmpeg/translations/translations.qrc6
-rw-r--r--lib/qmmp/Input/flac/CMakeLists.txt88
-rw-r--r--lib/qmmp/Input/flac/decoder_flac.cpp567
-rw-r--r--lib/qmmp/Input/flac/decoder_flac.h124
-rw-r--r--lib/qmmp/Input/flac/decoderflacfactory.cpp76
-rw-r--r--lib/qmmp/Input/flac/decoderflacfactory.h55
-rw-r--r--lib/qmmp/Input/flac/decoderflacfactory_interface.h17
-rw-r--r--lib/qmmp/Input/flac/detailsdialog.cpp94
-rw-r--r--lib/qmmp/Input/flac/detailsdialog.h46
-rw-r--r--lib/qmmp/Input/flac/detailsdialog.ui349
-rw-r--r--lib/qmmp/Input/flac/flac.pro30
-rw-r--r--lib/qmmp/Input/flac/tag.cpp97
-rw-r--r--lib/qmmp/Input/flac/tag.h61
-rw-r--r--lib/qmmp/Input/flac/translations/flac_plugin_ru.qmbin0 -> 2215 bytes
-rw-r--r--lib/qmmp/Input/flac/translations/flac_plugin_ru.ts154
-rw-r--r--lib/qmmp/Input/flac/translations/translations.qrc6
-rw-r--r--lib/qmmp/Input/mad/CMakeLists.txt92
-rw-r--r--lib/qmmp/Input/mad/decoder_mad.cpp574
-rw-r--r--lib/qmmp/Input/mad/decoder_mad.h95
-rw-r--r--lib/qmmp/Input/mad/decodermadfactory.cpp87
-rw-r--r--lib/qmmp/Input/mad/decodermadfactory.h54
-rw-r--r--lib/qmmp/Input/mad/detailsdialog.cpp168
-rw-r--r--lib/qmmp/Input/mad/detailsdialog.h51
-rw-r--r--lib/qmmp/Input/mad/detailsdialog.ui487
-rw-r--r--lib/qmmp/Input/mad/id3tag.cpp172
-rw-r--r--lib/qmmp/Input/mad/id3tag.h70
-rw-r--r--lib/qmmp/Input/mad/mad.pro32
-rw-r--r--lib/qmmp/Input/mad/settingsdialog.cpp113
-rw-r--r--lib/qmmp/Input/mad/settingsdialog.h50
-rw-r--r--lib/qmmp/Input/mad/settingsdialog.ui278
-rw-r--r--lib/qmmp/Input/mad/translations/mad_plugin_ru.qmbin0 -> 3625 bytes
-rw-r--r--lib/qmmp/Input/mad/translations/mad_plugin_ru.ts242
-rw-r--r--lib/qmmp/Input/mad/translations/translations.qrc6
-rw-r--r--lib/qmmp/Input/mpc/CMakeLists.txt88
-rw-r--r--lib/qmmp/Input/mpc/decoder_mpc.cpp387
-rw-r--r--lib/qmmp/Input/mpc/decoder_mpc.h80
-rw-r--r--lib/qmmp/Input/mpc/decodermpcfactory.cpp76
-rw-r--r--lib/qmmp/Input/mpc/decodermpcfactory.h55
-rw-r--r--lib/qmmp/Input/mpc/detailsdialog.cpp91
-rw-r--r--lib/qmmp/Input/mpc/detailsdialog.h46
-rw-r--r--lib/qmmp/Input/mpc/detailsdialog.ui349
-rw-r--r--lib/qmmp/Input/mpc/mpc.pro24
-rw-r--r--lib/qmmp/Input/mpc/tag.cpp97
-rw-r--r--lib/qmmp/Input/mpc/tag.h61
-rw-r--r--lib/qmmp/Input/mpc/translations/mpc_plugin_ru.qmbin0 -> 2230 bytes
-rw-r--r--lib/qmmp/Input/mpc/translations/mpc_plugin_ru.ts149
-rw-r--r--lib/qmmp/Input/mpc/translations/translations.qrc6
-rw-r--r--lib/qmmp/Input/vorbis/CMakeLists.txt98
-rw-r--r--lib/qmmp/Input/vorbis/decoder_vorbis.cpp368
-rw-r--r--lib/qmmp/Input/vorbis/decoder_vorbis.h61
-rw-r--r--lib/qmmp/Input/vorbis/decodervorbisfactory.cpp79
-rw-r--r--lib/qmmp/Input/vorbis/decodervorbisfactory.h55
-rw-r--r--lib/qmmp/Input/vorbis/detailsdialog.cpp96
-rw-r--r--lib/qmmp/Input/vorbis/detailsdialog.h46
-rw-r--r--lib/qmmp/Input/vorbis/detailsdialog.ui384
-rw-r--r--lib/qmmp/Input/vorbis/tag.cpp97
-rw-r--r--lib/qmmp/Input/vorbis/tag.h61
-rw-r--r--lib/qmmp/Input/vorbis/translations/translations.qrc6
-rw-r--r--lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.qmbin0 -> 2569 bytes
-rw-r--r--lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.ts164
-rw-r--r--lib/qmmp/Input/vorbis/vorbis.pro29
-rw-r--r--lib/qmmp/Output/CMakeLists.txt16
-rw-r--r--lib/qmmp/Output/Output.pro18
-rw-r--r--lib/qmmp/Output/alsa/CMakeLists.txt65
-rw-r--r--lib/qmmp/Output/alsa/alsa.pro26
-rw-r--r--lib/qmmp/Output/alsa/outputalsa.cpp559
-rw-r--r--lib/qmmp/Output/alsa/outputalsa.h85
-rw-r--r--lib/qmmp/Output/alsa/outputalsafactory.cpp60
-rw-r--r--lib/qmmp/Output/alsa/outputalsafactory.h48
-rw-r--r--lib/qmmp/Output/alsa/settingsdialog.cpp237
-rw-r--r--lib/qmmp/Output/alsa/settingsdialog.h59
-rw-r--r--lib/qmmp/Output/alsa/settingsdialog.ui261
-rw-r--r--lib/qmmp/Output/alsa/translations/alsa_plugin_ru.qmbin0 -> 1580 bytes
-rw-r--r--lib/qmmp/Output/alsa/translations/alsa_plugin_ru.ts89
-rw-r--r--lib/qmmp/Output/alsa/translations/translations.qrc6
-rw-r--r--lib/qmmp/Output/jack/CMakeLists.txt74
-rw-r--r--lib/qmmp/Output/jack/bio2jack.c2635
-rw-r--r--lib/qmmp/Output/jack/bio2jack.h145
-rw-r--r--lib/qmmp/Output/jack/jack.pro26
-rw-r--r--lib/qmmp/Output/jack/outputjack.cpp210
-rw-r--r--lib/qmmp/Output/jack/outputjack.h49
-rw-r--r--lib/qmmp/Output/jack/outputjackfactory.cpp57
-rw-r--r--lib/qmmp/Output/jack/outputjackfactory.h48
-rw-r--r--lib/qmmp/Output/jack/translations/jack_plugin_ru.qmbin0 -> 540 bytes
-rw-r--r--lib/qmmp/Output/jack/translations/jack_plugin_ru.ts26
-rw-r--r--lib/qmmp/Output/jack/translations/translations.qrc6
-rw-r--r--lib/qmmp/qmmp.pro4
-rw-r--r--lib/recycler.cpp103
-rw-r--r--lib/recycler.h49
-rw-r--r--lib/soundcore.cpp314
-rw-r--r--lib/soundcore.h166
-rw-r--r--lib/visualization.h74
-rw-r--r--qmmp.pri8
-rw-r--r--qmmp.pro2
-rw-r--r--qmmp.spec57
-rw-r--r--src/CMakeLists.txt158
-rw-r--r--src/aboutdialog.cpp62
-rw-r--r--src/aboutdialog.h44
-rw-r--r--src/aboutdialog.ui174
-rw-r--r--src/balancebar.cpp136
-rw-r--r--src/balancebar.h66
-rw-r--r--src/button.cpp58
-rw-r--r--src/button.h54
-rw-r--r--src/configdialog.cpp334
-rw-r--r--src/configdialog.h74
-rw-r--r--src/configdialog.ui694
-rw-r--r--src/default/balance.pngbin0 -> 1464 bytes
-rw-r--r--src/default/cbuttons.pngbin0 -> 3148 bytes
-rw-r--r--src/default/eq_ex.pngbin0 -> 4423 bytes
-rw-r--r--src/default/eqmain.pngbin0 -> 34985 bytes
-rw-r--r--src/default/main.pngbin0 -> 25055 bytes
-rw-r--r--src/default/monoster.pngbin0 -> 1060 bytes
-rw-r--r--src/default/numbers.pngbin0 -> 210 bytes
-rw-r--r--src/default/playpaus.pngbin0 -> 179 bytes
-rw-r--r--src/default/pledit.pngbin0 -> 19730 bytes
-rw-r--r--src/default/pledit.txt6
-rw-r--r--src/default/posbar.pngbin0 -> 3279 bytes
-rw-r--r--src/default/shufrep.pngbin0 -> 2560 bytes
-rw-r--r--src/default/text.pngbin0 -> 525 bytes
-rw-r--r--src/default/titlebar.pngbin0 -> 11329 bytes
-rw-r--r--src/default/viscolor.txt23
-rw-r--r--src/default/volume.pngbin0 -> 1727 bytes
-rw-r--r--src/display.cpp275
-rw-r--r--src/display.h104
-rw-r--r--src/dock.cpp239
-rw-r--r--src/dock.h64
-rw-r--r--src/eqgraph.cpp163
-rw-r--r--src/eqgraph.h56
-rw-r--r--src/eqpreset.cpp56
-rw-r--r--src/eqpreset.h47
-rw-r--r--src/eqslider.cpp147
-rw-r--r--src/eqslider.h71
-rw-r--r--src/eqtitlebar.cpp76
-rw-r--r--src/eqtitlebar.h58
-rw-r--r--src/eqwidget.cpp407
-rw-r--r--src/eqwidget.h96
-rw-r--r--src/fft.c296
-rw-r--r--src/fft.h45
-rw-r--r--src/fileloader.cpp108
-rw-r--r--src/fileloader.h72
-rw-r--r--src/guard.cpp125
-rw-r--r--src/guard.h36
-rw-r--r--src/html/about_en.html81
-rw-r--r--src/html/about_ru.html81
-rw-r--r--src/html/about_zh_CN.html81
-rw-r--r--src/html/authors_en.txt12
-rw-r--r--src/html/authors_ru.txt9
-rw-r--r--src/html/authors_zh_CN.txt16
-rw-r--r--src/html/thanks_en.txt3
-rw-r--r--src/html/thanks_ru.txt3
-rw-r--r--src/html/thanks_zh_CN.txt3
-rw-r--r--src/images/advanced.pngbin0 -> 432 bytes
-rw-r--r--src/images/images.qrc14
-rw-r--r--src/images/interface.pngbin0 -> 391 bytes
-rw-r--r--src/images/logo-qmmp.pngbin0 -> 61213 bytes
-rw-r--r--src/images/pause.pngbin0 -> 233 bytes
-rw-r--r--src/images/play.pngbin0 -> 261 bytes
-rw-r--r--src/images/playlist.pngbin0 -> 306 bytes
-rw-r--r--src/images/plugins.pngbin0 -> 360 bytes
-rw-r--r--src/images/qmmp.xpm278
-rw-r--r--src/images/stop.pngbin0 -> 229 bytes
-rw-r--r--src/inlines.h505
-rw-r--r--src/jumptotrackdialog.cpp127
-rw-r--r--src/jumptotrackdialog.h62
-rw-r--r--src/jumptotrackdialog.ui110
-rw-r--r--src/keyboardmanager.cpp260
-rw-r--r--src/keyboardmanager.h71
-rw-r--r--src/listwidget.cpp482
-rw-r--r--src/listwidget.h126
-rw-r--r--src/logscale.cpp74
-rw-r--r--src/logscale.h31
-rw-r--r--src/mainvisual.cpp921
-rw-r--r--src/mainvisual.h179
-rw-r--r--src/mainwindow.cpp677
-rw-r--r--src/mainwindow.h137
-rw-r--r--src/mediafile.cpp109
-rw-r--r--src/mediafile.h62
-rw-r--r--src/monostereo.cpp68
-rw-r--r--src/monostereo.h50
-rw-r--r--src/mp3player.cpp48
-rw-r--r--src/number.cpp45
-rw-r--r--src/number.h49
-rw-r--r--src/pixmapwidget.cpp46
-rw-r--r--src/pixmapwidget.h50
-rw-r--r--src/playlist.cpp462
-rw-r--r--src/playlist.h123
-rw-r--r--src/playlistcontrol.cpp55
-rw-r--r--src/playlistcontrol.h52
-rw-r--r--src/playlistformat.cpp289
-rw-r--r--src/playlistformat.h120
-rw-r--r--src/playlistmodel.cpp821
-rw-r--r--src/playlistmodel.h331
-rw-r--r--src/playlistslider.cpp135
-rw-r--r--src/playlistslider.h63
-rw-r--r--src/playlisttitlebar.cpp118
-rw-r--r--src/playlisttitlebar.h61
-rw-r--r--src/playstate.cpp138
-rw-r--r--src/playstate.h109
-rw-r--r--src/playstatus.cpp63
-rw-r--r--src/playstatus.h55
-rw-r--r--src/pluginitem.cpp91
-rw-r--r--src/pluginitem.h72
-rw-r--r--src/positionbar.cpp137
-rw-r--r--src/positionbar.h71
-rw-r--r--src/preseteditor.cpp79
-rw-r--r--src/preseteditor.h57
-rw-r--r--src/preseteditor.ui88
-rw-r--r--src/qmmpstarter.cpp156
-rw-r--r--src/qmmpstarter.h72
-rw-r--r--src/skin.cpp697
-rw-r--r--src/skin.h308
-rw-r--r--src/src.pro134
-rw-r--r--src/stuff.qrc28
-rw-r--r--src/symboldisplay.cpp87
-rw-r--r--src/symboldisplay.h64
-rw-r--r--src/tcpserver.cpp69
-rw-r--r--src/tcpserver.h51
-rw-r--r--src/textscroller.cpp105
-rw-r--r--src/textscroller.h62
-rw-r--r--src/timeindicator.cpp125
-rw-r--r--src/timeindicator.h63
-rw-r--r--src/titlebar.cpp111
-rw-r--r--src/titlebar.h71
-rw-r--r--src/togglebutton.cpp74
-rw-r--r--src/togglebutton.h58
-rw-r--r--src/translations/qmmp_locales.qrc8
-rw-r--r--src/translations/qmmp_ru.qmbin0 -> 8812 bytes
-rw-r--r--src/translations/qmmp_ru.ts644
-rw-r--r--src/translations/qmmp_tr.qmbin0 -> 8960 bytes
-rw-r--r--src/translations/qmmp_tr.ts644
-rw-r--r--src/translations/qmmp_zh_CN.qmbin0 -> 7081 bytes
-rw-r--r--src/translations/qmmp_zh_CN.ts507
-rw-r--r--src/version.h10
-rw-r--r--src/volumebar.cpp135
-rw-r--r--src/volumebar.h71
285 files changed, 35028 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 000000000..3d2c27997
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,12 @@
+Core Developers:
+
+ Ilya Kotov <forkotov02@hotmail.ru> (idea and base code)
+ Vladimir Kuznetsov <vovanec@gmail.com> (look&feel and many improvements)
+
+Plugin Developers:
+
+ Yuriy Zhuravlev <stalkerg@gmail.com> (jack plugin)
+
+Thanks to:
+
+ Vadim Kalinnikov <moose@ylsoftware.com> (project hosting)
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 000000000..1403f8cb8
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_subdirectory(lib)
+add_subdirectory(lib/qmmp)
+add_subdirectory(src)
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 000000000..5b6e7c66c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 000000000..8b9d8607d
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,93 @@
+Changelog of Qt-based Multimedia Player
+
+--------------
+Version 0.0.1
+* first test release
+
+Version 0.0.2
+* fixed bug in FLAC plugin
+* fixed fft.c: changed g_malloc() to malloc()
+* fixed UTF bug in FLAC plugin
+* fixed UTF bug in Ogg Vorbis plugin
+
+Version 0.0.3
+* added tray support (based on Pavel's Kirpichev's patch)
+* added menu and hot keys support
+* added directory $HOME/.qmmp/skins in skin search pathes
+* fixed UTF bug in FLAC plugin (previous correction dasn't fix bug)
+* fixed UTF bug in Ogg Vorbis plugin (previous correction dasn't fix bug)
+* fixed bug in playlist which can cause program crash
+* add drag&drop support (thanks to Vladimir Kuznetsov)
+
+Version 0.0.4
+* added shift insertion support (Vladimir Kuznetsov)
+* added inserted songs moving (Vladimir Kuznetsov)
+* added queue support (Vladimir Kuznetsov)
+* added sort menu (Vladimir Kuznetsov)
+* added shuffle and repeat functions (Vladimir Kuznetsov)
+* improved playlist slider
+* added volume and balance control
+* fixed some bugs in mad plugin
+* various improves in plugin system
+
+Version 0.0.5
+* added playlist indicators (Vladimir Kuznetsov)
+* added playlist control buttons (Vladimir Kuznetsov)
+* improved shuffle and repeate functions (Vladimir Kuznetsov)
+* added support for m3u, pls, xspf playlists (Vladimir Kuznetsov)
+* added region.txt file support
+* added scroll control for equalizer bars
+* added autosave equalizer settings
+* added Jack support plugin (Yuriy Zhuravlev)
+* added Musepack support plugin
+* fixed program crush with some skins
+* fixed bug in balance control
+* fixed bug in alsa plugin which cause crush in some soundcards (thanks to Vadim Kalinnikov)
+* fixed bug in processing of double click on playlist
+
+Version 0.0.6
+* improved insertion (Vladimir Kuznetsov)
+* added command line support (Vladimir Kuznetsov)
+* added playlist loading by thread (Vladimir Kuznetsov)
+* added equalizer preset support
+* added ffmpeg support plugin for playing WMA files
+* fixed bug in mad plugin, which cause program crash with invalid file pathes
+* fixed bug in Jack plugin, which cause program crash when Jack server is
+ not running (Yuriy Zhuravlev)
+* added pkgconfig support for building
+
+Version 0.1
+* added jump to file dialog (Vladimir Kuznetsov)
+* added about dialog (Vladimir Kuznetsov)
+* added russian translation
+* added installation support
+* added cmake support(experimental)
+* added default skin
+* improved settings dialog
+* fixed "accertion failed" bug
+* fixed hotkeys in playlist menu
+* added english readme (Vladimir Kuznetsov)
+* fixed showing bitrate over 999 kbps
+* added Winamp EQF import
+* added rest time show (Vladimir Kuznetsov)
+* fixed time indicator blinking (Vladimir Kuznetsov)
+* fixed "segmentation failed" with some buggy skins (Vladimir Kuznetsov)
+* fixed parsing some skins
+
+Version 0.1.1
+* fixed ffmpeg plugin build
+* fixed playlist file types (Vladimir Kuznetsov)
+* fixed zero duration bug in mad plugin
+* fixed codepage in about dialog
+* fixed bug in cmake build scripts, which cause conflict in translation resources
+
+Version 0.1.2
+* fixed "segmentation failed" in remaining time mode (Vladimir Kuznetsov)
+* fixed "segmentation failed" when resume playback with empty playlist
+* fixed settings saving in tray mode
+* added hotkeys in playlist (Up, Down, Alt- and Shift- modifiers) (Vladimir Kuznetsov)
+* added broken files skiping (Vladimir Kuznetsov)
+* added Turkish translation (Mustafa GUNAY)
+* added custom close action (Vladimir Kuznetsov)
+* fixed and improved cmake scripts
+* fixed "Delete" hotkey in Russian translation
diff --git a/ChangeLog.rus b/ChangeLog.rus
new file mode 100644
index 000000000..d589ab4bb
--- /dev/null
+++ b/ChangeLog.rus
@@ -0,0 +1,98 @@
+Список изменений проекта Qt-based Multimedia Player
+
+--------------
+Версия 0.0.1
+* первый тестовый релиз
+
+Версия 0.0.2
+* исправлена ошибка в модуле FLAC
+* исправлен fft.c: заменён вызов g_malloc() на malloc()
+* исправлена ошибка UTF в модуле FLAC
+* исправлена ошибка UTF в модуле Ogg Vorbis
+
+Версия 0.0.3
+* добавлена поддержка системного лотка (базируется на изменениях Павла Кирпичева)
+* добавлена поддержка меню и горячих клавиш
+* добавлена директория $HOME/.qmmp/skins для поиска обложек
+* исправлена ошибка UTF в модуле FLAC (предыдущее изменение не исправляет ошибку)
+* исправлена ошибка UTF в модуле Ogg Vorbis (предыдущее изменение не исправляет ошибку)
+* исправлена ошибка в списке воспроизведения, которая может привести к аварийному завершению
+ программы
+* добавлена поддержка drag&drop (благодарности Владимиру Кузнецову)
+
+Версия 0.0.4
+* добавлена поддержка выделения по shift (Владимир Кузнецов)
+* добавлено перемещение выделенных фрагментов (Владимир Кузнецов)
+* добавлена поддержка очереди (Владимир Кузнецов)
+* добавлено меню сортировки (Владимир Кузнецов)
+* добавлены функции повтора и случайного воспроизведения (Владимир Кузнецов)
+* улучшен ползунок прокрутки списка воспроизведения
+* добавлена регулировка громкости и баланса
+* исправлены некоторые ошибки в модуле MAD
+* различные улучшения в модульной системе
+
+Версия 0.0.5
+* добавлены индикаторы в окно списка воспроизведения (Владимир Кузнецов)
+* добавлены кнопки управления в окно списка воспроизведения (Владимир Кузнецов)
+* улучшены функции повтора и случайного воспроизведения (Владимир Кузнецов)
+* добавлена поддержка m3u-, pls-, xspf- форматов списка воспроизведения (Владимир Кузнецов)
+* добавлена поддержка файла region.txt
+* добавлена регулировка эквалайзера с помощью колеса мыши
+* добавлено авто-сохранение настроек эквалайзера
+* добавлен модуль поддержки Jack (Юрий Журавлёв)
+* добавлен модуль поддержки Musepack
+* исправлено аварийное завершение программы на некоторых обложках
+* исправлена ошибка в управлении балансом
+* исправлена ошибка в модуле alsa, которая приводит к аварийному завершению программы на
+ некоторых звуковых картах (благодарности Вадиму Калинникову)
+* исправлена ошибка обработки двойного щелчка по списку воспроизведения
+
+Версия 0.0.6
+* улучшено выделение (Владимир Кузнецов)
+* добавлена поддержка командной строки (Владимир Кузнецов)
+* добавлена загрузка списка воспроизведения потоком (Владимир Кузнецов)
+* добавлена поддержка предустановок эквалайзера
+* добавлен модуль поддержки ffmpeg для воспроизведения WMA-файлов
+* исправлена ошибка в модуле mad, которая вызывает аварийное завершение программы при
+ неправильном пути файла
+* исправлена ошибка в модуле Jack, которая вызывает аварийное завершение программы, когда
+ сервер Jack не запущен (Юрий Журавлёв)
+* добавлена поддержка pkgconfig для сборки
+
+Версия 0.1
+* добавлен диалог "перейти к файлу" (Владимир Кузнецов)
+* добавлен диалог "о программе" (Владимир Кузнецов)
+* добавлен русский перевод
+* добавлена поддержка установки
+* добавлена поддержка cmake (экспериментальная)
+* добавлена обложка по умолчанию
+* улучшен диалог настроек
+* исправлена ошибка "accertion failed"
+* исправлены горячие клавиши в меню управления списками
+* добавлен файл readme на английском языке (Владимир Кузнецов)
+* исправлен показ битовой частоты выше чем 999 кб/с
+* добавлен импорт файлов Winamp EQF
+* добавлен показ оставшегося времени (Владимир Кузнецов)
+* исправлено мигание индикатора времени (Владимир Кузнецов)
+* исправлена ошибка сегментации с некоторыми обложками (Владимир Кузнецов)
+* исправлено чтение некоторых обложек
+
+Версия 0.1.1
+* исправлена сборка модуля ffmpeg
+* исправлены типы файлов списка воспроизведения (Владимир Кузнецов)
+* исправлена ошибка нулевой длительности в модуле mad
+* исправлена кодировка в диалоге "О программе"
+* исправлена ошибка в скриптах сборки cmake, которая вызывает конфликт в
+ ресурсах перевода
+
+Версия 0.1.2
+* исправлена "ошибка сегментации" в режиме оставшегося времени (Владимир Кузнецов)
+* исправлена "ошибка сегментации" в момент продолжения воспроизведения при пустом списке
+* исправлено сохранение настроек в режиме системного значка
+* добавлены горячие клавиши для списка воспроизведения (Вверх, Вниз, Alt- и
+ Shift- модификаторы) (Владимир Кузнецов)
+* добавлен пропуск повреждённых файлов (Владимир Кузнецов)
+* добавлен турецкий перевод (Mustafa GUNAY)
+* добавлена возможность изменять реакцию на закрытие (Владимир Кузнецов)
+* исправлены и улучшены скрипты cmake
+* исправлена грячая клавиша "Delete" в Русском переводе
diff --git a/README b/README
new file mode 100644
index 000000000..577f53abd
--- /dev/null
+++ b/README
@@ -0,0 +1,63 @@
+Qmmp - Qt-based multimedia player
+
+This program is an audio-player, written with help of Qt library.
+
+Main opportunities:
+- unpacked winamp skins support
+- plugins support
+- MPEG1 layer 1/2/3 support
+- Ogg Vorbis support
+- native FLAC support
+- Musepack support
+- AlSA sound output
+- JACK sound output
+
+Requirements:
+- OS GNU Linux
+- Qt >= 4.2
+- MAD
+- Ogg Vorbis
+- ALSA >= 1.0.1
+- TagLib >= 1.4
+- Flac >= 1.1.3 (Optional)
+- libmpcdec >= 1.2.6 (Optional)
+- Jack >= 0.102.5 (Optional)
+
+Build:
+qmake-qt4 && make
+
+Installation:
+make install INSTALL_ROOT=/usr/local
+
+Executable:
+./bin/qmmp
+
+If someone module doesn't build or doesn't needed you can disable it
+in qmmp.pri file - just comment corresponding line( symbol '#').
+Attention! By default all modules are enabled.
+
+Also you can use cmake for building and installation (for testing only).
+
+Configure:
+cmake ./
+
+Build:
+make
+
+Installation:
+make install
+
+If someone module doesn't build or doesn't needed you can disable it by running:
+cmake ./ -D USE_JACK:BOOL=FALSE
+This command will disable JACK Plugin for example. Available options:
+USE_MAD, USE_MPC, USE_VORBIS, USE_FFMPEG, USE_FLAC, USE_ALSA, USE_JACK.
+Also you can use ccmake for changing plugins configuration.
+By default programm will be installed in /usr/local. You can change default path by running:
+cmake ./ -D CMAKE_INSTALL_PREFIX=custom_path
+
+Attention! By default all modules are enabled.
+
+
+All patches, bug reports, ideas etc. send to forkotov02@hotmail.ru.
+
+
diff --git a/README.RUS b/README.RUS
new file mode 100644
index 000000000..1c2ef9a15
--- /dev/null
+++ b/README.RUS
@@ -0,0 +1,51 @@
+Qmmp - Qt-based multimedia player
+
+Программа является аудио-плеером, написанным с использованием библиотеки Qt.
+
+Сборка:
+qmake-qt4 && make
+
+Установка:
+make install INSTALL_ROOT=/usr/local
+
+Исполняемый файл:
+./bin/qmmp
+
+Если какой-либо модуль не собирается или не нужен, в файле qmmp.pri можно его отключить.
+Для отключения необходимо закомментировать соответствующую строчку (символ "#").
+Внимание! по умолчанию включены все модули.
+
+Начиная с версии 0.1 представлена экспериментальная сборка с помощью cmake. Требуется
+тестирование и усовершенствование скриптов.
+
+Конфигурирование:
+cmake ./
+
+Сборка:
+make
+
+Устнановка:
+make install
+
+Если какой-либо модуль не собирается или не нужен, вы можете отключить его командой:
+cmake ./ -D USE_JACK:BOOL=FALSE
+Эта команда оключит модуль JACK. Доступные опции:
+USE_MAD, USE_MPC, USE_VORBIS, USE_FFMPEG, USE_FLAC, USE_ALSA, USE_JACK.
+Также вы можете использовать ccmake для изменения конфигурации модулей.
+По умолчанию программа будет установлена в /usr/local. Вы можете изменить этот путь командой:
+cmake ./ -D CMAKE_INSTALL_PREFIX=другой_путь
+
+Внимание! по умолчанию включены все модули.
+
+Патчи, багрепорты, идеи и т.п.: forkotov02@hotmail.ru.
+
+Автор выражает благодарность Вадиму Калинникову (www.ylsoftware.com) за предоставленный
+хостинг и тестирование программы.
+
+Также выражаеются благодарности всем, приславшим патчи и багрепорты.
+
+Просьба присылающим пачти: оставляйте, пожалуйста, свои имена, фамилии и контактную информацию.
+Наиболее активные участники будут занесены в список :)
+
+Внимание: проекту требуются дизайнеры для создания обложек и иконок, лучшая работы станет
+"лицом" программы.
diff --git a/bin/qmmp b/bin/qmmp
new file mode 100755
index 000000000..ae13c2b9f
--- /dev/null
+++ b/bin/qmmp
@@ -0,0 +1,8 @@
+#!/bin/sh
+MYDIR=$(dirname $0)
+LD_LIBRARY_PATH=$MYDIR/../lib:$LD_LIBRARY_PATH
+printf '\nSetting LD_LIBRARY_PATH to '$LD_LIBRARY_PATH
+export LD_LIBRARY_PATH
+printf '\n'$LD_LIBRARY_PATH
+printf '\n'
+exec $MYDIR/qmmp.real "$@"
diff --git a/clear_cmake.sh b/clear_cmake.sh
new file mode 100755
index 000000000..32d90449f
--- /dev/null
+++ b/clear_cmake.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+
+clean () { rm -rf CMakeFiles; rm -f Makefile; rm -f cmake_install.cmake; rm -f CMakeCache.txt; }
+
+make clean
+clean
+#clear src
+cd src
+clean
+cd ..
+#clear lib
+cd lib
+clean
+#clear qmmp
+cd qmmp
+clean
+#clear Input
+cd Input
+clean
+#clear ffmpeg
+cd ffmpeg
+clean
+#clear mad
+cd ..
+cd mad
+clean
+#clear mpc
+cd ..
+cd mpc
+clean
+#clear flac
+cd ..
+cd flac
+clean
+#clear vorbis
+cd ..
+cd vorbis
+clean
+#clear Output
+cd ..
+cd ..
+cd Output
+clean
+#clear alsa
+cd alsa
+clean
+#clear jack
+cd ..
+cd jack
+clean
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
new file mode 100644
index 000000000..3b2fc8df9
--- /dev/null
+++ b/lib/CMakeLists.txt
@@ -0,0 +1,53 @@
+project(libqmmp)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+include(FindQt4)
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt lib
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+#ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(libqmmp_SRCS
+ recycler.cpp
+ decoder.cpp
+ output.cpp
+ equ/iir.c
+ equ/iir_cfs.c
+ equ/iir_fpu.c
+ soundcore.cpp
+)
+
+SET(libqmmp_MOC_HDRS
+ visualization.h
+ recycler.h
+ buffer.h
+ constants.h
+ decoder.h
+ output.h
+ filetag.h
+ outputfactory.h
+ equ/iir_cfs.h
+ equ/iir_fpu.h
+ equ/iir.h
+ decoderfactory.h
+ soundcore.h
+)
+
+QT4_WRAP_CPP(libqmmp_MOC_SRCS ${libqmmp_MOC_HDRS})
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+
+ADD_LIBRARY(qmmp SHARED ${libqmmp_SRCS} ${libqmmp_MOC_SRCS})
+target_link_libraries(qmmp ${QT_LIBRARIES})
+install(TARGETS qmmp LIBRARY DESTINATION lib)
+
diff --git a/lib/buffer.h b/lib/buffer.h
new file mode 100644
index 000000000..b04ea0ed8
--- /dev/null
+++ b/lib/buffer.h
@@ -0,0 +1,37 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __buffer_h
+#define __buffer_h
+
+#include "constants.h"
+
+class Buffer {
+public:
+ Buffer()
+ {
+ data = new unsigned char[Buffer::size()];
+ nbytes = 0;
+ rate = 0;
+ }
+ ~Buffer()
+ {
+ delete data;
+ data = 0;
+ nbytes = 0;
+ rate = 0;
+ }
+
+ unsigned char *data;
+ unsigned long nbytes;
+ unsigned long rate;
+
+ static unsigned long size() { return globalBlockSize; }
+};
+
+
+#endif // __buffer_h
+
diff --git a/lib/constants.h b/lib/constants.h
new file mode 100644
index 000000000..468dc08c1
--- /dev/null
+++ b/lib/constants.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __constants_h
+#define __constants_h
+
+#define VERSION "MQ3 Release Fourteen"
+
+#if defined(Q_OS_UNIX)
+//# include "../config.h"
+# ifndef INSTALLDIR
+# define INSTALLDIR "/usr/local"
+# endif
+#elif defined( Q_OS_WIN32 )
+# define INSTALLDIR "c:/depot/mq3/main"
+#endif
+
+const unsigned int historySize = 100;
+const unsigned int globalBlockSize = 2 * 1024; //2*1024
+const unsigned int globalBufferSize = globalBlockSize * 32;
+const unsigned int groupOpenTimeout = 750;
+
+#if defined( Q_OS_WIN32 )
+#define BUFFERBLOCKS 16
+#endif
+
+#endif // __constants_h
diff --git a/lib/decoder.cpp b/lib/decoder.cpp
new file mode 100644
index 000000000..b77b780b4
--- /dev/null
+++ b/lib/decoder.cpp
@@ -0,0 +1,270 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+#include <QtGui>
+#include <QObject>
+#include <QStringList>
+#include <QApplication>
+#include <QSettings>
+
+
+#include "constants.h"
+#include "buffer.h"
+#include "output.h"
+#include "visualization.h"
+#include "decoderfactory.h"
+extern "C"{
+#include "equ/iir.h"
+}
+#include "decoder.h"
+
+
+Decoder::Decoder(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : QThread(parent), fctry(d), in(i), out(o), blksize(0),m_eqInited(FALSE),
+ m_useEQ(FALSE)
+{
+ out->recycler()->clear();
+ int b[] = {0,0,0,0,0,0,0,0,0,0};
+ setEQ(b, 0);
+ qRegisterMetaType<DecoderState>("DecoderState");
+}
+
+Decoder::~Decoder()
+{
+ fctry = 0;
+ in = 0;
+ out = 0;
+ blksize = 0;
+}
+
+// static methods
+
+static QList<DecoderFactory*> *factories = 0;
+static QStringList files;
+static QStringList blacklist;
+
+static void checkFactories()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ blacklist = settings.value("Decoder/disabled_plugins").toStringList ();
+ if (! factories)
+ {
+ files.clear();
+ factories = new QList<DecoderFactory *>;
+
+ QDir pluginsDir (qApp->applicationDirPath());
+ pluginsDir.cdUp();
+ pluginsDir.cd("lib/qmmp/Input");
+ foreach (QString fileName, pluginsDir.entryList(QDir::Files))
+ {
+ QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
+ QObject *plugin = loader.instance();
+ if (loader.isLoaded())
+ {
+ qDebug("Decoder: plugin loaded - %s", qPrintable(fileName));
+ }
+ DecoderFactory *factory = 0;
+ if (plugin)
+ factory = qobject_cast<DecoderFactory *>(plugin);
+
+ if (factory)
+ {
+ factories->append(factory);
+ files << pluginsDir.absoluteFilePath(fileName);
+ }
+ }
+ //remove physically deleted plugins from blacklist
+ QStringList names;
+ foreach (QString filePath, files)
+ {
+ names.append(filePath.section('/',-1));
+ }
+ int i = 0;
+ while (i < blacklist.size())
+ {
+ if (!names.contains(blacklist.at(i)))
+ blacklist.removeAt(i);
+ else
+ i++;
+ }
+ settings.setValue("Decoder/disabled_plugins",blacklist);
+ }
+}
+
+
+QStringList Decoder::all()
+{
+ checkFactories();
+
+ QStringList l;
+ DecoderFactory *fact;
+ foreach(fact, *factories)
+ {
+ l << fact->description();
+ }
+ return l;
+}
+
+QStringList Decoder::decoderFiles()
+{
+ checkFactories();
+ return files;
+}
+
+
+bool Decoder::supports(const QString &source)
+{
+ checkFactories();
+
+ for (int i=0; i<factories->size(); ++i)
+ {
+ if (factories->at(i)->supports(source) &&
+ !blacklist.contains(files.at(i).section('/',-1)))
+ {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+Decoder *Decoder::create(QObject *parent, const QString &source,
+ QIODevice *input,
+ Output *output)
+{
+ Decoder *decoder = 0;
+
+ DecoderFactory *fact = Decoder::findFactory(source);
+ if (fact)
+ {
+ decoder = fact->create(parent, input, output);
+ }
+ return decoder;
+}
+
+DecoderFactory *Decoder::findFactory(const QString& source)
+{
+ checkFactories();
+
+ for (int i=0; i<factories->size(); ++i)
+ {
+ if (factories->at(i)->supports(source) &&
+ !blacklist.contains(files.at(i).section('/',-1)))
+ {
+ return factories->at(i);
+ }
+ }
+ qDebug("Decoder: unable to find factory");
+ return 0;
+}
+
+FileTag *Decoder::createTag(const QString& source)
+{
+ DecoderFactory *fact = Decoder::findFactory(source);
+ if (fact)
+ {
+ return fact->createTag(source);
+ }
+ return 0;
+}
+
+QString Decoder::filter()
+{
+ QString allflt(tr("All Supported Bitstreams ("));
+ QString flt;
+
+ checkFactories();
+ DecoderFactory *fact;
+ for (int i = 0; i<factories->size(); ++i)
+ {
+ if (!blacklist.contains(files.at(i).section('/',-1)))
+ {
+ fact = (*factories)[i];
+ allflt +=fact->filter().toLower() +" ";
+ flt += fact->description() + " (" + fact->filter().toLower() + ")";
+ flt += ";;";
+ }
+ }
+ if (!flt.isEmpty ())
+ flt = flt.left(flt.size ()-2);
+
+ allflt += ");;";
+
+ return allflt + flt;
+}
+
+QStringList Decoder::nameFilters()
+{
+ checkFactories();
+ QStringList filters;
+ for (int i=0; i<factories->size(); ++i)
+ {
+ if (!blacklist.contains(files.at(i).section('/',-1)))
+ filters << factories->at(i)->filter().split(" ", QString::SkipEmptyParts);
+ }
+ return filters;
+}
+
+QList<DecoderFactory*> *Decoder::decoderFactories()
+{
+ checkFactories();
+ return factories;
+}
+
+void Decoder::dispatch(const DecoderState &st)
+{
+ emit stateChanged(st);
+}
+
+void Decoder::dispatch(DecoderState::Type st)
+{
+ emit stateChanged(DecoderState(st));
+}
+
+void Decoder::error(const QString &e)
+{
+ emit stateChanged(DecoderState(e));
+}
+
+ulong Decoder::produceSound(char *data, ulong output_bytes, ulong bitrate, int nch)
+{
+ ulong sz = output_bytes < blksize ? output_bytes : blksize;
+ Buffer *b = output()->recycler()->get();
+
+ if (!m_eqInited)
+ {
+ init_iir();
+ m_eqInited = TRUE;
+ }
+ if (m_useEQ)
+ {
+ iir((void*) data,sz,nch);
+ }
+ memcpy(b->data, data, sz);
+
+ if (sz != blksize)
+ memset(b->data + sz, 0, blksize - sz);
+
+ b->nbytes = blksize;
+ b->rate = bitrate;
+
+ output()->recycler()->add();
+
+ output_bytes -= sz;
+ memmove(data, data + sz, output_bytes);
+ return sz;
+}
+
+void Decoder::setEQ(int bands[10], int preamp)
+{
+ set_preamp(0, 1.0 + 0.0932471 *preamp + 0.00279033 * preamp * preamp);
+ set_preamp(1, 1.0 + 0.0932471 *preamp + 0.00279033 * preamp * preamp);
+ for (int i=0; i<10; ++i)
+ {
+ int value = bands[i];
+ set_gain(i,0, 0.03*value+0.000999999*value*value);
+ set_gain(i,1, 0.03*value+0.000999999*value*value);
+ }
+}
+
diff --git a/lib/decoder.h b/lib/decoder.h
new file mode 100644
index 000000000..c7b099067
--- /dev/null
+++ b/lib/decoder.h
@@ -0,0 +1,158 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef DECODER_H
+#define DECODER_H
+
+#include <QThread>
+#include <QList>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QObject>
+
+
+#include "fileinfo.h"
+#include "filetag.h"
+
+class QObject;
+class QIODevice;
+
+class Decoder;
+class DecoderFactory;
+class Buffer;
+class Recycler;
+class Output;
+class Visualization;
+
+
+
+class DecoderState
+{
+public:
+ enum Type { Decoding, Stopped, Finished, Error };
+
+ DecoderState(Type t)
+ : m_type(t), m_error_msg(0)
+ {}
+
+ DecoderState(const QString &e)
+ : m_type(Error)
+ {
+ m_error_msg = new QString(e);
+ }
+
+ DecoderState()
+ : m_type(Stopped), m_error_msg(0)
+ {}
+
+ ~DecoderState()
+ {
+ if (m_error_msg)
+ delete m_error_msg;
+ }
+
+ const QString *errorMessage() const
+ {
+ return m_error_msg;
+ }
+ const Type &type() const
+ {
+ return m_type;
+ }
+
+private:
+ Type m_type;
+ const QString *m_error_msg;
+};
+
+
+
+class Decoder : public QThread
+{
+Q_OBJECT
+public:
+ Decoder(QObject *parent, DecoderFactory *d,
+ QIODevice *i, Output *o);
+ virtual ~Decoder();
+
+ // Standard Decoder API
+ virtual bool initialize() = 0;
+ virtual double lengthInSeconds() = 0;
+ virtual void seek(double) = 0;
+ virtual void stop() = 0;
+
+ DecoderFactory *factory() const
+ {
+ return fctry;
+ }
+
+ QIODevice *input()
+ {
+ return in;
+ }
+ Output *output()
+ {
+ return out;
+ }
+
+ QMutex *mutex()
+ {
+ return &mtx;
+ }
+ QWaitCondition *cond()
+ {
+ return &cnd;
+ }
+
+ void setBlockSize(unsigned int sz)
+ {
+ blksize = sz;
+ }
+
+ unsigned int blockSize() const
+ {
+ return blksize;
+ }
+ ulong produceSound(char *data, ulong output_bytes, ulong bitrate, int nch);
+ void setEQ(int bands[10], int preamp);
+ void setEQEnabled(bool on) { m_useEQ = on; };
+
+ // static methods
+ static QStringList all();
+ static bool supports(const QString &);
+ //static void registerFactory(DecoderFactory *);
+ static Decoder *create(QObject *, const QString &, QIODevice *, Output *);
+ static DecoderFactory *findFactory(const QString&);
+ static FileTag *createTag(const QString&);
+ static QString filter();
+ static QStringList nameFilters();
+ static QList<DecoderFactory*> *decoderFactories();
+ static QStringList decoderFiles();
+
+signals:
+ void stateChanged(const DecoderState&);
+
+protected:
+ void dispatch(DecoderState::Type);
+ void dispatch(const DecoderState&);
+ void error(const QString&);
+
+private:
+ DecoderFactory *fctry;
+
+ QList<QObject*> listeners;
+ QIODevice *in;
+ Output *out;
+
+ QMutex mtx;
+ QWaitCondition cnd;
+
+ uint blksize;
+ bool m_eqInited;
+ bool m_useEQ;
+};
+
+#endif // DECODER_H
diff --git a/lib/decoderfactory.h b/lib/decoderfactory.h
new file mode 100644
index 000000000..9fd5b55cd
--- /dev/null
+++ b/lib/decoderfactory.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef DECODERFACTORY_H
+#define DECODERFACTORY_H
+
+class QObject;
+class QString;
+class QIODevice;
+class QWidget;
+class QTranslator;
+
+class Decoder;
+class Output;
+class FileTag;
+
+class DecoderFactory
+{
+public:
+ virtual ~DecoderFactory() {}
+ virtual bool supports(const QString &source) const = 0;
+ virtual const QString &name() const = 0;
+ virtual const QString &filter() const = 0;
+ virtual const QString &description() const = 0; //i.e. file description
+ virtual Decoder *create(QObject *, QIODevice *, Output *) = 0;
+ virtual FileTag *createTag(const QString &source) = 0;
+ virtual void showDetails(QWidget *parent, const QString &path) = 0;
+ virtual void showSettings(QWidget *parent) = 0;
+ virtual void showAbout(QWidget *parent) = 0;
+ virtual QTranslator *createTranslator(QObject *parent) = 0;
+};
+
+Q_DECLARE_INTERFACE(DecoderFactory, "DecoderFactory/1.0");
+
+#endif
diff --git a/lib/equ/iir.c b/lib/equ/iir.c
new file mode 100644
index 000000000..9d826b86c
--- /dev/null
+++ b/lib/equ/iir.c
@@ -0,0 +1,85 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir.c,v 1.15 2005/10/17 01:57:59 liebremx Exp $
+ */
+
+#include <math.h>
+#include "iir.h"
+
+/* Coefficients */
+sIIRCoefficients *iir_cf;
+
+/* Volume gain
+ * values should be between 0.0 and 1.0
+ * Use the preamp from XMMS for now
+ * */
+float preamp[EQ_CHANNELS];
+
+#ifdef BENCHMARK
+#include "benchmark.h"
+double timex = 0.0;
+int count = 0;
+unsigned int blength = 0;
+#endif
+
+/*
+ * Global vars
+ */
+int rate;
+int band_count;
+
+void set_preamp(int chn, float val)
+{
+ preamp[chn] = val;
+}
+
+/* Init the filters */
+void init_iir()
+{
+ calc_coeffs();
+#if 0
+ band_count = cfg.band_num;
+#endif
+
+ band_count = 10;
+
+ rate = 44100;
+
+ iir_cf = get_coeffs(&band_count, rate);
+ clean_history();
+}
+
+#ifdef ARCH_X86
+/* Round function provided by Frank Klemm which saves around 100K
+ * CPU cycles in my PIII for each call to the IIR function with 4K samples
+ */
+__inline__ int round_trick(float floatvalue_to_round)
+{
+ float floattmp ;
+ int rounded_value ;
+
+ floattmp = (int) 0x00FD8000L + (floatvalue_to_round);
+ rounded_value = *(int*)(&floattmp) - (int)0x4B7D8000L;
+
+ if ( rounded_value != (short) rounded_value )
+ rounded_value = ( rounded_value >> 31 ) ^ 0x7FFF;
+ return rounded_value;
+}
+#endif
diff --git a/lib/equ/iir.h b/lib/equ/iir.h
new file mode 100644
index 000000000..e7ea5ef1a
--- /dev/null
+++ b/lib/equ/iir.h
@@ -0,0 +1,84 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir.h,v 1.12 2005/10/17 01:57:59 liebremx Exp $
+ */
+#ifndef IIR_H
+#define IIR_H
+
+//#include <glib.h>
+//#include "main.h"
+#include "iir_cfs.h"
+
+/*
+ * Flush-to-zero to avoid flooding the CPU with underflow exceptions
+ */
+#ifdef SSE_MATH
+#define FTZ 0x8000
+#define FTZ_ON { \
+ unsigned int mxcsr; \
+ __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); \
+ mxcsr |= FTZ; \
+ __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); \
+}
+#define FTZ_OFF { \
+ unsigned int mxcsr; \
+ __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); \
+ mxcsr &= ~FTZ; \
+ __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); \
+}
+#else
+#define FTZ_ON
+#define FTZ_OFF
+#endif
+
+/*
+ * Function prototypes
+ */
+void init_iir();
+void clean_history();
+void set_gain(int index, int chn, float val);
+void set_preamp(int chn, float val);
+
+
+ int iir(void * d, int length, int nch);
+
+#ifdef ARCH_X86
+__inline__ int round_trick(float floatvalue_to_round);
+#endif
+#ifdef ARCH_PPC
+__inline__ int round_ppc(float x);
+#endif
+
+#define EQ_CHANNELS 2
+#define EQ_MAX_BANDS 10
+
+extern float preamp[EQ_CHANNELS];
+extern sIIRCoefficients *iir_cf;
+extern int rate;
+extern int band_count;
+
+#ifdef BENCHMARK
+extern double timex;
+extern int count;
+extern unsigned int blength;
+#endif
+
+#endif /* #define IIR_H */
+
diff --git a/lib/equ/iir_cfs.c b/lib/equ/iir_cfs.c
new file mode 100644
index 000000000..f8e6f88a6
--- /dev/null
+++ b/lib/equ/iir_cfs.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Coefficient stuff
+ *
+ * $Id: iir_cfs.c,v 1.1 2005/10/17 01:57:59 liebremx Exp $
+ */
+
+#include "iir_cfs.h"
+#include <stdio.h>
+#include <math.h>
+
+/***************************
+ * IIR filter coefficients *
+ ***************************/
+static sIIRCoefficients iir_cf10_11k_11025[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf10_22k_22050[10] __attribute__((aligned));
+static sIIRCoefficients iir_cforiginal10_44100[10] __attribute__((aligned));
+static sIIRCoefficients iir_cforiginal10_48000[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf10_44100[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf10_48000[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf15_44100[15] __attribute__((aligned));
+static sIIRCoefficients iir_cf15_48000[15] __attribute__((aligned));
+static sIIRCoefficients iir_cf25_44100[25] __attribute__((aligned));
+static sIIRCoefficients iir_cf25_48000[25] __attribute__((aligned));
+static sIIRCoefficients iir_cf31_44100[31] __attribute__((aligned));
+static sIIRCoefficients iir_cf31_48000[31] __attribute__((aligned));
+
+/******************************************************************
+ * Definitions and data structures to calculate the coefficients
+ ******************************************************************/
+static const double band_f011k[] =
+{ 31, 62, 125, 250, 500, 1000, 2000, 3000, 4000, 5500
+};
+static const double band_f022k[] =
+{ 31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 11000
+};
+static const double band_f010[] =
+{ 31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000
+};
+static const double band_original_f010[] =
+{ 60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000
+};
+static const double band_f015[] =
+{ 25,40,63,100,160,250,400,630,1000,1600,2500,4000,6300,10000,16000
+};
+static const double band_f025[] =
+{ 20,31.5,40,50,80,100,125,160,250,315,400,500,800,
+ 1000,1250,1600,2500,3150,4000,5000,8000,10000,12500,16000,20000
+};
+static const double band_f031[] =
+{ 20,25,31.5,40,50,63,80,100,125,160,200,250,315,400,500,630,800,
+ 1000,1250,1600,2000,2500,3150,4000,5000,6300,8000,10000,12500,16000,20000
+};
+
+#define GAIN_F0 1.0
+#define GAIN_F1 GAIN_F0 / M_SQRT2
+
+#define SAMPLING_FREQ 44100.0
+#define TETA(f) (2*M_PI*(double)f/bands[n].sfreq)
+#define TWOPOWER(value) (value * value)
+
+#define BETA2(tf0, tf) \
+(TWOPOWER(GAIN_F1)*TWOPOWER(cos(tf0)) \
+ - 2.0 * TWOPOWER(GAIN_F1) * cos(tf) * cos(tf0) \
+ + TWOPOWER(GAIN_F1) \
+ - TWOPOWER(GAIN_F0) * TWOPOWER(sin(tf)))
+#define BETA1(tf0, tf) \
+ (2.0 * TWOPOWER(GAIN_F1) * TWOPOWER(cos(tf)) \
+ + TWOPOWER(GAIN_F1) * TWOPOWER(cos(tf0)) \
+ - 2.0 * TWOPOWER(GAIN_F1) * cos(tf) * cos(tf0) \
+ - TWOPOWER(GAIN_F1) + TWOPOWER(GAIN_F0) * TWOPOWER(sin(tf)))
+#define BETA0(tf0, tf) \
+ (0.25 * TWOPOWER(GAIN_F1) * TWOPOWER(cos(tf0)) \
+ - 0.5 * TWOPOWER(GAIN_F1) * cos(tf) * cos(tf0) \
+ + 0.25 * TWOPOWER(GAIN_F1) \
+ - 0.25 * TWOPOWER(GAIN_F0) * TWOPOWER(sin(tf)))
+
+#define GAMMA(beta, tf0) ((0.5 + beta) * cos(tf0))
+#define ALPHA(beta) ((0.5 - beta)/2.0)
+
+struct {
+ sIIRCoefficients *coeffs;
+ const double *cfs;
+ double octave;
+ int band_count;
+ double sfreq;
+} bands[] = {
+ { iir_cf10_11k_11025, band_f011k, 1.0, 10, 11025.0 },
+ { iir_cf10_22k_22050, band_f022k, 1.0, 10, 22050.0 },
+ { iir_cforiginal10_44100, band_original_f010, 1.0, 10, 44100.0 },
+ { iir_cforiginal10_48000, band_original_f010, 1.0, 10, 48000.0 },
+ { iir_cf10_44100, band_f010, 1.0, 10, 44100.0 },
+ { iir_cf10_48000, band_f010, 1.0, 10, 48000.0 },
+ { iir_cf15_44100, band_f015, 2.0/3.0, 15, 44100.0 },
+ { iir_cf15_48000, band_f015, 2.0/3.0, 15, 48000.0 },
+ { iir_cf25_44100, band_f025, 1.0/3.0, 25, 44100.0 },
+ { iir_cf25_48000, band_f025, 1.0/3.0, 25, 48000.0 },
+ { iir_cf31_44100, band_f031, 1.0/3.0, 31, 44100.0 },
+ { iir_cf31_48000, band_f031, 1.0/3.0, 31, 48000.0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+/*************
+ * Functions *
+ *************/
+
+/* Get the coeffs for a given number of bands and sampling frequency */
+sIIRCoefficients* get_coeffs(int *bands, int sfreq)
+{
+ sIIRCoefficients *iir_cf = 0;
+ switch(sfreq)
+ {
+ case 11025: iir_cf = iir_cf10_11k_11025;
+ *bands = 10;
+ break;
+ case 22050: iir_cf = iir_cf10_22k_22050;
+ *bands = 10;
+ break;
+ case 48000:
+ switch(*bands)
+ {
+ case 31: iir_cf = iir_cf31_48000; break;
+ case 25: iir_cf = iir_cf25_48000; break;
+ case 15: iir_cf = iir_cf15_48000; break;
+ default:
+ /*iir_cf = use_xmms_original_freqs ?
+ iir_cforiginal10_48000 :
+ iir_cf10_48000;*/
+ iir_cf = iir_cforiginal10_48000;
+ break;
+ }
+ break;
+ default:
+ switch(*bands)
+ {
+ case 31: iir_cf = iir_cf31_44100; break;
+ case 25: iir_cf = iir_cf25_44100; break;
+ case 15: iir_cf = iir_cf15_44100; break;
+ default:
+ /*iir_cf = use_xmms_original_freqs ?
+ iir_cforiginal10_44100 :
+ iir_cf10_44100;*/
+ iir_cf = iir_cforiginal10_44100;
+ break;
+ }
+ break;
+ }
+ return iir_cf;
+}
+
+/* Get the freqs at both sides of F0. These will be cut at -3dB */
+static void find_f1_and_f2(double f0, double octave_percent, double *f1, double *f2)
+{
+ double octave_factor = pow(2.0, octave_percent/2.0);
+ *f1 = f0/octave_factor;
+ *f2 = f0*octave_factor;
+}
+
+/* Find the quadratic root
+ * Always return the smallest root */
+static int find_root(double a, double b, double c, double *x0) {
+ double k = c-((b*b)/(4.*a));
+ double h = -(b/(2.*a));
+ double x1 = 0.;
+ if (-(k/a) < 0.)
+ return -1;
+ *x0 = h - sqrt(-(k/a));
+ x1 = h + sqrt(-(k/a));
+ if (x1 < *x0)
+ *x0 = x1;
+ return 0;
+}
+
+/* Calculate all the coefficients as specified in the bands[] array */
+void calc_coeffs()
+{
+ int i, n;
+ double f1, f2;
+ double x0;
+
+ n = 0;
+ for (; bands[n].cfs; n++) {
+ double *freqs = (double *)bands[n].cfs;
+ for (i=0; i<bands[n].band_count; i++)
+ {
+
+ /* Find -3dB frequencies for the center freq */
+ find_f1_and_f2(freqs[i], bands[n].octave, &f1, &f2);
+ /* Find Beta */
+ if ( find_root(
+ BETA2(TETA(freqs[i]), TETA(f1)),
+ BETA1(TETA(freqs[i]), TETA(f1)),
+ BETA0(TETA(freqs[i]), TETA(f1)),
+ &x0) == 0)
+ {
+ /* Got a solution, now calculate the rest of the factors */
+ /* Take the smallest root always (find_root returns the smallest one)
+ *
+ * NOTE: The IIR equation is
+ * y[n] = 2 * (alpha*(x[n]-x[n-2]) + gamma*y[n-1] - beta*y[n-2])
+ * Now the 2 factor has been distributed in the coefficients
+ */
+ /* Now store the coefficients */
+ bands[n].coeffs[i].beta = 2.0 * x0;
+ bands[n].coeffs[i].alpha = 2.0 * ALPHA(x0);
+ bands[n].coeffs[i].gamma = 2.0 * GAMMA(x0, TETA(freqs[i]));
+#ifdef DEBUG
+ printf("Freq[%d]: %f. Beta: %.10e Alpha: %.10e Gamma %.10e\n",
+ i, freqs[i], bands[n].coeffs[i].beta,
+ bands[n].coeffs[i].alpha, bands[n].coeffs[i].gamma);
+#endif
+ } else {
+ /* Shouldn't happen */
+ bands[n].coeffs[i].beta = 0.;
+ bands[n].coeffs[i].alpha = 0.;
+ bands[n].coeffs[i].gamma = 0.;
+ printf(" **** Where are the roots?\n");
+ }
+ }// for i
+ }//for n
+}
diff --git a/lib/equ/iir_cfs.h b/lib/equ/iir_cfs.h
new file mode 100644
index 000000000..c4cc4a0fd
--- /dev/null
+++ b/lib/equ/iir_cfs.h
@@ -0,0 +1,39 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir_cfs.h,v 1.1 2005/10/17 01:57:59 liebremx Exp $
+ */
+#ifndef IIR_CFS_H
+#define IIR_CFS_H
+
+//#include <glib.h>
+
+/* Coefficients entry */
+typedef struct
+{
+ float beta;
+ float alpha;
+ float gamma;
+ float dummy; // Word alignment
+}sIIRCoefficients;
+
+sIIRCoefficients* get_coeffs(int *bands, int sfreq); //, bool use_xmms_original_freqs);
+void calc_coeffs();
+
+#endif
diff --git a/lib/equ/iir_fpu.c b/lib/equ/iir_fpu.c
new file mode 100644
index 000000000..ae0051fdf
--- /dev/null
+++ b/lib/equ/iir_fpu.c
@@ -0,0 +1,210 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir_fpu.c,v 1.3 2005/11/13 20:02:58 lisanet Exp $
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+//#include <glib.h>
+#include "iir.h"
+#include "iir_fpu.h"
+
+static sXYData data_history[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned));
+static sXYData data_history2[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned));
+float gain[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned));
+/* random noise */
+sample_t dither[256];
+int di;
+
+void set_gain(int index, int chn, float val)
+{
+ gain[index][chn] = val;
+}
+
+void clean_history()
+{
+ int n;
+ /* Zero the history arrays */
+ bzero(data_history, sizeof(sXYData) * EQ_MAX_BANDS * EQ_CHANNELS);
+ bzero(data_history2, sizeof(sXYData) * EQ_MAX_BANDS * EQ_CHANNELS);
+ /* this is only needed if we use fpu code and there's no other place for
+ the moment to init the dither array*/
+ for (n = 0; n < 256; n++) {
+ dither[n] = (rand() % 4) - 2;
+ }
+ di = 0;
+}
+
+__inline__ int iir(void * d, int length, int nch)
+{
+/* FTZ_ON; */
+ short *data = (short *) d;
+ /* Indexes for the history arrays
+ * These have to be kept between calls to this function
+ * hence they are static */
+ static int i = 2, j = 1, k = 0;
+
+ int index, band, channel;
+ int tempgint, halflength;
+ sample_t out[EQ_CHANNELS], pcm[EQ_CHANNELS];
+
+#if 0
+ /* Load the correct filter table according to the sampling rate if needed */
+ if (srate != rate)
+ {
+ band_count = eqcfg.band_num;
+ rate = srate;
+ iir_cf = get_coeffs(&band_count, rate, eqcfg.use_xmms_original_freqs);
+ clean_history();
+ }
+#endif
+
+#ifdef BENCHMARK
+ start_counter();
+#endif /* BENCHMARK */
+
+ /**
+ * IIR filter equation is
+ * y[n] = 2 * (alpha*(x[n]-x[n-2]) + gamma*y[n-1] - beta*y[n-2])
+ *
+ * NOTE: The 2 factor was introduced in the coefficients to save
+ * a multiplication
+ *
+ * This algorithm cascades two filters to get nice filtering
+ * at the expense of extra CPU cycles
+ */
+ /* 16bit, 2 bytes per sample, so divide by two the length of
+ * the buffer (length is in bytes)
+ */
+ halflength = (length >> 1);
+ for (index = 0; index < halflength; index+=nch)
+ {
+ /* For each channel */
+ for (channel = 0; channel < nch; channel++)
+ {
+ pcm[channel] = data[index+channel] * 4;
+ /* Preamp gain */
+ pcm[channel] *= (preamp[channel] / 2);
+
+ /* add random noise */
+ pcm[channel] += dither[di];
+
+ out[channel] = 0.0;
+ /* For each band */
+ for (band = 0; band < band_count; band++)
+ {
+ /* Store Xi(n) */
+ data_history[band][channel].x[i] = pcm[channel];
+ /* Calculate and store Yi(n) */
+ data_history[band][channel].y[i] =
+ (
+ /* = alpha * [x(n)-x(n-2)] */
+ iir_cf[band].alpha * ( data_history[band][channel].x[i]
+ - data_history[band][channel].x[k])
+ /* + gamma * y(n-1) */
+ + iir_cf[band].gamma * data_history[band][channel].y[j]
+ /* - beta * y(n-2) */
+ - iir_cf[band].beta * data_history[band][channel].y[k]
+ );
+ /*
+ * The multiplication by 2.0 was 'moved' into the coefficients to save
+ * CPU cycles here */
+ /* Apply the gain */
+ out[channel] += data_history[band][channel].y[i]*gain[band][channel]; /* * 2.0; */
+ } /* For each band */
+
+ //if (cfg.eq_extra_filtering)
+ {
+ /* Filter the sample again */
+ for (band = 0; band < band_count; band++)
+ {
+ /* Store Xi(n) */
+ data_history2[band][channel].x[i] = out[channel];
+ /* Calculate and store Yi(n) */
+ data_history2[band][channel].y[i] =
+ (
+ /* y(n) = alpha * [x(n)-x(n-2)] */
+ iir_cf[band].alpha * (data_history2[band][channel].x[i]
+ - data_history2[band][channel].x[k])
+ /* + gamma * y(n-1) */
+ + iir_cf[band].gamma * data_history2[band][channel].y[j]
+ /* - beta * y(n-2) */
+ - iir_cf[band].beta * data_history2[band][channel].y[k]
+ );
+ /* Apply the gain */
+ out[channel] += data_history2[band][channel].y[i]*gain[band][channel];
+ } /* For each band */
+ }
+
+ /* Volume stuff
+ Scale down original PCM sample and add it to the filters
+ output. This substitutes the multiplication by 0.25
+ Go back to use the floating point multiplication before the
+ conversion to give more dynamic range
+ */
+ out[channel] += pcm[channel]*0.25;
+
+ /* remove random noise */
+ out[channel] -= dither[di]*0.25;
+
+ /* Round and convert to integer */
+#ifdef ARCH_PPC
+ tempgint = round_ppc(out[channel]);
+#else
+#ifdef ARCH_X86
+ tempgint = round_trick(out[channel]);
+#else
+ tempgint = (int)out[channel];
+#endif
+#endif
+
+ /* Limit the output */
+ if (tempgint < -32768)
+ data[index+channel] = -32768;
+ else if (tempgint > 32767)
+ data[index+channel] = 32767;
+ else
+ data[index+channel] = tempgint;
+ } /* For each channel */
+
+ /* Wrap around the indexes */
+ i = (i+1)%3;
+ j = (j+1)%3;
+ k = (k+1)%3;
+ /* random noise index */
+ di = (di + 1) % 256;
+
+ }/* For each pair of samples */
+
+#ifdef BENCHMARK
+ timex += get_counter();
+ blength += length;
+ if (count++ == 1024)
+ {
+ printf("FLOATING POINT: %f %d\n",timex/1024.0, blength/1024);
+ blength = 0;
+ timex = 0.;
+ count = 0;
+ }
+#endif /* BENCHMARK */
+
+/* FTZ_OFF; */
+ return length;
+}
diff --git a/lib/equ/iir_fpu.h b/lib/equ/iir_fpu.h
new file mode 100644
index 000000000..990eebf97
--- /dev/null
+++ b/lib/equ/iir_fpu.h
@@ -0,0 +1,39 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir_fpu.h,v 1.2 2005/11/01 15:59:20 lisanet Exp $$
+ */
+#ifndef IIR_FPU_H
+#define IIR_FPU_H
+
+#define sample_t double
+
+/*
+ * Normal FPU implementation data structures
+ */
+/* Coefficient history for the IIR filter */
+typedef struct
+{
+ sample_t x[3]; /* x[n], x[n-1], x[n-2] */
+ sample_t y[3]; /* y[n], y[n-1], y[n-2] */
+ sample_t dummy1; // Word alignment
+ sample_t dummy2;
+}sXYData;
+
+#endif
diff --git a/lib/fileinfo.cpp b/lib/fileinfo.cpp
new file mode 100644
index 000000000..fb4b051ec
--- /dev/null
+++ b/lib/fileinfo.cpp
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2006 by for_kotov *
+ * for_kotov@mail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "fileinfo.h"
+
+FileInfo::FileInfo(const QString &path, const QString &name, const int &time)
+{
+ m_path = path;
+ m_name = name;
+ m_length = time;
+}
+
+
+FileInfo::~FileInfo()
+{
+ //qDebug("FileInfo deleted");
+}
+
+const QString FileInfo::path()
+{
+ return m_path;
+}
+
+const QString FileInfo::name()
+{
+ return m_name;
+}
+
+const int FileInfo::length()
+{
+ return m_length;
+}
diff --git a/lib/fileinfo.h b/lib/fileinfo.h
new file mode 100644
index 000000000..3135b5584
--- /dev/null
+++ b/lib/fileinfo.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2006 by for_kotov *
+ * for_kotov@mail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef FILEINFO_H
+#define FILEINFO_H
+
+#include <QString>
+/**
+ @author for_kotov <for_kotov@mail.ru>
+*/
+
+
+class FileInfo
+{
+public:
+ FileInfo() {};
+ FileInfo(const QString &path, const QString &name, const int &time);
+
+ ~FileInfo();
+ //FileInfo operator=(const FileInfo &other);
+
+ const QString path();
+ const QString name();
+ const int length();
+
+private:
+ QString m_path, m_name;
+ int m_length;
+
+};
+
+#endif
diff --git a/lib/filetag.h b/lib/filetag.h
new file mode 100644
index 000000000..80383c2ab
--- /dev/null
+++ b/lib/filetag.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef FILETAG_H
+#define FILETAG_H
+
+
+#include <QString>
+
+class FileTag
+{
+public:
+ FileTag() {};
+
+ virtual ~FileTag() {};
+ virtual QString title () = 0;
+ virtual QString artist () = 0;
+ virtual QString album () = 0;
+ virtual QString comment () = 0 ;
+ virtual QString genre () = 0;
+ virtual uint year () = 0;
+ virtual uint track () = 0;
+ virtual uint length () = 0;
+ virtual bool isEmpty () = 0;
+
+};
+
+#endif
diff --git a/lib/iir.c b/lib/iir.c
new file mode 100644
index 000000000..317fce8e4
--- /dev/null
+++ b/lib/iir.c
@@ -0,0 +1,85 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir.c,v 1.15 2005/10/17 01:57:59 liebremx Exp $
+ */
+
+#include <math.h>
+#include "iir.h"
+
+/* Coefficients */
+sIIRCoefficients *iir_cf;
+
+/* Volume gain
+ * values should be between 0.0 and 1.0
+ * Use the preamp from XMMS for now
+ * */
+float preamp[EQ_CHANNELS];
+
+#ifdef BENCHMARK
+#include "benchmark.h"
+double timex = 0.0;
+int count = 0;
+unsigned int blength = 0;
+#endif
+
+/*
+ * Global vars
+ */
+int rate;
+int band_count;
+
+void set_preamp(int chn, float val)
+{
+ preamp[chn] = val;
+}
+
+/* Init the filters */
+void init_iir()
+{
+ calc_coeffs();
+#if 0
+ band_count = cfg.band_num;
+#endif
+
+ band_count = 10;
+
+ rate = 44100;
+
+ iir_cf = get_coeffs(&band_count, rate, TRUE);
+ clean_history();
+}
+
+#ifdef ARCH_X86
+/* Round function provided by Frank Klemm which saves around 100K
+ * CPU cycles in my PIII for each call to the IIR function with 4K samples
+ */
+__inline__ int round_trick(float floatvalue_to_round)
+{
+ float floattmp ;
+ int rounded_value ;
+
+ floattmp = (int) 0x00FD8000L + (floatvalue_to_round);
+ rounded_value = *(int*)(&floattmp) - (int)0x4B7D8000L;
+
+ if ( rounded_value != (short) rounded_value )
+ rounded_value = ( rounded_value >> 31 ) ^ 0x7FFF;
+ return rounded_value;
+}
+#endif
diff --git a/lib/iir.h b/lib/iir.h
new file mode 100644
index 000000000..de6fa04e5
--- /dev/null
+++ b/lib/iir.h
@@ -0,0 +1,84 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir.h,v 1.12 2005/10/17 01:57:59 liebremx Exp $
+ */
+#ifndef IIR_H
+#define IIR_H
+
+//#include <glib.h>
+//#include "main.h"
+#include "iir_cfs.h"
+
+/*
+ * Flush-to-zero to avoid flooding the CPU with underflow exceptions
+ */
+#ifdef SSE_MATH
+#define FTZ 0x8000
+#define FTZ_ON { \
+ unsigned int mxcsr; \
+ __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); \
+ mxcsr |= FTZ; \
+ __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); \
+}
+#define FTZ_OFF { \
+ unsigned int mxcsr; \
+ __asm__ __volatile__ ("stmxcsr %0" : "=m" (*&mxcsr)); \
+ mxcsr &= ~FTZ; \
+ __asm__ __volatile__ ("ldmxcsr %0" : : "m" (*&mxcsr)); \
+}
+#else
+#define FTZ_ON
+#define FTZ_OFF
+#endif
+
+/*
+ * Function prototypes
+ */
+void init_iir();
+void clean_history();
+void set_gain(int index, int chn, float val);
+void set_preamp(int chn, float val);
+
+
+__inline__ int iir(void *d, int length, int nch);
+
+#ifdef ARCH_X86
+__inline__ int round_trick(float floatvalue_to_round);
+#endif
+#ifdef ARCH_PPC
+__inline__ int round_ppc(float x);
+#endif
+
+#define EQ_CHANNELS 2
+#define EQ_MAX_BANDS 10
+
+extern float preamp[EQ_CHANNELS];
+extern sIIRCoefficients *iir_cf;
+extern int rate;
+extern int band_count;
+
+#ifdef BENCHMARK
+extern double timex;
+extern int count;
+extern unsigned int blength;
+#endif
+
+#endif /* #define IIR_H */
+
diff --git a/lib/iir_cfs.c b/lib/iir_cfs.c
new file mode 100644
index 000000000..88ecbadc6
--- /dev/null
+++ b/lib/iir_cfs.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *
+ * Coefficient stuff
+ *
+ * $Id: iir_cfs.c,v 1.1 2005/10/17 01:57:59 liebremx Exp $
+ */
+
+#include "iir_cfs.h"
+#include <stdio.h>
+#include <math.h>
+
+/***************************
+ * IIR filter coefficients *
+ ***************************/
+static sIIRCoefficients iir_cf10_11k_11025[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf10_22k_22050[10] __attribute__((aligned));
+static sIIRCoefficients iir_cforiginal10_44100[10] __attribute__((aligned));
+static sIIRCoefficients iir_cforiginal10_48000[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf10_44100[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf10_48000[10] __attribute__((aligned));
+static sIIRCoefficients iir_cf15_44100[15] __attribute__((aligned));
+static sIIRCoefficients iir_cf15_48000[15] __attribute__((aligned));
+static sIIRCoefficients iir_cf25_44100[25] __attribute__((aligned));
+static sIIRCoefficients iir_cf25_48000[25] __attribute__((aligned));
+static sIIRCoefficients iir_cf31_44100[31] __attribute__((aligned));
+static sIIRCoefficients iir_cf31_48000[31] __attribute__((aligned));
+
+/******************************************************************
+ * Definitions and data structures to calculate the coefficients
+ ******************************************************************/
+static const double band_f011k[] =
+{ 31, 62, 125, 250, 500, 1000, 2000, 3000, 4000, 5500
+};
+static const double band_f022k[] =
+{ 31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 11000
+};
+static const double band_f010[] =
+{ 31, 62, 125, 250, 500, 1000, 2000, 4000, 8000, 16000
+};
+static const double band_original_f010[] =
+{ 60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000
+};
+static const double band_f015[] =
+{ 25,40,63,100,160,250,400,630,1000,1600,2500,4000,6300,10000,16000
+};
+static const double band_f025[] =
+{ 20,31.5,40,50,80,100,125,160,250,315,400,500,800,
+ 1000,1250,1600,2500,3150,4000,5000,8000,10000,12500,16000,20000
+};
+static const double band_f031[] =
+{ 20,25,31.5,40,50,63,80,100,125,160,200,250,315,400,500,630,800,
+ 1000,1250,1600,2000,2500,3150,4000,5000,6300,8000,10000,12500,16000,20000
+};
+
+#define GAIN_F0 1.0
+#define GAIN_F1 GAIN_F0 / M_SQRT2
+
+#define SAMPLING_FREQ 44100.0
+#define TETA(f) (2*M_PI*(double)f/bands[n].sfreq)
+#define TWOPOWER(value) (value * value)
+
+#define BETA2(tf0, tf) \
+(TWOPOWER(GAIN_F1)*TWOPOWER(cos(tf0)) \
+ - 2.0 * TWOPOWER(GAIN_F1) * cos(tf) * cos(tf0) \
+ + TWOPOWER(GAIN_F1) \
+ - TWOPOWER(GAIN_F0) * TWOPOWER(sin(tf)))
+#define BETA1(tf0, tf) \
+ (2.0 * TWOPOWER(GAIN_F1) * TWOPOWER(cos(tf)) \
+ + TWOPOWER(GAIN_F1) * TWOPOWER(cos(tf0)) \
+ - 2.0 * TWOPOWER(GAIN_F1) * cos(tf) * cos(tf0) \
+ - TWOPOWER(GAIN_F1) + TWOPOWER(GAIN_F0) * TWOPOWER(sin(tf)))
+#define BETA0(tf0, tf) \
+ (0.25 * TWOPOWER(GAIN_F1) * TWOPOWER(cos(tf0)) \
+ - 0.5 * TWOPOWER(GAIN_F1) * cos(tf) * cos(tf0) \
+ + 0.25 * TWOPOWER(GAIN_F1) \
+ - 0.25 * TWOPOWER(GAIN_F0) * TWOPOWER(sin(tf)))
+
+#define GAMMA(beta, tf0) ((0.5 + beta) * cos(tf0))
+#define ALPHA(beta) ((0.5 - beta)/2.0)
+
+struct {
+ sIIRCoefficients *coeffs;
+ const double *cfs;
+ double octave;
+ int band_count;
+ double sfreq;
+} bands[] = {
+ { iir_cf10_11k_11025, band_f011k, 1.0, 10, 11025.0 },
+ { iir_cf10_22k_22050, band_f022k, 1.0, 10, 22050.0 },
+ { iir_cforiginal10_44100, band_original_f010, 1.0, 10, 44100.0 },
+ { iir_cforiginal10_48000, band_original_f010, 1.0, 10, 48000.0 },
+ { iir_cf10_44100, band_f010, 1.0, 10, 44100.0 },
+ { iir_cf10_48000, band_f010, 1.0, 10, 48000.0 },
+ { iir_cf15_44100, band_f015, 2.0/3.0, 15, 44100.0 },
+ { iir_cf15_48000, band_f015, 2.0/3.0, 15, 48000.0 },
+ { iir_cf25_44100, band_f025, 1.0/3.0, 25, 44100.0 },
+ { iir_cf25_48000, band_f025, 1.0/3.0, 25, 48000.0 },
+ { iir_cf31_44100, band_f031, 1.0/3.0, 31, 44100.0 },
+ { iir_cf31_48000, band_f031, 1.0/3.0, 31, 48000.0 },
+ { 0, 0, 0, 0, 0 }
+};
+
+/*************
+ * Functions *
+ *************/
+
+/* Get the coeffs for a given number of bands and sampling frequency */
+sIIRCoefficients* get_coeffs(gint *bands, gint sfreq, gboolean use_xmms_original_freqs)
+{
+ sIIRCoefficients *iir_cf = 0;
+ switch(sfreq)
+ {
+ case 11025: iir_cf = iir_cf10_11k_11025;
+ *bands = 10;
+ break;
+ case 22050: iir_cf = iir_cf10_22k_22050;
+ *bands = 10;
+ break;
+ case 48000:
+ switch(*bands)
+ {
+ case 31: iir_cf = iir_cf31_48000; break;
+ case 25: iir_cf = iir_cf25_48000; break;
+ case 15: iir_cf = iir_cf15_48000; break;
+ default:
+ iir_cf = use_xmms_original_freqs ?
+ iir_cforiginal10_48000 :
+ iir_cf10_48000;
+ break;
+ }
+ break;
+ default:
+ switch(*bands)
+ {
+ case 31: iir_cf = iir_cf31_44100; break;
+ case 25: iir_cf = iir_cf25_44100; break;
+ case 15: iir_cf = iir_cf15_44100; break;
+ default:
+ iir_cf = use_xmms_original_freqs ?
+ iir_cforiginal10_44100 :
+ iir_cf10_44100;
+ break;
+ }
+ break;
+ }
+ return iir_cf;
+}
+
+/* Get the freqs at both sides of F0. These will be cut at -3dB */
+static void find_f1_and_f2(double f0, double octave_percent, double *f1, double *f2)
+{
+ double octave_factor = pow(2.0, octave_percent/2.0);
+ *f1 = f0/octave_factor;
+ *f2 = f0*octave_factor;
+}
+
+/* Find the quadratic root
+ * Always return the smallest root */
+static int find_root(double a, double b, double c, double *x0) {
+ double k = c-((b*b)/(4.*a));
+ double h = -(b/(2.*a));
+ double x1 = 0.;
+ if (-(k/a) < 0.)
+ return -1;
+ *x0 = h - sqrt(-(k/a));
+ x1 = h + sqrt(-(k/a));
+ if (x1 < *x0)
+ *x0 = x1;
+ return 0;
+}
+
+/* Calculate all the coefficients as specified in the bands[] array */
+void calc_coeffs()
+{
+ int i, n;
+ double f1, f2;
+ double x0;
+
+ n = 0;
+ for (; bands[n].cfs; n++) {
+ double *freqs = (double *)bands[n].cfs;
+ for (i=0; i<bands[n].band_count; i++)
+ {
+
+ /* Find -3dB frequencies for the center freq */
+ find_f1_and_f2(freqs[i], bands[n].octave, &f1, &f2);
+ /* Find Beta */
+ if ( find_root(
+ BETA2(TETA(freqs[i]), TETA(f1)),
+ BETA1(TETA(freqs[i]), TETA(f1)),
+ BETA0(TETA(freqs[i]), TETA(f1)),
+ &x0) == 0)
+ {
+ /* Got a solution, now calculate the rest of the factors */
+ /* Take the smallest root always (find_root returns the smallest one)
+ *
+ * NOTE: The IIR equation is
+ * y[n] = 2 * (alpha*(x[n]-x[n-2]) + gamma*y[n-1] - beta*y[n-2])
+ * Now the 2 factor has been distributed in the coefficients
+ */
+ /* Now store the coefficients */
+ bands[n].coeffs[i].beta = 2.0 * x0;
+ bands[n].coeffs[i].alpha = 2.0 * ALPHA(x0);
+ bands[n].coeffs[i].gamma = 2.0 * GAMMA(x0, TETA(freqs[i]));
+#ifdef DEBUG
+ printf("Freq[%d]: %f. Beta: %.10e Alpha: %.10e Gamma %.10e\n",
+ i, freqs[i], bands[n].coeffs[i].beta,
+ bands[n].coeffs[i].alpha, bands[n].coeffs[i].gamma);
+#endif
+ } else {
+ /* Shouldn't happen */
+ bands[n].coeffs[i].beta = 0.;
+ bands[n].coeffs[i].alpha = 0.;
+ bands[n].coeffs[i].gamma = 0.;
+ printf(" **** Where are the roots?\n");
+ }
+ }// for i
+ }//for n
+}
diff --git a/lib/iir_cfs.h b/lib/iir_cfs.h
new file mode 100644
index 000000000..c5885283b
--- /dev/null
+++ b/lib/iir_cfs.h
@@ -0,0 +1,41 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir_cfs.h,v 1.1 2005/10/17 01:57:59 liebremx Exp $
+ */
+#ifndef IIR_CFS_H
+#define IIR_CFS_H
+
+#include <math.h>
+//#include <glib.h>
+
+/* Coefficients entry */
+typedef struct
+{
+ float beta;
+ float alpha;
+ float gamma;
+ float dummy; // Word alignment
+}sIIRCoefficients;
+
+
+sIIRCoefficients* get_coeffs(int *bands, int sfreq, bool use_xmms_original_freqs);
+void calc_coeffs();
+
+#endif
diff --git a/lib/iir_fpu.c b/lib/iir_fpu.c
new file mode 100644
index 000000000..47c97134d
--- /dev/null
+++ b/lib/iir_fpu.c
@@ -0,0 +1,210 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir_fpu.c,v 1.3 2005/11/13 20:02:58 lisanet Exp $
+ */
+
+#include <strings.h>
+#include <stdlib.h>
+#include <glib.h>
+#include "iir.h"
+#include "iir_fpu.h"
+
+static sXYData data_history[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned));
+static sXYData data_history2[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned));
+float gain[EQ_MAX_BANDS][EQ_CHANNELS] __attribute__((aligned));
+/* random noise */
+sample_t dither[256];
+gint di;
+
+void set_gain(gint index, gint chn, float val)
+{
+ gain[index][chn] = val;
+}
+
+void clean_history()
+{
+ gint n;
+ /* Zero the history arrays */
+ bzero(data_history, sizeof(sXYData) * EQ_MAX_BANDS * EQ_CHANNELS);
+ bzero(data_history2, sizeof(sXYData) * EQ_MAX_BANDS * EQ_CHANNELS);
+ /* this is only needed if we use fpu code and there's no other place for
+ the moment to init the dither array*/
+ for (n = 0; n < 256; n++) {
+ dither[n] = (rand() % 4) - 2;
+ }
+ di = 0;
+}
+
+__inline__ int iir(gpointer * d, gint length, gint nch)
+{
+// FTZ_ON;
+ gint16 *data = (gint16 *) * d;
+ /* Indexes for the history arrays
+ * These have to be kept between calls to this function
+ * hence they are static */
+ static gint i = 2, j = 1, k = 0;
+
+ gint index, band, channel;
+ gint tempgint, halflength;
+ sample_t out[EQ_CHANNELS], pcm[EQ_CHANNELS];
+
+#if 0
+ // Load the correct filter table according to the sampling rate if needed
+ if (srate != rate)
+ {
+ band_count = eqcfg.band_num;
+ rate = srate;
+ iir_cf = get_coeffs(&band_count, rate, eqcfg.use_xmms_original_freqs);
+ clean_history();
+ }
+#endif
+
+#ifdef BENCHMARK
+ start_counter();
+#endif //BENCHMARK
+
+ /**
+ * IIR filter equation is
+ * y[n] = 2 * (alpha*(x[n]-x[n-2]) + gamma*y[n-1] - beta*y[n-2])
+ *
+ * NOTE: The 2 factor was introduced in the coefficients to save
+ * a multiplication
+ *
+ * This algorithm cascades two filters to get nice filtering
+ * at the expense of extra CPU cycles
+ */
+ /* 16bit, 2 bytes per sample, so divide by two the length of
+ * the buffer (length is in bytes)
+ */
+ halflength = (length >> 1);
+ for (index = 0; index < halflength; index+=nch)
+ {
+ /* For each channel */
+ for (channel = 0; channel < nch; channel++)
+ {
+ pcm[channel] = data[index+channel] * 4;
+ /* Preamp gain */
+ pcm[channel] *= preamp[channel];
+
+ /* add random noise */
+ pcm[channel] += dither[di];
+
+ out[channel] = 0.;
+ /* For each band */
+ for (band = 0; band < band_count; band++)
+ {
+ /* Store Xi(n) */
+ data_history[band][channel].x[i] = pcm[channel];
+ /* Calculate and store Yi(n) */
+ data_history[band][channel].y[i] =
+ (
+ /* = alpha * [x(n)-x(n-2)] */
+ iir_cf[band].alpha * ( data_history[band][channel].x[i]
+ - data_history[band][channel].x[k])
+ /* + gamma * y(n-1) */
+ + iir_cf[band].gamma * data_history[band][channel].y[j]
+ /* - beta * y(n-2) */
+ - iir_cf[band].beta * data_history[band][channel].y[k]
+ );
+ /*
+ * The multiplication by 2.0 was 'moved' into the coefficients to save
+ * CPU cycles here */
+ /* Apply the gain */
+ out[channel] += data_history[band][channel].y[i]*gain[band][channel]; // * 2.0;
+ } /* For each band */
+
+ if (cfg.eq_extra_filtering)
+ {
+ /* Filter the sample again */
+ for (band = 0; band < band_count; band++)
+ {
+ /* Store Xi(n) */
+ data_history2[band][channel].x[i] = out[channel];
+ /* Calculate and store Yi(n) */
+ data_history2[band][channel].y[i] =
+ (
+ /* y(n) = alpha * [x(n)-x(n-2)] */
+ iir_cf[band].alpha * (data_history2[band][channel].x[i]
+ - data_history2[band][channel].x[k])
+ /* + gamma * y(n-1) */
+ + iir_cf[band].gamma * data_history2[band][channel].y[j]
+ /* - beta * y(n-2) */
+ - iir_cf[band].beta * data_history2[band][channel].y[k]
+ );
+ /* Apply the gain */
+ out[channel] += data_history2[band][channel].y[i]*gain[band][channel];
+ } /* For each band */
+ }
+
+ /* Volume stuff
+ Scale down original PCM sample and add it to the filters
+ output. This substitutes the multiplication by 0.25
+ Go back to use the floating point multiplication before the
+ conversion to give more dynamic range
+ */
+ out[channel] += pcm[channel]*0.25;
+
+ /* remove random noise */
+ out[channel] -= dither[di]*0.25;
+
+ /* Round and convert to integer */
+#ifdef ARCH_PPC
+ tempgint = round_ppc(out[channel]);
+#else
+#ifdef ARCH_X86
+ tempgint = round_trick(out[channel]);
+#else
+ tempgint = (int)out[channel];
+#endif
+#endif
+
+ /* Limit the output */
+ if (tempgint < -32768)
+ data[index+channel] = -32768;
+ else if (tempgint > 32767)
+ data[index+channel] = 32767;
+ else
+ data[index+channel] = tempgint;
+ } /* For each channel */
+
+ /* Wrap around the indexes */
+ i = (i+1)%3;
+ j = (j+1)%3;
+ k = (k+1)%3;
+ /* random noise index */
+ di = (di + 1) % 256;
+
+ }/* For each pair of samples */
+
+#ifdef BENCHMARK
+ timex += get_counter();
+ blength += length;
+ if (count++ == 1024)
+ {
+ printf("FLOATING POINT: %f %d\n",timex/1024.0, blength/1024);
+ blength = 0;
+ timex = 0.;
+ count = 0;
+ }
+#endif // BENCHMARK
+
+// FTZ_OFF;
+ return length;
+}
diff --git a/lib/iir_fpu.h b/lib/iir_fpu.h
new file mode 100644
index 000000000..990eebf97
--- /dev/null
+++ b/lib/iir_fpu.h
@@ -0,0 +1,39 @@
+/*
+ * PCM time-domain equalizer
+ *
+ * Copyright (C) 2002-2005 Felipe Rivera <liebremx at 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: iir_fpu.h,v 1.2 2005/11/01 15:59:20 lisanet Exp $$
+ */
+#ifndef IIR_FPU_H
+#define IIR_FPU_H
+
+#define sample_t double
+
+/*
+ * Normal FPU implementation data structures
+ */
+/* Coefficient history for the IIR filter */
+typedef struct
+{
+ sample_t x[3]; /* x[n], x[n-1], x[n-2] */
+ sample_t y[3]; /* y[n], y[n-1], y[n-2] */
+ sample_t dummy1; // Word alignment
+ sample_t dummy2;
+}sXYData;
+
+#endif
diff --git a/lib/lib.pro b/lib/lib.pro
new file mode 100644
index 000000000..723998b9f
--- /dev/null
+++ b/lib/lib.pro
@@ -0,0 +1,36 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./libs
+# ???? - ??????????: nnp
+
+HEADERS += recycler.h \
+ buffer.h \
+ constants.h \
+ decoder.h \
+ output.h \
+ filetag.h \
+ outputfactory.h \
+ equ\iir_cfs.h \
+ equ\iir_fpu.h \
+ equ\iir.h \
+ decoderfactory.h \
+ soundcore.h \
+ visualization.h
+SOURCES += recycler.cpp \
+ decoder.cpp \
+ output.cpp \
+ equ\iir.c \
+ equ\iir_cfs.c \
+ equ\iir_fpu.c \
+ soundcore.cpp
+TARGET = qmmp
+CONFIG += release \
+warn_on \
+qt \
+thread
+
+TEMPLATE = lib
+target.path = /lib
+INSTALLS += target
+
+
diff --git a/lib/output.cpp b/lib/output.cpp
new file mode 100644
index 000000000..0d0e4de99
--- /dev/null
+++ b/lib/output.cpp
@@ -0,0 +1,192 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#include <QtGui>
+#include <QObject>
+#include <QStringList>
+#include <QApplication>
+#include <QTimer>
+
+#include "output.h"
+#include "visualization.h"
+
+#include <stdio.h>
+
+// static methods
+
+static QList<OutputFactory*> *factories = 0;
+static QStringList files;
+static QTimer *timer = 0;
+
+static void checkFactories()
+{
+ if ( ! factories )
+ {
+ files.clear();
+ factories = new QList<OutputFactory *>;
+
+ QDir pluginsDir ( qApp->applicationDirPath() );
+ pluginsDir.cdUp();
+ pluginsDir.cd ( "lib/qmmp/Output" );
+ foreach ( QString fileName, pluginsDir.entryList ( QDir::Files ) )
+ {
+ QPluginLoader loader ( pluginsDir.absoluteFilePath ( fileName ) );
+ QObject *plugin = loader.instance();
+ if ( loader.isLoaded() )
+ {
+ qDebug ( "Output: plugin loaded - %s", qPrintable ( fileName ) );
+ }
+ OutputFactory *factory = 0;
+ if ( plugin )
+ factory = qobject_cast<OutputFactory *> ( plugin );
+
+ if ( factory )
+ {
+ Output::registerFactory ( factory );
+ files << pluginsDir.absoluteFilePath(fileName);
+ }
+ }
+ }
+}
+
+void Output::registerFactory ( OutputFactory *fact )
+{
+ factories->append ( fact );
+}
+
+Output *Output::create ( QObject *parent )
+{
+ Output *output = 0;
+
+ checkFactories();
+ if ( factories->isEmpty () )
+ {
+ qDebug("Output: unable to find output plugins");
+ return output;
+ }
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ QString pluginFileName =
+ settings.value("Output/plugin_file","libalsa.so").toString();
+ int j = 0;
+ for (int i = 0; i < factories->size(); ++i)
+ {
+ if (files.at(i).section('/',-1) == pluginFileName)
+ j = i;
+ }
+ OutputFactory *fact = factories->at ( j );
+ if ( fact )
+ {
+ output = fact->create ( parent );
+ }
+ switch ((int) output->volumeControl())
+ {
+ case Output::Standard:
+ {
+ break;
+ }
+ case Output::Custom:
+ {
+ timer = new QTimer(output);
+ connect(timer, SIGNAL(timeout()), output, SLOT(checkVolume()));
+ timer->start(125);
+ break;
+ }
+ case Output::Disabled:
+ {
+ break;
+ }
+ }
+ return output;
+}
+
+QList<OutputFactory*> *Output::outputFactories()
+{
+ checkFactories();
+ return factories;
+}
+
+QStringList Output::outputFiles()
+{
+ checkFactories();
+ return files;
+}
+
+Output::Output ( QObject* parent, VolumeType vt) : QThread (parent), r (stackSize())
+{
+ qRegisterMetaType<OutputState>("OutputState");
+ m_vol = vt;
+}
+
+
+Output::~Output()
+{}
+
+void Output::error ( const QString &e )
+{
+ emit stateChanged ( OutputState ( e ) );
+}
+
+
+void Output::addVisual ( Visualization *v )
+{
+ if ( visuals.indexOf ( v ) == -1 )
+ {
+ visuals.append ( v );
+ }
+}
+
+
+void Output::removeVisual ( Visualization *v )
+{
+ visuals.removeAll ( v );
+}
+
+void Output::dispatchVisual ( Buffer *buffer, unsigned long written,
+ int chan, int prec )
+{
+ if ( ! buffer || !visuals.size())
+ return;
+ Visualization *visual = 0;
+ foreach (visual , visuals );
+ {
+ visual->mutex()->lock ();
+ visual->add ( buffer, written, chan, prec );
+ visual->mutex()->unlock();
+ }
+}
+
+
+void Output::prepareVisuals()
+{
+ //Visual *visual = visuals.first();
+ /*while (visual) {
+ visual->mutex()->lock();
+ visual->prepare();
+ visual->mutex()->unlock();
+
+ visual = visuals.next();
+ }*/
+}
+
+void Output::dispatch(OutputState::Type st)
+{
+ emit stateChanged ( OutputState(st) );
+}
+
+void Output::dispatch(long s, unsigned long w, int b, int f, int p, int c)
+{
+ emit stateChanged ( OutputState(s, w, b, f, p, c) );
+}
+
+void Output::dispatch ( const OutputState &st )
+{
+ emit stateChanged ( st );
+}
+
+void Output::dispatchVolume(int L, int R)
+{
+ emit stateChanged ( OutputState(L, R) );
+}
diff --git a/lib/output.h b/lib/output.h
new file mode 100644
index 000000000..c5fa0941a
--- /dev/null
+++ b/lib/output.h
@@ -0,0 +1,156 @@
+
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __output_h
+#define __output_h
+
+class Output;
+
+#include <QObject>
+#include <QThread>
+#include <QEvent>
+#include <QList>
+#include <QIODevice>
+#include <outputfactory.h>
+
+#include "recycler.h"
+
+class QTimer;
+
+class Visualization;
+
+
+class OutputState
+{
+public:
+
+ enum Type { Playing, Buffering, Info, Paused, Stopped, Volume, Error };
+
+ OutputState()
+ : m_type(Stopped), m_error_msg(0), m_elasped_seconds(0),
+ m_written_bytes(0), m_brate(0), m_freq(0), m_prec(0), m_chan(0),
+ m_left(0), m_right(0)
+ {}
+
+ OutputState(Type t)
+ : m_type(t), m_error_msg(0), m_elasped_seconds(0),
+ m_written_bytes(0), m_brate(0), m_freq(0), m_prec(0), m_chan(0),
+ m_left(0), m_right(0)
+ {}
+ OutputState(long s, unsigned long w, int b, int f, int p, int c)
+ : m_type(Info), m_error_msg(0), m_elasped_seconds(s),
+ m_written_bytes(w), m_brate(b), m_freq(f), m_prec(p), m_chan(c),
+ m_left(0), m_right(0)
+ {}
+ OutputState(int L, int R)
+ : m_type(Volume), m_error_msg(0), m_elasped_seconds(0),
+ m_written_bytes(0), m_brate(0), m_freq(0), m_prec(0), m_chan(0),
+ m_left(L), m_right(R)
+ {}
+ OutputState(const QString &e)
+ : m_type(Error), m_elasped_seconds(0), m_written_bytes(0),
+ m_brate(0), m_freq(0), m_prec(0), m_chan(0),
+ m_left(0), m_right(0)
+ {
+ m_error_msg = new QString(e);
+ }
+ ~OutputState()
+ {
+ if (m_error_msg)
+ delete m_error_msg;
+ }
+
+ const QString *errorMessage() const { return m_error_msg; }
+
+ const long &elapsedSeconds() const { return m_elasped_seconds; }
+ const unsigned long &writtenBytes() const { return m_written_bytes; }
+ const int &bitrate() const { return m_brate; }
+ const int &frequency() const { return m_freq; }
+ const int &precision() const { return m_prec; }
+ const int &channels() const { return m_chan; }
+ const Type &type() const { return m_type; }
+ const int leftVolume() const { return m_left; }
+ const int rightVolume() const { return m_right; }
+
+private:
+ Type m_type;
+ QString *m_error_msg;
+ long m_elasped_seconds;
+ unsigned long m_written_bytes;
+ int m_brate, m_freq, m_prec, m_chan;
+ int m_left, m_right; //volume
+};
+
+
+
+
+class Output : public QThread
+{
+ Q_OBJECT
+public:
+
+ enum VolumeType { Standard, Custom, Disabled };
+
+ Output(QObject * parent = 0, VolumeType vt = Standard);
+ ~Output();
+
+ Recycler *recycler()
+ {
+ return &r;
+ }
+
+ void addVisual(Visualization *);
+ void removeVisual(Visualization *);
+
+ QMutex *mutex()
+ {
+ return &mtx;
+ }
+
+ VolumeType volumeControl(){return m_vol; };
+
+ // abstract
+ virtual bool isInitialized() const = 0;
+ virtual bool initialize() = 0;
+ virtual void uninitialize() = 0;
+ virtual void configure(long, int, int, int) = 0;
+ virtual void pause() = 0;
+ virtual void stop() = 0;
+ virtual long written() = 0;
+ virtual long latency() = 0;
+ virtual void seek(long) = 0;
+ virtual void setVolume(int, int) {};
+
+ static void registerFactory(OutputFactory *);
+ static Output *create(QObject *);
+ static QList<OutputFactory*> *outputFactories();
+ static QStringList outputFiles();
+
+public slots:
+ virtual void checkVolume() {};
+
+signals:
+ void stateChanged(const OutputState&);
+
+protected:
+ void dispatch(OutputState::Type);
+ void dispatch(long s, unsigned long w, int b, int f, int p, int c);
+ void dispatch(const OutputState&);
+ void dispatchVolume(int L, int R);
+ void error(const QString &e);
+ void dispatchVisual(Buffer *, unsigned long, int, int);
+ void prepareVisuals();
+
+private:
+ QMutex mtx;
+ Recycler r;
+ QList<Visualization *> visuals;
+ VolumeType m_vol;
+};
+
+
+#endif // __output_h
diff --git a/lib/outputfactory.h b/lib/outputfactory.h
new file mode 100644
index 000000000..a398cf2b5
--- /dev/null
+++ b/lib/outputfactory.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef OUTPUTFACTORY_H
+#define OUTPUTFACTORY_H
+
+class QObject;
+class QString;
+class QIODevice;
+class QWidget;
+class QTranslator;
+
+class Decoder;
+class Output;
+
+class OutputFactory
+{
+public:
+ virtual ~OutputFactory() {}
+ virtual const QString &name() const = 0;
+ virtual Output *create(QObject *) = 0;
+ virtual void showSettings(QWidget *parent) = 0;
+ virtual void showAbout(QWidget *parent) = 0;
+ virtual QTranslator *createTranslator(QObject *parent) = 0;
+};
+
+Q_DECLARE_INTERFACE(OutputFactory, "OutputFactory/1.0")
+
+#endif
diff --git a/lib/qmmp/CMakeLists.txt b/lib/qmmp/CMakeLists.txt
new file mode 100644
index 000000000..26ad9b3ef
--- /dev/null
+++ b/lib/qmmp/CMakeLists.txt
@@ -0,0 +1,3 @@
+add_subdirectory(Input)
+add_subdirectory(Output)
+
diff --git a/lib/qmmp/Input/CMakeLists.txt b/lib/qmmp/Input/CMakeLists.txt
new file mode 100644
index 000000000..31a303d86
--- /dev/null
+++ b/lib/qmmp/Input/CMakeLists.txt
@@ -0,0 +1,41 @@
+
+SET(USE_MAD TRUE CACHE BOOL "enable/disable mad plugin")
+SET(USE_FLAC TRUE CACHE BOOL "enable/disable flac plugin")
+SET(USE_VORBIS TRUE CACHE BOOL "enable/disable ogg vorbis plugin")
+SET(USE_FFMPEG TRUE CACHE BOOL "enable/disable ffmpeg plugin")
+SET(USE_MPC TRUE CACHE BOOL "enable/disable mpc plugin")
+
+IF(USE_MAD)
+MESSAGE( STATUS "MAD ON")
+add_subdirectory(mad)
+ELSE(USE_MAD)
+MESSAGE( STATUS "MAD OFF")
+ENDIF(USE_MAD)
+
+IF(USE_FLAC)
+MESSAGE( STATUS "FLAC ON")
+add_subdirectory(flac)
+ELSE(USE_FLAC)
+MESSAGE( STATUS "FLAC OFF")
+ENDIF(USE_FLAC)
+
+IF(USE_VORBIS)
+MESSAGE( STATUS "VORBIS ON")
+add_subdirectory(vorbis)
+ELSE(USE_VORBIS)
+MESSAGE( STATUS "VORBIS OFF")
+ENDIF(USE_VORBIS)
+
+IF(USE_FFMPEG)
+MESSAGE( STATUS "FFMPEG ON")
+add_subdirectory(ffmpeg)
+ELSE(USE_FFMPEG)
+MESSAGE( STATUS "FFMPEG OFF")
+ENDIF(USE_FFMPEG)
+
+IF(USE_MPC)
+MESSAGE( STATUS "MPC ON")
+add_subdirectory(mpc)
+ELSE(USE_MPC)
+MESSAGE( STATUS "MPC OFF")
+ENDIF(USE_MPC)
diff --git a/lib/qmmp/Input/Input.pro b/lib/qmmp/Input/Input.pro
new file mode 100644
index 000000000..794407c76
--- /dev/null
+++ b/lib/qmmp/Input/Input.pro
@@ -0,0 +1,26 @@
+include(../../../qmmp.pri)
+
+SUBDIRS += vorbis mad
+TEMPLATE = subdirs
+
+contains(CONFIG, MUSEPACK_PLUGIN){
+ SUBDIRS += mpc
+ message(***************************)
+ message(* Musepack plugin enabled *)
+ message(***************************)
+}
+
+contains(CONFIG, FLAC_PLUGIN){
+ SUBDIRS += flac
+ message(***********************)
+ message(* FLAC plugin enabled *)
+ message(***********************)
+}
+
+contains(CONFIG, FFMPEG_PLUGIN){
+ SUBDIRS += ffmpeg
+ message(*************************)
+ message(* FFMPEG plugin enabled *)
+ message(*************************)
+}
+
diff --git a/lib/qmmp/Input/ffmpeg/CMakeLists.txt b/lib/qmmp/Input/ffmpeg/CMakeLists.txt
new file mode 100644
index 000000000..bd0481a39
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/CMakeLists.txt
@@ -0,0 +1,96 @@
+project(libffmpeg)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+# fixes ffmpeg defines
+ADD_DEFINITIONS(-D__STDC_CONSTANT_MACROS)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+# libffmpeg and taglib
+PKGCONFIG(libavcodec LIBAVCODEC_INCLUDE_DIR LIBAVCODEC_LINK_DIR LIBAVCODEC_LINK_FLAGS LIBAVCODEC_CFLAGS)
+PKGCONFIG(libavformat LIBAVFORMAT_INCLUDE_DIR LIBAVFORMAT_LINK_DIR LIBAVFORMAT_LINK_FLAGS LIBAVFORMAT_CFLAGS)
+PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS)
+
+IF(NOT LIBAVCODEC_LINK_FLAGS)
+ SET(LIBAVCODEC_LINK_FLAGS -lavcodec)
+ENDIF(NOT LIBAVCODEC_LINK_FLAGS)
+
+IF(NOT LIBAVFORMAT_LINK_FLAGS)
+ SET(LIBAVFORMAT_LINK_FLAGS -lavformat)
+ENDIF(NOT LIBAVFORMAT_LINK_FLAGS)
+
+IF(NOT TAGLIB_LINK_FLAGS)
+ SET(TAGLIB_LINK_FLAGS -ltag)
+ SET(TAGLIB_INCLUDE_DIR /usr/include/taglib)
+ SET(TAGLIB_CFLAGS -I/usr/include/taglib)
+ENDIF(NOT TAGLIB_LINK_FLAGS)
+
+include_directories(${FLAC_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR})
+link_directories(${FLAC_LINK_DIR} ${TAGLIB_LINK_DIR})
+
+ADD_DEFINITIONS(${LIBAVCODEC_CFLAGS})
+ADD_DEFINITIONS(${LIBAVFORMAT_CFLAGS})
+ADD_DEFINITIONS(${TAGLIB_CFLAGS})
+
+
+SET(libffmpeg_SRCS
+ decoder_ffmpeg.cpp
+ decoderffmpegfactory.cpp
+ detailsdialog.cpp
+ tag.cpp
+)
+
+SET(libffmpeg_MOC_HDRS
+ tag.h
+ decoderffmpegfactory.h
+ decoder_ffmpeg.h
+ detailsdialog.h
+)
+
+SET(libffmpeg_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libffmpeg_RCC_SRCS ${libffmpeg_RCCS})
+
+QT4_WRAP_CPP(libffmpeg_MOC_SRCS ${libffmpeg_MOC_HDRS})
+
+# user interface
+
+
+SET(libffmpeg_UIS
+ detailsdialog.ui
+)
+
+QT4_WRAP_UI(libffmpeg_UIS_H ${libffmpeg_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(ffmpeg SHARED ${libffmpeg_SRCS} ${libffmpeg_MOC_SRCS} ${libffmpeg_UIS_H}
+ ${libffmpeg_RCC_SRCS})
+target_link_libraries(ffmpeg ${QT_LIBRARIES} -lqmmp ${LIBAVCODEC_LINK_FLAGS} ${LIBAVFORMAT_LINK_FLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS})
+install(TARGETS ffmpeg DESTINATION lib/qmmp/Input)
+
diff --git a/lib/qmmp/Input/ffmpeg/decoder_ffmpeg.cpp b/lib/qmmp/Input/ffmpeg/decoder_ffmpeg.cpp
new file mode 100644
index 000000000..832a6c11a
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/decoder_ffmpeg.cpp
@@ -0,0 +1,346 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QObject>
+#include <QFile>
+
+#include "constants.h"
+#include "buffer.h"
+#include "output.h"
+#include "recycler.h"
+
+#include "decoder_ffmpeg.h"
+
+// Decoder class
+
+DecoderFFmpeg::DecoderFFmpeg(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : Decoder(parent, d, i, o)
+{
+ inited = FALSE;
+ user_stop = FALSE;
+ stat = 0;
+ output_buf = 0;
+ output_bytes = 0;
+ output_at = 0;
+ bks = 0;
+ done = FALSE;
+ finish = FALSE;
+ len = 0;
+ freq = 0;
+ bitrate = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+ chan = 0;
+ output_size = 0;
+ ic = 0;
+ wma_outbuf = 0;
+
+
+
+
+}
+
+
+DecoderFFmpeg::~DecoderFFmpeg()
+{
+ deinit();
+ if (wma_outbuf)
+ {
+ delete [] wma_outbuf;
+ wma_outbuf = 0;
+ }
+ if (output_buf)
+ delete [] output_buf;
+ output_buf = 0;
+
+ if(ic)
+ av_close_input_file(ic);
+}
+
+
+void DecoderFFmpeg::stop()
+{
+ user_stop = TRUE;
+}
+
+
+void DecoderFFmpeg::flush(bool final)
+{
+ ulong min = final ? 0 : bks;
+
+ while ((! done && ! finish) && output_bytes > min)
+ {
+ output()->recycler()->mutex()->lock ();
+
+ while ((! done && ! finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+
+ mutex()->lock ();
+ done = user_stop;
+ }
+
+ if (user_stop || finish)
+ {
+ inited = FALSE;
+ done = TRUE;
+ }
+ else
+ {
+ output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan);
+ output_size += bks;
+ output_at = output_bytes;
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
+
+
+bool DecoderFFmpeg::initialize()
+{
+ bks = blockSize();
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+
+
+ if (! input())
+ {
+ error("DecoderFFmpeg: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input())
+ {
+ error("DecoderFFmpeg: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ QString filename = qobject_cast<QFile*>(input())->fileName ();
+ avcodec_init();
+ avcodec_register_all();
+ av_register_all();
+
+ AVCodec *codec;
+ if (av_open_input_file(&ic, filename.toLocal8Bit(), NULL,0, NULL) < 0)
+ {
+ qDebug("DecoderFFmpeg: cannot open input file");
+ return FALSE;
+ }
+ for (wma_idx = 0; wma_idx < ic->nb_streams; wma_idx++)
+ {
+ c = ic->streams[wma_idx]->codec;
+ if (c->codec_type == CODEC_TYPE_AUDIO) break;
+ }
+
+ av_find_stream_info(ic);
+
+ codec = avcodec_find_decoder(c->codec_id);
+
+ if (!codec) return FALSE;
+ if (avcodec_open(c, codec) < 0)
+ return FALSE;
+
+ totalTime = ic->duration/AV_TIME_BASE;
+
+ if (output())
+ output()->configure(c->sample_rate, c->channels, 16, c->bit_rate);
+
+ bitrate = c->bit_rate;
+ chan = c->channels;
+ wma_outbuf = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*sizeof(int16_t)];
+ inited = TRUE;
+ qDebug("DecoderFFmpeg: initialize succes");
+ return TRUE;
+}
+
+
+double DecoderFFmpeg::lengthInSeconds()
+{
+ if (! inited)
+ return 0;
+
+ return totalTime;
+}
+
+
+void DecoderFFmpeg::seek(double pos)
+{
+ seekTime = pos;
+}
+
+
+void DecoderFFmpeg::deinit()
+{
+ //FLAC__stream_decoder_finish (data()->decoder);
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+}
+
+void DecoderFFmpeg::run()
+{
+// mpc_uint32_t vbrAcc = 0;
+// mpc_uint32_t vbrUpd = 0;
+ uint8_t *inbuf_ptr;
+ int out_size, size, len;
+ AVPacket pkt;
+
+ mutex()->lock ();
+
+ if (! inited)
+ {
+ mutex()->unlock();
+
+ return;
+ }
+ stat = DecoderState::Decoding;
+ mutex()->unlock();
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ while (! done && ! finish)
+ {
+ mutex()->lock ();
+ // decode
+
+ if (seekTime >= 0.0)
+ {
+ int64_t timestamp;
+ timestamp = int64_t(seekTime)*AV_TIME_BASE;
+ if (ic->start_time != AV_NOPTS_VALUE)
+ timestamp += ic->start_time;
+ av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_ANY);
+ seekTime = -1.0;
+ }
+
+ int l = 0;
+ len = 0;
+ if (av_read_frame(ic, &pkt) < 0)
+ {
+ finish = TRUE;
+ goto end;
+ }
+ size = pkt.size;
+ inbuf_ptr = pkt.data;
+
+ len = 0;
+ out_size = 0;
+ while (size > 0)
+ {
+ //mutex()->lock();
+ l = avcodec_decode_audio(c, (int16_t *)(wma_outbuf), &out_size, inbuf_ptr, size);
+
+ ffmpeg_out(out_size);
+ size -= l;
+ inbuf_ptr += l;
+ if (pkt.data) av_free_packet(&pkt);
+ len = out_size;
+ }
+ bitrate = c->bit_rate/1024;
+end:
+ if (len == 0 || finish)
+ {
+ flush(TRUE);
+
+ if (output())
+ {
+ output()->recycler()->mutex()->lock ();
+ // end of stream
+ while (! output()->recycler()->empty() && ! user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+
+ done = TRUE;
+ if (! user_stop)
+ {
+ finish = TRUE;
+ }
+ }
+
+ mutex()->unlock();
+
+ }
+
+ mutex()->lock ();
+
+ if (finish)
+ stat = DecoderState::Finished;
+ else if (user_stop)
+ stat = DecoderState::Stopped;
+
+ mutex()->unlock();
+
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ deinit();
+}
+
+void DecoderFFmpeg::ffmpeg_out(int size)
+{
+ if (size == 0)
+ return;
+ int at = 0;
+ int to_copy = 0;
+ while (size > 0)
+ {
+ to_copy = qMin(int(globalBufferSize - output_at), int(size) );
+ memmove ( (char *) (output_buf + output_at), wma_outbuf + at, to_copy);
+ at += to_copy;
+ size -= to_copy;
+ output_at += to_copy;
+ output_bytes += to_copy;
+ if (output())
+ flush();
+ }
+}
diff --git a/lib/qmmp/Input/ffmpeg/decoder_ffmpeg.h b/lib/qmmp/Input/ffmpeg/decoder_ffmpeg.h
new file mode 100644
index 000000000..86a415a8d
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/decoder_ffmpeg.h
@@ -0,0 +1,78 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef __decoder_ffmeg_h
+#define __decoder_ffmeg_h
+
+extern "C"{
+#include <ffmpeg/avformat.h>
+#include <ffmpeg/avcodec.h>
+}
+#include "decoder.h"
+
+class DecoderFFmpeg : public Decoder
+{
+public:
+ DecoderFFmpeg(QObject *, DecoderFactory *, QIODevice *, Output *);
+ virtual ~DecoderFFmpeg();
+
+ // Standard Decoder API
+ bool initialize();
+ double lengthInSeconds();
+ void seek(double);
+ void stop();
+
+ // Equalizer
+ bool isEQSupported() const { return FALSE; }
+ void setEQEnabled(bool) { ; }
+ void setEQGain(int) { ; }
+ void setEQBands(int[10]) { ; }
+
+
+private:
+ // thread run function
+ void run();
+ // helper functions
+ void flush(bool = FALSE);
+ void deinit();
+ void ffmpeg_out(int size);
+
+ bool inited, user_stop;
+ int stat;
+
+ // output buffer
+ char *output_buf;
+ ulong output_bytes, output_at;
+
+ AVFormatContext *ic;
+ AVCodecContext *c;
+ int wma_st_buff, wma_idx, wma_idx2;
+ uint8_t *wma_outbuf;
+
+ unsigned int bks;
+ bool done, finish;
+ long len, freq, bitrate;
+ int chan;
+ unsigned long output_size;
+ double totalTime, seekTime;
+};
+
+
+#endif // __decoder_mpc_h
diff --git a/lib/qmmp/Input/ffmpeg/decoderffmpegfactory.cpp b/lib/qmmp/Input/ffmpeg/decoderffmpegfactory.cpp
new file mode 100644
index 000000000..0f4d4ed2b
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/decoderffmpegfactory.cpp
@@ -0,0 +1,75 @@
+#include <QtGui>
+
+#include "detailsdialog.h"
+#include "decoder_ffmpeg.h"
+#include "tag.h"
+#include "decoderffmpegfactory.h"
+
+
+// DecoderFFmpegFactory
+
+bool DecoderFFmpegFactory::supports(const QString &source) const
+{
+
+ return (source.right(4).toLower() == ".wma");
+}
+
+const QString &DecoderFFmpegFactory::name() const
+{
+ static QString name (tr("FFmpeg Plugin"));
+ return name;
+}
+
+
+const QString &DecoderFFmpegFactory::filter() const
+{
+ static QString filter("*.wma");
+ return filter;
+}
+
+
+const QString &DecoderFFmpegFactory::description() const
+{
+ static QString desc(tr("WMA Files"));
+ return desc;
+}
+
+
+Decoder *DecoderFFmpegFactory::create(QObject *parent, QIODevice *input,
+ Output *output)
+{
+ return new DecoderFFmpeg(parent, this, input, output);
+}
+
+FileTag *DecoderFFmpegFactory::createTag(const QString &source)
+{
+ FileTag *tag = new Tag(source);
+ return tag;
+}
+
+void DecoderFFmpegFactory::showDetails(QWidget *parent, const QString &path)
+{
+ DetailsDialog *d = new DetailsDialog(parent, path);
+ d -> show();
+}
+
+void DecoderFFmpegFactory::showSettings(QWidget *)
+{}
+
+void DecoderFFmpegFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About FFmpeg Audio Plugin"),
+ tr("Qmmp FFmpeg Audio Plugin")+"\n"+
+ tr("Suppored formats: WMA")+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>"));
+}
+
+QTranslator *DecoderFFmpegFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/ffmpeg_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(DecoderFFmpegFactory)
diff --git a/lib/qmmp/Input/ffmpeg/decoderffmpegfactory.h b/lib/qmmp/Input/ffmpeg/decoderffmpegfactory.h
new file mode 100644
index 000000000..6eeef53cd
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/decoderffmpegfactory.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DECODERFFMPEGFACTORY_H
+#define DECODERFFMPEGFACTORY_H
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <decoder.h>
+#include <output.h>
+#include <decoderfactory.h>
+#include <filetag.h>
+
+
+
+
+class DecoderFFmpegFactory : public QObject,
+ DecoderFactory
+{
+Q_OBJECT
+Q_INTERFACES(DecoderFactory);
+
+public:
+ bool supports(const QString &source) const;
+ const QString &name() const;
+ const QString &filter() const;
+ const QString &description() const;
+ Decoder *create(QObject *, QIODevice *, Output *);
+ FileTag *createTag(const QString &source);
+ void showDetails(QWidget *parent, const QString &path);
+ void showSettings(QWidget *parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+};
+
+#endif
diff --git a/lib/qmmp/Input/ffmpeg/detailsdialog.cpp b/lib/qmmp/Input/ffmpeg/detailsdialog.cpp
new file mode 100644
index 000000000..a4551872b
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/detailsdialog.cpp
@@ -0,0 +1,103 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+extern "C"
+{
+#include <ffmpeg/avformat.h>
+#include <ffmpeg/avcodec.h>
+}
+
+
+#include "detailsdialog.h"
+
+DetailsDialog::DetailsDialog(QWidget *parent, const QString &path)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ m_path = path;
+ setWindowTitle (path.section('/',-1));
+ path.section('/',-1);
+
+ ui.pathLineEdit->setText(m_path);
+ loadInfo();
+
+}
+
+
+DetailsDialog::~DetailsDialog()
+{}
+
+void DetailsDialog::loadInfo()
+{
+ AVFormatContext *in;
+ avcodec_init();
+ avcodec_register_all();
+ av_register_all();
+ if (av_open_input_file(&in, m_path.toLocal8Bit(), NULL,0, NULL) < 0)
+ return;
+ av_find_stream_info(in);
+ QString string = QString::fromUtf8(in->title).trimmed();
+ ui.titleLineEdit->setText(string);
+ string = QString::fromUtf8(in->author).trimmed();
+ ui.artistLineEdit->setText(string);
+ string = QString::fromUtf8(in->album).trimmed();
+ ui.albumLineEdit->setText(string);
+ string = QString::fromUtf8(in->comment).trimmed();
+ ui.commentLineEdit->setText(string);
+ string = QString("%1").arg(in->year);
+ ui.yearLineEdit->setText(string);
+ string = QString("%1").arg(in->track);
+ ui.trackLineEdit->setText(string);
+ string = QString::fromUtf8(in->genre).trimmed();
+ ui.genreLineEdit->setText(string);
+
+ QString text;
+ text = QString("%1").arg(int(in->duration/AV_TIME_BASE)/60);
+ text +=":"+QString("%1").arg(int(in->duration/AV_TIME_BASE)%60,2,10,QChar('0'));
+ ui.lengthLabel->setText(text);
+
+
+ text = QString("%1").arg(in->file_size/1024)+" "+tr("KB");
+ ui.fileSizeLabel->setText(text);
+ text = QString("%1").arg(in->bit_rate/1000);
+ ui.bitrateLabel->setText(text+" "+tr("kbps"));
+
+ AVCodecContext *c = 0;
+ int wma_idx;
+
+ for (wma_idx = 0; wma_idx < in->nb_streams; wma_idx++)
+ {
+ c = in->streams[wma_idx]->codec;
+ if (c->codec_type == CODEC_TYPE_AUDIO) break;
+ }
+
+ if (c)
+ {
+ text = QString("%1").arg(c->sample_rate);
+ ui.sampleRateLabel->setText(text+" "+tr("Hz"));
+ text = QString("%1").arg(c->channels);
+ ui.channelsLabel->setText(text);
+ }
+
+ av_close_input_file(in);
+}
+
+
diff --git a/lib/qmmp/Input/ffmpeg/detailsdialog.h b/lib/qmmp/Input/ffmpeg/detailsdialog.h
new file mode 100644
index 000000000..258a1bd21
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/detailsdialog.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DETAILSDIALOG_H
+#define DETAILSDIALOG_H
+
+#include <QDialog>
+
+#include "ui_detailsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class DetailsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ DetailsDialog(QWidget *parent = 0, const QString &path = 0);
+
+ ~DetailsDialog();
+
+private:
+ void loadInfo();
+ Ui::DetailsDialog ui;
+ QString m_path;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/ffmpeg/detailsdialog.ui b/lib/qmmp/Input/ffmpeg/detailsdialog.ui
new file mode 100644
index 000000000..70ed57052
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/detailsdialog.ui
@@ -0,0 +1,332 @@
+<ui version="4.0" >
+ <class>DetailsDialog</class>
+ <widget class="QDialog" name="DetailsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>449</width>
+ <height>375</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Details</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item rowspan="2" row="1" column="0" colspan="2" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="minimumSize" >
+ <size>
+ <width>175</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="title" >
+ <string>ASF Info</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="4" column="1" >
+ <widget class="QLabel" name="bitrateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QLabel" name="channelsLabel" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Bitrate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>File size:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_10" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>Channels:</string>
+ </property>
+ <property name="textFormat" >
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="sampleRateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Sample rate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="lengthLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Length:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="fileSizeLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>74</width>
+ <height>151</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="2" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>WMA Tag</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="1" colspan="2" >
+ <widget class="QPushButton" name="pushButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3" >
+ <widget class="QLineEdit" name="trackLineEdit" />
+ </item>
+ <item row="4" column="2" >
+ <widget class="QLabel" name="label_26" >
+ <property name="text" >
+ <string>Track number:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLineEdit" name="yearLineEdit" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_25" >
+ <property name="text" >
+ <string>Year:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_27" >
+ <property name="text" >
+ <string>Genre:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_24" >
+ <property name="text" >
+ <string>Comment:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_23" >
+ <property name="text" >
+ <string>Album:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_22" >
+ <property name="text" >
+ <string>Artist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_21" >
+ <property name="text" >
+ <string>Title:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="titleLineEdit" />
+ </item>
+ <item row="1" column="1" colspan="3" >
+ <widget class="QLineEdit" name="artistLineEdit" />
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QLineEdit" name="albumLineEdit" />
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="commentLineEdit" />
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLineEdit" name="genreLineEdit" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="3" >
+ <widget class="QPushButton" name="pushButton_3" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_28" >
+ <property name="text" >
+ <string>File path:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="pathLineEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>pushButton_3</sender>
+ <signal>clicked()</signal>
+ <receiver>DetailsDialog</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>623</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>539</x>
+ <y>352</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Input/ffmpeg/ffmpeg.pro b/lib/qmmp/Input/ffmpeg/ffmpeg.pro
new file mode 100644
index 000000000..e10985311
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/ffmpeg.pro
@@ -0,0 +1,25 @@
+FORMS += detailsdialog.ui
+HEADERS += decoderffmpegfactory.h \
+ tag.h \
+ detailsdialog.h \
+ decoder_ffmpeg.h
+SOURCES += tag.cpp \
+ detailsdialog.cpp \
+ decoder_ffmpeg.cpp \
+ decoderffmpegfactory.cpp
+DESTDIR = ../
+QMAKE_CLEAN = ../libffmpeg.so
+INCLUDEPATH += ../../../
+CONFIG += release \
+warn_on \
+plugin \
+link_pkgconfig
+TEMPLATE = lib
+QMAKE_LIBDIR += ../../../
+LIBS += -lqmmp -L/usr/lib -I/usr/include
+DEFINES += __STDC_CONSTANT_MACROS
+PKGCONFIG += libavcodec libavformat
+#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Input
+INSTALLS += target
diff --git a/lib/qmmp/Input/ffmpeg/tag.cpp b/lib/qmmp/Input/ffmpeg/tag.cpp
new file mode 100644
index 000000000..7a642de87
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/tag.cpp
@@ -0,0 +1,98 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "tag.h"
+
+Tag::Tag(const QString &source)
+ : FileTag()
+{
+ m_length = 0;
+ m_year = 0;
+ m_track = 0;
+ m_empty = TRUE;
+
+ avcodec_init();
+ avcodec_register_all();
+ av_register_all();
+
+ if (av_open_input_file(&m_in, source.toLocal8Bit(), NULL,0, NULL) < 0)
+ return;
+ av_find_stream_info(m_in);
+ m_album = QString::fromUtf8(m_in->album).trimmed();
+ m_artist = QString::fromUtf8(m_in->author).trimmed();
+ m_comment = QString::fromUtf8(m_in->comment).trimmed();
+ m_genre = QString::fromUtf8(m_in->genre).trimmed();
+ m_title = QString::fromUtf8(m_in->title).trimmed();
+ m_year = m_in->year;
+ m_track = m_in->track;
+ m_length = int(m_in->duration/AV_TIME_BASE);
+ m_empty = FALSE;
+
+ av_close_input_file(m_in);
+}
+
+
+Tag::~Tag()
+{}
+
+
+bool Tag::isEmpty()
+{
+ return m_empty;
+}
+
+QString Tag::album()
+{
+ return m_album;
+}
+
+QString Tag::artist()
+{
+ return m_artist;
+}
+
+QString Tag::comment()
+{
+ return m_comment;
+}
+
+QString Tag::genre()
+{
+ return m_genre;
+}
+
+QString Tag::title()
+{
+ return m_title;
+}
+
+uint Tag::length()
+{
+ return m_length;
+}
+
+uint Tag::track()
+{
+ return m_track;
+}
+
+uint Tag::year()
+{
+ return m_year;
+}
diff --git a/lib/qmmp/Input/ffmpeg/tag.h b/lib/qmmp/Input/ffmpeg/tag.h
new file mode 100644
index 000000000..df55ae24e
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/tag.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef XIPHCOMMENT_H
+#define XIPHCOMMENT_H
+
+extern "C"{
+#include <ffmpeg/avformat.h>
+#include <ffmpeg/avcodec.h>
+}
+
+#include <filetag.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Tag : public FileTag
+{
+public:
+ Tag(const QString &source);
+
+ ~Tag();
+
+ virtual QString album();
+ virtual QString artist();
+ virtual QString comment();
+ virtual QString genre();
+ virtual QString title();
+ virtual uint length();
+ virtual uint track();
+ virtual uint year();
+ virtual bool isEmpty();
+
+private:
+ //TagLib::Tag *m_tag;
+ AVFormatContext *m_in;
+ QString m_album;
+ QString m_artist;
+ QString m_comment;
+ QString m_genre;
+ QString m_title;
+ uint m_length;
+ uint m_year;
+ uint m_track;
+ bool m_empty;
+};
+
+#endif
diff --git a/lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.qm b/lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.qm
new file mode 100644
index 000000000..68ceb6f8b
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts b/lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts
new file mode 100644
index 000000000..e782e411c
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>DecoderFFmpegFactory</name>
+ <message>
+ <location filename="../decoderffmegfactory.cpp" line="19"/>
+ <source>FFmpeg Plugin</source>
+ <translation>Модуль FFmpeg</translation>
+ </message>
+ <message>
+ <location filename="../decoderffmegfactory.cpp" line="33"/>
+ <source>WMA Files</source>
+ <translation>Файлы WMA</translation>
+ </message>
+ <message>
+ <location filename="../decoderffmegfactory.cpp" line="61"/>
+ <source>About FFmpeg Audio Plugin</source>
+ <translation>Об аудио-модуле FFmpeg</translation>
+ </message>
+ <message>
+ <location filename="../decoderffmegfactory.cpp" line="62"/>
+ <source>Qmmp FFmpeg Audio Plugin</source>
+ <translation>Аудио-модуль FFmpeg для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../decoderffmegfactory.cpp" line="63"/>
+ <source>Suppored formats: WMA</source>
+ <translation>Поддерживаемые форматы: WMA</translation>
+ </message>
+ <message>
+ <location filename="../decoderffmegfactory.cpp" line="64"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>DetailsDialog</name>
+ <message>
+ <location filename="../detailsdialog.cpp" line="78"/>
+ <source>KB</source>
+ <translation>Кб</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="95"/>
+ <source>Hz</source>
+ <translation>Гц</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="31"/>
+ <source>ASF Info</source>
+ <translation>Информация ASF</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="130"/>
+ <source>-</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="60"/>
+ <source>Bitrate:</source>
+ <translation>Битовая частота:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="70"/>
+ <source>File size:</source>
+ <translation>Размер файла:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="83"/>
+ <source>Channels:</source>
+ <translation>Каналов:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="103"/>
+ <source>Sample rate:</source>
+ <translation>Дискретизация:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="120"/>
+ <source>Length:</source>
+ <translation>Длительность:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="161"/>
+ <source>WMA Tag</source>
+ <translation>WMA-тег</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="176"/>
+ <source>Save</source>
+ <translation>Сохранить</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="186"/>
+ <source>Track number:</source>
+ <translation>Номер трека:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="199"/>
+ <source>Year:</source>
+ <translation>Год:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="209"/>
+ <source>Genre:</source>
+ <translation>Жанр:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="219"/>
+ <source>Comment:</source>
+ <translation>Комментарий:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="229"/>
+ <source>Album:</source>
+ <translation>Альбом:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="239"/>
+ <source>Artist:</source>
+ <translation>Исполнитель:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="249"/>
+ <source>Title:</source>
+ <translation>Название:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="290"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="297"/>
+ <source>File path:</source>
+ <translation>Путь к файлу:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="81"/>
+ <source>kbps</source>
+ <translation>Кб/с</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="13"/>
+ <source>Details</source>
+ <translation>Информация</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Input/ffmpeg/translations/translations.qrc b/lib/qmmp/Input/ffmpeg/translations/translations.qrc
new file mode 100644
index 000000000..5e15f321d
--- /dev/null
+++ b/lib/qmmp/Input/ffmpeg/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>ffmpeg_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/Input/flac/CMakeLists.txt b/lib/qmmp/Input/flac/CMakeLists.txt
new file mode 100644
index 000000000..1efc7bb43
--- /dev/null
+++ b/lib/qmmp/Input/flac/CMakeLists.txt
@@ -0,0 +1,88 @@
+project(libflac)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+# libflac and taglib
+PKGCONFIG(flac FLAC_INCLUDE_DIR FLAC_LINK_DIR FLAC_LINK_FLAGS FLAC_CFLAGS)
+PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS)
+
+IF(NOT FLAC_LINK_FLAGS)
+ SET(FLAC_LINK_FLAGS -lFLAC)
+ENDIF(NOT FLAC_LINK_FLAGS)
+
+IF(NOT TAGLIB_LINK_FLAGS)
+ SET(TAGLIB_LINK_FLAGS -ltag)
+ SET(TAGLIB_INCLUDE_DIR /usr/include/taglib)
+ SET(TAGLIB_CFLAGS -I/usr/include/taglib)
+ENDIF(NOT TAGLIB_LINK_FLAGS)
+
+include_directories(${FLAC_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR})
+link_directories(${FLAC_LINK_DIR} ${TAGLIB_LINK_DIR})
+
+ADD_DEFINITIONS(${FLAC_CFLAGS})
+ADD_DEFINITIONS(${TAGLIB_CFLAGS})
+
+
+SET(libflac_SRCS
+ decoder_flac.cpp
+ decoderflacfactory.cpp
+ detailsdialog.cpp
+ tag.cpp
+)
+
+SET(libflac_MOC_HDRS
+ tag.h
+ decoderflacfactory.h
+ decoder_flac.h
+ detailsdialog.h
+)
+
+SET(libflac_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libflac_RCC_SRCS ${libflac_RCCS})
+
+QT4_WRAP_CPP(libflac_MOC_SRCS ${libflac_MOC_HDRS})
+
+# user interface
+
+
+SET(libflac_UIS
+ detailsdialog.ui
+)
+
+QT4_WRAP_UI(libflac_UIS_H ${libflac_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(flac SHARED ${libflac_SRCS} ${libflac_MOC_SRCS} ${libflac_UIS_H}
+ ${libflac_RCC_SRCS})
+target_link_libraries(flac ${QT_LIBRARIES} -lqmmp ${FLAC_LINK_FLAGS} ${FLAC_CFLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS})
+install(TARGETS flac DESTINATION lib/qmmp/Input)
+
diff --git a/lib/qmmp/Input/flac/decoder_flac.cpp b/lib/qmmp/Input/flac/decoder_flac.cpp
new file mode 100644
index 000000000..d4f3d6304
--- /dev/null
+++ b/lib/qmmp/Input/flac/decoder_flac.cpp
@@ -0,0 +1,567 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+
+#include "constants.h"
+#include "buffer.h"
+#include "output.h"
+#include "recycler.h"
+
+#include <qobject.h>
+#include <qiodevice.h>
+#include <FLAC/all.h>
+#include "decoder_flac.h"
+
+
+
+static size_t pack_pcm_signed (FLAC__byte *data,
+ const FLAC__int32 * const input[],
+ unsigned wide_samples,
+ unsigned channels, unsigned bps)
+{
+ FLAC__byte * const start = data;
+ FLAC__int32 sample;
+ const FLAC__int32 *input_;
+ unsigned samples, channel;
+ unsigned bytes_per_sample;
+ unsigned incr;
+
+ if (bps == 24)
+ bps = 32; /* we encode to 32-bit words */
+ bytes_per_sample = bps / 8;
+ incr = bytes_per_sample * channels;
+
+ for (channel = 0; channel < channels; channel++)
+ {
+ samples = wide_samples;
+ data = start + bytes_per_sample * channel;
+ input_ = input[channel];
+
+ while (samples--)
+ {
+ sample = *input_++;
+
+ switch (bps)
+ {
+ case 8:
+ data[0] = sample;
+ break;
+ case 16:
+ data[1] = (FLAC__byte)(sample >> 8);
+ data[0] = (FLAC__byte)sample;
+ break;
+ case 32:
+ data[3] = (FLAC__byte)(sample >> 16);
+ data[2] = (FLAC__byte)(sample >> 8);
+ data[1] = (FLAC__byte)sample;
+ data[0] = 0;
+ break;
+ }
+
+ data += incr;
+ }
+ }
+
+ return wide_samples * channels * bytes_per_sample;
+}
+
+static int flac_decode (void *void_data, char *buf, int buf_len) /*,
+ struct sound_params *sound_params)*/
+{
+ //struct flac_data *data = (struct flac_data *)void_data;
+ DecoderFLAC *dflac = (DecoderFLAC *) void_data;
+ unsigned to_copy;
+ int bytes_per_sample;
+ FLAC__uint64 decode_position;
+
+ bytes_per_sample = dflac->data()->bits_per_sample / 8;
+
+ /*switch (bytes_per_sample) {
+ case 1:
+ sound_params->fmt = SFMT_S8;
+ break;
+ case 2:
+ sound_params->fmt = SFMT_S16 | SFMT_LE;
+ break;
+ case 3:
+ sound_params->fmt = SFMT_S32 | SFMT_LE;
+ break;
+ }
+
+ sound_params->rate = data->sample_rate;
+ sound_params->channels = data->channels;*/
+
+ //decoder_error_clear (&data->error);
+
+ if (!dflac->data()->sample_buffer_fill)
+ {
+
+ if (FLAC__stream_decoder_get_state(dflac->data()->decoder)
+ == FLAC__STREAM_DECODER_END_OF_STREAM)
+ {
+ return 0;
+ }
+
+ if (!FLAC__stream_decoder_process_single(
+ dflac->data()->decoder))
+ {
+ return 0;
+ }
+
+ /* Count the bitrate */
+ if (!FLAC__stream_decoder_get_decode_position(
+ dflac->data()->decoder, &decode_position))
+ decode_position = 0;
+ if (decode_position > dflac->data()->last_decode_position)
+ {
+ int bytes_per_sec = bytes_per_sample * dflac->data()->sample_rate
+ * dflac->data()->channels;
+
+ dflac->data()->bitrate = int(((float)decode_position -
+ dflac->data()->last_decode_position) * 8.0 *
+ bytes_per_sec /
+ dflac->data()->sample_buffer_fill / 1000);
+ }
+
+ dflac->data()->last_decode_position = decode_position;
+ }
+
+ to_copy = qMin((unsigned)buf_len, dflac->data()->sample_buffer_fill);
+ memcpy (buf, dflac->data()->sample_buffer, to_copy);
+ memmove (dflac->data()->sample_buffer,
+ dflac->data()->sample_buffer + to_copy,
+ dflac->data()->sample_buffer_fill - to_copy);
+ dflac->data()->sample_buffer_fill -= to_copy;
+ return to_copy;
+}
+
+
+static FLAC__StreamDecoderReadStatus
+flac_callback_read (const FLAC__StreamDecoder *, FLAC__byte buffer[],
+ size_t *bytes, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ qint64 res;
+
+ res = dflac->input()->read((char *)buffer, *bytes);
+
+ if (res > 0)
+ {
+ *bytes = res;
+ return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ }
+ if (res == 0)
+ {
+ *bytes = res;
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }
+
+ return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+
+}
+
+static FLAC__StreamDecoderWriteStatus
+flac_callback_write (const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ const unsigned wide_samples = frame->header.blocksize;
+
+ if (dflac->data()->abort)
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+
+ dflac->data()->sample_buffer_fill = pack_pcm_signed (
+ dflac->data()->sample_buffer,
+ buffer, wide_samples,
+ dflac->data()->channels,
+ dflac->data()->bits_per_sample);
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+static FLAC__StreamDecoderTellStatus
+flac_callback_tell (const FLAC__StreamDecoder *, FLAC__uint64 *offset, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ *offset = dflac->input()->pos ();
+ return FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+static FLAC__StreamDecoderSeekStatus
+flac_callback_seek (const FLAC__StreamDecoder *, FLAC__uint64 offset, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+
+ return dflac->input()->seek(offset)
+ ? FLAC__STREAM_DECODER_SEEK_STATUS_OK
+ : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+}
+
+static FLAC__StreamDecoderLengthStatus
+flac_callback_length (const FLAC__StreamDecoder *, FLAC__uint64 *stream_length, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+ *stream_length = dflac->input()->size();
+ return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+static void
+flac_callback_metadata (const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data)
+{
+ DecoderFLAC *dflac = (DecoderFLAC *) client_data;
+
+ if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
+ {
+ qDebug ("DecoderFLAC: getting metadata info");
+
+ dflac->data()->total_samples =
+ (unsigned)(metadata->data.stream_info.total_samples
+ & 0xffffffff);
+ dflac->data()->bits_per_sample =
+ metadata->data.stream_info.bits_per_sample;
+ dflac->data()->channels = metadata->data.stream_info.channels;
+ dflac->data()->sample_rate = metadata->data.stream_info.sample_rate;
+ dflac->data()->length = dflac->data()->total_samples / dflac->data()->sample_rate;
+ }
+}
+
+static FLAC__bool
+flac_callback_eof (const FLAC__StreamDecoder *, void *)
+{
+ return FALSE;
+}
+
+static void
+flac_callback_error (const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *client_data)
+{}
+
+// Decoder class
+
+DecoderFLAC::DecoderFLAC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : Decoder(parent, d, i, o)
+{
+ inited = FALSE;
+ user_stop = FALSE;
+ stat = 0;
+ output_buf = 0;
+ output_bytes = 0;
+ output_at = 0;
+ bks = 0;
+ done = FALSE;
+ finish = FALSE;
+ len = 0;
+ freq = 0;
+ bitrate = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+ chan = 0;
+ output_size = 0;
+ m_data = 0;
+
+
+
+
+}
+
+
+DecoderFLAC::~DecoderFLAC()
+{
+ deinit();
+ if (data())
+ {
+ if (data()->decoder)
+ FLAC__stream_decoder_delete (data()->decoder);
+ delete data();
+ m_data = 0;
+ }
+
+ if (output_buf)
+ delete [] output_buf;
+ output_buf = 0;
+}
+
+
+void DecoderFLAC::stop()
+{
+ user_stop = TRUE;
+}
+
+
+void DecoderFLAC::flush(bool final)
+{
+ //qDebug("DecoderFLAC: flush()");
+ ulong min = final ? 0 : bks;
+
+ while ((! done && ! finish) && output_bytes > min)
+ {
+ output()->recycler()->mutex()->lock();
+
+ while ((! done && ! finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+
+ mutex()->lock();
+ done = user_stop;
+ }
+
+ if (user_stop || finish)
+ {
+ inited = FALSE;
+ done = TRUE;
+ }
+ else
+ {
+ output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan);
+ output_size += bks;
+ output_at = output_bytes;
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
+
+
+bool DecoderFLAC::initialize()
+{
+ bks = blockSize();
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+
+
+ if (! input())
+ {
+ error("DecoderFLAC: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input()->isOpen())
+ {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+
+ return FALSE;
+ }
+ }
+
+
+ if (! input())
+ {
+ error("DecoderFLAC: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input()->isOpen())
+ {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+ return FALSE;
+ }
+ }
+ if (!m_data)
+ {
+ m_data = new flac_data;
+ m_data->decoder = NULL;
+ }
+
+ m_data->bitrate = -1;
+ m_data->abort = 0;
+ m_data->sample_buffer_fill = 0;
+ m_data->last_decode_position = 0;
+ if (!m_data->decoder)
+ {
+ qDebug("DecoderFLAC: creating FLAC__StreamDecoder");
+ m_data->decoder = FLAC__stream_decoder_new ();
+ }
+ qDebug("DecoderFLAC: setting callbacks");
+ if (FLAC__stream_decoder_init_stream(
+ m_data->decoder,
+ flac_callback_read,
+ flac_callback_seek,
+ flac_callback_tell,
+ flac_callback_length,
+ flac_callback_eof,
+ flac_callback_write,
+ flac_callback_metadata,
+ flac_callback_error,
+ this) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
+ {
+ data()->ok = 0;
+ return FALSE;
+ }
+
+ if (!FLAC__stream_decoder_process_until_end_of_metadata(
+ data()->decoder))
+ {
+ data()->ok = 0;
+ return FALSE;
+ }
+ chan = data()->channels;
+ if (output())
+ output()->configure(data()->sample_rate, data()->channels, 16, bitrate);
+ totalTime = data()->length;
+
+ inited = TRUE;
+ qDebug("DecoderFLAC: initialize succes");
+ return TRUE;
+}
+
+
+double DecoderFLAC::lengthInSeconds()
+{
+ if (! inited)
+ return 0;
+
+ return totalTime;
+}
+
+
+void DecoderFLAC::seek(double pos)
+{
+ seekTime = pos;
+}
+
+
+void DecoderFLAC::deinit()
+{
+ FLAC__stream_decoder_finish (data()->decoder);
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+}
+
+void DecoderFLAC::run()
+{
+ mutex()->lock ();
+
+ if (! inited)
+ {
+ mutex()->unlock();
+
+ return;
+ }
+ stat = DecoderState::Decoding;
+ mutex()->unlock();
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ while (! done && ! finish)
+ {
+ mutex()->lock ();
+ // decode
+
+ if (seekTime >= 0.0)
+ {
+ FLAC__uint64 target_sample;
+
+ target_sample = (FLAC__uint64)((seekTime/(double)data()->length) *
+ (double)data()->total_samples);
+
+ FLAC__stream_decoder_seek_absolute(data()->decoder,
+ target_sample);
+ seekTime = -1.0;
+ }
+ len = flac_decode (this, (char *) (output_buf + output_at), bks);
+
+ if (len > 0)
+ {
+ bitrate = data()->bitrate;
+ output_at += len;
+ output_bytes += len;
+
+ if (output())
+ flush();
+
+ }
+ else if (len == 0)
+ {
+ flush(TRUE);
+
+ if (output())
+ {
+ output()->recycler()->mutex()->lock ();
+ // end of stream
+ while (! output()->recycler()->empty() && ! user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+
+ done = TRUE;
+ if (! user_stop)
+ {
+ finish = TRUE;
+ }
+ }
+ else
+ {
+ // error in read
+ error("DecoderFLAC: Error while decoding stream, File appears to be "
+ "corrupted");
+
+ finish = TRUE;
+ }
+
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+
+ if (finish)
+ stat = DecoderState::Finished;
+ else if (user_stop)
+ stat = DecoderState::Stopped;
+
+ mutex()->unlock();
+
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ deinit();
+}
diff --git a/lib/qmmp/Input/flac/decoder_flac.h b/lib/qmmp/Input/flac/decoder_flac.h
new file mode 100644
index 000000000..4c85176ed
--- /dev/null
+++ b/lib/qmmp/Input/flac/decoder_flac.h
@@ -0,0 +1,124 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef __decoder_flac_h
+#define __decoder_flac_h
+
+class DecoderOgg;
+
+#include "decoder.h"
+
+#include <FLAC/all.h>
+
+#define MAX_SUPPORTED_CHANNELS 2
+
+#define SAMPLES_PER_WRITE 512
+#define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * MAX_SUPPORTED_CHANNELS * (32/8))
+
+struct flac_data
+{
+ //FLAC__SeekableStreamDecoder *decoder;
+ FLAC__StreamDecoder *decoder;
+ struct io_stream *stream;
+ int bitrate;
+ int abort; /* abort playing (due to an error) */
+
+ unsigned length;
+ unsigned total_samples;
+
+ FLAC__byte sample_buffer[SAMPLE_BUFFER_SIZE];
+ unsigned sample_buffer_fill;
+
+ /* sound parameters */
+ unsigned bits_per_sample;
+ unsigned sample_rate;
+ unsigned channels;
+
+ FLAC__uint64 last_decode_position;
+
+ int ok; /* was this stream successfully opened? */
+ //struct decoder_error error;
+};
+
+class DecoderFLAC : public Decoder
+{
+public:
+ DecoderFLAC(QObject *, DecoderFactory *, QIODevice *, Output *);
+ virtual ~DecoderFLAC();
+
+ // Standard Decoder API
+ bool initialize();
+ double lengthInSeconds();
+ void seek(double);
+ void stop();
+
+ // Equalizer
+ bool isEQSupported() const
+ {
+ return FALSE;
+ }
+ void setEQEnabled(bool)
+ {
+ ;
+ }
+ void setEQGain(int)
+ {
+ ;
+ }
+ void setEQBands(int[10])
+ {
+ ;
+ }
+
+ struct flac_data *data()
+ {
+ return m_data;
+ }
+
+
+private:
+ // thread run function
+ void run();
+ struct flac_data *m_data;
+ // helper functions
+ void flush(bool = FALSE);
+ void deinit();
+
+ bool inited, user_stop;
+ int stat;
+
+ // output buffer
+ char *output_buf;
+ ulong output_bytes, output_at;
+
+ // FLAC Decoder
+ //FLAC__SeekableStreamDecoder *m_flacDecoder;
+ FLAC__StreamDecoder *m_flacDecoder;
+
+ unsigned int bks;
+ bool done, finish;
+ long len, freq, bitrate;
+ int chan;
+ unsigned long output_size;
+ double totalTime, seekTime;
+};
+
+
+#endif // __decoder_flac_h
diff --git a/lib/qmmp/Input/flac/decoderflacfactory.cpp b/lib/qmmp/Input/flac/decoderflacfactory.cpp
new file mode 100644
index 000000000..e8214924e
--- /dev/null
+++ b/lib/qmmp/Input/flac/decoderflacfactory.cpp
@@ -0,0 +1,76 @@
+#include <QtGui>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include "detailsdialog.h"
+#include "decoder_flac.h"
+#include "tag.h"
+#include "decoderflacfactory.h"
+
+
+// DecoderFLACFactory
+
+bool DecoderFLACFactory::supports(const QString &source) const
+{
+
+ return (source.right(5).toLower() == ".flac");
+}
+
+const QString &DecoderFLACFactory::name() const
+{
+ static QString name (tr("FLAC Plugin"));
+ return name;
+}
+
+
+const QString &DecoderFLACFactory::filter() const
+{
+ static QString filter("*.flac");
+ return filter;
+}
+
+
+const QString &DecoderFLACFactory::description() const
+{
+ static QString desc(tr("FLAC Files"));
+ return desc;
+}
+
+
+Decoder *DecoderFLACFactory::create(QObject *parent, QIODevice *input,
+ Output *output)
+{
+ return new DecoderFLAC(parent, this, input, output);
+}
+
+FileTag *DecoderFLACFactory::createTag(const QString &source)
+{
+ FileTag *tag = new Tag(source);
+ return tag;
+}
+
+void DecoderFLACFactory::showDetails(QWidget *parent, const QString &path)
+{
+ DetailsDialog *d = new DetailsDialog(parent, path);
+ d -> show();
+}
+
+void DecoderFLACFactory::showSettings(QWidget *)
+{}
+
+void DecoderFLACFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About FLAC Audio Plugin"),
+ tr("Qmmp FLAC Audio Plugin")+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>"));
+}
+
+QTranslator *DecoderFLACFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/flac_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(DecoderFLACFactory)
diff --git a/lib/qmmp/Input/flac/decoderflacfactory.h b/lib/qmmp/Input/flac/decoderflacfactory.h
new file mode 100644
index 000000000..ea5d62068
--- /dev/null
+++ b/lib/qmmp/Input/flac/decoderflacfactory.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DECODERFLACFACTORY_H
+#define DECODERFLACFACTORY_H
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <decoder.h>
+#include <output.h>
+#include <decoderfactory.h>
+#include <filetag.h>
+
+
+
+
+class DecoderFLACFactory : public QObject,
+ DecoderFactory
+{
+Q_OBJECT
+Q_INTERFACES(DecoderFactory);
+
+public:
+ bool supports(const QString &source) const;
+ const QString &name() const;
+ const QString &filter() const;
+ const QString &description() const;
+ Decoder *create(QObject *, QIODevice *, Output *);
+ FileTag *createTag(const QString &source);
+ void showDetails(QWidget *parent, const QString &path);
+ void showSettings(QWidget *parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+};
+
+#endif
diff --git a/lib/qmmp/Input/flac/decoderflacfactory_interface.h b/lib/qmmp/Input/flac/decoderflacfactory_interface.h
new file mode 100644
index 000000000..357b6a948
--- /dev/null
+++ b/lib/qmmp/Input/flac/decoderflacfactory_interface.h
@@ -0,0 +1,17 @@
+#ifndef __DECODERFLACFACTORY_INTERFACE_H
+#define __DECODERFLACFACTORY_INTERFACE_H
+
+class DecoderFLACFactoryInterface
+{
+public:
+ DecoderFLACFactoryInterface() {}
+ virtual ~DecoderFLACFactoryInterface() {}
+
+
+private:
+ DecoderFLACFactoryInterface( const DecoderFLACFactoryInterface& source );
+ void operator = ( const DecoderFLACFactoryInterface& source );
+};
+
+
+#endif // __DECODERFLACFACTORY_INTERFACE_H
diff --git a/lib/qmmp/Input/flac/detailsdialog.cpp b/lib/qmmp/Input/flac/detailsdialog.cpp
new file mode 100644
index 000000000..3834e7ba9
--- /dev/null
+++ b/lib/qmmp/Input/flac/detailsdialog.cpp
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/flacfile.h>
+
+#include "detailsdialog.h"
+
+DetailsDialog::DetailsDialog(QWidget *parent, const QString &path)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ m_path = path;
+ setWindowTitle (path.section('/',-1));
+ path.section('/',-1);
+ ui.pathLineEdit->setText(m_path);
+ loadFLACInfo();
+ loadTag();
+
+}
+
+
+DetailsDialog::~DetailsDialog()
+{}
+
+void DetailsDialog::loadFLACInfo()
+{
+ TagLib::FLAC::File f (m_path.toLocal8Bit());
+ //l.label
+ //ui. f.audioProperties()->level();
+ QString text;
+ text = QString("%1").arg(f.audioProperties()->length()/60);
+ text +=":"+QString("%1").arg(f.audioProperties()->length()%60,2,10,QChar('0'));
+ ui.lengthLabel->setText(text);
+ text = QString("%1").arg(f.audioProperties()->sampleRate());
+ ui.sampleRateLabel->setText(text+" "+tr("Hz"));
+ text = QString("%1").arg(f.audioProperties()->channels());
+ ui.channelsLabel->setText(text);
+ text = QString("%1").arg(f.audioProperties()->bitrate());
+ ui.bitrateLabel->setText(text+" "+tr("kbps"));
+ text = QString("%1").arg(f.audioProperties()->sampleWidth());
+ ui.sampleWidthLabel->setText(text+" "+tr("bits"));
+ text = QString("%1 "+tr("KB")).arg(f.length()/1024);
+ ui.fileSizeLabel->setText(text);
+
+}
+
+void DetailsDialog::loadTag()
+{
+ TagLib::FileRef f (m_path.toLocal8Bit());
+
+ if (f.tag())
+ { //TODO: load codec name from config
+
+ TagLib::String title = f.tag()->title();
+ TagLib::String artist = f.tag()->artist();
+ TagLib::String album = f.tag()->album();
+ TagLib::String comment = f.tag()->comment();
+ TagLib::String genre = f.tag()->genre();
+ QString string = QString::fromUtf8(title.toCString(TRUE)).trimmed();
+ ui.titleLineEdit->setText(string);
+ string = QString::fromUtf8(artist.toCString(TRUE)).trimmed();
+ ui.artistLineEdit->setText(string);
+ string = QString::fromUtf8(album.toCString(TRUE)).trimmed();
+ ui.albumLineEdit->setText(string);
+ string = QString::fromUtf8(comment.toCString(TRUE)).trimmed();
+ ui.commentLineEdit->setText(string);
+ string = QString("%1").arg(f.tag()->year());
+ ui.yearLineEdit->setText(string);
+ string = QString("%1").arg(f.tag()->track());
+ ui.trackLineEdit->setText(string);
+ string = QString::fromUtf8(genre.toCString(TRUE)).trimmed();
+ ui.genreLineEdit->setText(string);
+ }
+}
+
diff --git a/lib/qmmp/Input/flac/detailsdialog.h b/lib/qmmp/Input/flac/detailsdialog.h
new file mode 100644
index 000000000..5e5e57ad6
--- /dev/null
+++ b/lib/qmmp/Input/flac/detailsdialog.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DETAILSDIALOG_H
+#define DETAILSDIALOG_H
+
+#include <QDialog>
+
+#include "ui_detailsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class DetailsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ DetailsDialog(QWidget *parent = 0, const QString &path = 0);
+
+ ~DetailsDialog();
+
+private:
+ void loadFLACInfo();
+ void loadTag();
+ Ui::DetailsDialog ui;
+ QString m_path;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/flac/detailsdialog.ui b/lib/qmmp/Input/flac/detailsdialog.ui
new file mode 100644
index 000000000..6968c78be
--- /dev/null
+++ b/lib/qmmp/Input/flac/detailsdialog.ui
@@ -0,0 +1,349 @@
+<ui version="4.0" >
+ <class>DetailsDialog</class>
+ <widget class="QDialog" name="DetailsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>449</width>
+ <height>375</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Details</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item rowspan="2" row="1" column="0" colspan="2" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="minimumSize" >
+ <size>
+ <width>175</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="title" >
+ <string>FLAC Info</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>74</width>
+ <height>151</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1" colspan="2" >
+ <widget class="QLabel" name="fileSizeLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Length:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <widget class="QLabel" name="lengthLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Sample rate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="2" >
+ <widget class="QLabel" name="sampleRateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_10" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>Channels:</string>
+ </property>
+ <property name="textFormat" >
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>File size:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Bitrate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2" >
+ <widget class="QLabel" name="channelsLabel" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="2" >
+ <widget class="QLabel" name="bitrateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Sample width:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLabel" name="sampleWidthLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="pathLineEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_28" >
+ <property name="text" >
+ <string>File path:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3" >
+ <widget class="QPushButton" name="pushButton_3" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>FLAC Tag</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="1" colspan="2" >
+ <widget class="QPushButton" name="pushButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3" >
+ <widget class="QLineEdit" name="trackLineEdit" />
+ </item>
+ <item row="4" column="2" >
+ <widget class="QLabel" name="label_26" >
+ <property name="text" >
+ <string>Track number:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLineEdit" name="yearLineEdit" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_25" >
+ <property name="text" >
+ <string>Year:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_27" >
+ <property name="text" >
+ <string>Genre:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_24" >
+ <property name="text" >
+ <string>Comment:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_23" >
+ <property name="text" >
+ <string>Album:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_22" >
+ <property name="text" >
+ <string>Artist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_21" >
+ <property name="text" >
+ <string>Title:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="titleLineEdit" />
+ </item>
+ <item row="1" column="1" colspan="3" >
+ <widget class="QLineEdit" name="artistLineEdit" />
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QLineEdit" name="albumLineEdit" />
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="commentLineEdit" />
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLineEdit" name="genreLineEdit" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>pushButton_3</sender>
+ <signal>clicked()</signal>
+ <receiver>DetailsDialog</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>623</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>539</x>
+ <y>352</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Input/flac/flac.pro b/lib/qmmp/Input/flac/flac.pro
new file mode 100644
index 000000000..ec81ffd10
--- /dev/null
+++ b/lib/qmmp/Input/flac/flac.pro
@@ -0,0 +1,30 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Input/flac
+# ???? - ??????????:
+
+FORMS += detailsdialog.ui
+HEADERS += tag.h \
+ decoderflacfactory.h \
+ decoder_flac.h \
+ detailsdialog.h
+SOURCES += decoder_flac.cpp \
+ decoderflacfactory.cpp \
+ detailsdialog.cpp \
+ tag.cpp
+DESTDIR = ../
+QMAKE_CLEAN += ../libflac.so
+INCLUDEPATH += ../../../
+CONFIG += release \
+warn_on \
+plugin \
+link_pkgconfig
+TEMPLATE = lib
+QMAKE_LIBDIR += ../../../
+LIBS += -lqmmp -L/usr/lib -I/usr/include
+PKGCONFIG += taglib flac
+#TRANSLATIONS = translations/flac_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Input
+INSTALLS += target
+
diff --git a/lib/qmmp/Input/flac/tag.cpp b/lib/qmmp/Input/flac/tag.cpp
new file mode 100644
index 000000000..71feb9051
--- /dev/null
+++ b/lib/qmmp/Input/flac/tag.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "tag.h"
+
+Tag::Tag(const QString &source)
+ : FileTag()
+{
+ m_length = 0;
+ m_year = 0;
+ m_track = 0;
+ m_empty = TRUE;
+ TagLib::FileRef fileRef(source.toLocal8Bit ());
+
+ m_tag = fileRef.tag();
+ if(m_tag && !m_tag->isEmpty())
+ {
+ m_album = QString::fromUtf8(m_tag->album().toCString(TRUE)).trimmed();
+ m_artist = QString::fromUtf8(m_tag->artist().toCString(TRUE)).trimmed();
+ m_comment = QString::fromUtf8(m_tag->comment().toCString(TRUE)).trimmed();
+ m_genre = QString::fromUtf8(m_tag->genre().toCString(TRUE)).trimmed();
+ m_title = QString::fromUtf8(m_tag->title().toCString(TRUE)).trimmed();
+ m_year = 0;
+ m_track = 0;
+ if(fileRef.audioProperties())
+ m_length = fileRef.audioProperties()->length();
+ m_empty = FALSE;
+ }
+ else
+ m_tag = 0;
+}
+
+
+Tag::~Tag()
+{}
+
+
+bool Tag::isEmpty()
+{
+ return m_empty;
+}
+
+QString Tag::album()
+{
+ return m_album;
+}
+
+QString Tag::artist()
+{
+ return m_artist;
+}
+
+QString Tag::comment()
+{
+ return m_comment;
+}
+
+QString Tag::genre()
+{
+ return m_genre;
+}
+
+QString Tag::title()
+{
+ return m_title;
+}
+
+uint Tag::length()
+{
+ return m_length;
+}
+
+uint Tag::track()
+{
+ return m_track;
+}
+
+uint Tag::year()
+{
+ return m_year;
+}
diff --git a/lib/qmmp/Input/flac/tag.h b/lib/qmmp/Input/flac/tag.h
new file mode 100644
index 000000000..514f64d2a
--- /dev/null
+++ b/lib/qmmp/Input/flac/tag.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef TAG_H
+#define TAG_H
+
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include <filetag.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Tag : public FileTag
+{
+public:
+ Tag(const QString &source);
+
+ ~Tag();
+
+ virtual QString album();
+ virtual QString artist();
+ virtual QString comment();
+ virtual QString genre();
+ virtual QString title();
+ virtual uint length();
+ virtual uint track();
+ virtual uint year();
+ virtual bool isEmpty();
+
+private:
+ TagLib::Tag *m_tag;
+ QString m_album;
+ QString m_artist;
+ QString m_comment;
+ QString m_genre;
+ QString m_title;
+ uint m_length;
+ uint m_year;
+ uint m_track;
+ bool m_empty;
+};
+
+#endif
diff --git a/lib/qmmp/Input/flac/translations/flac_plugin_ru.qm b/lib/qmmp/Input/flac/translations/flac_plugin_ru.qm
new file mode 100644
index 000000000..c90963cf0
--- /dev/null
+++ b/lib/qmmp/Input/flac/translations/flac_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Input/flac/translations/flac_plugin_ru.ts b/lib/qmmp/Input/flac/translations/flac_plugin_ru.ts
new file mode 100644
index 000000000..8e70ef53d
--- /dev/null
+++ b/lib/qmmp/Input/flac/translations/flac_plugin_ru.ts
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>DecoderFLACFactory</name>
+ <message>
+ <location filename="../decoderflacfactory.cpp" line="21"/>
+ <source>FLAC Plugin</source>
+ <translation>Модуль FLAC</translation>
+ </message>
+ <message>
+ <location filename="../decoderflacfactory.cpp" line="35"/>
+ <source>FLAC Files</source>
+ <translation>Файлы FLAC</translation>
+ </message>
+ <message>
+ <location filename="../decoderflacfactory.cpp" line="63"/>
+ <source>About FLAC Audio Plugin</source>
+ <translation>Об аудио-модуле FLAC</translation>
+ </message>
+ <message>
+ <location filename="../decoderflacfactory.cpp" line="64"/>
+ <source>Qmmp FLAC Audio Plugin</source>
+ <translation>Аудио-модуль FLAC для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../decoderflacfactory.cpp" line="65"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>DetailsDialog</name>
+ <message>
+ <location filename="../detailsdialog.cpp" line="54"/>
+ <source>Hz</source>
+ <translation>Гц</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="60"/>
+ <source>bits</source>
+ <translation>бит</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="31"/>
+ <source>FLAC Info</source>
+ <translation>Информация FLAC</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="160"/>
+ <source>-</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="63"/>
+ <source>Length:</source>
+ <translation>Длительность:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="80"/>
+ <source>Sample rate:</source>
+ <translation>Частота сэмплов:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="100"/>
+ <source>Channels:</source>
+ <translation>Каналов:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="113"/>
+ <source>File size:</source>
+ <translation>Размер файла:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="123"/>
+ <source>Bitrate:</source>
+ <translation>Битовая частота:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="150"/>
+ <source>Sample width:</source>
+ <translation>Ширина кадра:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="177"/>
+ <source>File path:</source>
+ <translation>Путь к файлу:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="187"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="215"/>
+ <source>FLAC Tag</source>
+ <translation>FLAC-тег</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="230"/>
+ <source>Save</source>
+ <translation>Сохранить</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="240"/>
+ <source>Track number:</source>
+ <translation>Номер трека:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="253"/>
+ <source>Year:</source>
+ <translation>Год:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="263"/>
+ <source>Genre:</source>
+ <translation>Жанр:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="273"/>
+ <source>Comment:</source>
+ <translation>Комментарий:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="283"/>
+ <source>Album:</source>
+ <translation>Алюбом:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="293"/>
+ <source>Artist:</source>
+ <translation>Исполнитель:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="303"/>
+ <source>Title:</source>
+ <translation>Название:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="58"/>
+ <source>kbps</source>
+ <translation>Кб/с</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="61"/>
+ <source>KB</source>
+ <translation>Кб</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="13"/>
+ <source>Details</source>
+ <translation>Информация</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Input/flac/translations/translations.qrc b/lib/qmmp/Input/flac/translations/translations.qrc
new file mode 100644
index 000000000..cd630dfce
--- /dev/null
+++ b/lib/qmmp/Input/flac/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>flac_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/Input/mad/CMakeLists.txt b/lib/qmmp/Input/mad/CMakeLists.txt
new file mode 100644
index 000000000..7b280dff7
--- /dev/null
+++ b/lib/qmmp/Input/mad/CMakeLists.txt
@@ -0,0 +1,92 @@
+project(libmad)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+# libmad and taglib
+PKGCONFIG(mad MAD_INCLUDE_DIR MAD_LINK_DIR MAD_LINK_FLAGS MAD_CFLAGS)
+PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS)
+
+IF(NOT MAD_LINK_FLAGS)
+ MESSAGE("Can not find mad.pc")
+ SET(MAD_LINK_FLAGS -lmad)
+ENDIF(NOT MAD_LINK_FLAGS)
+
+IF(NOT TAGLIB_LINK_FLAGS)
+ SET(TAGLIB_LINK_FLAGS -ltag)
+ SET(TAGLIB_INCLUDE_DIR /usr/include/taglib)
+ SET(TAGLIB_CFLAGS -I/usr/include/taglib)
+ENDIF(NOT TAGLIB_LINK_FLAGS)
+
+include_directories(${MAD_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR})
+link_directories(${MAD_LINK_DIR} ${TAGLIB_LINK_DIR})
+
+#ADD_DEFINITIONS(${MAD_CFLAGS})
+ADD_DEFINITIONS(${TAGLIB_CFLAGS})
+
+
+SET(libmad_SRCS
+ decoder_mad.cpp
+ decodermadfactory.cpp
+ detailsdialog.cpp
+ id3tag.cpp
+ settingsdialog.cpp
+)
+
+SET(libmad_MOC_HDRS
+ settingsdialog.h
+ id3tag.h
+ decodermadfactory.h
+ decoder_mad.h
+ detailsdialog.h
+)
+
+SET(libmad_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libmad_RCC_SRCS ${libmad_RCCS})
+
+QT4_WRAP_CPP(libmad_MOC_SRCS ${libmad_MOC_HDRS})
+
+# user interface
+
+
+SET(libmad_UIS
+ detailsdialog.ui
+ settingsdialog.ui
+)
+
+QT4_WRAP_UI(libmad_UIS_H ${libmad_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(mad SHARED ${libmad_SRCS} ${libmad_MOC_SRCS} ${libmad_UIS_H}
+ ${libmad_RCC_SRCS})
+
+target_link_libraries(mad ${QT_LIBRARIES} -lqmmp ${MAD_LINK_FLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS})
+install(TARGETS mad DESTINATION lib/qmmp/Input)
diff --git a/lib/qmmp/Input/mad/decoder_mad.cpp b/lib/qmmp/Input/mad/decoder_mad.cpp
new file mode 100644
index 000000000..6bb16daab
--- /dev/null
+++ b/lib/qmmp/Input/mad/decoder_mad.cpp
@@ -0,0 +1,574 @@
+#include <QtGui>
+
+#include "decoder_mad.h"
+#include "constants.h"
+#include "buffer.h"
+#include "output.h"
+
+#include <math.h>
+#include <stdio.h>
+
+# define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g')
+
+
+DecoderMAD::DecoderMAD(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : Decoder(parent, d, i, o)
+{
+ inited = false;
+ user_stop = false;
+ done = false;
+ finish = false;
+ derror = false;
+ eof = false;
+ useeq = false;
+ totalTime = 0.;
+ seekTime = -1.;
+ channels = 0;
+ bks = 0;
+ bitrate = 0;
+ freq = 0;
+ len = 0;
+ input_buf = 0;
+ input_bytes = 0;
+ output_buf = 0;
+ output_bytes = 0;
+ output_at = 0;
+ output_size = 0;
+}
+
+DecoderMAD::~DecoderMAD()
+{
+ wait();
+ deinit();
+ mutex()->lock();
+ if (input_buf)
+ {
+ qDebug("DecoderMAD: deleting input_buf");
+ delete [] input_buf;
+ }
+ input_buf = 0;
+
+ if (output_buf)
+ {
+ qDebug("DecoderMAD: deleting output_buf");
+ delete [] output_buf;
+ }
+ output_buf = 0;
+ mutex()->unlock();
+}
+
+bool DecoderMAD::initialize()
+{
+ bks = blockSize();
+
+ inited = false;
+ user_stop = false;
+ done = false;
+ finish = false;
+ derror = false;
+ eof = false;
+ totalTime = 0.;
+ seekTime = -1.;
+ channels = 0;
+ bitrate = 0;
+ freq = 0;
+ len = 0;
+ input_bytes = 0;
+ output_bytes = 0;
+ output_at = 0;
+ output_size = 0;
+
+ if (! input())
+ {
+ error("DecoderMAD: cannot initialize. No input.");
+ return FALSE;
+ }
+
+ if (! input_buf)
+ input_buf = new char[globalBufferSize];
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+
+ if (! input()->isOpen())
+ {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+ error("DecoderMAD: Failed to open input. Error " +
+ QString::number(input()->isOpen()) + ".");
+ return FALSE;
+ }
+ }
+
+ mad_stream_init(&stream);
+ mad_frame_init(&frame);
+ mad_synth_init(&synth);
+
+ if (! findHeader())
+ {
+ qDebug("DecoderMAD: Cannot find a valid MPEG header.");
+ return FALSE;
+ }
+ if (output())
+ output()->configure(freq, channels, 16, bitrate);
+
+ inited = TRUE;
+ return TRUE;
+}
+
+
+void DecoderMAD::deinit()
+{
+ if(!inited)
+ return;
+
+ mad_synth_finish(&synth);
+ mad_frame_finish(&frame);
+ mad_stream_finish(&stream);
+
+ inited = false;
+ user_stop = false;
+ done = false;
+ finish = false;
+ derror = false;
+ eof = false;
+ useeq = false;
+ totalTime = 0.;
+ seekTime = -1.;
+ channels = 0;
+ bks = 0;
+ bitrate = 0;
+ freq = 0;
+ len = 0;
+ input_bytes = 0;
+ output_bytes = 0;
+ output_at = 0;
+ output_size = 0;
+}
+
+bool DecoderMAD::findXingHeader(struct mad_bitptr ptr, unsigned int bitlen)
+{
+ if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC)
+ goto fail;
+
+ xing.flags = mad_bit_read(&ptr, 32);
+ bitlen -= 64;
+
+ if (xing.flags & XING_FRAMES)
+ {
+ if (bitlen < 32)
+ goto fail;
+
+ xing.frames = mad_bit_read(&ptr, 32);
+ bitlen -= 32;
+ }
+
+ if (xing.flags & XING_BYTES)
+ {
+ if (bitlen < 32)
+ goto fail;
+
+ xing.bytes = mad_bit_read(&ptr, 32);
+ bitlen -= 32;
+ }
+
+ if (xing.flags & XING_TOC)
+ {
+ int i;
+
+ if (bitlen < 800)
+ goto fail;
+
+ for (i = 0; i < 100; ++i)
+ xing.toc[i] = mad_bit_read(&ptr, 8);
+
+ bitlen -= 800;
+ }
+
+ if (xing.flags & XING_SCALE)
+ {
+ if (bitlen < 32)
+ goto fail;
+
+ xing.scale = mad_bit_read(&ptr, 32);
+ bitlen -= 32;
+ }
+
+ return true;
+
+fail:
+ xing.flags = 0;
+ xing.frames = 0;
+ xing.bytes = 0;
+ xing.scale = 0;
+ return false;
+}
+
+bool DecoderMAD::findHeader()
+{
+ bool result = false;
+ int count = 0;
+
+ while (1)
+ {
+ if (input_bytes < globalBufferSize)
+ {
+ int bytes = input()->read(input_buf + input_bytes,
+ globalBufferSize - input_bytes);
+ if (bytes <= 0)
+ {
+ if (bytes == -1)
+ result = false;
+ ;
+ break;
+ }
+ input_bytes += bytes;
+ }
+
+ mad_stream_buffer(&stream, (unsigned char *) input_buf, input_bytes);
+
+ bool done = false;
+ while (! done)
+ {
+ if (mad_frame_decode(&frame, &stream) != -1)
+ done = true;
+ else if (!MAD_RECOVERABLE(stream.error))
+ {
+ qWarning("DecoderMAD: Can't decode frame");
+ break;
+ }
+
+ count++;
+ }
+
+ findXingHeader(stream.anc_ptr, stream.anc_bitlen);
+ result = done;
+ if ((stream.error != MAD_ERROR_BUFLEN))
+ break;
+
+ input_bytes = &input_buf[input_bytes] - (char *) stream.next_frame;
+ memmove(input_buf, stream.next_frame, input_bytes);
+ }
+
+ if (result && count)
+ {
+ freq = frame.header.samplerate;
+ channels = MAD_NCHANNELS(&frame.header);
+ bitrate = frame.header.bitrate / 1000;
+ calcLength(&frame.header);
+ }
+
+ return result;
+}
+
+void DecoderMAD::calcLength(struct mad_header *header)
+{
+ if (! input() || input()->isSequential())
+ return;
+
+ totalTime = 0.;
+ if (xing.flags & XING_FRAMES)
+ {
+ mad_timer_t timer;
+
+ timer = header->duration;
+ mad_timer_multiply(&timer, xing.frames);
+
+ totalTime = double(mad_timer_count(timer, MAD_UNITS_MILLISECONDS)) / 1000.;
+ }
+ else if (header->bitrate > 0)
+ totalTime = input()->size() * 8 / header->bitrate;
+}
+
+double DecoderMAD::lengthInSeconds()
+{
+ if (! inited)
+ return 0.;
+ return totalTime;
+}
+
+void DecoderMAD::seek(double pos)
+{
+ seekTime = pos;
+}
+
+void DecoderMAD::stop()
+{
+ user_stop = TRUE;
+}
+
+void DecoderMAD::flush(bool final)
+{
+ ulong min = final ? 0 : bks;
+
+ while ((! done && ! finish) && output_bytes > min)
+ {
+ output()->recycler()->mutex()->lock();
+
+ while ((! done && ! finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+
+ mutex()->lock();
+ done = user_stop;
+ }
+
+ if (user_stop || finish)
+ {
+ inited = FALSE;
+ done = TRUE;
+ }
+ else
+ {
+ output_bytes -= produceSound(output_buf, output_bytes, bitrate, channels);
+ output_size += bks;
+ output_at = output_bytes;
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
+
+void DecoderMAD::run()
+{
+ mutex()->lock();
+
+ if (! inited)
+ {
+ mutex()->unlock();
+ return;
+ }
+
+ DecoderState::Type stat = DecoderState::Decoding;
+
+ mutex()->unlock();
+
+ dispatch(stat);
+
+ while (! done && ! finish && ! derror)
+ {
+ mutex()->lock();
+
+ if (seekTime >= 0.0)
+ {
+ long seek_pos = long(seekTime * input()->size() / totalTime);
+ input()->seek(seek_pos);
+ output_size = long(seekTime) * long(freq * channels * 16 / 2);
+ input_bytes = 0;
+ output_at = 0;
+ output_bytes = 0;
+ eof = false;
+ }
+
+ finish = eof;
+
+ if (! eof)
+ {
+ if (stream.next_frame && seekTime == -1.)
+ {
+ input_bytes = &input_buf[input_bytes] - (char *) stream.next_frame;
+ memmove(input_buf, stream.next_frame, input_bytes);
+ }
+
+ if (input_bytes < globalBufferSize)
+ {
+ int len = input()->read((char *) input_buf + input_bytes,
+ globalBufferSize - input_bytes);
+
+ if (len == 0)
+ {
+ eof = true;
+ }
+ else if (len < 0)
+ {
+ derror = true;
+ break;
+ }
+
+ input_bytes += len;
+ }
+
+ mad_stream_buffer(&stream, (unsigned char *) input_buf, input_bytes);
+ }
+
+ seekTime = -1.;
+
+ mutex()->unlock();
+
+ // decode
+ while (! done && ! finish && ! derror)
+ {
+ if (mad_frame_decode(&frame, &stream) == -1)
+ {
+ if (stream.error == MAD_ERROR_BUFLEN)
+ break;
+
+ // error in decoding
+ if (! MAD_RECOVERABLE(stream.error))
+ {
+ derror = true;
+ break;
+ }
+ continue;
+ }
+
+ mutex()->lock();
+
+ if (seekTime >= 0.)
+ {
+ mutex()->unlock();
+ break;
+ }
+
+ if (useeq)
+ {
+ unsigned int nch, ch, ns, s, sb;
+
+ nch = MAD_NCHANNELS(&frame.header);
+ ns = MAD_NSBSAMPLES(&frame.header);
+
+ for (ch = 0; ch < nch; ++ch)
+ for (s = 0; s < ns; ++s)
+ for (sb = 0; sb < 32; ++sb)
+ frame.sbsample[ch][s][sb] =
+ mad_f_mul(frame.sbsample[ch][s][sb], eqbands[sb]);
+ }
+
+ mad_synth_frame(&synth, &frame);
+ madOutput();
+ mutex()->unlock();
+ }
+ }
+
+ mutex()->lock();
+
+ if (! user_stop && eof)
+ {
+ flush(TRUE);
+
+ if (output())
+ {
+ output()->recycler()->mutex()->lock();
+ // end of stream
+ while (! output()->recycler()->empty() && ! user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+
+ done = TRUE;
+ if (! user_stop)
+ finish = TRUE;
+ }
+
+ if (finish)
+ stat = DecoderState::Finished;
+ else if (user_stop)
+ stat = DecoderState::Stopped;
+
+ mutex()->unlock();
+
+ dispatch(stat);
+
+ if (input())
+ input()->close();
+ deinit();
+
+}
+
+static inline signed int scale(mad_fixed_t sample)
+{
+ /* round */
+ sample += (1L << (MAD_F_FRACBITS - 16));
+
+ /* clip */
+ if (sample >= MAD_F_ONE)
+ sample = MAD_F_ONE - 1;
+ else if (sample < -MAD_F_ONE)
+ sample = -MAD_F_ONE;
+
+ /* quantize */
+ return sample >> (MAD_F_FRACBITS + 1 - 16);
+}
+
+static inline signed long fix_sample(unsigned int bits, mad_fixed_t sample)
+{
+ mad_fixed_t quantized, check;
+ // clip
+ quantized = sample;
+ check = (sample >> MAD_F_FRACBITS) + 1;
+ if (check & ~1)
+ {
+ if (sample >= MAD_F_ONE)
+ quantized = MAD_F_ONE - 1;
+ else if (sample < -MAD_F_ONE)
+ quantized = -MAD_F_ONE;
+ }
+ // quantize
+ quantized &= ~((1L << (MAD_F_FRACBITS + 1 - bits)) - 1);
+ // scale
+ return quantized >> (MAD_F_FRACBITS + 1 - bits);
+}
+
+enum mad_flow DecoderMAD::madOutput()
+{
+ unsigned int samples, channels;
+ mad_fixed_t const *left, *right;
+
+ samples = synth.pcm.length;
+ channels = synth.pcm.channels;
+ left = synth.pcm.samples[0];
+ right = synth.pcm.samples[1];
+
+
+ bitrate = frame.header.bitrate / 1000;
+ done = user_stop;
+
+ while (samples-- && !user_stop)
+ {
+ signed int sample;
+
+ if (output_bytes + 4096 > globalBufferSize)
+ flush();
+
+ sample = fix_sample(16, *left++);
+ *(output_buf + output_at++) = ((sample >> 0) & 0xff);
+ *(output_buf + output_at++) = ((sample >> 8) & 0xff);
+ output_bytes += 2;
+
+ if (channels == 2)
+ {
+ sample = fix_sample(16, *right++);
+ *(output_buf + output_at++) = ((sample >> 0) & 0xff);
+ *(output_buf + output_at++) = ((sample >> 8) & 0xff);
+ output_bytes += 2;
+ }
+ }
+
+ if (done || finish)
+ {
+ return MAD_FLOW_STOP;
+ }
+
+ return MAD_FLOW_CONTINUE;
+}
+
+enum mad_flow DecoderMAD::madError(struct mad_stream *stream,
+ struct mad_frame *)
+{
+ if (MAD_RECOVERABLE(stream->error))
+ return MAD_FLOW_CONTINUE;
+ qFatal("MADERROR!\n");
+ return MAD_FLOW_STOP;
+}
diff --git a/lib/qmmp/Input/mad/decoder_mad.h b/lib/qmmp/Input/mad/decoder_mad.h
new file mode 100644
index 000000000..ecbb160cb
--- /dev/null
+++ b/lib/qmmp/Input/mad/decoder_mad.h
@@ -0,0 +1,95 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __decoder_mad_h
+#define __decoder_mad_h
+
+class DecoderMAD;
+
+#include "decoder.h"
+#include "decodermadfactory.h"
+
+extern "C" {
+#include <mad.h>
+}
+
+
+class DecoderMAD : public Decoder
+{
+public:
+ DecoderMAD(QObject *parent = 0, DecoderFactory *d = 0,
+ QIODevice *i = 0, Output *o = 0);
+ virtual ~DecoderMAD();
+
+ // standard decoder API
+ bool initialize();
+ double lengthInSeconds();
+ void seek(double);
+ void stop();
+
+ // Equalizer
+ //bool isEQSupported() const { return TRUE; }
+ //void setEQEnabled(bool);
+ //void setEQ(const EqPreset &);
+
+ static const int maxDecodeRetries;
+ static const int maxFrameSize;
+ static const int maxFrameCheck;
+ static const int initialFrameSize;
+
+
+private:
+ // thread run function
+ void run();
+
+ enum mad_flow madOutput();
+ enum mad_flow madError(struct mad_stream *, struct mad_frame *);
+
+ // helper functions
+ void flush(bool = FALSE);
+ void deinit();
+ bool findHeader();
+ bool findXingHeader(struct mad_bitptr, unsigned int);
+ void calcLength(struct mad_header *);
+
+ bool inited, user_stop, done, finish, derror, eof, useeq;
+ double totalTime, seekTime;
+ int channels;
+ long bitrate, freq, len;
+ unsigned int bks;
+ mad_fixed_t eqbands[32];
+
+ // file input buffer
+ char *input_buf;
+ unsigned long input_bytes;
+
+ // output buffer
+ char *output_buf;
+ unsigned long output_bytes, output_at, output_size;
+
+ // MAD decoder
+ struct {
+ int flags;
+ unsigned long frames;
+ unsigned long bytes;
+ unsigned char toc[100];
+ long scale;
+ } xing;
+
+ enum {
+ XING_FRAMES = 0x0001,
+ XING_BYTES = 0x0002,
+ XING_TOC = 0x0004,
+ XING_SCALE = 0x0008
+ };
+
+ struct mad_stream stream;
+ struct mad_frame frame;
+ struct mad_synth synth;
+};
+
+
+#endif // __decoder_mad_h
diff --git a/lib/qmmp/Input/mad/decodermadfactory.cpp b/lib/qmmp/Input/mad/decodermadfactory.cpp
new file mode 100644
index 000000000..a511dc995
--- /dev/null
+++ b/lib/qmmp/Input/mad/decodermadfactory.cpp
@@ -0,0 +1,87 @@
+#include <QtGui>
+#include <QDialog>
+#include <QMessageBox>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/id3v1tag.h>
+#include <taglib/id3v2tag.h>
+#include <taglib/tfile.h>
+#include <taglib/mpegfile.h>
+
+#include "detailsdialog.h"
+#include "settingsdialog.h"
+#include "decoder_mad.h"
+#include "id3tag.h"
+#include "decodermadfactory.h"
+
+// DecoderMADFactory
+
+bool DecoderMADFactory::supports(const QString &source) const
+{
+ QString ext = source.right(4).toLower();
+ return ext == ".mp1" || ext == ".mp2" || ext == ".mp3";
+}
+
+const QString &DecoderMADFactory::name() const
+{
+ static QString name (tr("MPEG Plugin"));
+ return name;
+}
+
+const QString &DecoderMADFactory::filter() const
+{
+ static QString filter("*.mp1 *.mp2 *.mp3");
+ return filter;
+}
+
+const QString &DecoderMADFactory::description() const
+{
+ static QString desc(tr("MPEG Files"));
+ return desc;
+}
+
+Decoder *DecoderMADFactory::create(QObject *parent, QIODevice *input, Output *output)
+{
+ return new DecoderMAD(parent, this, input, output);
+}
+
+FileTag *DecoderMADFactory::createTag(const QString &source)
+{
+ FileTag *tag = new ID3Tag(source);
+ return tag;
+}
+
+void DecoderMADFactory::showDetails(QWidget *parent, const QString &path)
+{
+ DetailsDialog *d = new DetailsDialog(parent, path);
+ d -> show();
+}
+
+void DecoderMADFactory::showSettings(QWidget *parent)
+{
+ SettingsDialog *s = new SettingsDialog(parent);
+ s -> show();
+}
+
+void DecoderMADFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About MPEG Audio Plugin"),
+ tr("Qmmp MPEG Audio Plugin")+"\n"+
+ tr("Compiled against libmad version:")+" "+
+ QString("%1.%2.%3%4").arg(MAD_VERSION_MAJOR)
+ .arg(MAD_VERSION_MINOR)
+ .arg(MAD_VERSION_PATCH).arg(MAD_VERSION_EXTRA)+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")+"\n"+
+ tr("Source code based on mq3 progect")
+ );
+}
+
+QTranslator *DecoderMADFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/mad_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(DecoderMADFactory)
diff --git a/lib/qmmp/Input/mad/decodermadfactory.h b/lib/qmmp/Input/mad/decodermadfactory.h
new file mode 100644
index 000000000..9becdf022
--- /dev/null
+++ b/lib/qmmp/Input/mad/decodermadfactory.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DECODERMADFACTORY_H
+#define DECODERMADFACTORY_H
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <decoder.h>
+#include <output.h>
+#include <decoderfactory.h>
+
+
+
+
+class DecoderMADFactory : public QObject,
+ DecoderFactory
+{
+Q_OBJECT
+Q_INTERFACES(DecoderFactory);
+
+public:
+ bool supports(const QString &source) const;
+ const QString &name() const;
+ const QString &filter() const; // file extension, ie. ".mp3" or ".ogg"
+ const QString &description() const; // file type, ie. "MPEG Audio Files"
+ Decoder *create(QObject *, QIODevice *, Output *);
+ FileTag *createTag(const QString &source);
+ void showDetails(QWidget *parent, const QString &path);
+ void showSettings(QWidget *parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+};
+
+#endif
diff --git a/lib/qmmp/Input/mad/detailsdialog.cpp b/lib/qmmp/Input/mad/detailsdialog.cpp
new file mode 100644
index 000000000..ac47c2456
--- /dev/null
+++ b/lib/qmmp/Input/mad/detailsdialog.cpp
@@ -0,0 +1,168 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QTextCodec>
+#include <QSettings>
+#include <QDir>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/id3v1tag.h>
+#include <taglib/id3v2tag.h>
+#include <taglib/tfile.h>
+#include <taglib/mpegfile.h>
+#include <taglib/mpegheader.h>
+#include <taglib/mpegproperties.h>
+
+#include "detailsdialog.h"
+
+DetailsDialog::DetailsDialog(QWidget *parent, const QString &path)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ m_path = path;
+ setWindowTitle (path.section('/',-1));
+ ui.pathLineEdit->setText(m_path);
+
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("MAD");
+ QTextCodec *codec_v1 =
+ QTextCodec::codecForName(settings.value("ID3v1_encoding","" )
+ .toByteArray ());
+ QTextCodec *codec_v2 =
+ QTextCodec::codecForName(settings.value("ID3v2_encoding","" )
+ .toByteArray ());
+ if (!codec_v1)
+ codec_v1 = QTextCodec::codecForLocale ();
+ if (!codec_v2)
+ codec_v2 = QTextCodec::codecForName ("UTF-8");
+ settings.endGroup();
+
+ loadMPEGInfo();
+ m_codec = codec_v1;
+ loadID3v1Tag();
+ m_codec = codec_v2;
+ loadID3v2Tag();
+}
+
+
+DetailsDialog::~DetailsDialog()
+{}
+
+void DetailsDialog::loadMPEGInfo()
+{
+ TagLib::MPEG::File f (m_path.toLocal8Bit());
+ //l.label
+ //ui. f.audioProperties()->level();
+ QString text;
+ text = QString("%1").arg(f.audioProperties()->layer());
+ ui.levelLabel->setText("MPEG layer "+text); //TODO: add MPEG version
+ text = QString("%1").arg(f.audioProperties()->bitrate());
+ ui.bitRateLabel->setText(text+" "+tr("kbps"));
+ text = QString("%1").arg(f.audioProperties()->sampleRate());
+ ui.sampleRateLabel->setText(text+" "+tr("Hz"));
+ switch (f.audioProperties()->channelMode())
+ {
+ case TagLib::MPEG::Header::Stereo:
+ ui.modeLabel->setText("Stereo");
+ break;
+ case TagLib::MPEG::Header::JointStereo:
+ ui.modeLabel->setText("Joint stereo");
+ break;
+ case TagLib::MPEG::Header::DualChannel:
+ ui.modeLabel->setText("Dual channel");
+ break;
+ case TagLib::MPEG::Header::SingleChannel:
+ ui.modeLabel->setText("Single channel");
+ break;
+ }
+ text = QString("%1 "+tr("KB")).arg(f.length()/1024);
+ ui.fileSizeLabel->setText(text);
+ /*if (f.audioProperties()->protectionEnabled())
+ ui.errProtectionLabel->setText(tr("Yes"));
+ else
+ ui.errProtectionLabel->setText(tr("No"));*/
+ if (f.audioProperties()->isCopyrighted())
+ ui.copyrightLabel->setText(tr("Yes"));
+ else
+ ui.copyrightLabel->setText(tr("No"));
+ if (f.audioProperties()->isOriginal())
+ ui.originalLabel->setText(tr("Yes"));
+ else
+ ui.originalLabel->setText(tr("No"));
+}
+
+void DetailsDialog::loadID3v1Tag()
+{
+ TagLib::MPEG::File f (m_path.toLocal8Bit());
+
+ if (f.ID3v1Tag())
+ {
+ bool utf = m_codec->name().contains("UTF");
+ TagLib::String title = f.ID3v1Tag()->title();
+ TagLib::String artist = f.ID3v1Tag()->artist();
+ TagLib::String album = f.ID3v1Tag()->album();
+ TagLib::String comment = f.ID3v1Tag()->comment();
+ TagLib::String genre = f.ID3v1Tag()->genre();
+ QString string = m_codec->toUnicode(title.toCString(utf)).trimmed();
+ ui.titleLineEdit_v1->setText(string);
+ string = m_codec->toUnicode(artist.toCString(utf)).trimmed();
+ ui.artistLineEdit_v1->setText(string);
+ string = m_codec->toUnicode(album.toCString(utf)).trimmed();
+ ui.albumLineEdit_v1->setText(string);
+ string = m_codec->toUnicode(comment.toCString(utf)).trimmed();
+ ui.commentLineEdit_v1->setText(string);
+ string = QString("%1").arg(f.ID3v1Tag()->year());
+ ui.yearLineEdit_v1->setText(string);
+ string = QString("%1").arg(f.ID3v1Tag()->track());
+ ui.trackLineEdit_v1->setText(string);
+ string = m_codec->toUnicode(genre.toCString(utf)).trimmed();
+ ui.genreLineEdit_v1->setText(string);
+ }
+}
+
+void DetailsDialog::loadID3v2Tag()
+{
+ TagLib::MPEG::File f (m_path.toLocal8Bit());
+
+ if (f.ID3v2Tag())
+ {
+ bool utf = m_codec->name().contains("UTF");
+ TagLib::String title = f.ID3v2Tag()->title();
+ TagLib::String artist = f.ID3v2Tag()->artist();
+ TagLib::String album = f.ID3v2Tag()->album();
+ TagLib::String comment = f.ID3v2Tag()->comment();
+ TagLib::String genre = f.ID3v2Tag()->genre();
+ QString string = m_codec->toUnicode(title.toCString(utf)).trimmed();
+ ui.titleLineEdit_v2->setText(string);
+ string = m_codec->toUnicode(artist.toCString(utf)).trimmed();
+ ui.artistLineEdit_v2->setText(string);
+ string = m_codec->toUnicode(album.toCString(utf)).trimmed();
+ ui.albumLineEdit_v2->setText(string);
+ string = m_codec->toUnicode(comment.toCString(utf)).trimmed();
+ ui.commentLineEdit_v2->setText(string);
+ string = QString("%1").arg(f.ID3v2Tag()->year());
+ ui.yearLineEdit_v2->setText(string);
+ string = QString("%1").arg(f.ID3v2Tag()->track());
+ ui.trackLineEdit_v2->setText(string);
+ string = m_codec->toUnicode(genre.toCString(utf)).trimmed();
+ ui.genreLineEdit_v2->setText(string);
+ }
+}
+
diff --git a/lib/qmmp/Input/mad/detailsdialog.h b/lib/qmmp/Input/mad/detailsdialog.h
new file mode 100644
index 000000000..a790dcc7b
--- /dev/null
+++ b/lib/qmmp/Input/mad/detailsdialog.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DETAILSDIALOG_H
+#define DETAILSDIALOG_H
+
+#include <QDialog>
+
+#include "ui_detailsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QTextCodec;
+
+class DetailsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ DetailsDialog(QWidget *parent = 0, const QString &path = 0);
+
+ ~DetailsDialog();
+
+private:
+ void loadMPEGInfo();
+ void loadID3v1Tag();
+ void loadID3v2Tag();
+ Ui::DetailsDialog ui;
+ QString m_path;
+ QTextCodec *m_codec;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/mad/detailsdialog.ui b/lib/qmmp/Input/mad/detailsdialog.ui
new file mode 100644
index 000000000..b096c1a53
--- /dev/null
+++ b/lib/qmmp/Input/mad/detailsdialog.ui
@@ -0,0 +1,487 @@
+<ui version="4.0" >
+ <class>DetailsDialog</class>
+ <widget class="QDialog" name="DetailsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>709</width>
+ <height>375</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Details</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item rowspan="2" row="1" column="0" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="minimumSize" >
+ <size>
+ <width>200</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="title" >
+ <string>MPEG Info</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="7" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType" >
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>85</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>File size:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="sampleRateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Sample rate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Bit rate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="bitRateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLabel" name="modeLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QLabel" name="fileSizeLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>MPEG level:</string>
+ </property>
+ <property name="textFormat" >
+ <enum>Qt::AutoText</enum>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="levelLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_6" >
+ <property name="text" >
+ <string>Mode:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_8" >
+ <property name="text" >
+ <string>Copyright:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" >
+ <widget class="QLabel" name="copyrightLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="1" >
+ <widget class="QLabel" name="originalLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0" >
+ <widget class="QLabel" name="label_9" >
+ <property name="text" >
+ <string>Original:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>ID3v1 Tag</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="1" colspan="2" >
+ <widget class="QPushButton" name="pushButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3" >
+ <widget class="QLineEdit" name="trackLineEdit_v1" />
+ </item>
+ <item row="4" column="2" >
+ <widget class="QLabel" name="label_26" >
+ <property name="text" >
+ <string>Track number:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLineEdit" name="yearLineEdit_v1" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_25" >
+ <property name="text" >
+ <string>Year:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_27" >
+ <property name="text" >
+ <string>Genre:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_24" >
+ <property name="text" >
+ <string>Comment:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_23" >
+ <property name="text" >
+ <string>Album:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_22" >
+ <property name="text" >
+ <string>Artist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_21" >
+ <property name="text" >
+ <string>Title:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="titleLineEdit_v1" />
+ </item>
+ <item row="1" column="1" colspan="3" >
+ <widget class="QLineEdit" name="artistLineEdit_v1" />
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QLineEdit" name="albumLineEdit_v1" />
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="commentLineEdit_v1" />
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLineEdit" name="genreLineEdit_v1" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="3" >
+ <widget class="QPushButton" name="pushButton_3" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_2_2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>ID3v2 Tag</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="4" column="3" >
+ <widget class="QLineEdit" name="trackLineEdit_v2" />
+ </item>
+ <item row="4" column="2" >
+ <widget class="QLabel" name="label_26_3" >
+ <property name="text" >
+ <string>Track number:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLineEdit" name="yearLineEdit_v2" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_25_3" >
+ <property name="text" >
+ <string>Year:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_27_3" >
+ <property name="text" >
+ <string>Genre:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_24_3" >
+ <property name="text" >
+ <string>Comment:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_23_3" >
+ <property name="text" >
+ <string>Album:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_22_3" >
+ <property name="text" >
+ <string>Artist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_21_3" >
+ <property name="text" >
+ <string>Title:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="titleLineEdit_v2" />
+ </item>
+ <item row="1" column="1" colspan="3" >
+ <widget class="QLineEdit" name="artistLineEdit_v2" />
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QLineEdit" name="albumLineEdit_v2" />
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="commentLineEdit_v2" />
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLineEdit" name="genreLineEdit_v2" />
+ </item>
+ <item row="6" column="1" colspan="2" >
+ <widget class="QPushButton" name="pushButton_4" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_28" >
+ <property name="text" >
+ <string>File path:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="pathLineEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>pushButton_3</sender>
+ <signal>clicked()</signal>
+ <receiver>DetailsDialog</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>623</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>539</x>
+ <y>352</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Input/mad/id3tag.cpp b/lib/qmmp/Input/mad/id3tag.cpp
new file mode 100644
index 000000000..1fddb3958
--- /dev/null
+++ b/lib/qmmp/Input/mad/id3tag.cpp
@@ -0,0 +1,172 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+
+#include <QString>
+#include <QTextCodec>
+#include <QSettings>
+#include <QDir>
+/*#include <filetag.h>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/id3v1tag.h>
+#include <taglib/id3v2tag.h>
+#include <taglib/tfile.h>
+#include <taglib/mpegfile.h>*/
+
+#include "id3tag.h"
+
+ID3Tag::ID3Tag(const QString &source): FileTag(), m_tag(0)
+{
+ m_length = 0;
+ m_year = 0;
+ m_track = 0;
+ m_empty = TRUE;
+ TagLib::MPEG::File fileRef(source.toLocal8Bit ());
+
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("MAD");
+ QTextCodec *codec_v1 =
+ QTextCodec::codecForName(settings.value("ID3v1_encoding","" )
+ .toByteArray ());
+ QTextCodec *codec_v2 =
+ QTextCodec::codecForName(settings.value("ID3v2_encoding","" )
+ .toByteArray ());
+ if(!codec_v1)
+ codec_v1 = QTextCodec::codecForLocale ();
+ if(!codec_v2)
+ codec_v2 = QTextCodec::codecForName ("UTF-8");
+
+ QTextCodec *codec = 0;
+
+ int ver = settings.value("ID3_version", 1).toInt();
+ if (ver == 1 && settings.value("ID3v1_enable", TRUE).toBool() &&
+ fileRef.ID3v1Tag())
+ {
+ m_tag = fileRef.ID3v1Tag();
+ codec = codec_v1;
+ if(m_tag->isEmpty())
+ {
+ m_tag = 0;
+ if (settings.value("ID3v2_enable", TRUE).toBool() &&
+ fileRef.ID3v2Tag())
+ {
+ if(!fileRef.ID3v2Tag()->isEmpty())
+ {
+ m_tag = fileRef.ID3v2Tag();
+ codec = codec_v2;
+ }
+ }
+ }
+ }
+ else
+ ver = 2;
+ if (ver == 2 && settings.value("ID3v2_enable", TRUE).toBool() &&
+ fileRef.ID3v2Tag())
+ {
+ m_tag = fileRef.ID3v2Tag();
+ codec = codec_v2;
+ if(m_tag->isEmpty())
+ {
+ m_tag = 0;
+ if (settings.value("ID3v1_enable", TRUE).toBool() &&
+ fileRef.ID3v1Tag())
+ {
+ if(fileRef.ID3v1Tag()->isEmpty())
+ {
+ m_tag = fileRef.ID3v1Tag();
+ codec = codec_v1;
+ }
+ }
+ }
+ }
+ settings.endGroup();
+
+ if (m_tag && codec)
+ {
+ bool utf = codec->name ().contains("UTF");
+ TagLib::String album = m_tag->album();
+ TagLib::String artist = m_tag->artist();
+ TagLib::String comment = m_tag->comment();
+ TagLib::String genre = m_tag->genre();
+ TagLib::String title = m_tag->title();
+
+ m_album = codec->toUnicode(album.toCString(utf)).trimmed();
+ m_artist = codec->toUnicode(artist.toCString(utf)).trimmed();
+ m_comment = codec->toUnicode(comment.toCString(utf)).trimmed();
+ m_genre = codec->toUnicode(genre.toCString(utf)).trimmed();
+ m_title = codec->toUnicode(title.toCString(utf)).trimmed();
+ m_year = m_tag->year();
+ m_track = m_tag->track();
+ m_empty = FALSE;
+ }
+ if(fileRef.audioProperties())
+ m_length = fileRef.audioProperties()->length();
+}
+
+
+ID3Tag::~ID3Tag()
+{}
+
+
+uint ID3Tag::length()
+{
+ return m_length;
+}
+
+uint ID3Tag::track()
+{
+ return m_track;
+}
+
+uint ID3Tag::year()
+{
+ return m_year;
+}
+
+QString ID3Tag::album()
+{
+ return m_album;
+}
+
+QString ID3Tag::artist()
+{
+ return m_artist;
+}
+
+QString ID3Tag::comment()
+{
+ return m_comment;
+}
+
+QString ID3Tag::genre()
+{
+ return m_genre;
+}
+
+QString ID3Tag::title()
+{
+ return m_title;
+}
+
+bool ID3Tag::isEmpty ()
+{
+ return m_empty;
+}
diff --git a/lib/qmmp/Input/mad/id3tag.h b/lib/qmmp/Input/mad/id3tag.h
new file mode 100644
index 000000000..bb0a10d35
--- /dev/null
+++ b/lib/qmmp/Input/mad/id3tag.h
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef ID3TAG_H
+#define ID3TAG_H
+
+#include <QString>
+#include <filetag.h>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/id3v1tag.h>
+#include <taglib/id3v2tag.h>
+#include <taglib/tfile.h>
+#include <taglib/mpegfile.h>
+
+#include <filetag.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class QTextCodec;
+
+class ID3Tag : public FileTag
+{
+public:
+ ID3Tag(const QString &source);
+
+ ~ID3Tag();
+
+ uint length();
+ uint track();
+ uint year();
+ QString album();
+ QString artist();
+ QString comment();
+ QString genre();
+ QString title();
+ bool isEmpty ();
+
+private:
+ TagLib::Tag *m_tag;
+ QString m_album;
+ QString m_artist;
+ QString m_comment;
+ QString m_genre;
+ QString m_title;
+ uint m_length;
+ uint m_year;
+ uint m_track;
+ bool m_empty;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/mad/mad.pro b/lib/qmmp/Input/mad/mad.pro
new file mode 100644
index 000000000..6b52fed93
--- /dev/null
+++ b/lib/qmmp/Input/mad/mad.pro
@@ -0,0 +1,32 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Input/mad
+# ???? - ??????????:
+
+FORMS += detailsdialog.ui \
+ settingsdialog.ui
+HEADERS += decodermadfactory.h \
+ decoder_mad.h \
+ detailsdialog.h \
+ settingsdialog.h \
+ id3tag.h
+SOURCES += decoder_mad.cpp \
+ decodermadfactory.cpp \
+ detailsdialog.cpp \
+ settingsdialog.cpp \
+ id3tag.cpp
+DESTDIR = ../
+QMAKE_CLEAN += ../libmad.so
+INCLUDEPATH += ../../../
+CONFIG += release \
+warn_on \
+plugin \
+link_pkgconfig
+TEMPLATE = lib
+QMAKE_LIBDIR += ../../../
+LIBS += -lqmmp
+PKGCONFIG += taglib mad
+#TRANSLATIONS = translations/mad_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Input
+INSTALLS += target
diff --git a/lib/qmmp/Input/mad/settingsdialog.cpp b/lib/qmmp/Input/mad/settingsdialog.cpp
new file mode 100644
index 000000000..72a746de6
--- /dev/null
+++ b/lib/qmmp/Input/mad/settingsdialog.cpp
@@ -0,0 +1,113 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QTextCodec>
+#include <QSettings>
+#include <QDir>
+
+#include "settingsdialog.h"
+
+SettingsDialog::SettingsDialog(QWidget *parent)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ findCodecs();
+ foreach (QTextCodec *codec, codecs)
+ {
+ ui.id3v1EncComboBox->addItem(codec->name());
+ ui.id3v2EncComboBox->addItem(codec->name());
+ }
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("MAD");
+ int pos = ui.id3v1EncComboBox->findText
+ (settings.value("ID3v1_encoding",ui.id3v1EncComboBox->itemText(0)).toString());
+ ui.id3v1EncComboBox->setCurrentIndex(pos);
+ pos = ui.id3v2EncComboBox->findText
+ (settings.value("ID3v2_encoding",ui.id3v2EncComboBox->itemText(0)).toString());
+ ui.id3v2EncComboBox->setCurrentIndex(pos);
+
+ ui.id3v1CheckBox->setChecked(settings.value("ID3v1_enable", TRUE).toBool());
+ ui.id3v2CheckBox->setChecked(settings.value("ID3v2_enable", TRUE).toBool());
+ ui.id3v1EncComboBox->setEnabled(ui.id3v1CheckBox->isChecked());
+ ui.id3v2EncComboBox->setEnabled(ui.id3v2CheckBox->isChecked());
+ ui.defTagComboBox->setCurrentIndex(settings.value("ID3_version", 1).toInt() - 1);
+
+ settings.endGroup();
+ connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings()));
+}
+
+
+SettingsDialog::~SettingsDialog()
+{}
+
+void SettingsDialog::writeSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("MAD");
+ if(ui.id3v1EncComboBox->currentIndex() > 0)
+ settings.setValue("ID3v1_encoding", ui.id3v1EncComboBox->currentText());
+ if(ui.id3v2EncComboBox->currentIndex() > 0)
+ settings.setValue("ID3v2_encoding", ui.id3v2EncComboBox->currentText());
+
+ settings.setValue("ID3v1_enable", ui.id3v1CheckBox->isChecked());
+ settings.setValue("ID3v2_enable", ui.id3v2CheckBox->isChecked());
+ settings.setValue("ID3_version", ui.defTagComboBox->currentIndex()+1);
+
+ settings.endGroup();
+ accept();
+}
+
+void SettingsDialog::findCodecs()
+{
+ QMap<QString, QTextCodec *> codecMap;
+ QRegExp iso8859RegExp("ISO[- ]8859-([0-9]+).*");
+
+ foreach (int mib, QTextCodec::availableMibs())
+ {
+ QTextCodec *codec = QTextCodec::codecForMib(mib);
+
+ QString sortKey = codec->name().toUpper();
+ int rank;
+
+ if (sortKey.startsWith("UTF-8"))
+ {
+ rank = 1;
+ }
+ else if (sortKey.startsWith("UTF-16"))
+ {
+ rank = 2;
+ }
+ else if (iso8859RegExp.exactMatch(sortKey))
+ {
+ if (iso8859RegExp.cap(1).size() == 1)
+ rank = 3;
+ else
+ rank = 4;
+ }
+ else
+ {
+ rank = 5;
+ }
+ sortKey.prepend(QChar('0' + rank));
+
+ codecMap.insert(sortKey, codec);
+ }
+ codecs = codecMap.values();
+}
diff --git a/lib/qmmp/Input/mad/settingsdialog.h b/lib/qmmp/Input/mad/settingsdialog.h
new file mode 100644
index 000000000..86a8c8ad5
--- /dev/null
+++ b/lib/qmmp/Input/mad/settingsdialog.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include <QDialog>
+
+
+#include "ui_settingsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class SettingsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ SettingsDialog(QWidget *parent = 0);
+
+ ~SettingsDialog();
+
+private slots:
+ void writeSettings();
+
+private:
+ void findCodecs();
+
+ Ui::SettingsDialog ui;
+ QList<QTextCodec *> codecs;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/mad/settingsdialog.ui b/lib/qmmp/Input/mad/settingsdialog.ui
new file mode 100644
index 000000000..634ee920e
--- /dev/null
+++ b/lib/qmmp/Input/mad/settingsdialog.ui
@@ -0,0 +1,278 @@
+<ui version="4.0" >
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>333</width>
+ <height>224</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>MPEG Plugin Settings</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>ID3 Tags</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_15_2" >
+ <property name="text" >
+ <string>Default tag version:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="defTagComboBox" >
+ <property name="maximumSize" >
+ <size>
+ <width>67</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <item>
+ <property name="text" >
+ <string>ID3v1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>ID3v2</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="id3v1CheckBox" >
+ <property name="text" >
+ <string>Enable ID3v1</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="id3v2CheckBox" >
+ <property name="text" >
+ <string>Enable ID3v2</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_17_2_2" >
+ <property name="text" >
+ <string>ID3v1 encoding:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="id3v1EncComboBox" >
+ <item>
+ <property name="text" >
+ <string>Default</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_18_2_2" >
+ <property name="text" >
+ <string>ID3v2 encoding:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="id3v2EncComboBox" >
+ <item>
+ <property name="text" >
+ <string>Default</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>131</width>
+ <height>31</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="okButton" >
+ <property name="text" >
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="cancelButton" >
+ <property name="text" >
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>336</x>
+ <y>210</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>179</x>
+ <y>224</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>id3v2CheckBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>id3v2EncComboBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>84</x>
+ <y>104</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>165</x>
+ <y>163</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>id3v1CheckBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>id3v1EncComboBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>95</x>
+ <y>73</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>165</x>
+ <y>133</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Input/mad/translations/mad_plugin_ru.qm b/lib/qmmp/Input/mad/translations/mad_plugin_ru.qm
new file mode 100644
index 000000000..16bea746b
--- /dev/null
+++ b/lib/qmmp/Input/mad/translations/mad_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Input/mad/translations/mad_plugin_ru.ts b/lib/qmmp/Input/mad/translations/mad_plugin_ru.ts
new file mode 100644
index 000000000..3d092418f
--- /dev/null
+++ b/lib/qmmp/Input/mad/translations/mad_plugin_ru.ts
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>DecoderMADFactory</name>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="27"/>
+ <source>MPEG Plugin</source>
+ <translation>Модуль MPEG</translation>
+ </message>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="39"/>
+ <source>MPEG Files</source>
+ <translation>Файлы MPEG</translation>
+ </message>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="68"/>
+ <source>About MPEG Audio Plugin</source>
+ <translation>Об аудио-модуле MPEG</translation>
+ </message>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="69"/>
+ <source>Qmmp MPEG Audio Plugin</source>
+ <translation>Аудио-модуль MPEG для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="70"/>
+ <source>Compiled against libmad version:</source>
+ <translation>Собрано с версией libmad:</translation>
+ </message>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="74"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+ <message>
+ <location filename="../decodermadfactory.cpp" line="76"/>
+ <source>Source code based on mq3 progect</source>
+ <translation>Исходный код основан на проекте mq3</translation>
+ </message>
+</context>
+<context>
+ <name>DetailsDialog</name>
+ <message>
+ <location filename="../detailsdialog.cpp" line="79"/>
+ <source>Hz</source>
+ <translation>Гц</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="106"/>
+ <source>Yes</source>
+ <translation>Есть</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="108"/>
+ <source>No</source>
+ <translation>Нет</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="192"/>
+ <source>ID3v1 Tag</source>
+ <translation>ID3v1-тег</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="442"/>
+ <source>Save</source>
+ <translation>Сохранить</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="351"/>
+ <source>Track number:</source>
+ <translation>Номер трека:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="364"/>
+ <source>Year:</source>
+ <translation>Год:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="374"/>
+ <source>Genre:</source>
+ <translation>Жанр:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="384"/>
+ <source>Comment:</source>
+ <translation>Комментарий:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="394"/>
+ <source>Album:</source>
+ <translation>Альбом:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="404"/>
+ <source>Artist:</source>
+ <translation>Исполнитель:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="414"/>
+ <source>Title:</source>
+ <translation>Название:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="321"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="336"/>
+ <source>ID3v2 Tag</source>
+ <translation>ID3v2-тег</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="31"/>
+ <source>MPEG Info</source>
+ <translation>Информация MPEG</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="164"/>
+ <source>-</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="171"/>
+ <source>Original:</source>
+ <translation>Оригинальный:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="147"/>
+ <source>Copyright:</source>
+ <translation>Авторские права:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="137"/>
+ <source>Mode:</source>
+ <translation>Режим:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="59"/>
+ <source>File size:</source>
+ <translation>Размер файла:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="76"/>
+ <source>Sample rate:</source>
+ <translation>Дискретизация:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="86"/>
+ <source>Bit rate:</source>
+ <translation>Битовая частота:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="117"/>
+ <source>MPEG level:</source>
+ <translation>Уровень MPEG:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="452"/>
+ <source>File path:</source>
+ <translation>Путь к файлу:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="95"/>
+ <source>KB</source>
+ <translation>Кб</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="13"/>
+ <source>Details</source>
+ <translation>Информация</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="77"/>
+ <source>kbps</source>
+ <translation>Кб/с</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="../settingsdialog.ui" line="25"/>
+ <source>ID3 Tags</source>
+ <translation>ID3-теги</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="45"/>
+ <source>Default tag version:</source>
+ <translation>Версия тегов по умолчанию:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="62"/>
+ <source>ID3v1</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="67"/>
+ <source>ID3v2</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="90"/>
+ <source>Enable ID3v1</source>
+ <translation>Использовать ID3v1</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="97"/>
+ <source>Enable ID3v2</source>
+ <translation>Использовать ID3v2</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="112"/>
+ <source>ID3v1 encoding:</source>
+ <translation>Кодировка ID3v1:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="165"/>
+ <source>Default</source>
+ <translation>По-умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="154"/>
+ <source>ID3v2 encoding:</source>
+ <translation>Кодировка ID3v2:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="212"/>
+ <source>OK</source>
+ <translation>Применить</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="219"/>
+ <source>Cancel</source>
+ <translation>Отмена</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="13"/>
+ <source>MPEG Plugin Settings</source>
+ <translation>Настройка модуля MPEG</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Input/mad/translations/translations.qrc b/lib/qmmp/Input/mad/translations/translations.qrc
new file mode 100644
index 000000000..34dbabbca
--- /dev/null
+++ b/lib/qmmp/Input/mad/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>mad_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/Input/mpc/CMakeLists.txt b/lib/qmmp/Input/mpc/CMakeLists.txt
new file mode 100644
index 000000000..30b2af596
--- /dev/null
+++ b/lib/qmmp/Input/mpc/CMakeLists.txt
@@ -0,0 +1,88 @@
+project(libmpc)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+# libmpc and taglib
+PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS)
+
+IF(NOT TAGLIB_LINK_FLAGS)
+ SET(TAGLIB_LINK_FLAGS -ltag)
+ SET(TAGLIB_INCLUDE_DIR /usr/include/taglib)
+ SET(TAGLIB_CFLAGS -I/usr/include/taglib)
+ENDIF(NOT TAGLIB_LINK_FLAGS)
+
+include_directories(${FLAC_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR})
+link_directories(${FLAC_LINK_DIR} ${TAGLIB_LINK_DIR})
+
+ADD_DEFINITIONS(${TAGLIB_CFLAGS})
+
+
+SET(libmpc_SRCS
+ decoder_mpc.cpp
+ decodermpcfactory.cpp
+ detailsdialog.cpp
+ tag.cpp
+)
+
+SET(libmpc_MOC_HDRS
+ tag.h
+ decodermpcfactory.h
+ decoder_mpc.h
+ detailsdialog.h
+)
+
+SET(libmpc_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libmpc_RCC_SRCS ${libmpc_RCCS})
+
+QT4_WRAP_CPP(libmpc_MOC_SRCS ${libmpc_MOC_HDRS})
+
+# user interface
+
+
+SET(libmpc_UIS
+ detailsdialog.ui
+)
+
+QT4_WRAP_UI(libmpc_UIS_H ${libmpc_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(mpc SHARED ${libmpc_SRCS} ${libmpc_MOC_SRCS} ${libmpc_UIS_H}
+ ${libmpc_RCC_SRCS})
+target_link_libraries(mpc ${QT_LIBRARIES} -lqmmp -lmpcdec ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS})
+install(TARGETS mpc DESTINATION lib/qmmp/Input)
+
+# clean remaining files
+
+SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
+ "CMakeCache.txt;Makefile;cmake_install.cmake"
+)
+
diff --git a/lib/qmmp/Input/mpc/decoder_mpc.cpp b/lib/qmmp/Input/mpc/decoder_mpc.cpp
new file mode 100644
index 000000000..49a5c0cc4
--- /dev/null
+++ b/lib/qmmp/Input/mpc/decoder_mpc.cpp
@@ -0,0 +1,387 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+
+#include <QObject>
+#include <QIODevice>
+
+#include "constants.h"
+#include "buffer.h"
+#include "output.h"
+#include "recycler.h"
+
+#include "decoder_mpc.h"
+
+// this function used from xmms
+inline static void copyBuffer(MPC_SAMPLE_FORMAT* pInBuf, char* pOutBuf, unsigned pLength)
+{
+ unsigned pSize = 16;
+ int clipMin = -1 << (pSize - 1);
+ int clipMax = (1 << (pSize - 1)) - 1;
+ int floatScale = 1 << (pSize - 1);
+ for (unsigned n = 0; n < 2 * pLength; n++)
+ {
+ int val;
+#ifdef MPC_FIXED_POINT
+ val = shiftSigned(pInBuf[n], pSize - MPC_FIXED_POINT_SCALE_SHIFT);
+#else
+ val = (int) (pInBuf[n] * floatScale);
+#endif
+ if (val < clipMin)
+ val = clipMin;
+ else if (val > clipMax)
+ val = clipMax;
+ unsigned shift = 0;
+ do
+ {
+ pOutBuf[n * 2 + (shift / 8)] = (unsigned char) ((val >> shift) & 0xFF);
+ shift += 8;
+ }
+ while (shift < pSize);
+ }
+}
+
+// mpc callbacks
+
+static mpc_int32_t mpc_callback_read (void *data, void *buffer, mpc_int32_t size)
+{
+ DecoderMPC *dmpc = (DecoderMPC *) data;
+ qint64 res;
+
+ res = dmpc->input()->read((char *)buffer, size);
+
+ return res;
+}
+
+static mpc_bool_t mpc_callback_seek (void *data, mpc_int32_t offset)
+{
+ DecoderMPC *dmpc = (DecoderMPC *) data;
+
+ return dmpc->input()->seek(offset); // ? TRUE : FALSE;
+}
+
+static mpc_int32_t mpc_callback_tell (void *data)
+{
+ DecoderMPC *dmpc = (DecoderMPC *) data;
+ return dmpc->input()->pos ();
+}
+
+static mpc_bool_t mpc_callback_canseek (void *data)
+{
+ DecoderMPC *dmpc = (DecoderMPC *) data;
+ return !dmpc->input()->isSequential () ;
+}
+
+static mpc_int32_t mpc_callback_get_size (void *data)
+{
+ DecoderMPC *dmpc = (DecoderMPC *) data;
+ return dmpc->input()->size();
+}
+
+// Decoder class
+
+DecoderMPC::DecoderMPC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : Decoder(parent, d, i, o)
+{
+ inited = FALSE;
+ user_stop = FALSE;
+ stat = 0;
+ output_buf = 0;
+ output_bytes = 0;
+ output_at = 0;
+ bks = 0;
+ done = FALSE;
+ finish = FALSE;
+ len = 0;
+ freq = 0;
+ bitrate = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+ chan = 0;
+ output_size = 0;
+ m_data = 0;
+
+
+
+
+}
+
+
+DecoderMPC::~DecoderMPC()
+{
+ deinit();
+ if(data())
+ {
+ delete data();
+ m_data = 0;
+ }
+ if (output_buf)
+ delete [] output_buf;
+ output_buf = 0;
+}
+
+
+void DecoderMPC::stop()
+{
+ user_stop = TRUE;
+}
+
+
+void DecoderMPC::flush(bool final)
+{
+ ulong min = final ? 0 : bks;
+
+ while ((! done && ! finish) && output_bytes > min)
+ {
+ output()->recycler()->mutex()->lock ();
+
+ while ((! done && ! finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+
+ mutex()->lock ();
+ done = user_stop;
+ }
+
+ if (user_stop || finish)
+ {
+ inited = FALSE;
+ done = TRUE;
+ }
+ else
+ {
+ output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan);
+ output_size += bks;
+ output_at = output_bytes;
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
+
+
+bool DecoderMPC::initialize()
+{
+ bks = blockSize();
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+
+
+ if (! input())
+ {
+ error("DecoderMPC: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input())
+ {
+ error("DecoderMPC: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input()->isOpen())
+ {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+ error("DecoderMPC: cannot open input.");
+ return FALSE;
+ }
+ }
+ if (!m_data)
+ {
+ m_data = new mpc_data;
+ }
+
+ qDebug("DecoderMPC: setting callbacks");
+ m_data->reader.read = mpc_callback_read;
+ m_data->reader.seek = mpc_callback_seek;
+ m_data->reader.tell = mpc_callback_tell;
+ m_data->reader.canseek = mpc_callback_canseek;
+ m_data->reader.get_size = mpc_callback_get_size;
+ m_data->reader.data = this;
+
+ mpc_streaminfo_init (&m_data->info);
+
+ if (mpc_streaminfo_read (&m_data->info, &m_data->reader) != ERROR_CODE_OK)
+ return FALSE;
+ chan = data()->info.channels;
+ if (output())
+ output()->configure(data()->info.sample_freq, chan, 16, data()->info.bitrate);
+
+ mpc_decoder_setup (&data()->decoder, &data()->reader);
+
+ //mpc_decoder_scale_output (&data()->decoder, 3.0);
+
+ if (!mpc_decoder_initialize (&data()->decoder, &data()->info))
+ {
+ error("DecoderMPC: cannot get info.");
+ return FALSE;
+ }
+ totalTime = mpc_streaminfo_get_length(&data()->info);
+ inited = TRUE;
+ qDebug("DecoderMPC: initialize succes");
+ return TRUE;
+}
+
+
+double DecoderMPC::lengthInSeconds()
+{
+ if (! inited)
+ return 0;
+
+ return totalTime;
+}
+
+
+void DecoderMPC::seek(double pos)
+{
+ seekTime = pos;
+}
+
+
+void DecoderMPC::deinit()
+{
+ //FLAC__stream_decoder_finish (data()->decoder);
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+}
+
+void DecoderMPC::run()
+{
+ mpc_uint32_t vbrAcc = 0;
+ mpc_uint32_t vbrUpd = 0;
+ mutex()->lock ();
+
+ if (! inited)
+ {
+ mutex()->unlock();
+
+ return;
+ }
+ stat = DecoderState::Decoding;
+ mutex()->unlock();
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ while (! done && ! finish)
+ {
+ mutex()->lock ();
+ // decode
+
+ if (seekTime >= 0.0)
+ {
+ mpc_decoder_seek_seconds(&data()->decoder, seekTime);
+ seekTime = -1.0;
+ }
+ MPC_SAMPLE_FORMAT buffer[MPC_DECODER_BUFFER_LENGTH];
+
+ len = mpc_decoder_decode (&data()->decoder, buffer, &vbrAcc, &vbrUpd);
+
+ copyBuffer(buffer, (char *) (output_buf + output_at), len);
+
+ len = len * 4;
+
+ if (len > 0)
+ {
+ bitrate = vbrUpd * data()->info.sample_freq / 1152;
+ output_at += len;
+ output_bytes += len;
+
+ if (output())
+ flush();
+
+ }
+ else if (len == 0)
+ {
+ flush(TRUE);
+
+ if (output())
+ {
+ output()->recycler()->mutex()->lock ();
+ // end of stream
+ while (! output()->recycler()->empty() && ! user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+
+ done = TRUE;
+ if (! user_stop)
+ {
+ finish = TRUE;
+ }
+ }
+ else
+ {
+ // error in read
+ error("DecoderMPC: Error while decoding stream, File appears to be "
+ "corrupted");
+
+ finish = TRUE;
+ }
+
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+
+ if (finish)
+ stat = DecoderState::Finished;
+ else if (user_stop)
+ stat = DecoderState::Stopped;
+
+ mutex()->unlock();
+
+ {
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ deinit();
+}
diff --git a/lib/qmmp/Input/mpc/decoder_mpc.h b/lib/qmmp/Input/mpc/decoder_mpc.h
new file mode 100644
index 000000000..3b17b100e
--- /dev/null
+++ b/lib/qmmp/Input/mpc/decoder_mpc.h
@@ -0,0 +1,80 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef __decoder_mpc_h
+#define __decoder_mpc_h
+
+#include <mpcdec/mpcdec.h>
+
+#include "decoder.h"
+
+struct mpc_data
+{
+ mpc_decoder decoder;
+ mpc_reader reader;
+ mpc_streaminfo info;
+};
+
+class DecoderMPC : public Decoder
+{
+public:
+ DecoderMPC(QObject *, DecoderFactory *, QIODevice *, Output *);
+ virtual ~DecoderMPC();
+
+ // Standard Decoder API
+ bool initialize();
+ double lengthInSeconds();
+ void seek(double);
+ void stop();
+
+ // Equalizer
+ bool isEQSupported() const { return FALSE; }
+ void setEQEnabled(bool) { ; }
+ void setEQGain(int) { ; }
+ void setEQBands(int[10]) { ; }
+
+ struct mpc_data *data() { return m_data; }
+
+
+private:
+ // thread run function
+ void run();
+ struct mpc_data *m_data;
+ // helper functions
+ void flush(bool = FALSE);
+ void deinit();
+
+ bool inited, user_stop;
+ int stat;
+
+ // output buffer
+ char *output_buf;
+ ulong output_bytes, output_at;
+
+ unsigned int bks;
+ bool done, finish;
+ long len, freq, bitrate;
+ int chan;
+ unsigned long output_size;
+ double totalTime, seekTime;
+};
+
+
+#endif // __decoder_mpc_h
diff --git a/lib/qmmp/Input/mpc/decodermpcfactory.cpp b/lib/qmmp/Input/mpc/decodermpcfactory.cpp
new file mode 100644
index 000000000..98b537045
--- /dev/null
+++ b/lib/qmmp/Input/mpc/decodermpcfactory.cpp
@@ -0,0 +1,76 @@
+#include <QtGui>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include "detailsdialog.h"
+#include "decoder_mpc.h"
+#include "tag.h"
+#include "decodermpcfactory.h"
+
+
+// DecoderMPCFactory
+
+bool DecoderMPCFactory::supports(const QString &source) const
+{
+
+ return (source.right(4).toLower() == ".mpc");
+}
+
+const QString &DecoderMPCFactory::name() const
+{
+ static QString name (tr("Musepack Plugin"));
+ return name;
+}
+
+
+const QString &DecoderMPCFactory::filter() const
+{
+ static QString filter("*.mpc");
+ return filter;
+}
+
+
+const QString &DecoderMPCFactory::description() const
+{
+ static QString desc(tr("Musepack Files"));
+ return desc;
+}
+
+
+Decoder *DecoderMPCFactory::create(QObject *parent, QIODevice *input,
+ Output *output)
+{
+ return new DecoderMPC(parent, this, input, output);
+}
+
+FileTag *DecoderMPCFactory::createTag(const QString &source)
+{
+ FileTag *tag = new Tag(source);
+ return tag;
+}
+
+void DecoderMPCFactory::showDetails(QWidget *parent, const QString &path)
+{
+ DetailsDialog *d = new DetailsDialog(parent, path);
+ d -> show();
+}
+
+void DecoderMPCFactory::showSettings(QWidget *)
+{}
+
+void DecoderMPCFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About Musepack Audio Plugin"),
+ tr("Qmmp Musepack Audio Plugin")+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>"));
+}
+
+QTranslator *DecoderMPCFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/mpc_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(DecoderMPCFactory)
diff --git a/lib/qmmp/Input/mpc/decodermpcfactory.h b/lib/qmmp/Input/mpc/decodermpcfactory.h
new file mode 100644
index 000000000..651e76e18
--- /dev/null
+++ b/lib/qmmp/Input/mpc/decodermpcfactory.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DECODERMPCFACTORY_H
+#define DECODERMPCFACTORY_H
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <decoder.h>
+#include <output.h>
+#include <decoderfactory.h>
+#include <filetag.h>
+
+
+
+
+class DecoderMPCFactory : public QObject,
+ DecoderFactory
+{
+Q_OBJECT
+Q_INTERFACES(DecoderFactory);
+
+public:
+ bool supports(const QString &source) const;
+ const QString &name() const;
+ const QString &filter() const;
+ const QString &description() const;
+ Decoder *create(QObject *, QIODevice *, Output *);
+ FileTag *createTag(const QString &source);
+ void showDetails(QWidget *parent, const QString &path);
+ void showSettings(QWidget *parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+};
+
+#endif
diff --git a/lib/qmmp/Input/mpc/detailsdialog.cpp b/lib/qmmp/Input/mpc/detailsdialog.cpp
new file mode 100644
index 000000000..da1b3b83d
--- /dev/null
+++ b/lib/qmmp/Input/mpc/detailsdialog.cpp
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/mpcfile.h>
+
+#include "detailsdialog.h"
+
+DetailsDialog::DetailsDialog(QWidget *parent, const QString &path)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ m_path = path;
+ setWindowTitle (path.section('/',-1));
+ path.section('/',-1);
+ ui.pathLineEdit->setText(m_path);
+ loadMPCInfo();
+ loadTag();
+
+}
+
+
+DetailsDialog::~DetailsDialog()
+{}
+
+void DetailsDialog::loadMPCInfo()
+{
+ TagLib::MPC::File f (m_path.toLocal8Bit());
+ QString text;
+ text = QString("%1").arg(f.audioProperties()->length()/60);
+ text +=":"+QString("%1").arg(f.audioProperties()->length()%60,2,10,QChar('0'));
+ ui.lengthLabel->setText(text);
+ text = QString("%1").arg(f.audioProperties()->sampleRate());
+ ui.sampleRateLabel->setText(text+" "+tr("Hz"));
+ text = QString("%1").arg(f.audioProperties()->channels());
+ ui.channelsLabel->setText(text);
+ text = QString("%1").arg(f.audioProperties()->bitrate());
+ ui.bitrateLabel->setText(text+" "+tr("kbps"));
+ text = QString("%1").arg(f.audioProperties()->mpcVersion());
+ ui.versionLabel->setText(text);
+ text = QString("%1 "+tr("KB")).arg(f.length()/1024);
+ ui.fileSizeLabel->setText(text);
+}
+
+void DetailsDialog::loadTag()
+{
+ TagLib::FileRef f (m_path.toLocal8Bit());
+
+ if (f.tag())
+ { //TODO: load codec name from config
+
+ TagLib::String title = f.tag()->title();
+ TagLib::String artist = f.tag()->artist();
+ TagLib::String album = f.tag()->album();
+ TagLib::String comment = f.tag()->comment();
+ TagLib::String genre = f.tag()->genre();
+ QString string = QString::fromUtf8(title.toCString(TRUE)).trimmed();
+ ui.titleLineEdit->setText(string);
+ string = QString::fromUtf8(artist.toCString(TRUE)).trimmed();
+ ui.artistLineEdit->setText(string);
+ string = QString::fromUtf8(album.toCString(TRUE)).trimmed();
+ ui.albumLineEdit->setText(string);
+ string = QString::fromUtf8(comment.toCString(TRUE)).trimmed();
+ ui.commentLineEdit->setText(string);
+ string = QString("%1").arg(f.tag()->year());
+ ui.yearLineEdit->setText(string);
+ string = QString("%1").arg(f.tag()->track());
+ ui.trackLineEdit->setText(string);
+ string = QString::fromUtf8(genre.toCString(TRUE)).trimmed();
+ ui.genreLineEdit->setText(string);
+ }
+}
+
diff --git a/lib/qmmp/Input/mpc/detailsdialog.h b/lib/qmmp/Input/mpc/detailsdialog.h
new file mode 100644
index 000000000..4ff868414
--- /dev/null
+++ b/lib/qmmp/Input/mpc/detailsdialog.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DETAILSDIALOG_H
+#define DETAILSDIALOG_H
+
+#include <QDialog>
+
+#include "ui_detailsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class DetailsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ DetailsDialog(QWidget *parent = 0, const QString &path = 0);
+
+ ~DetailsDialog();
+
+private:
+ void loadMPCInfo();
+ void loadTag();
+ Ui::DetailsDialog ui;
+ QString m_path;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/mpc/detailsdialog.ui b/lib/qmmp/Input/mpc/detailsdialog.ui
new file mode 100644
index 000000000..bc8c09c0e
--- /dev/null
+++ b/lib/qmmp/Input/mpc/detailsdialog.ui
@@ -0,0 +1,349 @@
+<ui version="4.0" >
+ <class>DetailsDialog</class>
+ <widget class="QDialog" name="DetailsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>449</width>
+ <height>375</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Details</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item rowspan="2" row="1" column="0" colspan="2" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="minimumSize" >
+ <size>
+ <width>175</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="title" >
+ <string>Musepack Info</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>74</width>
+ <height>151</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="2" column="1" colspan="2" >
+ <widget class="QLabel" name="fileSizeLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Length:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="2" >
+ <widget class="QLabel" name="lengthLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Sample rate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" colspan="2" >
+ <widget class="QLabel" name="sampleRateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_10" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>Channels:</string>
+ </property>
+ <property name="textFormat" >
+ <enum>Qt::PlainText</enum>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>File size:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Bitrate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" colspan="2" >
+ <widget class="QLabel" name="channelsLabel" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" colspan="2" >
+ <widget class="QLabel" name="bitrateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Stream version:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLabel" name="versionLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="pathLineEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_28" >
+ <property name="text" >
+ <string>File path:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3" >
+ <widget class="QPushButton" name="pushButton_3" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>Musepack Tag</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="1" colspan="2" >
+ <widget class="QPushButton" name="pushButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3" >
+ <widget class="QLineEdit" name="trackLineEdit" />
+ </item>
+ <item row="4" column="2" >
+ <widget class="QLabel" name="label_26" >
+ <property name="text" >
+ <string>Track number:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLineEdit" name="yearLineEdit" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_25" >
+ <property name="text" >
+ <string>Year:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_27" >
+ <property name="text" >
+ <string>Genre:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_24" >
+ <property name="text" >
+ <string>Comment:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_23" >
+ <property name="text" >
+ <string>Album:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_22" >
+ <property name="text" >
+ <string>Artist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_21" >
+ <property name="text" >
+ <string>Title:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="titleLineEdit" />
+ </item>
+ <item row="1" column="1" colspan="3" >
+ <widget class="QLineEdit" name="artistLineEdit" />
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QLineEdit" name="albumLineEdit" />
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="commentLineEdit" />
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLineEdit" name="genreLineEdit" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>pushButton_3</sender>
+ <signal>clicked()</signal>
+ <receiver>DetailsDialog</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>623</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>539</x>
+ <y>352</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Input/mpc/mpc.pro b/lib/qmmp/Input/mpc/mpc.pro
new file mode 100644
index 000000000..d932740da
--- /dev/null
+++ b/lib/qmmp/Input/mpc/mpc.pro
@@ -0,0 +1,24 @@
+FORMS += detailsdialog.ui
+HEADERS += decodermpcfactory.h \
+ decoder_mpc.h \
+ tag.h \
+ detailsdialog.h
+SOURCES += decoder_mpc.cpp \
+ decodermpcfactory.cpp \
+ tag.cpp \
+ detailsdialog.cpp
+DESTDIR = ../
+QMAKE_CLEAN += ../libmpc.so
+INCLUDEPATH += ../../../
+CONFIG += release \
+warn_on \
+plugin \
+link_pkgconfig
+TEMPLATE = lib
+QMAKE_LIBDIR += ../../../
+LIBS += -lqmmp -L/usr/lib -lmpcdec -I/usr/include
+PKGCONFIG += taglib
+#TRANSLATIONS = translations/mpc_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Input
+INSTALLS += target
diff --git a/lib/qmmp/Input/mpc/tag.cpp b/lib/qmmp/Input/mpc/tag.cpp
new file mode 100644
index 000000000..71feb9051
--- /dev/null
+++ b/lib/qmmp/Input/mpc/tag.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "tag.h"
+
+Tag::Tag(const QString &source)
+ : FileTag()
+{
+ m_length = 0;
+ m_year = 0;
+ m_track = 0;
+ m_empty = TRUE;
+ TagLib::FileRef fileRef(source.toLocal8Bit ());
+
+ m_tag = fileRef.tag();
+ if(m_tag && !m_tag->isEmpty())
+ {
+ m_album = QString::fromUtf8(m_tag->album().toCString(TRUE)).trimmed();
+ m_artist = QString::fromUtf8(m_tag->artist().toCString(TRUE)).trimmed();
+ m_comment = QString::fromUtf8(m_tag->comment().toCString(TRUE)).trimmed();
+ m_genre = QString::fromUtf8(m_tag->genre().toCString(TRUE)).trimmed();
+ m_title = QString::fromUtf8(m_tag->title().toCString(TRUE)).trimmed();
+ m_year = 0;
+ m_track = 0;
+ if(fileRef.audioProperties())
+ m_length = fileRef.audioProperties()->length();
+ m_empty = FALSE;
+ }
+ else
+ m_tag = 0;
+}
+
+
+Tag::~Tag()
+{}
+
+
+bool Tag::isEmpty()
+{
+ return m_empty;
+}
+
+QString Tag::album()
+{
+ return m_album;
+}
+
+QString Tag::artist()
+{
+ return m_artist;
+}
+
+QString Tag::comment()
+{
+ return m_comment;
+}
+
+QString Tag::genre()
+{
+ return m_genre;
+}
+
+QString Tag::title()
+{
+ return m_title;
+}
+
+uint Tag::length()
+{
+ return m_length;
+}
+
+uint Tag::track()
+{
+ return m_track;
+}
+
+uint Tag::year()
+{
+ return m_year;
+}
diff --git a/lib/qmmp/Input/mpc/tag.h b/lib/qmmp/Input/mpc/tag.h
new file mode 100644
index 000000000..3404575f4
--- /dev/null
+++ b/lib/qmmp/Input/mpc/tag.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef TAG_H
+#define TAG_H
+
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include <filetag.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Tag : public FileTag
+{
+public:
+ Tag(const QString &source);
+
+ ~Tag();
+
+ virtual QString album();
+ virtual QString artist();
+ virtual QString comment();
+ virtual QString genre();
+ virtual QString title();
+ virtual uint length();
+ virtual uint track();
+ virtual uint year();
+ virtual bool isEmpty();
+
+private:
+ TagLib::Tag *m_tag;
+ QString m_album;
+ QString m_artist;
+ QString m_comment;
+ QString m_genre;
+ QString m_title;
+ uint m_length;
+ uint m_year;
+ uint m_track;
+ bool m_empty;
+};
+
+#endif
diff --git a/lib/qmmp/Input/mpc/translations/mpc_plugin_ru.qm b/lib/qmmp/Input/mpc/translations/mpc_plugin_ru.qm
new file mode 100644
index 000000000..0eb8c1533
--- /dev/null
+++ b/lib/qmmp/Input/mpc/translations/mpc_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Input/mpc/translations/mpc_plugin_ru.ts b/lib/qmmp/Input/mpc/translations/mpc_plugin_ru.ts
new file mode 100644
index 000000000..3c0ec3fe2
--- /dev/null
+++ b/lib/qmmp/Input/mpc/translations/mpc_plugin_ru.ts
@@ -0,0 +1,149 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>DecoderMPCFactory</name>
+ <message>
+ <location filename="../decodermpcfactory.cpp" line="21"/>
+ <source>Musepack Plugin</source>
+ <translation>Модуль Musepack</translation>
+ </message>
+ <message>
+ <location filename="../decodermpcfactory.cpp" line="35"/>
+ <source>Musepack Files</source>
+ <translation>Файлы Musepack</translation>
+ </message>
+ <message>
+ <location filename="../decodermpcfactory.cpp" line="63"/>
+ <source>About Musepack Audio Plugin</source>
+ <translation>Об аудио-модуле Musepack</translation>
+ </message>
+ <message>
+ <location filename="../decodermpcfactory.cpp" line="64"/>
+ <source>Qmmp Musepack Audio Plugin</source>
+ <translation>Аудио-модуль Musepack для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../decodermpcfactory.cpp" line="65"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>DetailsDialog</name>
+ <message>
+ <location filename="../detailsdialog.cpp" line="52"/>
+ <source>Hz</source>
+ <translation>Гц</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="31"/>
+ <source>Musepack Info</source>
+ <translation>Информация Musepack</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="160"/>
+ <source>-</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="63"/>
+ <source>Length:</source>
+ <translation>Длительность:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="80"/>
+ <source>Sample rate:</source>
+ <translation>Дискретизация:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="100"/>
+ <source>Channels:</source>
+ <translation>Каналов:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="113"/>
+ <source>File size:</source>
+ <translation>Размер файла:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="123"/>
+ <source>Bitrate:</source>
+ <translation>Битовая частота:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="150"/>
+ <source>Stream version:</source>
+ <translation>Версия потока:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="177"/>
+ <source>File path:</source>
+ <translation>Путь к файлу:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="187"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="215"/>
+ <source>Musepack Tag</source>
+ <translation>Musepack-тег</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="230"/>
+ <source>Save</source>
+ <translation>Сохранить</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="240"/>
+ <source>Track number:</source>
+ <translation>Номер трека:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="253"/>
+ <source>Year:</source>
+ <translation>Год:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="263"/>
+ <source>Genre:</source>
+ <translation>Жанр:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="273"/>
+ <source>Comment:</source>
+ <translation>Комментарий:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="283"/>
+ <source>Album:</source>
+ <translation>Альбом:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="293"/>
+ <source>Artist:</source>
+ <translation>Исполнитель:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="303"/>
+ <source>Title:</source>
+ <translation>Название:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="13"/>
+ <source>Details</source>
+ <translation>Информация</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="56"/>
+ <source>kbps</source>
+ <translation>Кб/с</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="59"/>
+ <source>KB</source>
+ <translation>Кб</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Input/mpc/translations/translations.qrc b/lib/qmmp/Input/mpc/translations/translations.qrc
new file mode 100644
index 000000000..cc88de9ce
--- /dev/null
+++ b/lib/qmmp/Input/mpc/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>mpc_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/Input/vorbis/CMakeLists.txt b/lib/qmmp/Input/vorbis/CMakeLists.txt
new file mode 100644
index 000000000..48f29e26f
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/CMakeLists.txt
@@ -0,0 +1,98 @@
+project(libvorbis)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+# libvorbis and taglib
+PKGCONFIG(ogg OGG_INCLUDE_DIR OGG_LINK_DIR OGG_LINK_FLAGS OGG_CFLAGS)
+PKGCONFIG(vorbis VORBIS_INCLUDE_DIR VORBIS_LINK_DIR VORBIS_LINK_FLAGS VORBIS_CFLAGS)
+PKGCONFIG(vorbisfile VORBISFILE_INCLUDE_DIR VORBISFILE_LINK_DIR VORBISFILE_LINK_FLAGS VORBISFILE_CFLAGS)
+
+PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS)
+
+IF(NOT OGG_LINK_FLAGS)
+ SET(OGG_LINK_FLAGS -logg)
+ENDIF(NOT OGG_LINK_FLAGS)
+
+IF(NOT VORBIS_LINK_FLAGS)
+ SET(VORBIS_LINK_FLAGS -lvorbis)
+ENDIF(NOT VORBIS_LINK_FLAGS)
+
+IF(NOT VORBISFILE_LINK_FLAGS)
+ SET(VORBISFILE_LINK_FLAGS -lvorbisfile)
+ENDIF(NOT VORBISFILE_LINK_FLAGS)
+
+IF(NOT ${TAGLIB_LINK_FLAGS})
+ SET(TAGLIB_LINK_FLAGS -ltag)
+ SET(TAGLIB_INCLUDE_DIR /usr/include/taglib)
+ SET(TAGLIB_CFLAGS -I/usr/include/taglib)
+ENDIF(NOT ${TAGLIB_LINK_FLAGS})
+
+include_directories(${VORBIS_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR})
+link_directories(${VORBIS_LINK_DIR} ${TAGLIB_LINK_DIR})
+
+#ADD_DEFINITIONS(${VORBIS_CFLAGS})
+ADD_DEFINITIONS(${TAGLIB_CFLAGS})
+
+
+SET(libvorbis_SRCS
+ decoder_vorbis.cpp
+ decodervorbisfactory.cpp
+ detailsdialog.cpp
+ tag.cpp
+)
+
+SET(libvorbis_MOC_HDRS
+ tag.h
+ decodervorbisfactory.h
+ decoder_vorbis.h
+ detailsdialog.h
+)
+
+SET(libvorbis_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libvorbis_RCC_SRCS ${libvorbis_RCCS})
+
+QT4_WRAP_CPP(libvorbis_MOC_SRCS ${libvorbis_MOC_HDRS})
+
+# user interface
+
+
+SET(libvorbis_UIS
+ detailsdialog.ui
+)
+
+QT4_WRAP_UI(libvorbis_UIS_H ${libvorbis_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(vorbis SHARED ${libvorbis_SRCS} ${libvorbis_MOC_SRCS} ${libvorbis_UIS_H}
+ ${libvorbis_RCC_SRCS})
+target_link_libraries(vorbis ${QT_LIBRARIES} -lqmmp ${VORBIS_LINK_FLAGS} ${VORBISFILE_LINK_FLAGS} ${OGG_LINK_FLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS})
+install(TARGETS vorbis DESTINATION lib/qmmp/Input)
diff --git a/lib/qmmp/Input/vorbis/decoder_vorbis.cpp b/lib/qmmp/Input/vorbis/decoder_vorbis.cpp
new file mode 100644
index 000000000..9555d6e6a
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/decoder_vorbis.cpp
@@ -0,0 +1,368 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#include "decoder_vorbis.h"
+#include "constants.h"
+#include "buffer.h"
+#include "output.h"
+#include "recycler.h"
+
+#include <qobject.h>
+#include <qiodevice.h>
+
+
+// ic functions for OggVorbis
+
+static size_t oggread (void *buf, size_t size, size_t nmemb, void *src)
+{
+ if (! src) return 0;
+
+ DecoderVorbis *dogg = (DecoderVorbis *) src;
+ int len = dogg->input()->read((char *) buf, (size * nmemb));
+ return len / size;
+}
+
+
+static int oggseek(void *src, int64_t offset, int whence)
+{
+ DecoderVorbis *dogg = (DecoderVorbis *) src;
+
+ if ( dogg->input()->isSequential ())
+ return -1;
+
+ long start = 0;
+ switch (whence)
+ {
+ case SEEK_END:
+ start = dogg->input()->size();
+ break;
+
+ case SEEK_CUR:
+ start = dogg->input()->pos();
+ break;
+
+ case SEEK_SET:
+ default:
+ start = 0;
+ }
+
+ if (dogg->input()->seek(start + offset))
+ return 0;
+ return -1;
+}
+
+
+static int oggclose(void *src)
+{
+ DecoderVorbis *dogg = (DecoderVorbis *) src;
+ dogg->input()->close();
+ return 0;
+}
+
+
+static long oggtell(void *src)
+{
+ DecoderVorbis *dogg = (DecoderVorbis *) src;
+ long t = dogg->input()->pos();
+ return t;
+}
+
+
+// Decoder class
+
+DecoderVorbis::DecoderVorbis(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o)
+ : Decoder(parent, d, i, o)
+{
+ inited = FALSE;
+ user_stop = FALSE;
+ stat = 0;
+ output_buf = 0;
+ output_bytes = 0;
+ output_at = 0;
+ bks = 0;
+ done = FALSE;
+ finish = FALSE;
+ len = 0;
+ freq = 0;
+ bitrate = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+ chan = 0;
+ output_size = 0;
+}
+
+
+DecoderVorbis::~DecoderVorbis()
+{
+ deinit();
+
+ if (output_buf)
+ delete [] output_buf;
+ output_buf = 0;
+}
+
+
+void DecoderVorbis::stop()
+{
+ user_stop = TRUE;
+}
+
+
+void DecoderVorbis::flush(bool final)
+{
+ ulong min = final ? 0 : bks;
+
+ while ((! done && ! finish) && output_bytes > min)
+ {
+ output()->recycler()->mutex()->lock ();
+
+ while ((! done && ! finish) && output()->recycler()->full())
+ {
+ mutex()->unlock();
+
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+
+ mutex()->lock ();
+ done = user_stop;
+ }
+
+ if (user_stop || finish)
+ {
+ inited = FALSE;
+ done = TRUE;
+ }
+ else
+ {
+ /*ulong sz = output_bytes < bks ? output_bytes : bks;
+ Buffer *b = output()->recycler()->get();
+
+ memcpy(b->data, output_buf, sz);
+ if (sz != bks) memset(b->data + sz, 0, bks - sz);
+
+ b->nbytes = bks;
+ b->rate = bitrate;
+ output_size += b->nbytes;
+ output()->recycler()->add();
+
+ output_bytes -= sz;
+ memmove(output_buf, output_buf + sz, output_bytes);*/
+ output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan);
+ output_size += bks;
+ output_at = output_bytes;
+ }
+
+ if (output()->recycler()->full())
+ {
+ output()->recycler()->cond()->wakeOne();
+ }
+
+ output()->recycler()->mutex()->unlock();
+ }
+}
+
+
+bool DecoderVorbis::initialize()
+{
+ bks = blockSize();
+
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+ seekTime = -1.0;
+ totalTime = 0.0;
+
+ if (! input())
+ {
+ error("DecoderVorbis: cannot initialize. No input.");
+
+ return FALSE;
+ }
+
+ if (! output_buf)
+ output_buf = new char[globalBufferSize];
+ output_at = 0;
+ output_bytes = 0;
+
+ if (! input()->isOpen())
+ {
+ //if (! input()->open(IO_ReadOnly)) {
+ if (! input()->open(QIODevice::ReadOnly))
+ {
+ //error("DecoderVorbis: Failed to open input. Error " +
+ // QString::number(input()->status()) + ".");
+ return FALSE;
+ }
+ }
+
+ ov_callbacks oggcb =
+ {
+ oggread,
+ oggseek,
+ oggclose,
+ oggtell
+ };
+ if (ov_open_callbacks(this, &oggfile, NULL, 0, oggcb) < 0)
+ {
+ error("DecoderVorbis: Cannot open stream.");
+
+ return FALSE;
+ }
+
+ freq = 0;
+ bitrate = ov_bitrate(&oggfile, -1) / 1000;
+ chan = 0;
+
+ totalTime = long(ov_time_total(&oggfile, 0));
+ totalTime = totalTime < 0 ? 0 : totalTime;
+
+ vorbis_info *ogginfo = ov_info(&oggfile, -1);
+ if (ogginfo)
+ {
+ freq = ogginfo->rate;
+ chan = ogginfo->channels;
+ }
+
+ if (output())
+ output()->configure(freq, chan, 16, bitrate);
+
+ inited = TRUE;
+ return TRUE;
+}
+
+
+double DecoderVorbis::lengthInSeconds()
+{
+ if (! inited)
+ return 0;
+
+ return totalTime;
+}
+
+
+void DecoderVorbis::seek(double pos)
+{
+ seekTime = pos;
+}
+
+
+void DecoderVorbis::deinit()
+{
+ ov_clear(&oggfile);
+
+ inited = user_stop = done = finish = FALSE;
+ len = freq = bitrate = 0;
+ stat = chan = 0;
+ output_size = 0;
+}
+
+void DecoderVorbis::run()
+{
+ mutex()->lock ();
+
+ if (! inited)
+ {
+ mutex()->unlock();
+
+ return;
+ }
+
+ //stat = DecoderEvent::Decoding;
+ stat = DecoderState::Decoding;
+ mutex()->unlock();
+
+ {
+ //DecoderEvent e((DecoderEvent::Type) stat);
+ //dispatch(e);
+ //DecoderStatus st ((DecoderStatus::Type) stat);
+ dispatch(DecoderState ((DecoderState::Type) stat));
+
+ //emit statusChanged(stat);
+ }
+
+ int section = 0;
+
+ while (! done && ! finish)
+ {
+ mutex()->lock ();
+ // decode
+
+ if (seekTime >= 0.0)
+ {
+ ov_time_seek(&oggfile, double(seekTime));
+ seekTime = -1.0;
+
+ output_size = long(ov_time_tell(&oggfile)) * long(freq * chan * 2);
+ }
+
+ len = ov_read(&oggfile, (char *) (output_buf + output_at), bks, 0, 2, 1,
+ &section);
+
+ if (len > 0)
+ {
+ bitrate = ov_bitrate_instant(&oggfile) / 1000;
+
+ output_at += len;
+ output_bytes += len;
+
+ if (output())
+ flush();
+ }
+ else if (len == 0)
+ {
+ flush(TRUE);
+
+ if (output())
+ {
+ output()->recycler()->mutex()->lock ();
+ // end of stream
+ while (! output()->recycler()->empty() && ! user_stop)
+ {
+ output()->recycler()->cond()->wakeOne();
+ mutex()->unlock();
+ output()->recycler()->cond()->wait(output()->recycler()->mutex());
+ mutex()->lock ();
+ }
+ output()->recycler()->mutex()->unlock();
+ }
+
+ done = TRUE;
+ if (! user_stop)
+ {
+ finish = TRUE;
+ }
+ }
+ else
+ {
+ // error in read
+ error("DecoderVorbis: Error while decoding stream, File appears to be "
+ "corrupted");
+
+ finish = TRUE;
+ }
+
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+
+ if (finish)
+ stat = DecoderState::Finished;
+ else if (user_stop)
+ stat = DecoderState::Stopped;
+
+ mutex()->unlock();
+
+ {
+ /*DecoderEvent e((DecoderEvent::Type) stat);
+ dispatch(e);*/
+ //DecoderStatus st ((DecoderStatus::Type) stat);
+ //emit statusChanged(st);
+ dispatch(DecoderState ((DecoderState::Type) stat));
+ }
+
+ deinit();
+}
diff --git a/lib/qmmp/Input/vorbis/decoder_vorbis.h b/lib/qmmp/Input/vorbis/decoder_vorbis.h
new file mode 100644
index 000000000..12a5de118
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/decoder_vorbis.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __decoder_vorbis_h
+#define __decoder_vorbis_h
+
+#include "decoder.h"
+
+#include <vorbis/vorbisfile.h>
+
+
+class DecoderVorbis : public Decoder
+{
+public:
+ DecoderVorbis(QObject *, DecoderFactory *, QIODevice *, Output *);
+ virtual ~DecoderVorbis();
+
+ // Standard Decoder API
+ bool initialize();
+ double lengthInSeconds();
+ void seek(double);
+ void stop();
+
+ // Equalizer
+ bool isEQSupported() const { return FALSE; }
+ void setEQEnabled(bool) { ; }
+ void setEQGain(int) { ; }
+ void setEQBands(int[10]) { ; }
+
+
+private:
+ // thread run function
+ void run();
+
+ // helper functions
+ void flush(bool = FALSE);
+ void deinit();
+
+ bool inited, user_stop;
+ int stat;
+
+ // output buffer
+ char *output_buf;
+ ulong output_bytes, output_at;
+
+ // OggVorbis Decoder
+ OggVorbis_File oggfile;
+
+ unsigned int bks;
+ bool done, finish;
+ long len, freq, bitrate;
+ int chan;
+ unsigned long output_size;
+ double totalTime, seekTime;
+};
+
+
+#endif // __decoder_vorbis_h
diff --git a/lib/qmmp/Input/vorbis/decodervorbisfactory.cpp b/lib/qmmp/Input/vorbis/decodervorbisfactory.cpp
new file mode 100644
index 000000000..e4f77b191
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/decodervorbisfactory.cpp
@@ -0,0 +1,79 @@
+#include <QtGui>
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include "detailsdialog.h"
+#include "decoder_vorbis.h"
+#include "tag.h"
+#include "decodervorbisfactory.h"
+
+
+// DecoderOggFactory
+
+bool DecoderVorbisFactory::supports(const QString &source) const
+{
+ //TODO: FLAC?
+ return source.right(4).toLower() == ".ogg";
+}
+
+const QString &DecoderVorbisFactory::name() const
+{
+ static QString name (tr("Ogg Vorbis Plugin"));
+ return name;
+}
+
+const QString &DecoderVorbisFactory::filter() const
+{
+ static QString filter("*.ogg");
+ return filter;
+}
+
+
+const QString &DecoderVorbisFactory::description() const
+{
+ static QString desc(tr("Ogg Vorbis Files"));
+ return desc;
+}
+
+Decoder *DecoderVorbisFactory::create(QObject *parent, QIODevice *input,
+ Output *output)
+{
+ return new DecoderVorbis(parent, this, input, output);
+}
+
+FileTag *DecoderVorbisFactory::createTag(const QString &source)
+{
+ FileTag *tag = new Tag(source);
+ return tag;
+}
+
+void DecoderVorbisFactory::showDetails(QWidget *parent, const QString &path)
+{
+ DetailsDialog *d = new DetailsDialog(parent, path);
+ d -> show();
+}
+
+void DecoderVorbisFactory::showSettings(QWidget *)
+{
+ /*SettingsDialog *s = new SettingsDialog(parent);
+ s -> show();*/
+}
+
+void DecoderVorbisFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About Ogg Vorbis Audio Plugin"),
+ tr("Qmmp Ogg Vorbis Audio Plugin")+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")+"\n"+
+ tr("Source code based on mq3 progect")
+ );
+}
+
+QTranslator *DecoderVorbisFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/vorbis_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(DecoderVorbisFactory)
diff --git a/lib/qmmp/Input/vorbis/decodervorbisfactory.h b/lib/qmmp/Input/vorbis/decodervorbisfactory.h
new file mode 100644
index 000000000..9c411c49d
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/decodervorbisfactory.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DECODERVORBISFACTORY_H
+#define DECODERVORBISFACTORY_H
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <decoder.h>
+#include <output.h>
+#include <decoderfactory.h>
+#include <filetag.h>
+
+
+
+
+class DecoderVorbisFactory : public QObject,
+ DecoderFactory
+{
+Q_OBJECT
+Q_INTERFACES(DecoderFactory)
+
+public:
+ bool supports(const QString &source) const;
+ const QString &name() const;
+ const QString &filter() const; // file extension, ie. ".mp3" or ".ogg"
+ const QString &description() const; // file type, ie. "MPEG Audio Files"
+ Decoder *create(QObject *, QIODevice *, Output *);
+ FileTag *createTag(const QString &source);
+ void showDetails(QWidget *parent, const QString &path);
+ void showSettings(QWidget *parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+};
+
+#endif
diff --git a/lib/qmmp/Input/vorbis/detailsdialog.cpp b/lib/qmmp/Input/vorbis/detailsdialog.cpp
new file mode 100644
index 000000000..9fb4f761c
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/detailsdialog.cpp
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+#include <taglib/vorbisfile.h>
+
+#include "detailsdialog.h"
+
+DetailsDialog::DetailsDialog(QWidget *parent, const QString &path)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ m_path = path;
+ setWindowTitle (path.section('/',-1));
+ path.section('/',-1);
+ ui.pathLineEdit->setText(m_path);
+ loadVorbisInfo();
+ loadTag();
+
+}
+
+
+DetailsDialog::~DetailsDialog()
+{}
+
+void DetailsDialog::loadVorbisInfo()
+{
+ TagLib::Ogg::Vorbis::File f (m_path.toLocal8Bit());
+ //l.label
+ //ui. f.audioProperties()->level();
+ QString text;
+ text = QString("%1").arg(f.audioProperties()->length()/60);
+ text +=":"+QString("%1").arg(f.audioProperties()->length()%60,2,10,QChar('0'));
+ ui.lengthLabel->setText(text);
+ text = QString("%1").arg(f.audioProperties()->sampleRate());
+ ui.sampleRateLabel->setText(text+" "+tr("Hz"));
+ text = QString("%1").arg(f.audioProperties()->channels());
+ ui.channelsLabel->setText(text);
+ text = QString("%1").arg(f.audioProperties()->bitrateNominal());
+ ui.nominalLabel->setText(text+" "+tr("kbps"));
+ text = QString("%1").arg(f.audioProperties()->bitrateMaximum());
+ ui.maximumLabel->setText(text+" "+tr("kbps"));
+ text = QString("%1").arg(f.audioProperties()->bitrateMinimum());
+ ui.minimumLabel->setText(text+" "+tr("kbps"));
+ text = QString("%1 "+tr("KB")).arg(f.length()/1024);
+ ui.fileSizeLabel->setText(text);
+
+}
+
+void DetailsDialog::loadTag()
+{
+ TagLib::FileRef f (m_path.toLocal8Bit());
+
+ if (f.tag())
+ { //TODO: load codec name from config
+
+ TagLib::String title = f.tag()->title();
+ TagLib::String artist = f.tag()->artist();
+ TagLib::String album = f.tag()->album();
+ TagLib::String comment = f.tag()->comment();
+ TagLib::String genre = f.tag()->genre();
+ QString string = QString::fromUtf8(title.toCString(TRUE)).trimmed();
+ ui.titleLineEdit->setText(string);
+ string = QString::fromUtf8(artist.toCString(TRUE)).trimmed();
+ ui.artistLineEdit->setText(string);
+ string = QString::fromUtf8(album.toCString(TRUE)).trimmed();
+ ui.albumLineEdit->setText(string);
+ string = QString::fromUtf8(comment.toCString(TRUE)).trimmed();
+ ui.commentLineEdit->setText(string);
+ string = QString("%1").arg(f.tag()->year());
+ ui.yearLineEdit->setText(string);
+ string = QString("%1").arg(f.tag()->track());
+ ui.trackLineEdit->setText(string);
+ string = QString::fromUtf8(genre.toCString(TRUE)).trimmed();
+ ui.genreLineEdit->setText(string);
+ }
+}
+
diff --git a/lib/qmmp/Input/vorbis/detailsdialog.h b/lib/qmmp/Input/vorbis/detailsdialog.h
new file mode 100644
index 000000000..60dda5e59
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/detailsdialog.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DETAILSDIALOG_H
+#define DETAILSDIALOG_H
+
+#include <QDialog>
+
+#include "ui_detailsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class DetailsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ DetailsDialog(QWidget *parent = 0, const QString &path = 0);
+
+ ~DetailsDialog();
+
+private:
+ void loadVorbisInfo();
+ void loadTag();
+ Ui::DetailsDialog ui;
+ QString m_path;
+
+};
+
+#endif
diff --git a/lib/qmmp/Input/vorbis/detailsdialog.ui b/lib/qmmp/Input/vorbis/detailsdialog.ui
new file mode 100644
index 000000000..67bedbc4f
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/detailsdialog.ui
@@ -0,0 +1,384 @@
+<ui version="4.0" >
+ <class>DetailsDialog</class>
+ <widget class="QDialog" name="DetailsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>536</width>
+ <height>375</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Details</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item rowspan="2" row="1" column="0" colspan="2" >
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="minimumSize" >
+ <size>
+ <width>220</width>
+ <height>16</height>
+ </size>
+ </property>
+ <property name="title" >
+ <string>Ogg Vorbis Info</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="fileSizeLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QLabel" name="channelsLabel" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Length:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="lengthLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Sample rate:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="sampleRateLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_10" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>Channels:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="4" column="0" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_3" >
+ <property name="title" >
+ <string>Bit Rate</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignHCenter</set>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="maximumLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="minimumLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_9" >
+ <property name="layoutDirection" >
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="text" >
+ <string>Minimum:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="nominalLabel" >
+ <property name="text" >
+ <string>-</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_8" >
+ <property name="text" >
+ <string>Maximum:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_7" >
+ <property name="text" >
+ <string>Nominal:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_5" >
+ <property name="text" >
+ <string>File size:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="pathLineEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_28" >
+ <property name="text" >
+ <string>File path:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="3" >
+ <widget class="QPushButton" name="pushButton_3" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>111</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" colspan="2" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title" >
+ <string>Ogg Vorbis Tag</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>8</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="6" column="1" colspan="2" >
+ <widget class="QPushButton" name="pushButton" >
+ <property name="enabled" >
+ <bool>false</bool>
+ </property>
+ <property name="text" >
+ <string>Save</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="3" >
+ <widget class="QLineEdit" name="trackLineEdit" />
+ </item>
+ <item row="4" column="2" >
+ <widget class="QLabel" name="label_26" >
+ <property name="text" >
+ <string>Track number:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1" >
+ <widget class="QLineEdit" name="yearLineEdit" />
+ </item>
+ <item row="4" column="0" >
+ <widget class="QLabel" name="label_25" >
+ <property name="text" >
+ <string>Year:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0" >
+ <widget class="QLabel" name="label_27" >
+ <property name="text" >
+ <string>Genre:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_24" >
+ <property name="text" >
+ <string>Comment:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_23" >
+ <property name="text" >
+ <string>Album:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_22" >
+ <property name="text" >
+ <string>Artist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_21" >
+ <property name="text" >
+ <string>Title:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" colspan="3" >
+ <widget class="QLineEdit" name="titleLineEdit" />
+ </item>
+ <item row="1" column="1" colspan="3" >
+ <widget class="QLineEdit" name="artistLineEdit" />
+ </item>
+ <item row="2" column="1" colspan="3" >
+ <widget class="QLineEdit" name="albumLineEdit" />
+ </item>
+ <item row="3" column="1" colspan="3" >
+ <widget class="QLineEdit" name="commentLineEdit" />
+ </item>
+ <item row="5" column="1" colspan="2" >
+ <widget class="QLineEdit" name="genreLineEdit" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>pushButton_3</sender>
+ <signal>clicked()</signal>
+ <receiver>DetailsDialog</receiver>
+ <slot>close()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>623</x>
+ <y>353</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>539</x>
+ <y>352</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Input/vorbis/tag.cpp b/lib/qmmp/Input/vorbis/tag.cpp
new file mode 100644
index 000000000..15964e615
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/tag.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "tag.h"
+
+Tag::Tag(const QString &source)
+ : FileTag()
+{
+ m_length = 0;
+ m_year = 0;
+ m_track = 0;
+ m_empty = TRUE;
+ TagLib::FileRef fileRef(source.toLocal8Bit ());
+
+ m_tag = fileRef.tag();
+ if(m_tag && !m_tag->isEmpty())
+ {
+ m_album = QString::fromUtf8(m_tag->album().toCString(TRUE)).trimmed();
+ m_artist = QString::fromUtf8(m_tag->artist().toCString(TRUE)).trimmed();
+ m_comment = QString::fromUtf8(m_tag->comment().toCString(TRUE)).trimmed();
+ m_genre = QString::fromUtf8(m_tag->genre().toCString(TRUE)).trimmed();
+ m_title = QString::fromUtf8(m_tag->title().toCString(TRUE)).trimmed();
+ m_year = 0;
+ m_track = 0;
+ if(fileRef.audioProperties())
+ m_length = fileRef.audioProperties()->length();
+ m_empty = FALSE;
+ }
+ else
+ m_tag = 0;
+}
+
+
+Tag::~Tag()
+{}
+
+
+bool Tag::isEmpty()
+{
+ return m_empty;
+}
+
+QString Tag::album()
+{
+ return m_album;
+}
+
+QString Tag::artist()
+{
+ return m_artist;
+}
+
+QString Tag::comment()
+{
+ return m_comment;
+}
+
+QString Tag::genre()
+{
+ return m_genre;
+}
+
+QString Tag::title()
+{
+ return m_title;
+}
+
+uint Tag::length()
+{
+ return m_length;
+}
+
+uint Tag::track()
+{
+ return m_track;
+}
+
+uint Tag::year()
+{
+ return m_year;
+}
diff --git a/lib/qmmp/Input/vorbis/tag.h b/lib/qmmp/Input/vorbis/tag.h
new file mode 100644
index 000000000..ece3b7d2c
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/tag.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef XIPHCOMMENT_H
+#define XIPHCOMMENT_H
+
+#include <taglib/tag.h>
+#include <taglib/fileref.h>
+
+#include <filetag.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Tag : public FileTag
+{
+public:
+ Tag(const QString &source);
+
+ ~Tag();
+
+ virtual QString album();
+ virtual QString artist();
+ virtual QString comment();
+ virtual QString genre();
+ virtual QString title();
+ virtual uint length();
+ virtual uint track();
+ virtual uint year();
+ virtual bool isEmpty();
+
+private:
+ TagLib::Tag *m_tag;
+ QString m_album;
+ QString m_artist;
+ QString m_comment;
+ QString m_genre;
+ QString m_title;
+ uint m_length;
+ uint m_year;
+ uint m_track;
+ bool m_empty;
+};
+
+#endif
diff --git a/lib/qmmp/Input/vorbis/translations/translations.qrc b/lib/qmmp/Input/vorbis/translations/translations.qrc
new file mode 100644
index 000000000..c5cacdfb0
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>vorbis_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.qm b/lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.qm
new file mode 100644
index 000000000..a6a3a77b7
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.ts b/lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.ts
new file mode 100644
index 000000000..7824256ea
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/translations/vorbis_plugin_ru.ts
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>DecoderVorbisFactory</name>
+ <message>
+ <location filename="../decodervorbisfactory.cpp" line="21"/>
+ <source>Ogg Vorbis Plugin</source>
+ <translation>Модуль Ogg Vorbis</translation>
+ </message>
+ <message>
+ <location filename="../decodervorbisfactory.cpp" line="34"/>
+ <source>Ogg Vorbis Files</source>
+ <translation>Файлы Ogg Vorbis</translation>
+ </message>
+ <message>
+ <location filename="../decodervorbisfactory.cpp" line="64"/>
+ <source>About Ogg Vorbis Audio Plugin</source>
+ <translation>Об аудио-модуле Ogg Vorbis</translation>
+ </message>
+ <message>
+ <location filename="../decodervorbisfactory.cpp" line="65"/>
+ <source>Qmmp Ogg Vorbis Audio Plugin</source>
+ <translation>Аудио-модуль Ogg Vorbis для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../decodervorbisfactory.cpp" line="66"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+ <message>
+ <location filename="../decodervorbisfactory.cpp" line="68"/>
+ <source>Source code based on mq3 progect</source>
+ <translation>Исходный код основан на проекте mq3</translation>
+ </message>
+</context>
+<context>
+ <name>DetailsDialog</name>
+ <message>
+ <location filename="../detailsdialog.cpp" line="54"/>
+ <source>Hz</source>
+ <translation>Гц</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="265"/>
+ <source>Save</source>
+ <translation>Сохранить</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="275"/>
+ <source>Track number:</source>
+ <translation>Номер трека:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="288"/>
+ <source>Year:</source>
+ <translation>Год:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="298"/>
+ <source>Genre:</source>
+ <translation>Жанр:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="308"/>
+ <source>Comment:</source>
+ <translation>Комментарий:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="318"/>
+ <source>Album:</source>
+ <translation>Альбом:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="328"/>
+ <source>Artist:</source>
+ <translation>Исполнитель:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="338"/>
+ <source>Title:</source>
+ <translation>Название:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="222"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="162"/>
+ <source>-</source>
+ <translation></translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="192"/>
+ <source>File size:</source>
+ <translation>Размер файла:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="77"/>
+ <source>Sample rate:</source>
+ <translation>Дискретизация:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="212"/>
+ <source>File path:</source>
+ <translation>Путь к файлу:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="31"/>
+ <source>Ogg Vorbis Info</source>
+ <translation>Информация Ogg Vorbis</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="60"/>
+ <source>Length:</source>
+ <translation>Длительность:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="97"/>
+ <source>Channels:</source>
+ <translation>Каналов:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="120"/>
+ <source>Bit Rate</source>
+ <translation>Битовая частота</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="152"/>
+ <source>Minimum:</source>
+ <translation>Минимальная:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="169"/>
+ <source>Maximum:</source>
+ <translation>Максимальная:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="179"/>
+ <source>Nominal:</source>
+ <translation>Номинальная:</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="250"/>
+ <source>Ogg Vorbis Tag</source>
+ <translation>Оgg Vorbis-тег</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="62"/>
+ <source>kbps</source>
+ <translation>Кб/с</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.cpp" line="63"/>
+ <source>KB</source>
+ <translation>Кб</translation>
+ </message>
+ <message>
+ <location filename="../detailsdialog.ui" line="13"/>
+ <source>Details</source>
+ <translation>Информация</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Input/vorbis/vorbis.pro b/lib/qmmp/Input/vorbis/vorbis.pro
new file mode 100644
index 000000000..ce3ee72a6
--- /dev/null
+++ b/lib/qmmp/Input/vorbis/vorbis.pro
@@ -0,0 +1,29 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Input/ogg
+# ???? - ??????????:
+
+FORMS += detailsdialog.ui
+HEADERS += decodervorbisfactory.h \
+ decoder_vorbis.h \
+ detailsdialog.h \
+ tag.h
+SOURCES += decoder_vorbis.cpp \
+ decodervorbisfactory.cpp \
+ detailsdialog.cpp \
+ tag.cpp
+DESTDIR = ../
+QMAKE_CLEAN += ../libvorbis.so
+INCLUDEPATH += ../../../
+CONFIG += release \
+warn_on \
+plugin \
+link_pkgconfig
+TEMPLATE = lib
+QMAKE_LIBDIR += ../../../
+LIBS += -lqmmp -L/usr/lib
+PKGCONFIG += taglib ogg vorbisfile vorbis
+#TRANSLATIONS = translations/vorbis_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Input
+INSTALLS += target
diff --git a/lib/qmmp/Output/CMakeLists.txt b/lib/qmmp/Output/CMakeLists.txt
new file mode 100644
index 000000000..85d236d39
--- /dev/null
+++ b/lib/qmmp/Output/CMakeLists.txt
@@ -0,0 +1,16 @@
+SET(USE_ALSA TRUE CACHE BOOL "enable/disable alsa plugin")
+SET(USE_JACK TRUE CACHE BOOL "enable/disable jack plugin")
+
+IF(USE_ALSA)
+MESSAGE( STATUS "ALSA ON")
+add_subdirectory(alsa)
+ELSE(USE_MAD)
+MESSAGE( STATUS "ALSA OFF")
+ENDIF(USE_ALSA)
+
+IF(USE_JACK)
+MESSAGE( STATUS "JACK ON")
+add_subdirectory(jack)
+ELSE(USE_FLAC)
+MESSAGE( STATUS "JACK OFF")
+ENDIF(USE_JACK)
diff --git a/lib/qmmp/Output/Output.pro b/lib/qmmp/Output/Output.pro
new file mode 100644
index 000000000..727d3ac20
--- /dev/null
+++ b/lib/qmmp/Output/Output.pro
@@ -0,0 +1,18 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Output
+# ???? - ?????? ? ?????????????
+
+include(../../../qmmp.pri)
+
+CONFIG += release warn_on
+TEMPLATE = subdirs
+
+SUBDIRS += alsa
+
+contains(CONFIG, JACK_PLUGIN){
+ SUBDIRS += jack
+ message(***********************)
+ message(* JACK plugin enabled *)
+ message(***********************)
+}
diff --git a/lib/qmmp/Output/alsa/CMakeLists.txt b/lib/qmmp/Output/alsa/CMakeLists.txt
new file mode 100644
index 000000000..987c6b5d3
--- /dev/null
+++ b/lib/qmmp/Output/alsa/CMakeLists.txt
@@ -0,0 +1,65 @@
+project(libalsa)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+SET(libalsa_SRCS
+ outputalsa.cpp
+ outputalsafactory.cpp
+ settingsdialog.cpp
+)
+
+SET(libalsa_MOC_HDRS
+ outputalsa.h
+ outputalsafactory.h
+ settingsdialog.h
+)
+
+SET(libalsa_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libalsa_RCC_SRCS ${libalsa_RCCS})
+
+QT4_WRAP_CPP(libalsa_MOC_SRCS ${libalsa_MOC_HDRS})
+
+# user interface
+
+
+SET(libalsa_UIS
+ settingsdialog.ui
+)
+
+QT4_WRAP_UI(libalsa_UIS_H ${libalsa_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(alsa SHARED ${libalsa_SRCS} ${libalsa_MOC_SRCS} ${libalsa_UIS_H}
+ ${libalsa_RCC_SRCS})
+target_link_libraries(alsa ${QT_LIBRARIES} -lqmmp -lasound)
+install(TARGETS alsa DESTINATION lib/qmmp/Output)
+
diff --git a/lib/qmmp/Output/alsa/alsa.pro b/lib/qmmp/Output/alsa/alsa.pro
new file mode 100644
index 000000000..06ef0b6ce
--- /dev/null
+++ b/lib/qmmp/Output/alsa/alsa.pro
@@ -0,0 +1,26 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Output/alsa
+# ???? - ??????????:
+
+HEADERS += outputalsa.h \
+ outputalsafactory.h \
+ settingsdialog.h
+SOURCES += outputalsa.cpp \
+ outputalsafactory.cpp \
+ settingsdialog.cpp
+INCLUDEPATH += ../../../
+QMAKE_LIBDIR += ../../../
+QMAKE_CLEAN = ../libalsa.so
+CONFIG += release \
+warn_on \
+thread \
+plugin
+DESTDIR = ../
+TEMPLATE = lib
+LIBS += -lqmmp -lasound
+FORMS += settingsdialog.ui
+#TRANSLATIONS = translations/alsa_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Output
+INSTALLS += target
diff --git a/lib/qmmp/Output/alsa/outputalsa.cpp b/lib/qmmp/Output/alsa/outputalsa.cpp
new file mode 100644
index 000000000..4faa53a47
--- /dev/null
+++ b/lib/qmmp/Output/alsa/outputalsa.cpp
@@ -0,0 +1,559 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QObject>
+#include <QApplication>
+#include <QtGlobal>
+#include <QDir>
+#include <QSettings>
+#include <QTimer>
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+
+#include "outputalsa.h"
+#include "constants.h"
+#include "buffer.h"
+#include "visualization.h"
+
+OutputALSA::OutputALSA(QObject * parent)
+ : Output(parent, Output::Custom), m_inited(FALSE), m_pause(FALSE), m_play(FALSE),
+ m_userStop(FALSE), m_totalWritten(0), m_currentSeconds(-1),
+ m_bps(-1), m_frequency(-1), m_channels(-1), m_precision(-1)
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ QString dev_name = settings.value("ALSA/device","default").toString();
+ pcm_name = strdup(dev_name.toAscii().data());
+ stream = SND_PCM_STREAM_PLAYBACK;
+ snd_pcm_hw_params_alloca(&hwparams);
+ pcm_handle = 0;
+ //alsa mixer
+ mixer = 0;
+ QString card = settings.value("ALSA/mixer_card","hw:0").toString();
+ QString dev = settings.value("ALSA/mixer_device", "PCM").toString();
+ setupMixer(card, dev);
+}
+
+OutputALSA::~OutputALSA()
+{
+ uninitialize();
+ free (pcm_name);
+ if(mixer)
+ snd_mixer_close(mixer);
+}
+
+void OutputALSA::stop()
+{
+ m_userStop = TRUE;
+}
+
+void OutputALSA::status()
+{
+ long ct = (m_totalWritten - latency()) / m_bps;
+
+ if (ct < 0)
+ ct = 0;
+
+ if (ct > m_currentSeconds)
+ {
+ m_currentSeconds = ct;
+ dispatch(m_currentSeconds, m_totalWritten, m_rate,
+ m_frequency, m_precision, m_channels);
+ }
+}
+
+long OutputALSA::written()
+{
+ return m_totalWritten;
+}
+
+void OutputALSA::seek(long pos)
+{
+ recycler()->mutex()->lock ();
+ recycler()->clear();
+ recycler()->mutex()->unlock();
+
+ m_totalWritten = (pos * m_bps);
+ m_currentSeconds = -1;
+}
+
+void OutputALSA::configure(long freq, int chan, int prec, int brate)
+{
+ // we need to configure
+ if (freq != m_frequency || chan != m_channels || prec != m_precision)
+ {
+ m_frequency = freq;
+ m_channels = chan;
+ m_precision = prec;
+ m_bps = freq * chan * (prec / 8);
+ snd_pcm_hw_params_alloca(&hwparams);
+ if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0)
+ {
+ qWarning("OutputALSA: Can not configure this PCM device.");
+ return;
+ }
+
+ uint rate = m_frequency; /* Sample rate */
+ uint exact_rate = m_frequency; /* Sample rate returned by */
+
+ /* load settings from config */
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ uint buffer_time = settings.value("buffer_time",500).toUInt()*1000;
+ uint period_time = settings.value("period_time",100).toUInt()*1000;
+ settings.endGroup();
+
+ if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
+ {
+ qWarning("OutputALSA: Error setting access.");
+ return;
+ }
+
+
+ if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
+ {
+ qDebug("OutputALSA: Error setting format.");
+ return;
+ }
+
+
+ exact_rate = rate;// = 11000;
+ qDebug("OutputALSA: frequency=%d, channels=%d, bitrate=%d",
+ rate, chan, brate);
+ if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0)
+ {
+ qWarning("OutputALSA: Error setting rate.\n");
+ return;
+ }
+ if (rate != exact_rate)
+ {
+ qWarning("OutputALSA: The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.", rate, exact_rate);
+ }
+
+ uint c = m_channels;
+ if (snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c) < 0)
+ {
+ qWarning("OutputALSA: Error setting channels.");
+ return;
+ }
+
+ if (snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams,
+ &period_time ,0) < 0 )
+ {
+ qWarning("OutputALSA: Error setting HW buffer.");
+ return;
+ }
+ if (snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams,
+ &buffer_time ,0) < 0 )
+ {
+ qWarning("Error setting HW buffer.\n");
+ return;
+ }
+ if (snd_pcm_hw_params(pcm_handle, hwparams) < 0)
+ {
+ qWarning("OutputALSA: Error setting HW params.");
+ return;
+ }
+ prepareVisuals();
+ }
+}
+
+void OutputALSA::reset()
+{
+ if (pcm_handle)
+ {
+ snd_pcm_close(pcm_handle);
+ pcm_handle = 0;
+ }
+ if (snd_pcm_open(&pcm_handle, pcm_name, stream, SND_PCM_NONBLOCK) < 0)
+ {
+ qWarning ("OutputALSA: Error opening PCM device %s", pcm_name);
+ return;
+ }
+}
+
+
+void OutputALSA::pause()
+{
+ if (!m_play)
+ return;
+ m_pause = (m_pause) ? FALSE : TRUE;
+ OutputState::Type state = m_pause ? OutputState::Paused: OutputState::Playing;
+ dispatch(state);
+}
+
+bool OutputALSA::initialize()
+{
+ m_inited = m_pause = m_play = m_userStop = FALSE;
+
+ if (!pcm_handle < 0)
+ return FALSE;
+
+ m_currentSeconds = -1;
+ m_totalWritten = 0;
+ if (snd_pcm_open(&pcm_handle, pcm_name, stream, SND_PCM_NONBLOCK) < 0)
+ {
+ qWarning ("OutputALSA: Error opening PCM device %s", pcm_name);
+ return FALSE;
+ }
+
+ m_inited = TRUE;
+ return TRUE;
+}
+
+
+long OutputALSA::latency()
+{
+ long used = 0;
+
+ /*if (! m_pause)
+ {
+ if (ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &used) == -1)
+ used = 0;
+ }*/
+
+ return used;
+}
+
+void OutputALSA::run()
+{
+
+ mutex()->lock ();
+ if (! m_inited)
+ {
+ mutex()->unlock();
+ return;
+ }
+
+ m_play = TRUE;
+
+ mutex()->unlock();
+
+ Buffer *b = 0;
+ bool done = FALSE;
+ unsigned long n = 0;
+ long m = 0;
+ snd_pcm_uframes_t l;
+
+ dispatch(OutputState::Playing);
+
+ while (! done)
+ {
+ mutex()->lock ();
+ recycler()->mutex()->lock ();
+
+ done = m_userStop;
+
+ while (! done && (recycler()->empty() || m_pause))
+ {
+ mutex()->unlock();
+ recycler()->cond()->wakeOne();
+ recycler()->cond()->wait(recycler()->mutex());
+ mutex()->lock ();
+ done = m_userStop;
+ status();
+
+ }
+
+ if (! b)
+ {
+ b = recycler()->next();
+ if (b->rate)
+ m_rate = b->rate;
+ }
+
+ recycler()->cond()->wakeOne();
+ recycler()->mutex()->unlock();
+
+ if (b)
+ {
+
+ l = qMin(int(globalBlockSize*2), int(b->nbytes - n));
+ l = snd_pcm_bytes_to_frames(pcm_handle, l);
+ //l = qMin(aval, int(l));
+ while (l>0)
+ {
+ m = snd_pcm_writei (pcm_handle, b->data+n, l);
+
+ if (m > 0)
+ {
+ n += snd_pcm_frames_to_bytes(pcm_handle, m);
+ l -= m;
+ status();
+ dispatchVisual(b, m_totalWritten, m_channels, m_precision);
+ }
+ else if (m == -EAGAIN)
+ {
+ usleep(500);
+ }
+ else if (m == -EPIPE)
+ {
+ qDebug ("OutputALSA: underrun!");
+ if ((m = snd_pcm_prepare(pcm_handle)) < 0)
+ {
+ qDebug ("OutputALSA: Can't recover after underrun: %s",
+ snd_strerror(m));
+ /* TODO: reopen the device */
+ break;
+ }
+ }
+ else if (m == -ESTRPIPE)
+ {
+ qDebug ("OutputALSA: Suspend, trying to resume");
+ while ((m = snd_pcm_resume(pcm_handle))
+ == -EAGAIN)
+ sleep (1);
+ if (m < 0)
+ {
+ qDebug ("OutputALSA: Failed, restarting");
+ if ((m = snd_pcm_prepare(pcm_handle))
+ < 0)
+ {
+ qDebug ("OutputALSA: Failed to restart device: %s.",
+ snd_strerror(m));
+ break;
+ }
+ }
+ }
+ else if (m < 0)
+ {
+ qDebug ("OutputALSA: Can't play: %s", snd_strerror(m));
+ break;
+ }
+ }
+ status();
+ // force buffer change
+ m_totalWritten += n;
+ n = b->nbytes;
+ m = 0;
+ }
+ if (n == b->nbytes)
+ {
+ recycler()->mutex()->lock ();
+ recycler()->done();
+ recycler()->mutex()->unlock();
+ b = 0;
+ n = 0;
+ }
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+
+ m_play = FALSE;
+
+ dispatch(OutputState::Stopped);
+
+ mutex()->unlock();
+
+}
+
+void OutputALSA::uninitialize()
+{
+ if (!m_inited)
+ return;
+ m_inited = FALSE;
+ m_pause = FALSE;
+ m_play = FALSE;
+ m_userStop = FALSE;
+ m_totalWritten = 0;
+ m_currentSeconds = -1;
+ m_bps = -1;
+ m_frequency = -1;
+ m_channels = -1;
+ m_precision = -1;
+ if (pcm_handle)
+ {
+ qDebug("OutputALSA: closing pcm_handle");
+ snd_pcm_close(pcm_handle);
+ pcm_handle = 0;
+ }
+ dispatch(OutputState::Stopped);
+}
+/* ****** MIXER ******* */
+
+int OutputALSA::setupMixer(QString card, QString device)
+{
+ char *name;
+ long int a, b;
+ long alsa_min_vol = 0, alsa_max_vol = 100;
+ int err, index;
+
+ qDebug("OutputALSA: setupMixer()");
+
+ if ((err = getMixer(&mixer, card)) < 0)
+ return err;
+
+ parseMixerName(device.toAscii().data(), &name, &index);
+
+ pcm_element = getMixerElem(mixer, name, index);
+
+ free(name);
+
+ if (!pcm_element)
+ {
+ qWarning("OutputALSA: Failed to find mixer element");
+ return -1;
+ }
+
+ /* This hack was copied from xmms.
+ * Work around a bug in alsa-lib up to 1.0.0rc2 where the
+ * new range don't take effect until the volume is changed.
+ * This hack should be removed once we depend on Alsa 1.0.0.
+ */
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_LEFT, &a);
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_RIGHT, &b);
+
+ snd_mixer_selem_get_playback_volume_range(pcm_element,
+ &alsa_min_vol, &alsa_max_vol);
+ snd_mixer_selem_set_playback_volume_range(pcm_element, 0, 100);
+
+ if (alsa_max_vol == 0)
+ {
+ pcm_element = NULL;
+ return -1;
+ }
+
+ setVolume(a * 100 / alsa_max_vol, b * 100 / alsa_max_vol);
+
+ qDebug("OutputALSA: setupMixer() succes");
+
+ return 0;
+}
+
+void OutputALSA::parseMixerName(char *str, char **name, int *index)
+{
+ char *end;
+
+ while (isspace(*str))
+ str++;
+
+ if ((end = strchr(str, ',')) != NULL)
+ {
+ *name = strndup(str, end - str);
+ end++;
+ *index = atoi(end);
+ }
+ else
+ {
+ *name = strdup(str);
+ *index = 0;
+ }
+}
+
+snd_mixer_elem_t* OutputALSA::getMixerElem(snd_mixer_t *mixer, char *name, int index)
+{
+ snd_mixer_selem_id_t *selem_id;
+ snd_mixer_elem_t* elem;
+ snd_mixer_selem_id_alloca(&selem_id);
+
+ if (index != -1)
+ snd_mixer_selem_id_set_index(selem_id, index);
+ if (name != NULL)
+ snd_mixer_selem_id_set_name(selem_id, name);
+
+ elem = snd_mixer_find_selem(mixer, selem_id);
+
+ return elem;
+}
+
+void OutputALSA::setVolume(int l, int r)
+{
+
+ if (!pcm_element)
+ return;
+
+ snd_mixer_selem_set_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_LEFT, l);
+ snd_mixer_selem_set_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_RIGHT, r);
+}
+
+int OutputALSA::getMixer(snd_mixer_t **mixer, QString card)
+{
+ char *dev;
+ int err;
+
+
+ dev = strdup(card.toAscii().data());
+
+ if ((err = snd_mixer_open(mixer, 0)) < 0)
+ {
+ qWarning("OutputALSA: Failed to open empty mixer: %s",
+ snd_strerror(-err));
+ mixer = NULL;
+ return -1;
+ }
+ if ((err = snd_mixer_attach(*mixer, dev)) < 0)
+ {
+ qWarning("OutputALSA: Attaching to mixer %s failed: %s",
+ dev, snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0)
+ {
+ qWarning("OutputALSA: Failed to register mixer: %s",
+ snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_load(*mixer)) < 0)
+ {
+ qWarning("OutputALSA: Failed to load mixer: %s",
+ snd_strerror(-err));
+ return -1;
+ }
+
+ free(dev);
+
+ return (*mixer != NULL);
+}
+
+void OutputALSA::checkVolume()
+{
+ long ll = 0, lr = 0;
+
+ if (!pcm_element)
+ return;
+
+ snd_mixer_handle_events(mixer);
+
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_LEFT,
+ &ll);
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_RIGHT,
+ &lr);
+ //qDebug("%d, %d",ll, lr);
+
+ ll = (ll > 100) ? 100 : ll;
+ lr = (lr > 100) ? 100 : lr;
+ ll = (ll < 0) ? 0 : ll;
+ lr = (lr < 0) ? 0 : lr;
+ if (bl!=ll || br!=lr)
+ {
+ bl = ll;
+ br = lr;
+ dispatchVolume(ll,lr);
+ }
+}
+
diff --git a/lib/qmmp/Output/alsa/outputalsa.h b/lib/qmmp/Output/alsa/outputalsa.h
new file mode 100644
index 000000000..5c872bec9
--- /dev/null
+++ b/lib/qmmp/Output/alsa/outputalsa.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef OUTPUTALSA_H
+#define OUTPUTALSA_H
+
+class OutputALSA;
+
+#include <output.h>
+#include <QObject>
+extern "C" {
+#include <alsa/asoundlib.h>
+}
+#if defined( Q_OS_WIN32 )
+#include <dsound.h>
+#include "constants.h"
+#endif
+
+
+class OutputALSA : public Output
+{
+Q_OBJECT
+public:
+ OutputALSA(QObject * parent = 0);
+ ~OutputALSA();
+
+ bool initialize();
+ bool isInitialized() const { return m_inited; }
+ void uninitialize();
+ void configure(long, int, int, int);
+ void stop();
+ void pause();
+ long written();
+ long latency();
+ void seek(long);
+ void setVolume(int l, int r);
+ void checkVolume();
+
+private:
+ // thread run function
+ void run();
+
+ // helper functions
+ void reset();
+ void status();
+
+ bool m_inited, m_pause, m_play, m_userStop;
+ long m_totalWritten, m_currentSeconds, m_bps;
+ int m_rate, m_frequency, m_channels, m_precision;
+ //alsa
+ snd_pcm_t *pcm_handle;
+ snd_pcm_stream_t stream;
+ snd_pcm_hw_params_t *hwparams;
+ char *pcm_name;
+ //alsa
+
+ //alsa mixer
+ int setupMixer(QString card, QString device);
+ void parseMixerName(char *str, char **name, int *index);
+ int getMixer(snd_mixer_t **mixer, QString card);
+ snd_mixer_elem_t* getMixerElem(snd_mixer_t *mixer, char *name, int index);
+ snd_mixer_t *mixer;
+ snd_mixer_elem_t *pcm_element;
+ long bl, br;
+};
+
+
+#endif // OUTPUTALSA_H
diff --git a/lib/qmmp/Output/alsa/outputalsafactory.cpp b/lib/qmmp/Output/alsa/outputalsafactory.cpp
new file mode 100644
index 000000000..25d8c337c
--- /dev/null
+++ b/lib/qmmp/Output/alsa/outputalsafactory.cpp
@@ -0,0 +1,60 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QtGui>
+
+#include "settingsdialog.h"
+#include "outputalsa.h"
+#include "outputalsafactory.h"
+
+
+const QString& OutputALSAFactory::name() const
+{
+ static QString name(tr("ALSA Plugin"));
+ return name;
+}
+
+Output* OutputALSAFactory::create(QObject* parent)
+{
+ return new OutputALSA(parent);
+}
+
+void OutputALSAFactory::showSettings(QWidget* parent)
+{
+ SettingsDialog *s = new SettingsDialog(parent);
+ s -> show();
+}
+
+void OutputALSAFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About ALSA Output Plugin"),
+ tr("Qmmp ALSA Output Plugin")+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>"));
+}
+
+QTranslator *OutputALSAFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/alsa_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(OutputALSAFactory)
diff --git a/lib/qmmp/Output/alsa/outputalsafactory.h b/lib/qmmp/Output/alsa/outputalsafactory.h
new file mode 100644
index 000000000..24129e2d5
--- /dev/null
+++ b/lib/qmmp/Output/alsa/outputalsafactory.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef OUTPUTALSAFACTORY_H
+#define OUTPUTALSAFACTORY_H
+
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <output.h>
+#include <outputfactory.h>
+
+
+class OutputALSAFactory : public QObject,
+ OutputFactory
+{
+Q_OBJECT
+Q_INTERFACES(OutputFactory);
+
+public:
+ const QString& name() const;
+ Output* create(QObject* parent);
+ void showSettings(QWidget* parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+
+};
+
+#endif
diff --git a/lib/qmmp/Output/alsa/settingsdialog.cpp b/lib/qmmp/Output/alsa/settingsdialog.cpp
new file mode 100644
index 000000000..89c6cae84
--- /dev/null
+++ b/lib/qmmp/Output/alsa/settingsdialog.cpp
@@ -0,0 +1,237 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QSettings>
+#include <QDir>
+
+extern "C"
+{
+#include <alsa/asoundlib.h>
+}
+
+#include "settingsdialog.h"
+
+SettingsDialog::SettingsDialog ( QWidget *parent )
+ : QDialog ( parent )
+{
+ ui.setupUi ( this );
+ setAttribute ( Qt::WA_DeleteOnClose );
+ ui.deviceComboBox->setEditable ( TRUE );
+ getCards();
+ connect (ui.deviceComboBox, SIGNAL(activated(int)),SLOT(setText(int)));
+ connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings()));
+ connect(ui.mixerCardComboBox, SIGNAL(activated(int)), SLOT(showMixerDevices(int)));
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ ui.deviceComboBox->setEditText(settings.value("device","default").toString());
+ ui.bufferSpinBox->setValue(settings.value("buffer_time",500).toInt());
+ ui.periodSpinBox->setValue(settings.value("period_time",100).toInt());
+
+ int d = m_cards.indexOf(settings.value("mixer_card","hw:0").toString());
+ if (d >= 0)
+ ui.mixerCardComboBox->setCurrentIndex(d);
+
+ showMixerDevices(ui.mixerCardComboBox->currentIndex ());
+ d = ui.mixerDeviceComboBox->findText(settings.value("mixer_device",
+ "PCM").toString());
+
+ if (d >= 0)
+ ui.mixerDeviceComboBox->setCurrentIndex(d);
+
+ settings.endGroup();
+}
+
+
+SettingsDialog::~SettingsDialog()
+{}
+
+void SettingsDialog::getCards()
+{
+ int card = -1, err;
+
+ m_devices.clear();
+ m_devices << "default";
+ ui.deviceComboBox->addItem("Default PCM device (default)");
+
+ if ((err = snd_card_next(&card)) !=0)
+ qWarning("SettingsDialog (ALSA): snd_next_card() failed: %s",
+ snd_strerror(-err));
+
+ while (card > -1)
+ {
+ getCardDevices(card);
+ m_cards << QString("hw:%1").arg(card);
+ if ((err = snd_card_next(&card)) !=0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_next_card() failed: %s",
+ snd_strerror(-err));
+ break;
+ }
+ }
+}
+
+void SettingsDialog::getCardDevices(int card)
+{
+ int pcm_device = -1, err;
+ snd_pcm_info_t *pcm_info;
+ snd_ctl_t *ctl;
+ char dev[64], *card_name;
+
+ sprintf(dev, "hw:%i", card);
+
+ if ((err = snd_ctl_open(&ctl, dev, 0)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_ctl_open() failed: %s",
+ snd_strerror(-err));
+ return;
+ }
+
+ if ((err = snd_card_get_name(card, &card_name)) != 0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_card_get_name() failed: %s",
+ snd_strerror(-err));
+ card_name = "Unknown soundcard";
+ }
+ ui.mixerCardComboBox->addItem(QString(card_name));
+
+ snd_pcm_info_alloca(&pcm_info);
+
+ qDebug("SettingsDialog (ALSA): detected sound cards:");
+
+ for (;;)
+ {
+ QString device;
+ if ((err = snd_ctl_pcm_next_device(ctl, &pcm_device)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_ctl_pcm_next_device() failed: %s",
+ snd_strerror(-err));
+ pcm_device = -1;
+ }
+ if (pcm_device < 0)
+ break;
+
+ snd_pcm_info_set_device(pcm_info, pcm_device);
+ snd_pcm_info_set_subdevice(pcm_info, 0);
+ snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK);
+
+ if ((err = snd_ctl_pcm_info(ctl, pcm_info)) < 0)
+ {
+ if (err != -ENOENT)
+ qWarning("SettingsDialog (ALSA): get_devices_for_card(): "
+ "snd_ctl_pcm_info() "
+ "failed (%d:%d): %s.", card,
+ pcm_device, snd_strerror(-err));
+ }
+ device = QString("hw:%1,%2").arg(card).arg(pcm_device);
+ m_devices << device;
+ QString str;
+ str = QString(card_name) + ": "+
+ snd_pcm_info_get_name(pcm_info)+" ("+device+")";
+ qDebug(str.toAscii());
+ ui.deviceComboBox->addItem(str);
+ }
+
+ snd_ctl_close(ctl);
+}
+
+void SettingsDialog::getMixerDevices(QString card)
+{
+ ui.mixerDeviceComboBox->clear();
+ int err;
+ snd_mixer_t *mixer;
+ snd_mixer_elem_t *current;
+
+ if ((err = getMixer(&mixer, card)) < 0)
+ return;
+
+ current = snd_mixer_first_elem(mixer);
+
+ while (current)
+ {
+ const char *sname = snd_mixer_selem_get_name(current);
+ if (snd_mixer_selem_is_active(current) &&
+ snd_mixer_selem_has_playback_volume(current))
+ ui.mixerDeviceComboBox->addItem(QString(sname));
+ current = snd_mixer_elem_next(current);
+ }
+}
+
+void SettingsDialog::setText(int n)
+{
+ ui.deviceComboBox->setEditText(m_devices.at(n));
+}
+
+void SettingsDialog::writeSettings()
+{
+ qDebug("SettingsDialog (ALSA):: writeSettings()");
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ settings.setValue("device", ui.deviceComboBox->currentText ());
+ settings.setValue("buffer_time",ui.bufferSpinBox->value());
+ settings.setValue("period_time",ui.periodSpinBox->value());
+ QString card = m_cards.at(ui.mixerCardComboBox->currentIndex());
+ settings.setValue("mixer_card", card);
+ settings.setValue("mixer_device", ui.mixerDeviceComboBox->currentText ());
+ settings.endGroup();
+ accept();
+}
+
+int SettingsDialog::getMixer(snd_mixer_t **mixer, QString card)
+{
+ char *dev;
+ int err;
+
+ dev = strdup(QString(card).toAscii().data());
+ if ((err = snd_mixer_open(mixer, 0)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): "
+ "Failed to open empty mixer: %s", snd_strerror(-err));
+ mixer = NULL;
+ return -1;
+ }
+ if ((err = snd_mixer_attach(*mixer, dev)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): "
+ "Attaching to mixer %s failed: %s", dev, snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): "
+ "Failed to register mixer: %s", snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_load(*mixer)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): Failed to load mixer: %s",
+ snd_strerror(-err));
+ return -1;
+ }
+
+ free (dev);
+
+ return (*mixer != NULL);
+}
+
+void SettingsDialog::showMixerDevices(int d)
+{
+ if (0<=d && d<m_cards.size())
+ getMixerDevices(m_cards.at(d));
+}
+
diff --git a/lib/qmmp/Output/alsa/settingsdialog.h b/lib/qmmp/Output/alsa/settingsdialog.h
new file mode 100644
index 000000000..467b25a03
--- /dev/null
+++ b/lib/qmmp/Output/alsa/settingsdialog.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include <QDialog>
+
+extern "C"{
+#include <alsa/asoundlib.h>
+}
+//#include <alsa/pcm_plugin.h>
+
+#include "ui_settingsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class SettingsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ SettingsDialog(QWidget *parent = 0);
+
+ ~SettingsDialog();
+
+private slots:
+ void setText(int);
+ void writeSettings();
+ void showMixerDevices(int);
+
+private:
+ Ui::SettingsDialog ui;
+ void getCards();
+ void getCardDevices(int card);
+ void getMixerDevices(QString card);
+ int getMixer(snd_mixer_t **mixer, QString card);
+ QStringList m_devices;
+ QList <QString> m_cards;
+
+};
+
+#endif
diff --git a/lib/qmmp/Output/alsa/settingsdialog.ui b/lib/qmmp/Output/alsa/settingsdialog.ui
new file mode 100644
index 000000000..2f9a20753
--- /dev/null
+++ b/lib/qmmp/Output/alsa/settingsdialog.ui
@@ -0,0 +1,261 @@
+<ui version="4.0" >
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>403</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>ALSA Plugin Settings</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" colspan="3" >
+ <widget class="QTabWidget" name="tabWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Device Settings</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Audio device</string>
+ </property>
+ <widget class="QComboBox" name="deviceComboBox" >
+ <property name="geometry" >
+ <rect>
+ <x>11</x>
+ <y>47</y>
+ <width>338</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Mixer</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="1" >
+ <widget class="QComboBox" name="mixerCardComboBox" />
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Mixer card:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Mixer device:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QComboBox" name="mixerDeviceComboBox" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <attribute name="title" >
+ <string>Advanced Settings</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3" >
+ <property name="title" >
+ <string>Soundcard</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="2" column="1" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>111</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>188</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QSpinBox" name="periodSpinBox" >
+ <property name="maximum" >
+ <number>5000</number>
+ </property>
+ <property name="minimum" >
+ <number>20</number>
+ </property>
+ <property name="value" >
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QSpinBox" name="bufferSpinBox" >
+ <property name="maximum" >
+ <number>10000</number>
+ </property>
+ <property name="minimum" >
+ <number>200</number>
+ </property>
+ <property name="value" >
+ <number>500</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Buffer time (ms):</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Period time (ms):</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>188</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>191</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QPushButton" name="cancelButton" >
+ <property name="text" >
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QPushButton" name="okButton" >
+ <property name="text" >
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>338</x>
+ <y>283</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>164</x>
+ <y>294</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/lib/qmmp/Output/alsa/translations/alsa_plugin_ru.qm b/lib/qmmp/Output/alsa/translations/alsa_plugin_ru.qm
new file mode 100644
index 000000000..78ed38962
--- /dev/null
+++ b/lib/qmmp/Output/alsa/translations/alsa_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Output/alsa/translations/alsa_plugin_ru.ts b/lib/qmmp/Output/alsa/translations/alsa_plugin_ru.ts
new file mode 100644
index 000000000..2bfab0f8c
--- /dev/null
+++ b/lib/qmmp/Output/alsa/translations/alsa_plugin_ru.ts
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>OutputALSAFactory</name>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="30"/>
+ <source>ALSA Plugin</source>
+ <translation>Модуль ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="47"/>
+ <source>About ALSA Output Plugin</source>
+ <translation>О модуле вывода ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="48"/>
+ <source>Qmmp ALSA Output Plugin</source>
+ <translation>Модуль вывода ALSA для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="49"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="../settingsdialog.ui" line="29"/>
+ <source>Device Settings</source>
+ <translation>Параметры устройства</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="41"/>
+ <source>Audio device</source>
+ <translation>Аудио устройство</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="58"/>
+ <source>Mixer</source>
+ <translation>Микшер</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="73"/>
+ <source>Mixer card:</source>
+ <translation>Карта микшера:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="83"/>
+ <source>Mixer device:</source>
+ <translation>Устройство микшера:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="100"/>
+ <source>Advanced Settings</source>
+ <translation>Дополнительные настройки</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="112"/>
+ <source>Soundcard</source>
+ <translation>Звуковая карта</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="176"/>
+ <source>Buffer time (ms):</source>
+ <translation>Время буферизации (мс):</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="186"/>
+ <source>Period time (ms):</source>
+ <translation>Время периода (мс):</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="229"/>
+ <source>Cancel</source>
+ <translation>Отмена</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="236"/>
+ <source>OK</source>
+ <translation>Применить</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="13"/>
+ <source>ALSA Plugin Settings</source>
+ <translation>Настройки модуля ALSA</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Output/alsa/translations/translations.qrc b/lib/qmmp/Output/alsa/translations/translations.qrc
new file mode 100644
index 000000000..beac1cd17
--- /dev/null
+++ b/lib/qmmp/Output/alsa/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>alsa_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/Output/jack/CMakeLists.txt b/lib/qmmp/Output/jack/CMakeLists.txt
new file mode 100644
index 000000000..a8e9b9d1a
--- /dev/null
+++ b/lib/qmmp/Output/jack/CMakeLists.txt
@@ -0,0 +1,74 @@
+project(libjack)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+# libjack and taglib
+PKGCONFIG(jack JACK_INCLUDE_DIR JACK_LINK_DIR JACK_LINK_FLAGS JACK_CFLAGS)
+PKGCONFIG(samplerate SAMPLERATE_INCLUDE_DIR SAMPLERATE_LINK_DIR SAMPLERATE_LINK_FLAGS SAMPLERATE_CFLAGS)
+
+
+IF(NOT JACK_LINK_FLAGS)
+ SET(JACK_LINK_FLAGS -ljack -lrt)
+ENDIF(NOT JACK_LINK_FLAGS)
+
+IF(NOT SAMPLERATE_LINK_FLAGS)
+ SET(SAMPLERATE_LINK_FLAGS -lsamplerate)
+ENDIF(NOT SAMPLERATE_LINK_FLAGS)
+
+include_directories(${JACK_INCLUDE_DIR} ${JACK_INCLUDE_DIR})
+link_directories(${SAMPLERATE_LINK_DIR} ${SAMPLERATE_LINK_DIR})
+
+ADD_DEFINITIONS(${JACK_CFLAGS})
+ADD_DEFINITIONS(${SAMPLERATE_CFLAGS})
+
+
+SET(libjack_SRCS
+ outputjackfactory.cpp
+ outputjack.cpp
+ bio2jack.c
+)
+
+SET(libjack_MOC_HDRS
+ outputjackfactory.h
+ outputjack.h
+ bio2jack.h
+)
+
+SET(libjack_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libjack_RCC_SRCS ${libjack_RCCS})
+
+QT4_WRAP_CPP(libjack_MOC_SRCS ${libjack_MOC_HDRS})
+
+
+
+ADD_LIBRARY(jack SHARED ${libjack_SRCS} ${libjack_MOC_SRCS} ${libjack_RCC_SRCS})
+target_link_libraries(jack ${QT_LIBRARIES} -lqmmp ${JACK_LINK_FLAGS} ${SAMPLERATE_LINK_FLAGS})
+install(TARGETS jack DESTINATION lib/qmmp/Output)
+
diff --git a/lib/qmmp/Output/jack/bio2jack.c b/lib/qmmp/Output/jack/bio2jack.c
new file mode 100644
index 000000000..aef7ea9b7
--- /dev/null
+++ b/lib/qmmp/Output/jack/bio2jack.c
@@ -0,0 +1,2635 @@
+/*
+ * Copyright 2003-2006 Chris Morgan <cmorgan@alum.wpi.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* NOTE: All functions that take a jack_driver_t* do NOT lock the device, in order to get a */
+/* jack_driver_t* you must call getDriver() which will pthread_mutex_lock() */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <math.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <jack/jack.h>
+#include <jack/ringbuffer.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <samplerate.h>
+
+#include "bio2jack.h"
+
+/* enable/disable TRACING through the JACK_Callback() function */
+/* this can sometimes be too much information */
+#define TRACE_CALLBACK 0
+
+/* set to 1 for verbose output */
+#define VERBOSE_OUTPUT 0
+
+/* set to 1 to enable debug messages */
+#define DEBUG_OUTPUT 0
+
+/* set to 1 to enable tracing */
+#define TRACE_ENABLE 0
+
+/* set to 1 to enable the function timers */
+#define TIMER_ENABLE 0
+
+/* set to 1 to enable tracing of getDriver() and releaseDriver() */
+#define TRACE_getReleaseDevice 0
+
+#define ENABLE_WARNINGS 0
+
+#define DEFAULT_RB_SIZE 4096
+
+#define OUTFILE stderr
+
+#if TIMER_ENABLE
+/* This seemingly construct makes timing arbitrary functions really easy
+ all you have to do is place a 'TIMER("start\n")' at the beginning and
+ a 'TIMER("stop\n")' at the end of any function and this does the rest
+ (naturally you can place any printf-compliant text you like in the argument
+ along with the associated values). */
+static struct timeval timer_now;
+#define TIMER(format,args...) gettimeofday(&timer_now,0); \
+ fprintf(OUTFILE, "%ld.%06ld: %s::%s(%d) "format, timer_now.tv_sec, timer_now.tv_usec, __FILE__, __FUNCTION__, __LINE__, ##args)
+#else
+#define TIMER(...)
+#endif
+
+#if TRACE_ENABLE
+#define TRACE(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args); \
+ fflush(OUTFILE);
+#else
+#define TRACE(...)
+#endif
+
+#if DEBUG_OUTPUT
+#define DEBUG(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args); \
+ fflush(OUTFILE);
+#else
+#define DEBUG(...)
+#endif
+
+#if TRACE_CALLBACK
+#define CALLBACK_TRACE(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args); \
+ fflush(OUTFILE);
+#else
+#define CALLBACK_TRACE(...)
+#endif
+
+#if ENABLE_WARNINGS
+#define WARN(format,args...) fprintf(OUTFILE, "WARN: %s::%s(%d) "format, __FILE__,__FUNCTION__,__LINE__,##args); \
+ fflush(OUTFILE);
+#else
+#define WARN(...)
+#endif
+
+#define ERR(format,args...) fprintf(OUTFILE, "ERR: %s::%s(%d) "format, __FILE__,__FUNCTION__,__LINE__,##args); \
+ fflush(OUTFILE);
+
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) < (b)) ? (b) : (a))
+
+#define MAX_OUTPUT_PORTS 10
+#define MAX_INPUT_PORTS 10
+
+typedef struct jack_driver_s
+{
+ bool allocated; /* whether or not this device has been allocated */
+
+ int deviceID; /* id of this device */
+ int clientCtr; /* to prevent overlapping client ids */
+ long jack_sample_rate; /* jack samples(frames) per second */
+
+ long client_sample_rate; /* client samples(frames) per second */
+ double output_sample_rate_ratio; /* ratio between jack's output rate & ours */
+ double input_sample_rate_ratio; /* ratio between our input rate & jack's */
+
+ unsigned long num_input_channels; /* number of input channels(1 is mono, 2 stereo etc..) */
+ unsigned long num_output_channels; /* number of output channels(1 is mono, 2 stereo etc..) */
+
+ unsigned long bits_per_channel; /* number of bits per channel (only 8 & 16 are currently supported) */
+
+ unsigned long bytes_per_output_frame; /* (num_output_channels * bits_per_channel) / 8 */
+ unsigned long bytes_per_input_frame; /* (num_input_channels * bits_per_channel) / 8 */
+
+ unsigned long bytes_per_jack_output_frame; /* (num_output_channels * bits_per_channel) / 8 */
+ unsigned long bytes_per_jack_input_frame; /* (num_input_channels * bits_per_channel) / 8 */
+
+ unsigned long latencyMS; /* latency in ms between writing and actual audio output of the written data */
+
+ long clientBytesInJack; /* number of INPUT bytes(from the client of bio2jack) we wrote to jack(not necessary the number of bytes we wrote to jack) */
+ long jack_buffer_size; /* size of the buffer jack will pass in to the process callback */
+
+ unsigned long callback_buffer1_size; /* number of bytes in the buffer allocated for processing data in JACK_Callback */
+ char *callback_buffer1;
+ unsigned long callback_buffer2_size; /* number of bytes in the buffer allocated for processing data in JACK_Callback */
+ char *callback_buffer2;
+
+ unsigned long rw_buffer1_size; /* number of bytes in the buffer allocated for processing data in JACK_(Read|Write) */
+ char *rw_buffer1;
+
+ struct timeval previousTime; /* time of last JACK_Callback() write to jack, allows for MS accurate bytes played */
+
+ unsigned long written_client_bytes; /* input bytes we wrote to jack, not necessarily actual bytes we wrote to jack due to channel and other conversion */
+ unsigned long played_client_bytes; /* input bytes that jack has played */
+
+ unsigned long client_bytes; /* total bytes written by the client of bio2jack via JACK_Write() */
+
+ jack_port_t *output_port[MAX_OUTPUT_PORTS]; /* output ports */
+ jack_port_t *input_port[MAX_OUTPUT_PORTS]; /* input ports */
+
+ jack_client_t *client; /* pointer to jack client */
+
+ char **jack_port_name; /* user given strings for the port names, can be NULL */
+ unsigned int jack_port_name_count; /* the number of port names given */
+
+ unsigned long jack_output_port_flags; /* flags to be passed to jack when opening the output ports */
+ unsigned long jack_input_port_flags; /* flags to be passed to jack when opening the output ports */
+
+ jack_ringbuffer_t *pPlayPtr; /* the playback ringbuffer */
+ jack_ringbuffer_t *pRecPtr; /* the recording ringbuffer */
+
+ SRC_STATE *output_src; /* SRC object for the output stream */
+ SRC_STATE *input_src; /* SRC object for the output stream */
+
+ enum status_enum state; /* one of PLAYING, PAUSED, STOPPED, CLOSED, RESET etc */
+
+ unsigned int volume[MAX_OUTPUT_PORTS]; /* percentage of sample value to preserve, 100 would be no attenuation */
+ enum JACK_VOLUME_TYPE volumeEffectType; /* linear or dbAttenuation, if dbAttenuation volume is the number of dBs of
+ attenuation to apply, 0 volume being no attenuation, full volume */
+
+ long position_byte_offset; /* an offset that we will apply to returned position queries to achieve */
+ /* the position that the user of the driver desires set */
+
+ bool in_use; /* true if this device is currently in use */
+
+ pthread_mutex_t mutex; /* mutex to lock this specific device */
+
+ /* variables used for trying to restart the connection to jack */
+ bool jackd_died; /* true if jackd has died and we should try to restart it */
+ struct timeval last_reconnect_attempt;
+} jack_driver_t;
+
+
+static char *client_name; /* the name bio2jack will use when creating a new
+ jack client. client_name_%deviceID% will be used */
+
+
+static bool do_sample_rate_conversion; /* whether the client has requested sample rate conversion,
+ default to on for improved compatibility */
+
+/*
+ Which SRC converter function we should use when doing sample rate conversion.
+ Default to the fastest of the 'good quality' set.
+ */
+static int preferred_src_converter = SRC_SINC_FASTEST;
+
+static bool init_done = 0; /* just to prevent clients from calling JACK_Init twice, that would be very bad */
+
+static enum JACK_PORT_CONNECTION_MODE port_connection_mode = CONNECT_ALL;
+
+/* enable/disable code that allows us to close a device without actually closing the jack device */
+/* this works around the issue where jack doesn't always close devices by the time the close function call returns */
+#define JACK_CLOSE_HACK 1
+
+typedef jack_default_audio_sample_t sample_t;
+typedef jack_nframes_t nframes_t;
+
+/* allocate devices for output */
+/* if you increase this past 10, you might want to update 'out_client_name = ... ' in JACK_OpenDevice */
+#define MAX_OUTDEVICES 10
+static jack_driver_t outDev[MAX_OUTDEVICES];
+
+static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER; /* this is to lock the entire outDev array
+ to make managing it in a threaded
+ environment sane */
+
+#if JACK_CLOSE_HACK
+static void JACK_CloseDevice(jack_driver_t * drv, bool close_client);
+#else
+static void JACK_CloseDevice(jack_driver_t * drv);
+#endif
+
+
+/* Prototypes */
+static int JACK_OpenDevice(jack_driver_t * drv);
+static unsigned long JACK_GetBytesFreeSpaceFromDriver(jack_driver_t * drv);
+static void JACK_ResetFromDriver(jack_driver_t * drv);
+static long JACK_GetPositionFromDriver(jack_driver_t * drv,
+ enum pos_enum position, int type);
+static void JACK_CleanupDriver(jack_driver_t * drv);
+
+
+/* Return the difference between two timeval structures in terms of milliseconds */
+long
+TimeValDifference(struct timeval *start, struct timeval *end)
+{
+ double long ms; /* milliseconds value */
+
+ ms = end->tv_sec - start->tv_sec; /* compute seconds difference */
+ ms *= (double) 1000; /* convert to milliseconds */
+
+ ms += (double) (end->tv_usec - start->tv_usec) / (double) 1000; /* add on microseconds difference */
+
+ return (long) ms;
+}
+
+/* get a device and lock the devices mutex */
+/* */
+/* also attempt to reconnect to jack since this function is called from */
+/* most other bio2jack functions it provides a good point to attempt reconnection */
+/* */
+/* Ok, I know this looks complicated and it kind of is. The point is that when you're
+ trying to trace mutexes it's more important to know *who* called us than just that
+ we were called. This uses from pre-processor trickery so that the fprintf is actually
+ placed in the function making the getDriver call. Thus, the __FUNCTION__ and __LINE__
+ macros will actually reference our caller, rather than getDriver. The reason the
+ fprintf call is passes as a parameter is because this macro has to still return a
+ jack_driver_t* and we want to log both before *and* after the getDriver call for
+ easier detection of blocked calls.
+ */
+#if TRACE_getReleaseDevice
+#define getDriver(x) _getDriver(x,fprintf(OUTFILE, "%s::%s(%d) getting driver %d\n", __FILE__, __FUNCTION__, __LINE__,x)); TRACE("got driver %d\n",x);
+jack_driver_t *
+_getDriver(int deviceID, int ignored)
+{
+ fflush(OUTFILE);
+#else
+jack_driver_t *
+getDriver(int deviceID)
+{
+#endif
+ jack_driver_t *drv = &outDev[deviceID];
+
+ if(pthread_mutex_lock(&drv->mutex) != 0)
+ ERR("lock returned an error\n");
+
+ /* should we try to restart the jack server? */
+ if(drv->jackd_died && drv->client == 0)
+ {
+ struct timeval now;
+ gettimeofday(&now, 0);
+
+ /* wait 250ms before trying again */
+ if(TimeValDifference(&drv->last_reconnect_attempt, &now) >= 250)
+ {
+ JACK_OpenDevice(drv);
+ drv->last_reconnect_attempt = now;
+ }
+ }
+
+ return drv;
+}
+
+#if TRACE_getReleaseDevice
+#define tryGetDriver(x) _tryGetDriver(x,fprintf(OUTFILE, "%s::%s(%d) trying to get driver %d\n", __FILE__, __FUNCTION__, __LINE__,x)); TRACE("got driver %d\n",x);
+jack_driver_t *
+_tryGetDriver(int deviceID, int ignored)
+{
+ fflush(OUTFILE);
+#else
+jack_driver_t *
+tryGetDriver(int deviceID)
+{
+#endif
+ jack_driver_t *drv = &outDev[deviceID];
+
+ int err;
+ if((err = pthread_mutex_trylock(&drv->mutex)) == 0)
+ return drv;
+
+ if(err == EBUSY)
+ {
+ TRACE("driver %d is busy\n",deviceID);
+ return 0;
+ }
+
+ ERR("lock returned an error\n");
+ return 0;
+}
+
+
+/* release a device's mutex */
+/* */
+/* This macro is similar to the one for getDriver above, only simpler since we only
+ really need to know when the lock was release for the sake of debugging.
+*/
+#if TRACE_getReleaseDevice
+#define releaseDriver(x) TRACE("releasing driver %d\n",x->deviceID); _releaseDriver(x);
+void
+_releaseDriver(jack_driver_t * drv)
+#else
+void
+releaseDriver(jack_driver_t * drv)
+#endif
+{
+ /*
+ #if TRACE_getReleaseDevice
+ TRACE("deviceID == %d\n", drv->deviceID);
+ #endif
+ */
+ if(pthread_mutex_unlock(&drv->mutex) != 0)
+ ERR("lock returned an error\n");
+}
+
+
+/* Return a string corresponding to the input state */
+char *
+DEBUGSTATE(enum status_enum state)
+{
+ if(state == PLAYING)
+ return "PLAYING";
+ else if(state == PAUSED)
+ return "PAUSED";
+ else if(state == STOPPED)
+ return "STOPPED";
+ else if(state == CLOSED)
+ return "CLOSED";
+ else if(state == RESET)
+ return "RESET";
+ else
+ return "unknown state";
+}
+
+
+#define SAMPLE_MAX_16BIT 32767.0f
+#define SAMPLE_MAX_8BIT 255.0f
+
+/* floating point volume routine */
+/* volume should be a value between 0.0 and 1.0 */
+static void
+float_volume_effect(sample_t * buf, unsigned long nsamples, float volume,
+ int skip)
+{
+ if(volume < 0)
+ volume = 0;
+ if(volume > 1.0)
+ volume = 1.0;
+
+ while(nsamples--)
+ {
+ *buf = (*buf) * volume;
+ buf += skip;
+ }
+}
+
+/* place one channel into a multi-channel stream */
+static inline void
+mux(sample_t * dst, sample_t * src, unsigned long nsamples,
+ unsigned long dst_skip)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ while(nsamples--)
+ {
+ *dst = *src;
+ dst += dst_skip;
+ src++;
+ }
+}
+
+/* pull one channel out of a multi-channel stream */
+static void
+demux(sample_t * dst, sample_t * src, unsigned long nsamples,
+ unsigned long src_skip)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ while(nsamples--)
+ {
+ *dst = *src;
+ dst++;
+ src += src_skip;
+ }
+}
+
+/* convert from 16 bit to floating point */
+static inline void
+sample_move_short_float(sample_t * dst, short *src, unsigned long nsamples)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ unsigned long i;
+ for(i = 0; i < nsamples; i++)
+ dst[i] = (sample_t) (src[i]) / SAMPLE_MAX_16BIT;
+}
+
+/* convert from floating point to 16 bit */
+static inline void
+sample_move_float_short(short *dst, sample_t * src, unsigned long nsamples)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ unsigned long i;
+ for(i = 0; i < nsamples; i++)
+ dst[i] = (short) ((src[i]) * SAMPLE_MAX_16BIT);
+}
+
+/* convert from 8 bit to floating point */
+static inline void
+sample_move_char_float(sample_t * dst, unsigned char *src, unsigned long nsamples)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ unsigned long i;
+ for(i = 0; i < nsamples; i++)
+ dst[i] = (sample_t) (src[i]) / SAMPLE_MAX_8BIT;
+}
+
+/* convert from floating point to 8 bit */
+static inline void
+sample_move_float_char(unsigned char *dst, sample_t * src, unsigned long nsamples)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ unsigned long i;
+ for(i = 0; i < nsamples; i++)
+ dst[i] = (char) ((src[i]) * SAMPLE_MAX_8BIT);
+}
+
+/* fill dst buffer with nsamples worth of silence */
+static inline void
+sample_silence_float(sample_t * dst, unsigned long nsamples)
+{
+ /* ALERT: signed sign-extension portability !!! */
+ while(nsamples--)
+ {
+ *dst = 0;
+ dst++;
+ }
+}
+
+static inline bool
+ensure_buffer_size(char **buffer, unsigned long *cur_size,
+ unsigned long needed_size)
+{
+ DEBUG("current size = %lu, needed size = %lu\n", *cur_size, needed_size);
+ if(*cur_size >= needed_size)
+ return TRUE;
+ DEBUG("reallocing\n");
+ char *tmp = realloc(*buffer, needed_size);
+ if(tmp)
+ {
+ *cur_size = needed_size;
+ *buffer = tmp;
+ return TRUE;
+ }
+ DEBUG("reallocing failed\n");
+ return FALSE;
+}
+
+/******************************************************************
+ * JACK_callback
+ *
+ * every time the jack server wants something from us it calls this
+ * function, so we either deliver it some sound to play or deliver it nothing
+ * to play
+ */
+static int
+JACK_callback(nframes_t nframes, void *arg)
+{
+ jack_driver_t *drv = (jack_driver_t *) arg;
+
+ unsigned int i;
+ int src_error = 0;
+
+ TIMER("start\n");
+ gettimeofday(&drv->previousTime, 0); /* record the current time */
+
+ CALLBACK_TRACE("nframes %ld, sizeof(sample_t) == %d\n", (long) nframes,
+ sizeof(sample_t));
+
+ if(!drv->client)
+ ERR("client is closed, this is weird...\n");
+
+ sample_t *out_buffer[MAX_OUTPUT_PORTS];
+ /* retrieve the buffers for the output ports */
+ for(i = 0; i < drv->num_output_channels; i++)
+ out_buffer[i] = (sample_t *) jack_port_get_buffer(drv->output_port[i], nframes);
+
+ sample_t *in_buffer[MAX_INPUT_PORTS];
+ /* retrieve the buffers for the input ports */
+ for(i = 0; i < drv->num_input_channels; i++)
+ in_buffer[i] = (sample_t *) jack_port_get_buffer(drv->input_port[i], nframes);
+
+ /* handle playing state */
+ if(drv->state == PLAYING)
+ {
+ /* handle playback data, if any */
+ if(drv->num_output_channels > 0)
+ {
+ unsigned long jackFramesAvailable = nframes; /* frames we have left to write to jack */
+ unsigned long numFramesToWrite; /* num frames we are writing */
+ size_t inputBytesAvailable = jack_ringbuffer_read_space(drv->pPlayPtr);
+ unsigned long inputFramesAvailable; /* frames we have available */
+
+ inputFramesAvailable = inputBytesAvailable / drv->bytes_per_jack_output_frame;
+ size_t jackBytesAvailable = jackFramesAvailable * drv->bytes_per_jack_output_frame;
+
+ long read = 0;
+
+ CALLBACK_TRACE("playing... jackFramesAvailable = %ld inputFramesAvailable = %ld\n",
+ jackFramesAvailable, inputFramesAvailable);
+
+#if JACK_CLOSE_HACK
+ if(drv->in_use == FALSE)
+ {
+ /* output silence if nothing is being outputted */
+ for(i = 0; i < drv->num_output_channels; i++)
+ sample_silence_float(out_buffer[i], nframes);
+
+ return -1;
+ }
+#endif
+
+ /* make sure our buffer is large enough for the data we are writing */
+ /* ie. callback_buffer2_size < (bytes we already wrote + bytes we are going to write in this loop) */
+ if(!ensure_buffer_size
+ (&drv->callback_buffer2, &drv->callback_buffer2_size,
+ jackBytesAvailable))
+ {
+ ERR("allocated %lu bytes, need %lu bytes\n",
+ drv->callback_buffer2_size, (unsigned long)jackBytesAvailable);
+ return -1;
+ }
+
+ /* do sample rate conversion if needed & requested */
+ if(drv->output_src && drv->output_sample_rate_ratio != 1.0)
+ {
+ long bytes_needed_write = nframes * drv->bytes_per_jack_output_frame;
+
+ /* make a very good guess at how many raw bytes we'll need to satisfy jack's request after conversion */
+ long bytes_needed_read = min(inputBytesAvailable,
+ (double) (bytes_needed_write +
+ drv->
+ output_sample_rate_ratio
+ *
+ drv->
+ bytes_per_jack_output_frame)
+ / drv->output_sample_rate_ratio);
+ DEBUG("guessing that we need %ld bytes in and %ld out for rate conversion ratio = %f\n",
+ bytes_needed_read, bytes_needed_write,
+ drv->output_sample_rate_ratio);
+
+ if(!ensure_buffer_size(&drv->callback_buffer1,
+ &drv->callback_buffer1_size,
+ bytes_needed_read))
+ {
+ ERR("could not realloc callback_buffer2!\n");
+ return 1;
+ }
+ if(!ensure_buffer_size(&drv->callback_buffer2,
+ &drv->callback_buffer2_size,
+ bytes_needed_write))
+ {
+ ERR("could not realloc callback_buffer2!\n");
+ return 1;
+ }
+
+ if(jackFramesAvailable && inputBytesAvailable > 0)
+ {
+ /* read in the data, but don't move the read pointer until we know how much SRC used */
+ jack_ringbuffer_peek(drv->pPlayPtr, drv->callback_buffer1,
+ bytes_needed_read);
+
+ SRC_DATA srcdata;
+ srcdata.data_in = (sample_t *) drv->callback_buffer1;
+ srcdata.input_frames = bytes_needed_read / drv->bytes_per_jack_output_frame;
+ srcdata.src_ratio = drv->output_sample_rate_ratio;
+ srcdata.data_out = (sample_t *) drv->callback_buffer2;
+ srcdata.output_frames = nframes;
+ srcdata.end_of_input = 0; // it's a stream, it never ends
+ DEBUG("input_frames = %ld, output_frames = %ld\n",
+ srcdata.input_frames, srcdata.output_frames);
+ /* convert the sample rate */
+ src_error = src_process(drv->output_src, &srcdata);
+ DEBUG("used = %ld, generated = %ld, error = %d: %s.\n",
+ srcdata.input_frames_used, srcdata.output_frames_gen,
+ src_error, src_strerror(src_error));
+
+ if(src_error == 0)
+ {
+ /* now we can move the read pointer */
+ jack_ringbuffer_read_advance(drv->pPlayPtr,
+ srcdata.
+ input_frames_used *
+ drv->bytes_per_jack_output_frame);
+ /* add on what we wrote */
+ read = srcdata.input_frames_used * drv->bytes_per_output_frame;
+ jackFramesAvailable -= srcdata.output_frames_gen; /* take away what was used */
+ }
+ }
+ }
+ else /* no resampling needed or requested */
+ {
+ /* read as much data from the buffer as is available */
+ if(jackFramesAvailable && inputBytesAvailable > 0)
+ {
+ /* write as many bytes as we have space remaining, or as much as we have data to write */
+ numFramesToWrite = min(jackFramesAvailable, inputFramesAvailable);
+ jack_ringbuffer_read(drv->pPlayPtr, drv->callback_buffer2,
+ jackBytesAvailable);
+ /* add on what we wrote */
+ read = numFramesToWrite * drv->bytes_per_output_frame;
+ jackFramesAvailable -= numFramesToWrite; /* take away what was written */
+ }
+ }
+
+ drv->written_client_bytes += read;
+ drv->played_client_bytes += drv->clientBytesInJack; /* move forward by the previous bytes we wrote since those must have finished by now */
+ drv->clientBytesInJack = read; /* record the input bytes we wrote to jack */
+
+ /* see if we still have jackBytesLeft here, if we do that means that we
+ ran out of wave data to play and had a buffer underrun, fill in
+ the rest of the space with zero bytes so at least there is silence */
+ if(jackFramesAvailable)
+ {
+ WARN("buffer underrun of %ld frames\n", jackFramesAvailable);
+ for(i = 0; i < drv->num_output_channels; i++)
+ sample_silence_float(out_buffer[i] +
+ (nframes - jackFramesAvailable),
+ jackFramesAvailable);
+ }
+
+ /* if we aren't converting or we are converting and src_error == 0 then we should */
+ /* apply volume and demux */
+ if(!(drv->output_src && drv->output_sample_rate_ratio != 1.0) || (src_error == 0))
+ {
+ /* apply volume */
+ for(i = 0; i < drv->num_output_channels; i++)
+ {
+ if(drv->volumeEffectType == dbAttenuation)
+ {
+ /* assume the volume setting is dB of attenuation, a volume of 0 */
+ /* is 0dB attenuation */
+ float volume = powf(10.0, -((float) drv->volume[i]) / 20.0);
+ float_volume_effect((sample_t *) drv->callback_buffer2 + i,
+ (nframes - jackFramesAvailable), volume, drv->num_output_channels);
+ } else
+ {
+ float_volume_effect((sample_t *) drv->callback_buffer2 + i, (nframes - jackFramesAvailable),
+ ((float) drv->volume[i] / 100.0),
+ drv->num_output_channels);
+ }
+ }
+
+ /* demux the stream: we skip over the number of samples we have output channels as the channel data */
+ /* is encoded like chan1,chan2,chan3,chan1,chan2,chan3... */
+ for(i = 0; i < drv->num_output_channels; i++)
+ {
+ demux(out_buffer[i],
+ (sample_t *) drv->callback_buffer2 + i,
+ (nframes - jackFramesAvailable), drv->num_output_channels);
+ }
+ }
+ }
+
+ /* handle record data, if any */
+ if(drv->num_input_channels > 0)
+ {
+ long jack_bytes = nframes * drv->bytes_per_jack_input_frame; /* how many bytes jack is feeding us */
+
+ if(!ensure_buffer_size(&drv->callback_buffer1, &drv->callback_buffer1_size, jack_bytes))
+ {
+ ERR("allocated %lu bytes, need %lu bytes\n",
+ drv->callback_buffer1_size, jack_bytes);
+ return -1;
+ }
+
+ /* mux the invividual channels into one stream */
+ for(i = 0; i < drv->num_input_channels; i++)
+ {
+ mux((sample_t *) drv->callback_buffer1 + i, in_buffer[i],
+ nframes, drv->num_input_channels);
+ }
+
+ /* do sample rate conversion if needed & requested */
+ if(drv->input_src && drv->input_sample_rate_ratio != 1.0)
+ {
+ /* make a very good guess at how many raw bytes we'll need to read all the data jack gave us */
+ long bytes_needed_write = (double) (jack_bytes +
+ drv->input_sample_rate_ratio *
+ drv->bytes_per_jack_input_frame) *
+ drv->input_sample_rate_ratio;
+ DEBUG("guessing that we need %ld bytes in and %ld out for rate conversion ratio = %f\n",
+ nframes * drv->bytes_per_jack_input_frame,
+ bytes_needed_write, drv->input_sample_rate_ratio);
+
+ if(!ensure_buffer_size(&drv->callback_buffer2,
+ &drv->callback_buffer2_size,
+ bytes_needed_write))
+ {
+ ERR("could not realloc callback_buffer2!\n");
+ return 1;
+ }
+
+ SRC_DATA srcdata;
+ srcdata.data_in = (sample_t *) drv->callback_buffer1;
+ srcdata.input_frames = nframes;
+ srcdata.src_ratio = drv->input_sample_rate_ratio;
+ srcdata.data_out = (sample_t *) drv->callback_buffer2;
+ srcdata.output_frames = drv->callback_buffer2_size / drv->bytes_per_jack_input_frame;
+ srcdata.end_of_input = 0; // it's a stream, it never ends
+ DEBUG("input_frames = %ld, output_frames = %ld\n",
+ srcdata.input_frames, srcdata.output_frames);
+ /* convert the sample rate */
+ src_error = src_process(drv->input_src, &srcdata);
+ DEBUG("used = %ld, generated = %ld, error = %d: %s.\n",
+ srcdata.input_frames_used, srcdata.output_frames_gen,
+ src_error, src_strerror(src_error));
+
+ if(src_error == 0)
+ {
+ long write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+ long bytes_used = srcdata.output_frames_gen * drv->bytes_per_jack_input_frame;
+ /* if there isn't enough room, make some. sure this discards data, but when dealing with input sources
+ it seems like it's better to throw away old data than new */
+ if(write_space < bytes_used)
+ {
+ /* the ringbuffer is designed such that only one thread should ever access each pointer.
+ since calling read_advance here will be touching the read pointer which is also accessed
+ by JACK_Read, we need to lock the mutex first for safety */
+ jack_driver_t *d = tryGetDriver(drv->deviceID);
+ if( d )
+ {
+ /* double check the write space after we've gained the lock, just
+ in case JACK_Read was being called before we gained it */
+ write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+ if(write_space < bytes_used)
+ {
+ /* hey, we warn about underruns, we might as well warn about overruns as well */
+ WARN("buffer overrun of %ld bytes\n", jack_bytes - write_space);
+ jack_ringbuffer_read_advance(drv->pRecPtr, bytes_used - write_space);
+ }
+
+ releaseDriver(drv);
+ }
+ }
+
+ jack_ringbuffer_write(drv->pRecPtr, drv->callback_buffer2,
+ bytes_used);
+ }
+ }
+ else /* no resampling needed */
+ {
+ long write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+ /* if there isn't enough room, make some. sure this discards data, but when dealing with input sources
+ it seems like it's better to throw away old data than new */
+ if(write_space < jack_bytes)
+ {
+ /* the ringbuffer is designed such that only one thread should ever access each pointer.
+ since calling read_advance here will be touching the read pointer which is also accessed
+ by JACK_Read, we need to lock the mutex first for safety */
+ jack_driver_t *d = tryGetDriver(drv->deviceID);
+ if( d )
+ {
+ /* double check the write space after we've gained the lock, just
+ in case JACK_Read was being called before we gained it */
+ write_space = jack_ringbuffer_write_space(drv->pRecPtr);
+ if(write_space < jack_bytes)
+ {
+ ERR("buffer overrun of %ld bytes\n", jack_bytes - write_space);
+ jack_ringbuffer_read_advance(drv->pRecPtr, jack_bytes - write_space);
+ }
+ releaseDriver(drv);
+ }
+ }
+
+ jack_ringbuffer_write(drv->pRecPtr, drv->callback_buffer1,
+ jack_bytes);
+ }
+ }
+ }
+ else if(drv->state == PAUSED ||
+ drv->state == STOPPED ||
+ drv->state == CLOSED || drv->state == RESET)
+ {
+ CALLBACK_TRACE("%s, outputting silence\n", DEBUGSTATE(drv->state));
+
+ /* output silence if nothing is being outputted */
+ for(i = 0; i < drv->num_output_channels; i++)
+ sample_silence_float(out_buffer[i], nframes);
+
+ /* if we were told to reset then zero out some variables */
+ /* and transition to STOPPED */
+ if(drv->state == RESET)
+ {
+ drv->written_client_bytes = 0;
+ drv->played_client_bytes = 0; /* number of the clients bytes that jack has played */
+
+ drv->client_bytes = 0; /* bytes that the client wrote to use */
+
+ drv->clientBytesInJack = 0; /* number of input bytes in jack(not necessary the number of bytes written to jack) */
+
+ drv->position_byte_offset = 0;
+
+ if(drv->pPlayPtr)
+ jack_ringbuffer_reset(drv->pPlayPtr);
+
+ if(drv->pRecPtr)
+ jack_ringbuffer_reset(drv->pRecPtr);
+
+ drv->state = STOPPED; /* transition to STOPPED */
+ }
+ }
+
+ CALLBACK_TRACE("done\n");
+ TIMER("finish\n");
+
+ return 0;
+}
+
+
+/******************************************************************
+ * JACK_bufsize
+ *
+ * Called whenever the jack server changes the the max number
+ * of frames passed to JACK_callback
+ */
+static int
+JACK_bufsize(nframes_t nframes, void *arg)
+{
+ jack_driver_t *drv = (jack_driver_t *) arg;
+ TRACE("the maximum buffer size is now %lu frames\n", (long) nframes);
+
+ drv->jack_buffer_size = nframes;
+
+ return 0;
+}
+
+/******************************************************************
+ * JACK_srate
+ */
+int
+JACK_srate(nframes_t nframes, void *arg)
+{
+ jack_driver_t *drv = (jack_driver_t *) arg;
+
+ drv->jack_sample_rate = (long) nframes;
+
+ /* make sure to recalculate the ratios needed for proper sample rate conversion */
+ drv->output_sample_rate_ratio = (double) drv->jack_sample_rate / (double) drv->client_sample_rate;
+ if(drv->output_src) src_set_ratio(drv->output_src, drv->output_sample_rate_ratio);
+
+ drv->input_sample_rate_ratio = (double) drv->client_sample_rate / (double) drv->jack_sample_rate;
+ if(drv->input_src) src_set_ratio(drv->input_src, drv->input_sample_rate_ratio);
+
+ TRACE("the sample rate is now %lu/sec\n", (long) nframes);
+ return 0;
+}
+
+
+/******************************************************************
+ * JACK_shutdown
+ *
+ * if this is called then jack shut down... handle this appropriately */
+void
+JACK_shutdown(void *arg)
+{
+ jack_driver_t *drv = (jack_driver_t *) arg;
+
+ TRACE("\n");
+
+ getDriver(drv->deviceID);
+
+ drv->client = 0; /* reset client */
+ drv->jackd_died = TRUE;
+
+ TRACE("jack shutdown, setting client to 0 and jackd_died to true, closing device\n");
+
+#if JACK_CLOSE_HACK
+ JACK_CloseDevice(drv, TRUE);
+#else
+ JACK_CloseDevice(drv);
+#endif
+
+ TRACE("trying to reconnect right now\n");
+ /* lets see if we can't reestablish the connection */
+ if(JACK_OpenDevice(drv) != ERR_SUCCESS)
+ {
+ ERR("unable to reconnect with jack\n");
+ }
+
+ releaseDriver(drv);
+}
+
+
+/******************************************************************
+ * JACK_Error
+ *
+ * Callback for jack errors
+ */
+static void
+JACK_Error(const char *desc)
+{
+ ERR("%s\n", desc);
+}
+
+
+/******************************************************************
+ * JACK_OpenDevice
+ *
+ * RETURNS: ERR_SUCCESS upon success
+ */
+static int
+JACK_OpenDevice(jack_driver_t * drv)
+{
+ const char **ports;
+ char *our_client_name = 0;
+ unsigned int i;
+ int failed = 0;
+
+ TRACE("creating jack client and setting up callbacks\n");
+
+#if JACK_CLOSE_HACK
+ /* see if this device is already open */
+ if(drv->client)
+ {
+ /* if this device is already in use then it is bad for us to be in here */
+ if(drv->in_use)
+ return ERR_OPENING_JACK;
+
+ TRACE("using existing client\n");
+ drv->in_use = TRUE;
+ return ERR_SUCCESS;
+ }
+#endif
+
+ /* set up an error handler */
+ jack_set_error_function(JACK_Error);
+
+
+ /* build the client name */
+ our_client_name = (char *) malloc(snprintf
+ (our_client_name, 0, "%s_%d_%d%02d", client_name, getpid(),
+ drv->deviceID, drv->clientCtr + 1) + 1);
+ sprintf(our_client_name, "%s_%d_%d%02d", client_name, getpid(),
+ drv->deviceID, drv->clientCtr++);
+
+ /* try to become a client of the JACK server */
+ TRACE("client name '%s'\n", our_client_name);
+ if((drv->client = jack_client_new(our_client_name)) == 0)
+ {
+ /* try once more */
+ TRACE("trying once more to jack_client_new");
+ if((drv->client = jack_client_new(our_client_name)) == 0)
+ {
+ ERR("jack server not running?\n");
+ free(our_client_name);
+ return ERR_OPENING_JACK;
+ }
+ }
+
+ free(our_client_name);
+
+ TRACE("setting up jack callbacks\n");
+
+ /* JACK server to call `JACK_callback()' whenever
+ there is work to be done. */
+ jack_set_process_callback(drv->client, JACK_callback, drv);
+
+ /* setup a buffer size callback */
+ jack_set_buffer_size_callback(drv->client, JACK_bufsize, drv);
+
+ /* tell the JACK server to call `srate()' whenever
+ the sample rate of the system changes. */
+ jack_set_sample_rate_callback(drv->client, JACK_srate, drv);
+
+ /* tell the JACK server to call `jack_shutdown()' if
+ it ever shuts down, either entirely, or if it
+ just decides to stop calling us. */
+ jack_on_shutdown(drv->client, JACK_shutdown, drv);
+
+ /* display the current sample rate. once the client is activated
+ (see below), you should rely on your own sample rate
+ callback (see above) for this value. */
+ drv->jack_sample_rate = jack_get_sample_rate(drv->client);
+ drv->output_sample_rate_ratio = (double) drv->jack_sample_rate / (double) drv->client_sample_rate;
+ drv->input_sample_rate_ratio = (double) drv->client_sample_rate / (double) drv->jack_sample_rate;
+ TRACE("client sample rate: %lu, jack sample rate: %lu, output ratio = %f, input ratio = %f\n",
+ drv->client_sample_rate, drv->jack_sample_rate,
+ drv->output_sample_rate_ratio, drv->input_sample_rate_ratio);
+
+ drv->jack_buffer_size = jack_get_buffer_size(drv->client);
+
+ /* create the output ports */
+ TRACE("creating output ports\n");
+ for(i = 0; i < drv->num_output_channels; i++)
+ {
+ char portname[32];
+ sprintf(portname, "out_%d", i);
+ TRACE("port %d is named '%s'\n", i, portname);
+ /* NOTE: Yes, this is supposed to be JackPortIsOutput since this is an output */
+ /* port FROM bio2jack */
+ drv->output_port[i] = jack_port_register(drv->client, portname,
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsOutput, 0);
+ }
+
+ /* create the input ports */
+ TRACE("creating input ports\n");
+ for(i = 0; i < drv->num_input_channels; i++)
+ {
+ char portname[32];
+ sprintf(portname, "in_%d", i);
+ TRACE("port %d is named '%s'\n", i, portname);
+ /* NOTE: Yes, this is supposed to be JackPortIsInput since this is an input */
+ /* port TO bio2jack */
+ drv->input_port[i] = jack_port_register(drv->client, portname,
+ JACK_DEFAULT_AUDIO_TYPE,
+ JackPortIsInput, 0);
+ }
+
+#if JACK_CLOSE_HACK
+ drv->in_use = TRUE;
+#endif
+
+ /* tell the JACK server that we are ready to roll */
+ TRACE("calling jack_activate()\n");
+ if(jack_activate(drv->client))
+ {
+ ERR("cannot activate client\n");
+ return ERR_OPENING_JACK;
+ }
+
+ /* if we have output channels and the port connection mode isn't CONNECT_NONE */
+ /* then we should connect up some ports */
+ if((drv->num_output_channels > 0) && (port_connection_mode != CONNECT_NONE))
+ {
+ /* determine how we are to acquire output port names */
+ if((drv->jack_port_name_count == 0) || (drv->jack_port_name_count == 1))
+ {
+ if(drv->jack_port_name_count == 0)
+ {
+ TRACE("jack_get_ports() passing in NULL/NULL\n");
+ ports = jack_get_ports(drv->client, NULL, NULL,
+ drv->jack_output_port_flags);
+ }
+ else
+ {
+ TRACE("jack_get_ports() passing in port of '%s'\n",
+ drv->jack_port_name[0]);
+ ports = jack_get_ports(drv->client, drv->jack_port_name[0], NULL,
+ drv->jack_output_port_flags);
+ }
+
+ /* display a trace of the output ports we found */
+ unsigned int num_ports = 0;
+ if(ports)
+ {
+ for(i = 0; ports[i]; i++)
+ {
+ TRACE("ports[%d] = '%s'\n", i, ports[i]);
+ num_ports++;
+ }
+ }
+
+ /* ensure that we found enough ports */
+ if(!ports || (i < drv->num_output_channels))
+ {
+ TRACE("ERR: jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+ drv->jack_output_port_flags);
+#if JACK_CLOSE_HACK
+ JACK_CloseDevice(drv, TRUE);
+#else
+ JACK_CloseDevice(drv);
+#endif
+ return ERR_PORT_NOT_FOUND;
+ }
+
+ /* connect a port for each output channel. Note: you can't do this before
+ the client is activated (this may change in the future). */
+ for(i = 0; i < drv->num_output_channels; i++)
+ {
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[i]);
+ if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[i]))
+ {
+ ERR("cannot connect to output port %d('%s')\n", i, ports[i]);
+ failed = 1;
+ }
+ }
+
+ /* only if we are in CONNECT_ALL mode should we keep connecting ports up beyond */
+ /* the minimum number of ports required for each output channel coming into bio2jack */
+ if(port_connection_mode == CONNECT_ALL)
+ {
+ /* It's much cheaper and easier to let JACK do the processing required to
+ connect 2 channels to 4 or 4 channels to 2 or any other combinations.
+ This effectively eliminates the need for sample_move_d16_d16() */
+ if(drv->num_output_channels < num_ports)
+ {
+ for(i = drv->num_output_channels; ports[i]; i++)
+ {
+ int n = i % drv->num_output_channels;
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[n]);
+ if(jack_connect(drv->client, jack_port_name(drv->output_port[n]), ports[i]))
+ {
+ // non fatal
+ ERR("cannot connect to output port %d('%s')\n", n, ports[i]);
+ }
+ }
+ }
+ else if(drv->num_output_channels > num_ports)
+ {
+ for(i = num_ports; i < drv->num_output_channels; i++)
+ {
+ int n = i % num_ports;
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[n]);
+ if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[n]))
+ {
+ // non fatal
+ ERR("cannot connect to output port %d('%s')\n", i, ports[n]);
+ }
+ }
+ }
+ }
+
+ free(ports); /* free the returned array of ports */
+ }
+ else
+ {
+ for(i = 0; i < drv->jack_port_name_count; i++)
+ {
+ TRACE("jack_get_ports() portname %d of '%s\n", i,
+ drv->jack_port_name[i]);
+ ports = jack_get_ports(drv->client, drv->jack_port_name[i], NULL,
+ drv->jack_output_port_flags);
+
+ if(!ports)
+ {
+ ERR("jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+ drv->jack_output_port_flags);
+ return ERR_PORT_NOT_FOUND;
+ }
+
+ TRACE("ports[%d] = '%s'\n", 0, ports[0]); /* display a trace of the output port we found */
+
+ /* connect the port */
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[i]);
+ if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[0]))
+ {
+ ERR("cannot connect to output port %d('%s')\n", 0, ports[0]);
+ failed = 1;
+ }
+ free(ports); /* free the returned array of ports */
+ }
+ }
+ } /* if( drv->num_output_channels > 0 ) */
+
+
+ if(drv->num_input_channels > 0)
+ {
+ /* determine how we are to acquire input port names */
+ if((drv->jack_port_name_count == 0) || (drv->jack_port_name_count == 1))
+ {
+ if(drv->jack_port_name_count == 0)
+ {
+ TRACE("jack_get_ports() passing in NULL/NULL\n");
+ ports = jack_get_ports(drv->client, NULL, NULL, drv->jack_input_port_flags);
+ }
+ else
+ {
+ TRACE("jack_get_ports() passing in port of '%s'\n",
+ drv->jack_port_name[0]);
+ ports = jack_get_ports(drv->client, drv->jack_port_name[0], NULL,
+ drv->jack_input_port_flags);
+ }
+
+ /* display a trace of the input ports we found */
+ unsigned int num_ports = 0;
+ if(ports)
+ {
+ for(i = 0; ports[i]; i++)
+ {
+ TRACE("ports[%d] = '%s'\n", i, ports[i]);
+ num_ports++;
+ }
+ }
+
+ /* ensure that we found enough ports */
+ if(!ports || (i < drv->num_input_channels))
+ {
+ TRACE("ERR: jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+ drv->jack_input_port_flags);
+#if JACK_CLOSE_HACK
+ JACK_CloseDevice(drv, TRUE);
+#else
+ JACK_CloseDevice(drv);
+#endif
+ return ERR_PORT_NOT_FOUND;
+ }
+
+ /* connect the ports. Note: you can't do this before
+ the client is activated (this may change in the future). */
+ for(i = 0; i < drv->num_input_channels; i++)
+ {
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[i]);
+ if(jack_connect(drv->client, ports[i], jack_port_name(drv->input_port[i])))
+ {
+ ERR("cannot connect to input port %d('%s')\n", i, ports[i]);
+ failed = 1;
+ }
+ }
+
+ /* It's much cheaper and easier to let JACK do the processing required to
+ connect 2 channels to 4 or 4 channels to 2 or any other combinations.
+ This effectively eliminates the need for sample_move_d16_d16() */
+ if(drv->num_input_channels < num_ports)
+ {
+ for(i = drv->num_input_channels; ports[i]; i++)
+ {
+ int n = i % drv->num_input_channels;
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[n]);
+ if(jack_connect(drv->client, ports[i], jack_port_name(drv->input_port[n])))
+ {
+ // non fatal
+ ERR("cannot connect to input port %d('%s')\n", n, ports[i]);
+ }
+ }
+ }
+ else if(drv->num_input_channels > num_ports)
+ {
+ for(i = num_ports; i < drv->num_input_channels; i++)
+ {
+ int n = i % num_ports;
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[n]);
+ if(jack_connect(drv->client, ports[n], jack_port_name(drv->input_port[i])))
+ {
+ // non fatal
+ ERR("cannot connect to input port %d('%s')\n", i, ports[n]);
+ }
+ }
+ }
+
+ free(ports); /* free the returned array of ports */
+ }
+ else
+ {
+ for(i = 0; i < drv->jack_port_name_count; i++)
+ {
+ TRACE("jack_get_ports() portname %d of '%s\n", i,
+ drv->jack_port_name[i]);
+ ports = jack_get_ports(drv->client, drv->jack_port_name[i], NULL,
+ drv->jack_input_port_flags);
+
+ if(!ports)
+ {
+ ERR("jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n",
+ drv->jack_input_port_flags);
+ return ERR_PORT_NOT_FOUND;
+ }
+
+ TRACE("ports[%d] = '%s'\n", 0, ports[0]); /* display a trace of the input port we found */
+
+ /* connect the port */
+ TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[i]);
+ if(jack_connect(drv->client, jack_port_name(drv->input_port[i]), ports[0]))
+ {
+ ERR("cannot connect to input port %d('%s')\n", 0, ports[0]);
+ failed = 1;
+ }
+ free(ports); /* free the returned array of ports */
+ }
+ }
+ } /* if( drv->num_input_channels > 0 ) */
+
+ /* if something failed we need to shut the client down and return 0 */
+ if(failed)
+ {
+ TRACE("failed, closing and returning error\n");
+#if JACK_CLOSE_HACK
+ JACK_CloseDevice(drv, TRUE);
+#else
+ JACK_CloseDevice(drv);
+#endif
+ return ERR_OPENING_JACK;
+ }
+
+ TRACE("success\n");
+
+ drv->jackd_died = FALSE; /* clear out this flag so we don't keep attempting to restart things */
+ drv->state = PLAYING; /* clients seem to behave much better with this on from the start, especially when recording */
+
+ return ERR_SUCCESS; /* return success */
+}
+
+
+/******************************************************************
+ * JACK_CloseDevice
+ *
+ * Close the connection to the server cleanly.
+ * If close_client is TRUE we close the client for this device instead of
+ * just marking the device as in_use(JACK_CLOSE_HACK only)
+ */
+#if JACK_CLOSE_HACK
+static void
+JACK_CloseDevice(jack_driver_t * drv, bool close_client)
+#else
+static void
+JACK_CloseDevice(jack_driver_t * drv)
+#endif
+{
+ unsigned int i;
+
+#if JACK_CLOSE_HACK
+ if(close_client)
+ {
+#endif
+
+ TRACE("closing the jack client thread\n");
+ if(drv->client)
+ {
+ TRACE("after jack_deactivate()\n");
+ int errorCode = jack_client_close(drv->client);
+ if(errorCode)
+ ERR("jack_client_close() failed returning an error code of %d\n",
+ errorCode);
+ }
+
+ /* reset client */
+ drv->client = 0;
+
+ /* free up the port strings */
+ TRACE("freeing up %d port strings\n", drv->jack_port_name_count);
+ if(drv->jack_port_name_count > 1)
+ {
+ for(i = 0; i < drv->jack_port_name_count; i++)
+ free(drv->jack_port_name[i]);
+ free(drv->jack_port_name);
+ }
+ JACK_CleanupDriver(drv);
+
+ JACK_ResetFromDriver(drv);
+
+#if JACK_CLOSE_HACK
+ } else
+ {
+ TRACE("setting in_use to FALSE\n");
+ drv->in_use = FALSE;
+
+ if(!drv->client)
+ {
+ TRACE("critical error, closing a device that has no client\n");
+ }
+ }
+#endif
+}
+
+
+
+
+/**************************************/
+/* External interface functions below */
+/**************************************/
+
+/* Clear out any buffered data, stop playing, zero out some variables */
+static void
+JACK_ResetFromDriver(jack_driver_t * drv)
+{
+ TRACE("resetting drv->deviceID(%d)\n", drv->deviceID);
+
+ /* NOTE: we use the RESET state so we don't need to worry about clearing out */
+ /* variables that the callback modifies while the callback is running */
+ /* we set the state to RESET and the callback clears the variables out for us */
+ drv->state = RESET; /* tell the callback that we are to reset, the callback will transition this to STOPPED */
+}
+
+/* Clear out any buffered data, stop playing, zero out some variables */
+void
+JACK_Reset(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ TRACE("resetting deviceID(%d)\n", deviceID);
+ JACK_ResetFromDriver(drv);
+ releaseDriver(drv);
+}
+
+
+/*
+ * open the audio device for writing to
+ *
+ * deviceID is set to the opened device
+ * if client is non-zero and in_use is FALSE then just set in_use to TRUE
+ *
+ * return value is zero upon success, non-zero upon failure
+ *
+ * if ERR_RATE_MISMATCH (*rate) will be updated with the jack servers rate
+ */
+int
+JACK_Open(int *deviceID, unsigned int bits_per_channel, unsigned long *rate,
+ int channels)
+{
+ /* we call through to JACK_OpenEx(), but default the input channels to 0 for better backwards
+ compatibility with clients written before recording was available */
+ return JACK_OpenEx(deviceID, bits_per_channel,
+ rate,
+ 0, channels,
+ NULL, 0, JackPortIsPhysical);
+}
+
+/*
+ * see JACK_Open() for comments
+ * NOTE: jack_port_name has three ways of being used:
+ * - NULL - finds all ports with the given flags
+ * - A single regex string used to retrieve all port names
+ * - A series of port names, one for each output channel
+ *
+ * we set *deviceID
+ */
+int
+JACK_OpenEx(int *deviceID, unsigned int bits_per_channel,
+ unsigned long *rate,
+ unsigned int input_channels, unsigned int output_channels,
+ const char **jack_port_name,
+ unsigned int jack_port_name_count, unsigned long jack_port_flags)
+{
+ jack_driver_t *drv = 0;
+ unsigned int i;
+ int retval;
+
+ if(input_channels < 1 && output_channels < 1)
+ {
+ ERR("no input OR output channels, nothing to do\n");
+ return ERR_OPENING_JACK;
+ }
+
+ switch (bits_per_channel)
+ {
+ case 8:
+ case 16:
+ break;
+ default:
+ ERR("invalid bits_per_channel\n");
+ return ERR_OPENING_JACK;
+ }
+
+ /* Lock the device_mutex and find one that's not allocated already.
+ We'll keep this lock until we've either made use of it, or given up. */
+ pthread_mutex_lock(&device_mutex);
+
+ for(i = 0; i < MAX_OUTDEVICES; i++)
+ {
+ if(!outDev[i].allocated)
+ {
+ drv = &outDev[i];
+ break;
+ }
+ }
+
+ if(!drv)
+ {
+ ERR("no more devices available\n");
+ return ERR_OPENING_JACK;
+ }
+
+ /* We found an unallocated device, now lock it for extra saftey */
+ getDriver(drv->deviceID);
+
+ TRACE("bits_per_channel=%d rate=%ld, input_channels=%d, output_channels=%d\n",
+ bits_per_channel, *rate, input_channels, output_channels);
+
+ if(output_channels > MAX_OUTPUT_PORTS)
+ {
+ ERR("output_channels == %d, MAX_OUTPUT_PORTS == %d\n", output_channels,
+ MAX_OUTPUT_PORTS);
+ releaseDriver(drv);
+ pthread_mutex_unlock(&device_mutex);
+ return ERR_TOO_MANY_OUTPUT_CHANNELS;
+ }
+
+ if(input_channels > MAX_INPUT_PORTS)
+ {
+ ERR("input_channels == %d, MAX_INPUT_PORTS == %d\n", input_channels,
+ MAX_INPUT_PORTS);
+ releaseDriver(drv);
+ pthread_mutex_unlock(&device_mutex);
+ return ERR_TOO_MANY_INPUT_CHANNELS;
+ }
+
+ drv->jack_output_port_flags = jack_port_flags | JackPortIsInput; /* port must be input(ie we can put data into it), so mask this in */
+ drv->jack_input_port_flags = jack_port_flags | JackPortIsOutput; /* port must be output(ie we can get data from it), so mask this in */
+
+ /* check that we have the correct number of port names
+ FIXME?: not sure how we should handle output ports vs input ports....
+ */
+ if((jack_port_name_count > 1)
+ && ((jack_port_name_count < output_channels)
+ || (jack_port_name_count < input_channels)))
+ {
+ ERR("specified individual port names but not enough, gave %d names, need %d\n",
+ jack_port_name_count, output_channels);
+ releaseDriver(drv);
+ pthread_mutex_unlock(&device_mutex);
+ return ERR_PORT_NAME_OUTPUT_CHANNEL_MISMATCH;
+ } else
+ {
+ /* copy this data into the device information */
+ drv->jack_port_name_count = jack_port_name_count;
+
+ if(drv->jack_port_name_count != 0)
+ {
+ drv->jack_port_name =
+ (char **) malloc(sizeof(char *) * drv->jack_port_name_count);
+ for(i = 0; i < drv->jack_port_name_count; i++)
+ {
+ drv->jack_port_name[i] = strdup(jack_port_name[i]);
+ TRACE("jack_port_name[%d] == '%s'\n", i, jack_port_name[i]);
+ }
+ } else
+ {
+ drv->jack_port_name = NULL;
+ TRACE("jack_port_name = NULL\n");
+ }
+ }
+
+ /* initialize some variables */
+ drv->in_use = FALSE;
+
+ JACK_ResetFromDriver(drv); /* flushes all queued buffers, sets status to STOPPED and resets some variables */
+
+ /* drv->jack_sample_rate is set by JACK_OpenDevice() */
+ drv->client_sample_rate = *rate;
+ drv->bits_per_channel = bits_per_channel;
+ drv->num_input_channels = input_channels;
+ drv->num_output_channels = output_channels;
+ drv->bytes_per_input_frame = (drv->bits_per_channel * drv->num_input_channels) / 8;
+ drv->bytes_per_output_frame = (drv->bits_per_channel * drv->num_output_channels) / 8;
+ drv->bytes_per_jack_output_frame = sizeof(sample_t) * drv->num_output_channels;
+ drv->bytes_per_jack_input_frame = sizeof(sample_t) * drv->num_input_channels;
+
+ if(drv->num_output_channels > 0)
+ {
+ drv->pPlayPtr = jack_ringbuffer_create(drv->num_output_channels *
+ drv->bytes_per_jack_output_frame *
+ DEFAULT_RB_SIZE);
+ }
+
+ if(drv->num_input_channels > 0)
+ {
+ drv->pRecPtr = jack_ringbuffer_create(drv->num_input_channels *
+ drv->bytes_per_jack_input_frame *
+ DEFAULT_RB_SIZE);
+ }
+
+ DEBUG("bytes_per_output_frame == %ld\n", drv->bytes_per_output_frame);
+ DEBUG("bytes_per_input_frame == %ld\n", drv->bytes_per_input_frame);
+ DEBUG("bytes_per_jack_output_frame == %ld\n",
+ drv->bytes_per_jack_output_frame);
+ DEBUG("bytes_per_jack_input_frame == %ld\n",
+ drv->bytes_per_jack_input_frame);
+
+ /* go and open up the device */
+ retval = JACK_OpenDevice(drv);
+ if(retval != ERR_SUCCESS)
+ {
+ TRACE("error opening jack device\n");
+ releaseDriver(drv);
+ pthread_mutex_unlock(&device_mutex);
+ return retval;
+ }
+ else
+ {
+ TRACE("succeeded opening jack device\n");
+ }
+
+ /* setup SRC objects just in case they'll be needed but only if requested */
+ if(do_sample_rate_conversion)
+ {
+ int error;
+ if(drv->num_output_channels > 0)
+ {
+ drv->output_src = src_new(preferred_src_converter, drv->num_output_channels, &error);
+ if(error != 0)
+ {
+ src_delete(drv->output_src);
+ drv->output_src = 0;
+ ERR("Could not created SRC object for output stream %d: %s\n",
+ error, src_strerror(error));
+ }
+ }
+ if(drv->num_input_channels > 0)
+ {
+ drv->input_src = src_new(preferred_src_converter, drv->num_input_channels, &error);
+ if(error != 0)
+ {
+ src_delete(drv->input_src);
+ drv->input_src = 0;
+ ERR("Could not created SRC object for input stream %d: %s\n",
+ error, src_strerror(error));
+ }
+ }
+ }
+ else if((long) (*rate) != drv->jack_sample_rate)
+ {
+ TRACE("rate of %ld doesn't match jack sample rate of %ld, returning error\n",
+ *rate, drv->jack_sample_rate);
+ *rate = drv->jack_sample_rate;
+#if JACK_CLOSE_HACK
+ JACK_CloseDevice(drv, TRUE);
+#else
+ JACK_CloseDevice(drv);
+#endif
+ releaseDriver(drv);
+ pthread_mutex_unlock(&device_mutex);
+ return ERR_RATE_MISMATCH;
+ }
+
+ drv->allocated = TRUE; /* record that we opened this device */
+
+ DEBUG("sizeof(sample_t) == %d\n", sizeof(sample_t));
+
+ int periodSize = jack_get_buffer_size(drv->client);
+ int periods = 0;
+ /* FIXME: maybe we should keep different latency values for input vs output? */
+ if(drv->num_output_channels > 0)
+ {
+ periods = jack_port_get_total_latency(drv->client,
+ drv->output_port[0]) / periodSize;
+ drv->latencyMS = periodSize * periods * 1000 / (drv->jack_sample_rate *
+ (drv->bits_per_channel / 8 *
+ drv->num_output_channels));
+ }
+ else if(drv->num_input_channels > 0)
+ {
+ periods = jack_port_get_total_latency(drv->client,
+ drv->input_port[0]) / periodSize;
+ drv->latencyMS =
+ periodSize * periods * 1000 / (drv->jack_sample_rate *
+ (drv->bits_per_channel / 8 *
+ drv->num_input_channels));
+ }
+
+ TRACE("drv->latencyMS == %ldms\n", drv->latencyMS);
+
+ *deviceID = drv->deviceID; /* set the deviceID for the caller */
+ releaseDriver(drv);
+ pthread_mutex_unlock(&device_mutex);
+ return ERR_SUCCESS; /* success */
+}
+
+/* Close the jack device */
+//FIXME: add error handling in here at some point...
+/* NOTE: return 0 for success, non-zero for failure */
+int
+JACK_Close(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+
+ TRACE("deviceID(%d)\n", deviceID);
+
+#if JACK_CLOSE_HACK
+ JACK_CloseDevice(drv, TRUE);
+#else
+ JACK_CloseDevice(drv);
+#endif
+
+ JACK_ResetFromDriver(drv); /* reset this device to a normal starting state */
+
+ pthread_mutex_lock(&device_mutex);
+
+ /* free buffer memory */
+ drv->callback_buffer1_size = 0;
+ if(drv->callback_buffer1) free(drv->callback_buffer1);
+ drv->callback_buffer1 = 0;
+
+ drv->callback_buffer2_size = 0;
+ if(drv->callback_buffer2) free(drv->callback_buffer2);
+ drv->callback_buffer2 = 0;
+
+ drv->rw_buffer1_size = 0;
+ if(drv->rw_buffer1) free(drv->rw_buffer1);
+ drv->rw_buffer1 = 0;
+
+ if(drv->pPlayPtr) jack_ringbuffer_free(drv->pPlayPtr);
+ drv->pPlayPtr = 0;
+
+ if(drv->pRecPtr) jack_ringbuffer_free(drv->pRecPtr);
+ drv->pRecPtr = 0;
+
+ /* free the SRC objects */
+ if(drv->output_src) src_delete(drv->output_src);
+ drv->output_src = 0;
+
+ if(drv->input_src) src_delete(drv->input_src);
+ drv->input_src = 0;
+
+ drv->allocated = FALSE; /* release this device */
+
+ pthread_mutex_unlock(&device_mutex);
+
+ releaseDriver(drv);
+
+ return 0;
+}
+
+/* If we haven't already taken in the max allowed data then create a wave header */
+/* to package the audio data and attach the wave header to the end of the */
+/* linked list of wave headers */
+/* These wave headers will be peeled off as they are played by the callback routine */
+/* Return value is the number of bytes written */
+/* NOTE: this function takes the length of data to be written bytes */
+long
+JACK_Write(int deviceID, unsigned char *data, unsigned long bytes)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+
+ long frames_free, frames;
+
+ TIMER("start\n");
+
+ TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes);
+
+ /* check and see that we have enough space for this audio */
+ frames_free =
+ jack_ringbuffer_write_space(drv->pPlayPtr) /
+ drv->bytes_per_jack_output_frame;
+ frames = bytes / drv->bytes_per_output_frame;
+ TRACE("frames free == %ld, bytes = %lu\n", frames_free, bytes);
+
+ TRACE("state = '%s'\n", DEBUGSTATE(drv->state));
+ /* if we are currently STOPPED we should start playing now...
+ do this before the check for bytes == 0 since some clients like
+ to write 0 bytes the first time out */
+ if(drv->state == STOPPED)
+ {
+ TRACE("currently STOPPED, transitioning to PLAYING\n");
+ drv->state = PLAYING;
+ }
+
+ /* handle the case where the user calls this routine with 0 bytes */
+ if(bytes == 0 || frames_free < 1)
+ {
+ TRACE("no room left\n");
+ TIMER("finish (nothing to do, buffer is full)\n");
+ releaseDriver(drv);
+ return 0; /* indicate that we couldn't write any bytes */
+ }
+
+ frames = min(frames, frames_free);
+ long jack_bytes = frames * drv->bytes_per_jack_output_frame;
+ if(!ensure_buffer_size(&drv->rw_buffer1, &drv->rw_buffer1_size, jack_bytes))
+ {
+ ERR("couldn't allocate enough space for the buffer\n");
+ releaseDriver(drv);
+ return 0;
+ }
+ /* adjust bytes to be how many client bytes we're actually writing */
+ bytes = frames * drv->bytes_per_output_frame;
+
+ /* convert from client samples to jack samples
+ we have to tell it how many samples there are, which is frames * channels */
+ switch (drv->bits_per_channel)
+ {
+ case 8:
+ sample_move_char_float((sample_t *) drv->rw_buffer1, (unsigned char *) data,
+ frames * drv->num_output_channels);
+ break;
+ case 16:
+ sample_move_short_float((sample_t *) drv->rw_buffer1, (short *) data,
+ frames * drv->num_output_channels);
+ break;
+ }
+
+ DEBUG("ringbuffer read space = %d, write space = %d\n",
+ jack_ringbuffer_read_space(drv->pPlayPtr),
+ jack_ringbuffer_write_space(drv->pPlayPtr));
+
+ jack_ringbuffer_write(drv->pPlayPtr, drv->rw_buffer1, jack_bytes);
+ DEBUG("wrote %lu bytes, %lu jack_bytes\n", bytes, jack_bytes);
+
+ DEBUG("ringbuffer read space = %d, write space = %d\n",
+ jack_ringbuffer_read_space(drv->pPlayPtr),
+ jack_ringbuffer_write_space(drv->pPlayPtr));
+
+ drv->client_bytes += bytes; /* update client_bytes */
+
+ TIMER("finish\n");
+
+ DEBUG("returning bytes written of %ld\n", bytes);
+
+ releaseDriver(drv);
+ return bytes; /* return the number of bytes we wrote out */
+}
+
+long
+JACK_Read(int deviceID, unsigned char *data, unsigned long bytes)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+
+ long frames_available, frames;
+
+ TIMER("start\n");
+
+ TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes);
+
+ /* find out if there's any room to write this data */
+ frames_available =
+ jack_ringbuffer_read_space(drv->pRecPtr) /
+ drv->bytes_per_jack_input_frame;
+ frames = bytes / drv->bytes_per_input_frame;
+ DEBUG("frames available = %ld, bytes = %lu\n", frames_available, bytes);
+
+ TRACE("state = '%s'\n", DEBUGSTATE(drv->state));
+ /* if we are currently STOPPED we should start recording now... */
+ if(drv->state == STOPPED)
+ {
+ TRACE("currently STOPPED, transitioning to PLAYING\n");
+ drv->state = PLAYING;
+ }
+
+ /* handle the case where the user calls this routine with 0 bytes */
+ if(bytes == 0 || frames_available < 1)
+ {
+ TRACE("no bytes in buffer\n");
+
+ TIMER("finish (nothing to do)\n");
+ releaseDriver(drv);
+ return 0;
+ }
+
+ frames = min(frames, frames_available);
+ long jack_bytes = frames * drv->bytes_per_jack_input_frame;
+ if(!ensure_buffer_size(&drv->rw_buffer1, &drv->rw_buffer1_size, jack_bytes))
+ {
+ ERR("couldn't allocate enough space for the buffer\n");
+ releaseDriver(drv);
+ return 0;
+ }
+
+ DEBUG("ringbuffer read space = %d, write space = %d\n",
+ jack_ringbuffer_read_space(drv->pRecPtr),
+ jack_ringbuffer_write_space(drv->pRecPtr));
+
+ jack_ringbuffer_read(drv->pRecPtr, drv->rw_buffer1,
+ frames * drv->bytes_per_jack_input_frame);
+
+ DEBUG("ringbuffer read space = %d, write space = %d\n",
+ jack_ringbuffer_read_space(drv->pRecPtr),
+ jack_ringbuffer_write_space(drv->pRecPtr));
+
+ unsigned int i;
+ for(i = 0; i < drv->num_output_channels; i++)
+ {
+ /* apply volume to the floating value */
+ if(drv->volumeEffectType == dbAttenuation)
+ {
+ /* assume the volume setting is dB of attenuation, a volume of 0 */
+ /* is 0dB attenuation */
+ float volume = powf(10.0, -((float) drv->volume[i]) / 20.0);
+ float_volume_effect((sample_t *) drv->rw_buffer1 + i,
+ frames, volume, drv->num_output_channels);
+ } else
+ {
+ float_volume_effect((sample_t *) drv->rw_buffer1 + i, frames,
+ ((float) drv->volume[i] / 100.0),
+ drv->num_output_channels);
+ }
+ }
+
+ /* convert from jack samples to client samples
+ we have to tell it how many samples there are, which is frames * channels */
+ switch (drv->bits_per_channel)
+ {
+ case 8:
+ sample_move_float_char((unsigned char *) data, (sample_t *) drv->rw_buffer1,
+ frames * drv->num_input_channels);
+ break;
+ case 16:
+ sample_move_float_short((short *) data, (sample_t *) drv->rw_buffer1,
+ frames * drv->num_input_channels);
+ break;
+ }
+
+ TIMER("finish\n");
+
+ long read_bytes = frames * drv->bytes_per_input_frame;
+
+ DEBUG("returning bytes read of %ld\n", bytes);
+
+ releaseDriver(drv);
+ return read_bytes;
+}
+
+/* return ERR_SUCCESS for success */
+static int
+JACK_SetVolumeForChannelFromDriver(jack_driver_t * drv,
+ unsigned int channel, unsigned int volume)
+{
+ /* TODO?: maybe we should have different volume levels for input & output */
+ /* ensure that we have the channel we are setting volume for */
+ if(channel > (drv->num_output_channels - 1))
+ return 1;
+
+ if(volume > 100)
+ volume = 100; /* check for values in excess of max */
+
+ drv->volume[channel] = volume;
+ return ERR_SUCCESS;
+}
+
+/* return ERR_SUCCESS for success */
+int
+JACK_SetVolumeForChannel(int deviceID, unsigned int channel,
+ unsigned int volume)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ int retval = JACK_SetVolumeForChannelFromDriver(drv, channel, volume);
+ releaseDriver(drv);
+ return retval;
+}
+
+/* Set the volume */
+/* return 0 for success */
+/* NOTE: we check for invalid volume values */
+int
+JACK_SetAllVolume(int deviceID, unsigned int volume)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ unsigned int i;
+
+ TRACE("deviceID(%d), setting volume of %d\n", deviceID, volume);
+
+ for(i = 0; i < drv->num_output_channels; i++)
+ {
+ if(JACK_SetVolumeForChannelFromDriver(drv, i, volume) != ERR_SUCCESS)
+ {
+ releaseDriver(drv);
+ return 1;
+ }
+ }
+
+ releaseDriver(drv);
+ return ERR_SUCCESS;
+}
+
+/* Return the current volume in the inputted pointers */
+/* NOTE: we check for null pointers being passed in just in case */
+void
+JACK_GetVolumeForChannel(int deviceID, unsigned int channel,
+ unsigned int *volume)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+
+ /* ensure that we have the channel we are getting volume for */
+ if(channel > (drv->num_output_channels - 1))
+ {
+ ERR("asking for channel index %d but we only have %ld channels\n", channel, drv->num_output_channels);
+ releaseDriver(drv);
+ return;
+ }
+
+ if(volume)
+ *volume = drv->volume[channel];
+
+#if VERBOSE_OUTPUT
+ if(volume)
+ {
+ TRACE("deviceID(%d), returning volume of %d for channel %d\n",
+ deviceID, *volume, channel);
+ }
+ else
+ {
+ TRACE("volume is null, can't dereference it\n");
+ }
+#endif
+
+ releaseDriver(drv);
+}
+
+
+/* linear means 0 volume is silence, 100 is full volume */
+/* dbAttenuation means 0 volume is 0dB attenuation */
+/* Bio2jack defaults to linear */
+enum JACK_VOLUME_TYPE
+JACK_SetVolumeEffectType(int deviceID, enum JACK_VOLUME_TYPE type)
+{
+ enum JACK_VOLUME_TYPE retval;
+ jack_driver_t *drv = getDriver(deviceID);
+
+ TRACE("setting type of '%s'\n",
+ (type == dbAttenuation ? "dbAttenuation" : "linear"));
+
+ retval = drv->volumeEffectType;
+ drv->volumeEffectType = type;
+
+ releaseDriver(drv);
+ return retval;
+}
+
+
+/* Controls the state of the playback(playing, paused, ...) */
+int
+JACK_SetState(int deviceID, enum status_enum state)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+
+ switch (state)
+ {
+ case PAUSED:
+ drv->state = PAUSED;
+ break;
+ case PLAYING:
+ drv->state = PLAYING;
+ break;
+ case STOPPED:
+ drv->state = STOPPED;
+ break;
+ default:
+ TRACE("unknown state of %d\n", state);
+ }
+
+ TRACE("%s\n", DEBUGSTATE(drv->state));
+
+ releaseDriver(drv);
+ return 0;
+}
+
+/* Retrieve the current state of the device */
+enum status_enum
+JACK_GetState(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ enum status_enum return_val;
+
+ return_val = drv->state;
+ releaseDriver(drv);
+
+ TRACE("deviceID(%d), returning current state of %s\n", deviceID,
+ DEBUGSTATE(return_val));
+ return return_val;
+}
+
+/* Retrieve the number of bytes per second we are outputting */
+unsigned long
+JACK_GetOutputBytesPerSecondFromDriver(jack_driver_t * drv)
+{
+ unsigned long return_val;
+
+ return_val = drv->bytes_per_output_frame * drv->client_sample_rate;
+
+#if VERBOSE_OUTPUT
+ TRACE("deviceID(%d), return_val = %ld\n", drv->deviceID, return_val);
+#endif
+
+ return return_val;
+}
+
+/* Retrieve the number of bytes per second we are outputting */
+unsigned long
+JACK_GetOutputBytesPerSecond(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ unsigned long return_val;
+
+ return_val = JACK_GetOutputBytesPerSecondFromDriver(drv);
+ releaseDriver(drv);
+
+ return return_val;
+}
+
+/* Retrieve the number of input bytes(from jack) per second we are outputting
+ to the user of bio2jack */
+static long
+JACK_GetInputBytesPerSecondFromDriver(jack_driver_t * drv)
+{
+ long return_val;
+
+ return_val = drv->bytes_per_input_frame * drv->client_sample_rate;
+#if VERBOSE_OUTPUT
+ TRACE("drv->deviceID(%d), return_val = %ld\n", drv->deviceID, return_val);
+#endif
+
+ return return_val;
+}
+
+/* Retrieve the number of input bytes(from jack) per second we are outputting
+ to the user of bio2jack */
+unsigned long
+JACK_GetInputBytesPerSecond(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val = JACK_GetInputBytesPerSecondFromDriver(drv);
+ releaseDriver(drv);
+
+#if VERBOSE_OUTPUT
+ TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+#endif
+
+ return return_val;
+}
+
+/* Return the number of bytes we have buffered thus far for output */
+/* NOTE: convert from output bytes to input bytes in here */
+static long
+JACK_GetBytesStoredFromDriver(jack_driver_t * drv)
+{
+ if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0)
+ return 0;
+
+ /* leave at least one frame in the buffer at all times to prevent underruns */
+ long return_val =
+ jack_ringbuffer_read_space(drv->pPlayPtr) - drv->jack_buffer_size;
+ if(return_val <= 0)
+ {
+ return_val = 0;
+ } else
+ {
+ /* adjust from jack bytes to client bytes */
+ return_val =
+ return_val / drv->bytes_per_jack_output_frame *
+ drv->bytes_per_output_frame;
+ }
+
+ return return_val;
+}
+
+/* An approximation of how many bytes we have to send out to jack */
+/* that is computed as if we were sending jack a continuous stream of */
+/* bytes rather than chunks during discrete callbacks. */
+/* Return the number of bytes we have buffered thus far for output */
+/* NOTE: convert from output bytes to input bytes in here */
+unsigned long
+JACK_GetBytesStored(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long retval = JACK_GetBytesStoredFromDriver(drv);
+ releaseDriver(drv);
+ TRACE("deviceID(%d), retval = %ld\n", deviceID, retval);
+ return retval;
+}
+
+static unsigned long
+JACK_GetBytesFreeSpaceFromDriver(jack_driver_t * drv)
+{
+ if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0)
+ return 0;
+
+ /* leave at least one frame in the buffer at all times to prevent underruns */
+ long return_val = jack_ringbuffer_write_space(drv->pPlayPtr) - drv->jack_buffer_size;
+ if(return_val <= 0)
+ {
+ return_val = 0;
+ } else
+ {
+ /* adjust from jack bytes to client bytes */
+ return_val =
+ return_val / drv->bytes_per_jack_output_frame *
+ drv->bytes_per_output_frame;
+ }
+
+ return return_val;
+}
+
+/* Return the number of bytes we can write to the device */
+unsigned long
+JACK_GetBytesFreeSpace(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ unsigned long return_val;
+
+ return_val = JACK_GetBytesFreeSpaceFromDriver(drv);
+ releaseDriver(drv);
+
+ TRACE("deviceID(%d), retval == %ld\n", deviceID, return_val);
+
+ return return_val;
+}
+
+/* bytes of space used in the input buffer */
+unsigned long
+JACK_GetBytesUsedSpace(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val;
+
+ if(drv->pRecPtr == 0 || drv->bytes_per_jack_input_frame == 0)
+ {
+ return_val = 0;
+ } else
+ {
+ /* adjust from jack bytes to client bytes */
+ return_val =
+ jack_ringbuffer_read_space(drv->pRecPtr) /
+ drv->bytes_per_jack_input_frame * drv->bytes_per_input_frame;
+ }
+
+ releaseDriver(drv);
+
+ if(return_val < 0)
+ return_val = 0;
+ TRACE("deviceID(%d), retval == %ld\n", deviceID, return_val);
+
+ return return_val;
+}
+
+/* Get the current position of the driver, either in bytes or */
+/* in milliseconds */
+/* NOTE: this is position relative to input bytes, output bytes may differ greatly due to
+ input vs. output channel count */
+static long
+JACK_GetPositionFromDriver(jack_driver_t * drv, enum pos_enum position,
+ int type)
+{
+ long return_val = 0;
+ struct timeval now;
+ long elapsedMS;
+ double sec2msFactor = 1000;
+
+ char *type_str = "UNKNOWN type";
+
+ /* if we are reset we should return a position of 0 */
+ if(drv->state == RESET)
+ {
+ TRACE("we are currently RESET, returning 0\n");
+ return 0;
+ }
+
+ if(type == WRITTEN)
+ {
+ type_str = "WRITTEN";
+ return_val = drv->client_bytes;
+ } else if(type == WRITTEN_TO_JACK)
+ {
+ type_str = "WRITTEN_TO_JACK";
+ return_val = drv->written_client_bytes;
+ } else if(type == PLAYED) /* account for the elapsed time for the played_bytes */
+ {
+ type_str = "PLAYED";
+ return_val = drv->played_client_bytes;
+ gettimeofday(&now, 0);
+
+ elapsedMS = TimeValDifference(&drv->previousTime, &now); /* find the elapsed milliseconds since last JACK_Callback() */
+
+ TRACE("elapsedMS since last callback is '%ld'\n", elapsedMS);
+
+ /* account for the bytes played since the last JACK_Callback() */
+ /* NOTE: [Xms * (Bytes/Sec)] * (1 sec/1,000ms) */
+ /* NOTE: don't do any compensation if no data has been sent to jack since the last callback */
+ /* as this would result a bogus computed result */
+ if(drv->clientBytesInJack != 0)
+ {
+ return_val += (long) ((double) elapsedMS *
+ ((double) JACK_GetOutputBytesPerSecondFromDriver(drv) /
+ sec2msFactor));
+ } else
+ {
+ TRACE("clientBytesInJack == 0\n");
+ }
+ }
+
+ /* add on the offset */
+ return_val += drv->position_byte_offset;
+
+ /* convert byte position to milliseconds value if necessary */
+ if(position == MILLISECONDS)
+ {
+ if(JACK_GetOutputBytesPerSecondFromDriver(drv) != 0)
+ {
+ return_val = (long) (((double) return_val /
+ (double) JACK_GetOutputBytesPerSecondFromDriver(drv)) *
+ (double) sec2msFactor);
+ } else
+ {
+ return_val = 0;
+ }
+ }
+
+ TRACE("drv->deviceID(%d), type(%s), return_val = %ld\n", drv->deviceID,
+ type_str, return_val);
+
+ return return_val;
+}
+
+/* Get the current position of the driver, either in bytes or */
+/* in milliseconds */
+/* NOTE: this is position relative to input bytes, output bytes may differ greatly due to input vs. output channel count */
+long
+JACK_GetPosition(int deviceID, enum pos_enum position, int type)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long retval = JACK_GetPositionFromDriver(drv, position, type);
+ releaseDriver(drv);
+ TRACE("retval == %ld\n", retval);
+ return retval;
+}
+
+// Set position always applies to written bytes
+// NOTE: we must apply this instantly because if we pass this as a message
+// to the callback we risk the user sending us audio data in the mean time
+// and there is no need to send this as a message, we don't modify any
+// internal variables
+void
+JACK_SetPositionFromDriver(jack_driver_t * drv, enum pos_enum position,
+ long value)
+{
+ double sec2msFactor = 1000;
+#if TRACE_ENABLE
+ long input_value = value;
+#endif
+
+ /* convert the incoming value from milliseconds into bytes */
+ if(position == MILLISECONDS)
+ {
+ value = (long) (((double) value *
+ (double) JACK_GetOutputBytesPerSecondFromDriver(drv)) /
+ sec2msFactor);
+ }
+
+ /* ensure that if the user asks for the position */
+ /* they will at this instant get the correct position */
+ drv->position_byte_offset = value - drv->client_bytes;
+
+ TRACE("deviceID(%d) input_value of %ld %s, new value of %ld, setting position_byte_offset to %ld\n",
+ drv->deviceID, input_value, (position == MILLISECONDS) ? "ms" : "bytes",
+ value, drv->position_byte_offset);
+}
+
+// Set position always applies to written bytes
+// NOTE: we must apply this instantly because if we pass this as a message
+// to the callback we risk the user sending us audio data in the mean time
+// and there is no need to send this as a message, we don't modify any
+// internal variables
+void
+JACK_SetPosition(int deviceID, enum pos_enum position, long value)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ JACK_SetPositionFromDriver(drv, position, value);
+ releaseDriver(drv);
+
+ TRACE("deviceID(%d) value of %ld\n", drv->deviceID, value);
+}
+
+/* Return the number of bytes per frame, or (output_channels * bits_per_channel) / 8 */
+unsigned long
+JACK_GetBytesPerOutputFrame(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val = drv->bytes_per_output_frame;
+ releaseDriver(drv);
+ TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+ return return_val;
+}
+
+/* Return the number of bytes per frame, or (input_channels * bits_per_channel) / 8 */
+unsigned long
+JACK_GetBytesPerInputFrame(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val = drv->bytes_per_input_frame;
+ releaseDriver(drv);
+ TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val);
+ return return_val;
+}
+
+/* Return the number of output bytes we buffer max */
+long
+JACK_GetMaxOutputBufferedBytes(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val;
+
+ if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0) return_val = 0;
+
+ /* adjust from jack bytes to client bytes */
+ return_val =
+ (jack_ringbuffer_read_space(drv->pPlayPtr) +
+ jack_ringbuffer_write_space(drv->pPlayPtr)) /
+ drv->bytes_per_jack_output_frame * drv->bytes_per_output_frame;
+
+ releaseDriver(drv);
+
+ TRACE("return_val = %ld\n", return_val);
+
+ return return_val;
+}
+
+/* Return the number of input bytes we buffer max */
+long
+JACK_GetMaxInputBufferedBytes(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val;
+
+ if(drv->pRecPtr == 0 || drv->bytes_per_jack_input_frame == 0) return_val = 0;
+
+ /* adjust from jack bytes to client bytes */
+ return_val =
+ (jack_ringbuffer_read_space(drv->pRecPtr) +
+ jack_ringbuffer_write_space(drv->pRecPtr)) /
+ drv->bytes_per_jack_input_frame * drv->bytes_per_input_frame;
+
+ releaseDriver(drv);
+
+ TRACE("return_val = %ld\n", return_val);
+
+ return return_val;
+}
+
+/* Get the number of output channels */
+int
+JACK_GetNumOutputChannels(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ int return_val = drv->num_output_channels;
+ releaseDriver(drv);
+ TRACE("getting num_output_channels of %d\n", return_val);
+ return return_val;
+}
+
+/* Get the number of input channels */
+int
+JACK_GetNumInputChannels(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ int return_val = drv->num_input_channels;
+ releaseDriver(drv);
+ TRACE("getting num_input_channels of %d\n", return_val);
+ return return_val;
+}
+
+/* Get the number of samples per second, the sample rate */
+long
+JACK_GetSampleRate(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ int return_val = drv->client_sample_rate;
+ releaseDriver(drv);
+ TRACE("getting sample_rate of %d\n", return_val);
+ return return_val;
+}
+
+void
+JACK_CleanupDriver(jack_driver_t * drv)
+{
+ TRACE("\n");
+ /* things that need to be reset both in JACK_Init & JACK_CloseDevice */
+ drv->client = 0;
+ drv->in_use = FALSE;
+ drv->state = CLOSED;
+ drv->jack_sample_rate = 0;
+ drv->output_sample_rate_ratio = 1.0;
+ drv->input_sample_rate_ratio = 1.0;
+ drv->jackd_died = FALSE;
+ gettimeofday(&drv->previousTime, 0); /* record the current time */
+ gettimeofday(&drv->last_reconnect_attempt, 0);
+}
+
+/* Initialize the jack porting library to a clean state */
+void
+JACK_Init(void)
+{
+ jack_driver_t *drv;
+ int x, y;
+
+ if(init_done)
+ {
+ TRACE("not initing twice\n");
+ return;
+ }
+
+ init_done = 1;
+
+ TRACE("\n");
+
+ pthread_mutex_lock(&device_mutex);
+
+ /* initialize the device structures */
+ for(x = 0; x < MAX_OUTDEVICES; x++)
+ {
+ drv = &outDev[x];
+
+ pthread_mutex_init(&drv->mutex, NULL);
+
+ getDriver(x);
+
+ memset(drv, 0, sizeof(jack_driver_t));
+ drv->volumeEffectType = linear;
+ drv->deviceID = x;
+
+ for(y = 0; y < MAX_OUTPUT_PORTS; y++) /* make all volume 25% as a default */
+ drv->volume[y] = 25;
+
+ JACK_CleanupDriver(drv);
+ JACK_ResetFromDriver(drv);
+ releaseDriver(drv);
+ }
+
+ client_name = 0; /* initialize the name to null */
+ do_sample_rate_conversion = TRUE; /* default to on */
+ JACK_SetClientName("bio2jack");
+
+ pthread_mutex_unlock(&device_mutex);
+
+ TRACE("finished\n");
+}
+
+/* Get the latency, in frames, of jack */
+long
+JACK_GetJackOutputLatency(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val = 0;
+
+ if(drv->client && drv->num_output_channels)
+ return_val = jack_port_get_total_latency(drv->client, drv->output_port[0]);
+
+ TRACE("got latency of %ld frames\n", return_val);
+
+ releaseDriver(drv);
+ return return_val;
+}
+
+/* Get the latency, in frames, of jack */
+long
+JACK_GetJackInputLatency(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val = 0;
+
+ if(drv->client && drv->num_input_channels)
+ return_val = jack_port_get_total_latency(drv->client, drv->input_port[0]);
+
+ TRACE("got latency of %ld frames\n", return_val);
+
+ releaseDriver(drv);
+ return return_val;
+}
+
+/* bytes that jack requests during each callback */
+unsigned long
+JACK_GetJackBufferedBytes(int deviceID)
+{
+ jack_driver_t *drv = getDriver(deviceID);
+ long return_val;
+
+ if(drv->bytes_per_jack_output_frame == 0)
+ {
+ return_val = 0;
+ } else
+ {
+ /* adjust from jack bytes to client bytes */
+ return_val =
+ drv->jack_buffer_size / drv->bytes_per_jack_output_frame *
+ drv->bytes_per_output_frame * drv->num_output_channels;
+ }
+
+ releaseDriver(drv);
+ return return_val;
+}
+
+/* value = TRUE, perform sample rate conversion */
+void
+JACK_DoSampleRateConversion(bool value)
+{
+ do_sample_rate_conversion = value;
+}
+
+/* FIXME: put the filename of the resample library header file with the decoders in here */
+/* consider mapping them in the bio2jack.h header file since its useless to the user unless */
+/* they can figure out wtf the settings on */
+void
+JACK_SetSampleRateConversionFunction(int converter)
+{
+ preferred_src_converter = converter;
+}
+
+/* set the client name that will be reported to jack when we open a */
+/* connection via JACK_OpenDevice() */
+void
+JACK_SetClientName(char *name)
+{
+ if(name)
+ {
+ if(client_name) free(client_name);
+
+ /* jack_client_name_size() is the max length of a client name, including
+ the terminating null. */
+ int size = strlen(name) + 1; /* take into account the terminating null */
+ if(size > jack_client_name_size())
+ size = jack_client_name_size();
+
+ client_name = malloc(size);
+ if(client_name)
+ snprintf(client_name, size, "%s", name);
+ else
+ ERR("unable to allocate %d bytes for client_name\n", size);
+ }
+}
+
+void
+JACK_SetPortConnectionMode(enum JACK_PORT_CONNECTION_MODE mode)
+{
+ port_connection_mode = mode;
+}
diff --git a/lib/qmmp/Output/jack/bio2jack.h b/lib/qmmp/Output/jack/bio2jack.h
new file mode 100644
index 000000000..f81a7c777
--- /dev/null
+++ b/lib/qmmp/Output/jack/bio2jack.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2003-2004 Chris Morgan <cmorgan@alum.wpi.edu>
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _H_JACK_OUT_H
+#define _H_JACK_OUT_H
+
+#include <jack/jack.h>
+
+#ifdef __cplusplus
+extern "C" {
+#else
+#define bool long
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define ERR_SUCCESS 0
+#define ERR_OPENING_JACK 1
+#define ERR_RATE_MISMATCH 2
+#define ERR_BYTES_PER_OUTPUT_FRAME_INVALID 3
+#define ERR_BYTES_PER_INPUT_FRAME_INVALID 4
+#define ERR_TOO_MANY_OUTPUT_CHANNELS 5
+#define ERR_PORT_NAME_OUTPUT_CHANNEL_MISMATCH 6
+#define ERR_PORT_NOT_FOUND 7
+#define ERR_TOO_MANY_INPUT_CHANNELS 8
+#define ERR_PORT_NAME_INPUT_CHANNEL_MISMATCH 9
+
+enum status_enum { PLAYING, PAUSED, STOPPED, CLOSED, RESET };
+enum pos_enum { BYTES, MILLISECONDS };
+
+#define PLAYED 1 /* played out of the speakers(estimated value but should be close */
+#define WRITTEN_TO_JACK 2 /* amount written out to jack */
+#define WRITTEN 3 /* amount written to the bio2jack device */
+
+/**********************/
+/* External functions */
+void JACK_Init(void); /* call this before any other bio2jack calls */
+void JACK_DoSampleRateConversion(bool value); /* whether the next device that's Open()d should do
+ sample rate conversion if necessary */
+void JACK_SetSampleRateConversionFunction(int converter); /* which SRC converter function should be used
+ for the next Open()d device */
+int JACK_Open(int *deviceID, unsigned int bits_per_sample, unsigned long *rate, int channels); /* Note: defaults to 0 input channels
+ if you need input (record) use OpenEx
+ instead */
+int JACK_OpenEx(int *deviceID, unsigned int bits_per_channel,
+ unsigned long *rate,
+ unsigned int input_channels, unsigned int output_channels,
+ const char **jack_port_name, unsigned int jack_port_name_count,
+ unsigned long jack_port_flags);
+int JACK_Close(int deviceID); /* return 0 for success */
+void JACK_Reset(int deviceID); /* free all buffered data and reset several values in the device */
+long JACK_Write(int deviceID, unsigned char *data, unsigned long bytes); /* returns the number of bytes written */
+long JACK_Read(int deviceID, unsigned char *data, unsigned long bytes); /* returns the number of bytes read */
+
+/* state setting values */
+/* set/get the written/played/buffered value based on a byte or millisecond input value */
+long JACK_GetPosition(int deviceID, enum pos_enum position, int type);
+void JACK_SetPosition(int deviceID, enum pos_enum position, long value);
+
+long JACK_GetJackLatency(int deviceID); /* deprectated, you probably want JACK_GetJackOutputLatency */
+long JACK_GetJackOutputLatency(int deviceID); /* return the output latency in frames */
+long JACK_GetJackInputLatency(int deviceID); /* return the input latency in frames */
+
+int JACK_SetState(int deviceID, enum status_enum state); /* playing, paused, stopped */
+enum status_enum JACK_GetState(int deviceID);
+
+long JACK_GetMaxOutputBufferedBytes(int deviceID);
+long JACK_GetMaxInputBufferedBytes(int deviceID);
+
+/* bytes that jack requests during each callback */
+unsigned long JACK_GetJackBufferedBytes(int deviceID);
+
+/* Properties of the jack driver */
+
+/* linear means 0 volume is silence, 100 is full volume */
+/* dbAttenuation means 0 volume is 0dB attenuation */
+/* Bio2jack defaults to linear */
+/* Note: volume controls only effect output channels for now */
+enum JACK_VOLUME_TYPE { linear, dbAttenuation };
+enum JACK_VOLUME_TYPE JACK_SetVolumeEffectType(int deviceID,
+ enum JACK_VOLUME_TYPE type);
+
+int JACK_SetAllVolume(int deviceID, unsigned int volume); /* returns 0 on success */
+int JACK_SetVolumeForChannel(int deviceID, unsigned int channel, unsigned int volume);
+void JACK_GetVolumeForChannel(int deviceID, unsigned int channel, unsigned int *volume);
+
+
+unsigned long JACK_GetOutputBytesPerSecond(int deviceID); /* bytes_per_output_frame * sample_rate */
+unsigned long JACK_GetInputBytesPerSecond(int deviceID); /* bytes_per_input_frame * sample_rate */
+unsigned long JACK_GetBytesStored(int deviceID); /* bytes currently buffered in the output buffer */
+unsigned long JACK_GetBytesFreeSpace(int deviceID); /* bytes of free space in the output buffer */
+unsigned long JACK_GetBytesUsedSpace(int deviceID); /* bytes of space used in the input buffer */
+unsigned long JACK_GetBytesPerOutputFrame(int deviceID);
+unsigned long JACK_GetBytesPerInputFrame(int deviceID);
+
+/* Note: these will probably be removed in a future release */
+int JACK_GetNumInputChannels(int deviceID);
+int JACK_GetNumOutputChannels(int deviceID);
+
+long JACK_GetSampleRate(int deviceID); /* samples per second */
+
+void JACK_SetClientName(char *name); /* sets the name that bio2jack will use when
+ creating a new jack client. name_%pid%_%deviceID%%counter%
+ will be used
+ NOTE: this defaults to name = bio2jack
+ NOTE: we limit the size of the client name to
+ jack_client_name_size() */
+
+enum JACK_PORT_CONNECTION_MODE
+{
+ CONNECT_ALL, /* connect to all avaliable ports */
+ CONNECT_OUTPUT, /* connect only to the ports we need for output */
+ CONNECT_NONE /* don't connect to any ports */
+};
+
+/* set the mode for port connections */
+/* defaults to CONNECT_ALL */
+void JACK_SetPortConnectionMode(enum JACK_PORT_CONNECTION_MODE mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* #ifndef JACK_OUT_H */
diff --git a/lib/qmmp/Output/jack/jack.pro b/lib/qmmp/Output/jack/jack.pro
new file mode 100644
index 000000000..1e81bfc08
--- /dev/null
+++ b/lib/qmmp/Output/jack/jack.pro
@@ -0,0 +1,26 @@
+
+
+HEADERS += outputjackfactory.h \
+ outputjack.h \
+ bio2jack.h
+
+SOURCES += outputjackfactory.cpp \
+ outputjack.cpp \
+ bio2jack.c
+
+INCLUDEPATH += ../../../
+QMAKE_LIBDIR += ../../../
+QMAKE_CLEAN = ../libjack.so
+CONFIG += release \
+warn_on \
+thread \
+plugin \
+link_pkgconfig
+DESTDIR = ../
+TEMPLATE = lib
+LIBS += -lqmmp
+PKGCONFIG += jack samplerate
+#TRANSLATIONS = translations/jack_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+target.path = /lib/qmmp/Output
+INSTALLS += target
diff --git a/lib/qmmp/Output/jack/outputjack.cpp b/lib/qmmp/Output/jack/outputjack.cpp
new file mode 100644
index 000000000..221968372
--- /dev/null
+++ b/lib/qmmp/Output/jack/outputjack.cpp
@@ -0,0 +1,210 @@
+#include <QObject>
+#include <QApplication>
+#include <QtGlobal>
+#include <QDir>
+#include <QSettings>
+
+#include "outputjack.h"
+#include "constants.h"
+#include "buffer.h"
+#include "visualization.h"
+
+#include <stdio.h>
+#include <string.h>
+
+void OutputJACK::stop()
+{
+ m_userStop = TRUE;
+}
+
+void OutputJACK::status()
+{
+ long ct = (m_totalWritten - latency()) / m_bps;
+
+ if (ct < 0)
+ ct = 0;
+
+ if (ct > m_currentSeconds)
+ {
+ m_currentSeconds = ct;
+ dispatch(m_currentSeconds, m_totalWritten, m_rate,
+ m_frequency, m_precision, m_channels);
+ }
+}
+
+long OutputJACK::written()
+{
+ return m_totalWritten;
+}
+
+void OutputJACK::seek(long pos)
+{
+ recycler()->mutex()->lock ();
+ recycler()->clear();
+ recycler()->mutex()->unlock();
+
+ m_totalWritten = (pos * m_bps);
+ m_currentSeconds = -1;
+}
+
+OutputJACK::OutputJACK(QObject * parent)
+ : Output(parent), m_inited(FALSE), m_pause(FALSE), m_play(FALSE),
+ m_userStop(FALSE), m_totalWritten(0), m_currentSeconds(-1),
+ m_bps(-1), m_frequency(-1), m_channels(-1), m_precision(-1)
+{
+ JACK_Init();
+}
+
+OutputJACK::~OutputJACK()
+{
+ uninitialize();
+}
+
+void OutputJACK::configure(long freq, int chan, int prec, int brate)
+{
+ qDebug("OutputJACK: configure");
+ m_precision = prec;
+ m_channels = chan;
+ m_frequency = freq;
+ m_bps = freq * chan * (prec / 8);
+ if(JACK_Open(&jack_device, prec, (unsigned long *)&freq, chan))
+ {
+ m_configure = FALSE;
+ return;
+ }
+ else
+ m_configure = TRUE;
+
+
+ prepareVisuals();
+ qDebug("OutputJACK: configure end");
+}
+
+void OutputJACK::pause()
+{
+ m_pause = (m_pause) ? FALSE : TRUE;
+ {
+ int state = m_pause ? OutputState::Paused: OutputState::Playing;
+ dispatch(OutputState((OutputState::Type) state));
+ }
+
+}
+
+bool OutputJACK::initialize()
+{
+ m_inited = m_pause = m_play = m_userStop = FALSE;
+ m_currentSeconds = -1;
+ m_totalWritten = 0;
+ if (m_inited)
+ m_inited = TRUE;
+ m_inited = TRUE;
+ m_configure = FALSE;
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+ jack_client_t *client = jack_client_open ("test_qmmp", options, &status, NULL);
+ if (client == NULL)
+ {
+ qDebug("jack_client_open() failed.");
+ if (status & JackServerFailed)
+ {
+ qDebug("Unable to connect to JACK server.");
+ }
+ return FALSE;
+ }
+ jack_client_close (client);
+ return TRUE;
+}
+
+long OutputJACK::latency()
+{
+ ulong used = 0;
+ return used;
+}
+
+void OutputJACK::run()
+{
+ mutex()->lock ();
+ if (! m_inited)
+ {
+ mutex()->unlock();
+ return;
+ }
+
+ m_play = TRUE;
+ Buffer *b = 0;
+ bool done = FALSE;
+ long m = 0;
+ unsigned long l;
+
+ dispatch(OutputState::Playing);
+
+ mutex()->unlock();
+ while (!done&&m_configure)
+ {
+ mutex()->lock ();
+ recycler()->mutex()->lock ();
+ done = m_userStop;
+
+ while (! done && (recycler()->empty() || m_pause))
+ {
+ mutex()->unlock();
+ recycler()->cond()->wakeOne();
+ recycler()->cond()->wait(recycler()->mutex());
+ mutex()->lock ();
+ done = m_userStop;
+ status();
+ }
+
+ if (! b)
+ {
+ b = recycler()->next();
+ if (b->rate)
+ m_rate = b->rate;
+ }
+
+ recycler()->cond()->wakeOne();
+ recycler()->mutex()->unlock();
+
+ if (b)
+ {
+ l = int(b->nbytes);
+ unsigned char *buf = b->data;
+ m_totalWritten += l;
+ while (l > 0)
+ {
+ m = JACK_Write(jack_device, (unsigned char*)buf, l);
+ if (!m)
+ usleep(2000);
+ usleep(((m/m_channels)*100000)/m_frequency);
+ l-=m;
+ buf+=m;
+ }
+
+ status();
+ dispatchVisual(b, m_totalWritten, m_channels, m_precision);
+ }
+ recycler()->mutex()->lock ();
+ recycler()->done();
+ recycler()->mutex()->unlock();
+ b = 0;
+ mutex()->unlock();
+ }
+ mutex()->lock ();
+ m_play = FALSE;
+ dispatch(OutputState::Stopped);
+ mutex()->unlock();
+}
+
+void OutputJACK::uninitialize()
+{
+ if (!m_inited)
+ return;
+ m_inited = FALSE;
+ m_inited = m_pause = m_play = m_userStop = FALSE;
+ m_currentSeconds = -1;
+ m_totalWritten = 0;
+ if (m_configure)
+ JACK_Close(jack_device);
+ dispatch(OutputState::Stopped);
+}
+
diff --git a/lib/qmmp/Output/jack/outputjack.h b/lib/qmmp/Output/jack/outputjack.h
new file mode 100644
index 000000000..e20a9570a
--- /dev/null
+++ b/lib/qmmp/Output/jack/outputjack.h
@@ -0,0 +1,49 @@
+#ifndef OUTPUTJACK_H
+#define OUTPUTJACK_H
+
+class OutputJACK;
+
+#include <output.h>
+#include <QObject>
+extern "C"
+{
+#include <jack/jack.h>
+}
+
+#include "bio2jack.h"
+
+class OutputJACK : public Output
+{
+ Q_OBJECT
+public:
+ OutputJACK(QObject * parent = 0);
+ ~OutputJACK();
+ bool initialize();
+ bool isInitialized() const
+ {
+ return m_inited;
+ }
+ void uninitialize();
+ void configure(long, int, int, int);
+ void stop();
+ void pause();
+ long written();
+ long latency();
+ void seek(long);
+
+private:
+ // thread run function
+ void run();
+ // helper functions
+ void status();
+ QString audio_device;
+ bool m_inited, m_configure, m_pause, m_play, m_userStop;
+ long m_totalWritten, m_currentSeconds, m_bps;
+ int m_rate, m_frequency, m_channels, m_precision, jack_device;
+ bool do_select;
+ int audio_fd;
+};
+
+
+#endif
+
diff --git a/lib/qmmp/Output/jack/outputjackfactory.cpp b/lib/qmmp/Output/jack/outputjackfactory.cpp
new file mode 100644
index 000000000..acb8d1376
--- /dev/null
+++ b/lib/qmmp/Output/jack/outputjackfactory.cpp
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Zhuravlev Uriy *
+ * stalkerg@gmail.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., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QtGui>
+
+#include "outputjack.h"
+#include "outputjackfactory.h"
+
+
+const QString& OutputJACKFactory::name() const
+{
+ static QString name(tr("JACK Plugin"));
+ return name;
+}
+
+Output* OutputJACKFactory::create(QObject* parent)
+{
+ return new OutputJACK(parent);
+}
+
+void OutputJACKFactory::showSettings(QWidget*)
+{
+}
+
+void OutputJACKFactory::showAbout(QWidget *parent)
+{
+QMessageBox::about (parent, tr("About Jack Output Plugin"),
+ tr("Qmmp Jack Output Plugin")+"\n"+
+ tr("Writen by: Yuriy Zhuravlev <slalkerg@gmail.com>"));
+}
+
+QTranslator *OutputJACKFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/jack_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(OutputJACKFactory)
diff --git a/lib/qmmp/Output/jack/outputjackfactory.h b/lib/qmmp/Output/jack/outputjackfactory.h
new file mode 100644
index 000000000..d96c73df5
--- /dev/null
+++ b/lib/qmmp/Output/jack/outputjackfactory.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef OUTPUTJACKFACTORY_H
+#define OUTPUTJACKFACTORY_H
+
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <output.h>
+#include <outputfactory.h>
+
+
+class OutputJACKFactory : public QObject,
+ OutputFactory
+{
+Q_OBJECT
+Q_INTERFACES(OutputFactory);
+
+public:
+ const QString& name() const;
+ Output* create(QObject* parent);
+ void showSettings(QWidget* parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+
+};
+
+#endif
diff --git a/lib/qmmp/Output/jack/translations/jack_plugin_ru.qm b/lib/qmmp/Output/jack/translations/jack_plugin_ru.qm
new file mode 100644
index 000000000..4050034bc
--- /dev/null
+++ b/lib/qmmp/Output/jack/translations/jack_plugin_ru.qm
Binary files differ
diff --git a/lib/qmmp/Output/jack/translations/jack_plugin_ru.ts b/lib/qmmp/Output/jack/translations/jack_plugin_ru.ts
new file mode 100644
index 000000000..1aff62f98
--- /dev/null
+++ b/lib/qmmp/Output/jack/translations/jack_plugin_ru.ts
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<context>
+ <name>OutputJACKFactory</name>
+ <message>
+ <location filename="../outputjackfactory.cpp" line="29"/>
+ <source>JACK Plugin</source>
+ <translation>Модуль JACK</translation>
+ </message>
+ <message>
+ <location filename="../outputjackfactory.cpp" line="44"/>
+ <source>About Jack Output Plugin</source>
+ <translation>О модуле вывода Jack</translation>
+ </message>
+ <message>
+ <location filename="../outputjackfactory.cpp" line="45"/>
+ <source>Qmmp Jack Output Plugin</source>
+ <translation>Модуль вывода Jack для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../outputjackfactory.cpp" line="46"/>
+ <source>Writen by: Yuriy Zhuravlev &lt;slalkerg@gmail.com&gt;</source>
+ <translation>Разработчик: Юрий Журавлёв &lt;slalkerg@gmail.com&gt;</translation>
+ </message>
+</context>
+</TS>
diff --git a/lib/qmmp/Output/jack/translations/translations.qrc b/lib/qmmp/Output/jack/translations/translations.qrc
new file mode 100644
index 000000000..af9447328
--- /dev/null
+++ b/lib/qmmp/Output/jack/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>jack_plugin_ru.qm</file>
+ </qresource>
+</RCC>
diff --git a/lib/qmmp/qmmp.pro b/lib/qmmp/qmmp.pro
new file mode 100644
index 000000000..04b244b6c
--- /dev/null
+++ b/lib/qmmp/qmmp.pro
@@ -0,0 +1,4 @@
+
+SUBDIRS += Input \
+ Output
+TEMPLATE = subdirs
diff --git a/lib/recycler.cpp b/lib/recycler.cpp
new file mode 100644
index 000000000..515652fb0
--- /dev/null
+++ b/lib/recycler.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#include "recycler.h"
+#include "constants.h"
+#include "buffer.h"
+
+
+Recycler::Recycler ( unsigned int sz )
+ : add_index ( 0 ), done_index ( 0 ), current_count ( 0 )
+{
+ buffer_count = ( sz / Buffer::size() );
+ if ( buffer_count < 1 )
+ {
+ buffer_count = 1;
+ }
+
+ buffers = new Buffer*[buffer_count];
+
+ for ( unsigned int i = 0; i < buffer_count; i ++ )
+ {
+ buffers[i] = new Buffer;
+ }
+}
+
+
+Recycler::~Recycler()
+{
+ for ( unsigned int i = 0; i < buffer_count; i++ )
+ {
+ delete buffers[i];
+ buffers[i] = 0;
+ }
+
+ delete [] buffers;
+}
+
+
+bool Recycler::full() const
+{
+ return current_count == buffer_count;
+}
+
+
+bool Recycler::empty() const
+{
+ return current_count == 0;
+}
+
+
+int Recycler::available() const
+{
+ return buffer_count - current_count;
+}
+
+
+int Recycler::used() const
+{
+ return current_count;
+}
+
+
+Buffer *Recycler::get()
+{
+ if ( full() )
+ return 0;
+ return buffers[add_index];
+}
+
+
+void Recycler::add()
+{
+ add_index = ++add_index % buffer_count;
+ current_count++;
+}
+
+
+Buffer *Recycler::next()
+{
+ return buffers[done_index];
+}
+
+
+void Recycler::done()
+{
+ done_index = ++done_index % buffer_count;
+ current_count--;
+}
+
+
+void Recycler::clear()
+{
+ add_index = done_index = current_count = 0;
+}
+
+
+unsigned int Recycler::size() const
+{
+ return buffer_count * Buffer::size();
+}
diff --git a/lib/recycler.h b/lib/recycler.h
new file mode 100644
index 000000000..b01742350
--- /dev/null
+++ b/lib/recycler.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __recycler_h
+#define __recycler_h
+
+#include <QMutex>
+#include <QWaitCondition>
+
+class Buffer;
+
+
+class Recycler
+{
+public:
+ Recycler(unsigned int sz); // sz = size in bytes
+ ~Recycler();
+
+ bool full() const;
+ bool empty() const;
+
+ int available() const;
+ int used() const;
+
+ Buffer *next(); // get next in queue
+ Buffer *get(); // get next in recycle
+
+ void add(); // add to queue
+ void done(); // add to recycle
+
+ void clear(); // clear queue
+
+ unsigned int size() const; // size in bytes
+
+ QMutex *mutex() { return &mtx; }
+ QWaitCondition *cond() { return &cnd; }
+
+
+private:
+ unsigned int buffer_count, add_index, done_index, current_count;
+ Buffer **buffers;
+ QMutex mtx;
+ QWaitCondition cnd;
+};
+
+#endif // __recycler_h
diff --git a/lib/soundcore.cpp b/lib/soundcore.cpp
new file mode 100644
index 000000000..a0716afe8
--- /dev/null
+++ b/lib/soundcore.cpp
@@ -0,0 +1,314 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QIODevice>
+#include <QFile>
+#include <QApplication>
+
+#include "decoderfactory.h"
+#include "constants.h"
+
+#include "soundcore.h"
+
+SoundCore::SoundCore(QObject *parent)
+ : QObject(parent)
+{
+ m_decoder = 0;
+ m_output = 0;
+ m_input = 0;
+ m_paused = FALSE;
+ m_useEQ = FALSE;
+ m_update = FALSE;
+ m_preamp = 0;
+ m_vis = 0;
+ for (int i = 1; i < 10; ++i)
+ m_bands[i] = 0;
+ m_error = NoError;
+ m_output = Output::create(this);
+ if (!m_output)
+ {
+ m_error = DecoderError;
+ qWarning("SoundCore: unable to create output");
+ }
+ connect(m_output, SIGNAL(stateChanged(const OutputState&)),
+ SIGNAL(outputStateChanged(const OutputState&)));
+
+ QList<OutputFactory*> *outputFactories = Output::outputFactories();
+ foreach(OutputFactory* of, *outputFactories)
+ qApp->installTranslator(of->createTranslator(this));
+
+ QList<DecoderFactory*> *decoderFactories = Decoder::decoderFactories();
+ foreach(DecoderFactory* df, *decoderFactories)
+ qApp->installTranslator(df->createTranslator(this));
+}
+
+
+SoundCore::~SoundCore()
+{}
+
+bool SoundCore::play(const QString &source)
+{
+ stop();
+ if (source.isEmpty())
+ {
+ m_error = DecoderError;
+ return FALSE;
+ }
+ m_input = new QFile(source);
+
+ m_error = OutputError;
+ if (!m_output)
+ {
+ m_output = Output::create(this);
+ if (!m_output)
+ {
+ qWarning("SoundCore: unable to create output");
+ return FALSE;
+ }
+ connect(m_output, SIGNAL(stateChanged(const OutputState&)),
+ SIGNAL(outputStateChanged(const OutputState&)));
+ }
+ if (! m_output->initialize())
+ return FALSE;
+
+ m_error = DecoderError;
+
+ if(m_vis)
+ {
+ m_vis->setOutput(m_output);
+ m_output->addVisual(m_vis);
+ }
+
+ //if (m_decoder && ! m_decoder->factory()->supports(source))
+ // m_decoder = 0;
+
+
+ if (! m_decoder)
+ {
+ qDebug ("SoundCore: creating decoder");
+ m_decoder = Decoder::create(this, source, m_input, m_output);
+
+ if (! m_decoder)
+ {
+ qWarning("SoundCore: unsupported fileformat");
+ stop();
+ emit decoderStateChanged(DecoderState(DecoderState::Error));
+ return FALSE;
+ }
+ qDebug ("ok");
+ m_decoder->setBlockSize(globalBlockSize);
+ connect(m_decoder, SIGNAL(stateChanged(const DecoderState&)),
+ SIGNAL(decoderStateChanged(const DecoderState&)));
+ setEQ(m_bands, m_preamp);
+ setEQEnabled(m_useEQ);
+ }
+
+ if (m_decoder->initialize())
+ {
+ m_output->start();
+ m_decoder->start();
+ m_error = NoError;
+ return TRUE;
+ }
+ stop();
+ return FALSE;
+}
+
+uint SoundCore::error()
+{
+ return m_error;
+}
+
+void SoundCore::stop()
+{
+ m_paused = FALSE;
+ if (m_decoder && m_decoder->isRunning())
+ {
+ m_decoder->mutex()->lock ();
+ m_decoder->stop();
+ m_decoder->mutex()->unlock();
+ }
+
+ if (m_output)
+ {
+ m_output->mutex()->lock ();
+ m_output->stop();
+ m_output->mutex()->unlock();
+ }
+
+ // wake up threads
+ if (m_decoder)
+ {
+ m_decoder->mutex()->lock ();
+ m_decoder->cond()->wakeAll();
+ m_decoder->mutex()->unlock();
+ }
+
+ if (m_output)
+ {
+ m_output->recycler()->mutex()->lock ();
+ m_output->recycler()->cond()->wakeAll();
+ m_output->recycler()->mutex()->unlock();
+ }
+
+ if (m_decoder)
+ m_decoder->wait();
+
+ if (m_output)
+ m_output->wait();
+
+ if (m_output && m_output->isInitialized())
+ {
+ m_output->uninitialize();
+ }
+ if (m_input)
+ {
+ delete m_input;
+ m_input = 0;
+ }
+ //display->setTime(0);
+ if (m_decoder)
+ {
+ delete m_decoder;
+ m_decoder = 0;
+ }
+ // recreate output
+ if (m_update && m_output)
+ {
+ delete m_output;
+ m_output = 0;
+ m_update = FALSE;
+ m_output = Output::create(this);
+ if (!m_output)
+ {
+ qWarning("SoundCore: unable to create output");
+ }
+ connect(m_output, SIGNAL(stateChanged(const OutputState&)),
+ SIGNAL(outputStateChanged(const OutputState&)));
+ }
+}
+
+void SoundCore::pause()
+{
+ if (m_output)
+ {
+ m_output->mutex()->lock ();
+ m_output->pause();
+ m_output->mutex()->unlock();
+ }
+
+ // wake up threads
+ if (m_decoder)
+ {
+ m_paused = !m_paused;
+ m_decoder->mutex()->lock ();
+ m_decoder->cond()->wakeAll();
+ m_decoder->mutex()->unlock();
+ }
+
+ if (m_output)
+ {
+ m_output->recycler()->mutex()->lock ();
+ m_output->recycler()->cond()->wakeAll();
+ m_output->recycler()->mutex()->unlock();
+ }
+}
+
+void SoundCore::seek(int pos)
+{
+ if (m_output && m_output->isRunning())
+ {
+ m_output->mutex()->lock ();
+ m_output->seek(pos);
+
+ if (m_decoder && m_decoder->isRunning())
+ {
+ m_decoder->mutex()->lock ();
+ m_decoder->seek(pos);
+ m_decoder->mutex()->unlock();
+ }
+
+ m_output->mutex()->unlock();
+ }
+}
+
+int SoundCore::length()
+{
+ if (m_decoder)
+ return int(m_decoder->lengthInSeconds());
+ return 0;
+}
+
+bool SoundCore::isInitialized()
+{
+ if (m_decoder)
+ return TRUE;
+ return FALSE;
+}
+
+bool SoundCore::isPaused()
+{
+ return m_paused;
+}
+
+void SoundCore::setEQ(int bands[10], const int &preamp)
+{
+ for (int i = 0; i < 10; ++i)
+ m_bands[i] = bands[i];
+ m_preamp = preamp;
+ if (m_decoder)
+ {
+ m_decoder->mutex()->lock ();
+ m_decoder->setEQ(m_bands, m_preamp);
+ m_decoder->setEQEnabled(m_useEQ);
+ m_decoder->mutex()->unlock();
+ }
+}
+
+void SoundCore::setEQEnabled(bool on)
+{
+ m_useEQ = on;
+ if (m_decoder)
+ {
+ m_decoder->mutex()->lock ();
+ m_decoder->setEQ(m_bands, m_preamp);
+ m_decoder->setEQEnabled(on);
+ m_decoder->mutex()->unlock();
+ }
+}
+
+void SoundCore::setVolume(int L, int R)
+{
+ if (m_output)
+ m_output->setVolume(L,R);
+}
+
+void SoundCore::updateConfig()
+{
+ m_update = TRUE;
+ if (isInitialized())
+ return;
+ stop();
+}
+
+void SoundCore::addVisualization(Visualization *visual)
+{
+ m_vis = visual;
+}
diff --git a/lib/soundcore.h b/lib/soundcore.h
new file mode 100644
index 000000000..1f88fe691
--- /dev/null
+++ b/lib/soundcore.h
@@ -0,0 +1,166 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef SOUNDCORE_H
+#define SOUNDCORE_H
+
+#include <QObject>
+
+#include "decoder.h"
+#include "output.h"
+#include "visualization.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QIODevice;
+
+class SoundCore : public QObject
+{
+ Q_OBJECT
+public:
+
+ /*! This enum describes the errors that may be returned by the error() function.
+ * Available values is:
+ * \b SoundCore:NoError - no error occurred,
+ * \b SoundCore:DecoderError - an error occurred when creating decoder,
+ * \b SoundCore:OutputError - an error occurred when creating output.
+ */
+ enum ErrorType { NoError = 0, DecoderError, OutputError };
+
+ SoundCore(QObject *parent = 0);
+
+ ~SoundCore();
+
+
+ //control
+
+ /*!
+ * This function plays file with given path, returns \b TRUE if all is OK, otherwise \b FALSE
+ */
+ bool play(const QString &source);
+
+ /*!
+ * Returns the playback error status.
+ * For example, if play() returns false, this function can be called to find out
+ * the reason why the operation failed.
+ */
+ uint error();
+
+ /*!
+ * Stops playing
+ */
+ void stop();
+
+ /*!
+ * Pauses/resumes playing
+ */
+ void pause();
+
+ /*!
+ *This function sets the current play position to \b pos
+ */
+ void seek(int pos);
+
+ // properties
+
+ /*!
+ * Returns length in seconds
+ */
+ int length();
+
+ /*!
+ * Returns \b TRUE if \b play() called successful, otherwise \b FALSE.
+ */
+ bool isReady();
+
+ /*!
+ * Returns \b TRUE if \b play() called successful, otherwise \b FALSE.
+ * Also this function returns \b FALSE if \b stop() called before
+ */
+ bool isInitialized();
+
+ /*!
+ * Returns \b TRUE if plugins in pause mode, otherwise \b FALSE.
+ */
+ bool isPaused();
+
+ //equalizer
+
+ /*!
+ * Sets equalizer settings. Each item of \b bands[] and \b reamp should be
+ * \b -100..100
+ */
+ void setEQ(int bands[10], const int &preamp);
+
+ /*!
+ * Enables equalizer if on is \b TRUE or disables it if on is \b FALSE
+ */
+ void setEQEnabled(bool on);
+
+ //volume
+
+ /*!
+ * Sets volume. \b L - volume of left channel. \b R - volume of right channel.
+ * \b L and \b R should be 0..100
+ */
+ void setVolume(int L, int R);
+
+ //config
+
+ /*!
+ * updates configuration
+ */
+ void updateConfig();
+
+ //visualization
+ /*!
+ * adds visualization
+ */
+ void addVisualization(Visualization *visual);
+
+signals:
+
+ /*!
+ * This signal is emited when the state of the decoder changes.
+ * The argument \b state is the new state of the decoder
+ */
+ void decoderStateChanged(const DecoderState& state);
+
+ /*!
+ * This signal is emited when the state of the output changes.
+ * The argument \b state is the new state of the output
+ */
+ void outputStateChanged(const OutputState& state);
+
+private:
+ Decoder* m_decoder;
+ Output* m_output;
+ QIODevice* m_input;
+ uint m_error;
+ bool m_paused;
+ bool m_useEQ;
+ bool m_update;
+ int m_preamp;
+ int m_bands[10];
+ Visualization *m_vis;
+};
+
+#endif
diff --git a/lib/visualization.h b/lib/visualization.h
new file mode 100644
index 000000000..e484606c2
--- /dev/null
+++ b/lib/visualization.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef VISUALIZATION_H
+#define VISUALIZATION_H
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+#include <QMutex>
+
+class Buffer;
+class Decoder;
+class Output;
+
+class Visualization
+{
+public:
+ Visualization() {};
+
+ virtual ~Visualization() {};
+
+ virtual void add(Buffer *, unsigned long, int, int) = 0;
+ virtual void prepare() = 0;
+
+ Decoder *decoder() const
+ {
+ return m_decoder;
+ }
+ void setDecoder(Decoder *decoder)
+ {
+ m_decoder = decoder;
+ }
+
+ Output *output() const
+ {
+ return m_output;
+ }
+ void setOutput(Output *output)
+ {
+ m_output = output;
+ }
+
+ QMutex *mutex()
+ {
+ return &m_mutex;
+ }
+
+
+private:
+ Decoder *m_decoder;
+ Output *m_output;
+ QMutex m_mutex;
+
+};
+
+#endif
diff --git a/qmmp.pri b/qmmp.pri
new file mode 100644
index 000000000..e14eebf13
--- /dev/null
+++ b/qmmp.pri
@@ -0,0 +1,8 @@
+#Common settings for Qmmp build
+
+#Comment/uncomment this if you want to change plugins list
+
+CONFIG += JACK_PLUGIN
+CONFIG += FLAC_PLUGIN
+CONFIG += MUSEPACK_PLUGIN
+CONFIG += FFMPEG_PLUGIN \ No newline at end of file
diff --git a/qmmp.pro b/qmmp.pro
new file mode 100644
index 000000000..380410100
--- /dev/null
+++ b/qmmp.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS = lib src lib/qmmp
diff --git a/qmmp.spec b/qmmp.spec
new file mode 100644
index 000000000..82b9657af
--- /dev/null
+++ b/qmmp.spec
@@ -0,0 +1,57 @@
+# This spec file was generated by KDevelop
+# Please report any problem to KDevelop Team <kdevelop-devel@kdevelop.org>
+# Thanks to Matthias Saou for his explanations on http://freshrpms.net/docs/fight.html
+
+Name: qmmp
+Version: 0.1.2
+Release: %mkrel 1
+#Vendor:
+Copyright: GPL
+Summary: Qt-based multimedia player
+Group: Multimedia
+#Packager:
+BuildRoot: %{_tmppath}/%{name}-root
+Source: http://www.ylsoftware.com/files/qmmp-0.1.2.tar.bz2
+Requires: libflac8
+Requires: libmad0
+Requires: jackit
+Requires: libffmpeg51
+Requires: libmpcdec5
+Requires: libvorbis0
+Requires: libvorbisfile3
+Requires: libogg0
+Requires: libflac8
+Requires: libsamplerate0
+Requires: alsa-lib
+Requires: qt4-common
+
+%description
+This program is an audio-player, written with help of Qt library.
+
+%prep
+%setup -q
+
+%build
+/usr/lib/qt4/bin/qmake
+make
+
+%install
+rm -rf %{buildroot}
+%makeinstall INSTALL_ROOT=/%{buildroot}/usr
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%defattr(0755,root,root,0755)
+%{_bindir}/qmmp
+%{_bindir}/qmmp.real
+%defattr(0644,root,root,0755)
+%dir %{_libdir}/qmmp
+%dir %{_libdir}/qmmp/Input
+%dir %{_libdir}/qmmp/Output
+%{_libdir}/libqmmp.*
+%{_libdir}/qmmp/Input/*.so
+%{_libdir}/qmmp/Output/*.so
+
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 000000000..a7dd13e8a
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,158 @@
+project(libsrc)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+SET(QT_USE_QTXML TRUE)
+SET(QT_USE_QTNETWORK TRUE)
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../lib)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../lib)
+link_directories(${CMAKE_INSTALL_PREFIX}/lib)
+
+SET(libsrc_SRCS
+ mainwindow.cpp
+ mp3player.cpp
+ fileloader.cpp
+ button.cpp
+ display.cpp
+ skin.cpp
+ titlebar.cpp
+ positionbar.cpp
+ number.cpp
+ playlist.cpp
+ mediafile.cpp
+ listwidget.cpp
+ playlistmodel.cpp
+ pixmapwidget.cpp
+ playlisttitlebar.cpp
+ configdialog.cpp
+ playlistslider.cpp
+ dock.cpp
+ eqwidget.cpp
+ eqtitlebar.cpp
+ eqslider.cpp
+ togglebutton.cpp
+ eqgraph.cpp
+ mainvisual.cpp
+ fft.c
+ logscale.cpp
+ textscroller.cpp
+ monostereo.cpp
+ playstatus.cpp
+ pluginitem.cpp
+ volumebar.cpp
+ balancebar.cpp
+ playstate.cpp
+ symboldisplay.cpp
+ playlistformat.cpp
+ playlistcontrol.cpp
+ qmmpstarter.cpp
+ tcpserver.cpp
+ guard.cpp
+ eqpreset.cpp
+ preseteditor.cpp
+ jumptotrackdialog.cpp
+ aboutdialog.cpp
+ timeindicator.cpp
+ keyboardmanager.cpp
+)
+
+SET(libsrc_MOC_HDRS
+ mainwindow.h
+ fileloader.h
+ button.h
+ display.h
+ skin.h
+ titlebar.h
+ positionbar.h
+ number.h
+ playlist.h
+ mediafile.h
+ listwidget.h
+ playlistmodel.h
+ pixmapwidget.h
+ playlisttitlebar.h
+ configdialog.h
+ playlistslider.h
+ dock.h
+ eqwidget.h
+ eqtitlebar.h
+ eqslider.h
+ togglebutton.h
+ eqgraph.h
+ mainvisual.h
+ inlines.h
+ fft.h
+ logscale.h
+ textscroller.h
+ monostereo.h
+ playstatus.h
+ pluginitem.h
+ volumebar.h
+ balancebar.h
+ playstate.h
+ symboldisplay.h
+ playlistformat.h
+ playlistcontrol.h
+ version.h
+ tcpserver.h
+ qmmpstarter.h
+ guard.h
+ eqpreset.h
+ preseteditor.h
+ jumptotrackdialog.h
+ aboutdialog.h
+ timeindicator.h
+ keyboardmanager.h
+)
+
+SET(libsrc_RCCS images/images.qrc stuff.qrc translations/qmmp_locales.qrc)
+
+QT4_ADD_RESOURCES(libsrc_RCC_SRCS ${libsrc_RCCS})
+
+QT4_AUTOMOC(${libsrc_MOC_SRC})
+QT4_WRAP_CPP(libsrc_MOC_SRCS ${libsrc_MOC_HDRS})
+
+# user interface
+
+
+SET(libsrc_UIS
+ configdialog.ui
+ preseteditor.ui
+ jumptotrackdialog.ui
+ aboutdialog.ui
+)
+
+SET(src_FILES
+ ../bin/qmmp
+)
+
+QT4_WRAP_UI(libsrc_UIS_H ${libsrc_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_EXECUTABLE(qmmp.real ${libsrc_SRCS} ${libsrc_MOC_SRCS} ${libsrc_UIS_H}
+ ${libsrc_RCC_SRCS})
+target_link_libraries(qmmp.real ${QT_LIBRARIES} -lqmmp)
+install(TARGETS qmmp.real DESTINATION bin PERMISSIONS WORLD_EXECUTE OWNER_READ OWNER_WRITE)
+install(FILES ${src_FILES} DESTINATION bin PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ
+WORLD_EXECUTE WORLD_READ)
diff --git a/src/aboutdialog.cpp b/src/aboutdialog.cpp
new file mode 100644
index 000000000..d9d1d0043
--- /dev/null
+++ b/src/aboutdialog.cpp
@@ -0,0 +1,62 @@
+/***************************************************************************
+* Copyright (C) 2006 by Ilya Kotov *
+* forkotov02@hotmail.ru *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+***************************************************************************/
+
+
+#include "aboutdialog.h"
+
+#include <QFile>
+#include <QTextStream>
+
+static QString getstringFromResource(const QString& res_file)
+{
+ QString ret_string;
+ QFile file(res_file);
+ if (file.open(QIODevice::ReadOnly))
+ {
+ QTextStream ts(&file);
+ ts.setCodec("UTF-8");
+ ret_string = ts.readAll();
+ file.close();
+ }
+ return ret_string;
+}
+
+AboutDialog::AboutDialog(QWidget* parent, Qt::WFlags fl)
+ : QDialog( parent, fl )
+{
+ setupUi(this);
+ licenseTextEdit->setPlainText(getstringFromResource(":COPYING"));
+ aboutTextEdit->setHtml(getstringFromResource(tr(":/html/about_en.html")));
+ authorsTextEdit->setPlainText(getstringFromResource(tr(":/html/authors_en.txt")));
+ thanksToTextEdit->setPlainText(getstringFromResource(tr(":/html/thanks_en.txt")));
+}
+
+AboutDialog::~AboutDialog()
+{}
+
+/*$SPECIALIZATION$*/
+void AboutDialog::accept()
+{
+ QDialog::accept();
+}
+
+
+
+
diff --git a/src/aboutdialog.h b/src/aboutdialog.h
new file mode 100644
index 000000000..10dcde4a5
--- /dev/null
+++ b/src/aboutdialog.h
@@ -0,0 +1,44 @@
+/***************************************************************************
+* Copyright (C) 2006 by Ilya Kotov *
+* forkotov02@hotmail.ru *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+***************************************************************************/
+
+#ifndef ABOUTDIALOG_H
+#define ABOUTDIALOG_H
+
+#include <QDialog>
+#include "ui_aboutdialog.h"
+
+/**
+ @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+
+class AboutDialog : public QDialog, private Ui::AboutDialog
+{
+ Q_OBJECT
+public:
+ AboutDialog(QWidget* parent = 0, Qt::WFlags fl = 0 );
+ ~AboutDialog();
+
+protected slots:
+ virtual void accept();
+
+};
+
+#endif
+
diff --git a/src/aboutdialog.ui b/src/aboutdialog.ui
new file mode 100644
index 000000000..56fbef9c5
--- /dev/null
+++ b/src/aboutdialog.ui
@@ -0,0 +1,174 @@
+<ui version="4.0" >
+ <class>AboutDialog</class>
+ <widget class="QDialog" name="AboutDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>518</width>
+ <height>414</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>About Qmmp</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="pixmapLabel" >
+ <property name="text" >
+ <string/>
+ </property>
+ <property name="pixmap" >
+ <pixmap resource="images/images.qrc" >:/logo-qmmp.png</pixmap>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="tabWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="aboutTab" >
+ <attribute name="title" >
+ <string>About</string>
+ </attribute>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QTextEdit" name="aboutTextEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="authorsTab" >
+ <attribute name="title" >
+ <string>Authors</string>
+ </attribute>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QTextEdit" name="authorsTextEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="thanksToTab" >
+ <attribute name="title" >
+ <string>Thanks To</string>
+ </attribute>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTextEdit" name="thanksToTextEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="licenseTab" >
+ <attribute name="title" >
+ <string>License Agreement</string>
+ </attribute>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QTextEdit" name="licenseTextEdit" >
+ <property name="readOnly" >
+ <bool>true</bool>
+ </property>
+ <property name="overwriteMode" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons" >
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="images/images.qrc" />
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>AboutDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>AboutDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/balancebar.cpp b/src/balancebar.cpp
new file mode 100644
index 000000000..32792b327
--- /dev/null
+++ b/src/balancebar.cpp
@@ -0,0 +1,136 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QMouseEvent>
+#include <QPainter>
+#include <math.h>
+
+#include "skin.h"
+#include "button.h"
+#include "mainwindow.h"
+
+#include "balancebar.h"
+
+
+BalanceBar::BalanceBar(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+ setPixmap(m_skin->getBalanceBar(0));
+ m_moving = FALSE;
+ m_min = -100;
+ m_max = 100;
+ m_old = m_value = 0;
+ draw(FALSE);
+}
+
+
+BalanceBar::~BalanceBar()
+{}
+
+void BalanceBar::mousePressEvent(QMouseEvent *e)
+{
+
+ m_moving = TRUE;
+ press_pos = e->x();
+ if(m_pos<e->x() && e->x()<m_pos+11)
+ {
+ press_pos = e->x()-m_pos;
+ }
+ else
+ {
+ m_value = convert(qMax(qMin(width()-18,e->x()-6),0));
+ press_pos = 6;
+ if (m_value!=m_old)
+ {
+ emit sliderMoved(m_value);
+
+ }
+ }
+ draw();
+}
+
+void BalanceBar::mouseMoveEvent (QMouseEvent *e)
+{
+ if(m_moving)
+ {
+ int po = e->x();
+ po = po - press_pos;
+
+ if(0<=po && po<=width()-13)
+ {
+ m_value = convert(po);
+ draw();
+ emit sliderMoved(m_value);
+ }
+ }
+}
+
+void BalanceBar::mouseReleaseEvent(QMouseEvent*)
+{
+ m_moving = FALSE;
+ draw(FALSE);
+ if (m_value!=m_old)
+ {
+ m_old = m_value;
+ }
+
+}
+
+void BalanceBar::setValue(int v)
+{
+ if (m_moving || m_max == 0)
+ return;
+ m_value = v;
+ draw(FALSE);
+}
+
+void BalanceBar::setMax(int max)
+{
+ m_max = max;
+ draw(FALSE);
+}
+
+void BalanceBar::updateSkin()
+{
+ draw(FALSE);
+}
+
+void BalanceBar::draw(bool pressed)
+{
+ if(abs(m_value)<6)
+ m_value = 0;
+ int p=int(ceil(double(m_value-m_min)*(width()-13)/(m_max-m_min)));
+ m_pixmap = m_skin->getBalanceBar(abs(27*m_value/m_max));
+ QPainter paint(&m_pixmap);
+ if(pressed)
+ paint.drawPixmap(p,1,m_skin->getButton(Skin::BT_BAL_P));
+ else
+ paint.drawPixmap(p,1,m_skin->getButton(Skin::BT_BAL_N));
+ setPixmap(m_pixmap);
+ m_pos = p;
+}
+
+int BalanceBar::convert(int p)
+{
+ return int(ceil(double(m_max-m_min)*(p)/(width()-13)+m_min));
+}
+
diff --git a/src/balancebar.h b/src/balancebar.h
new file mode 100644
index 000000000..6704671f9
--- /dev/null
+++ b/src/balancebar.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef BALANCEBAR_H
+#define BALANCEBAR_H
+
+#include <pixmapwidget.h>
+
+class Skin;
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class BalanceBar : public PixmapWidget
+{
+Q_OBJECT
+public:
+ BalanceBar(QWidget *parent = 0);
+
+ ~BalanceBar();
+
+ int value() {return m_value; };
+
+public slots:
+ void setValue(int);
+ void setMax(int);
+
+signals:
+ void sliderMoved (int);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ bool m_moving;
+ int press_pos;
+ int m_max, m_min, m_pos, m_value, m_old;
+ QPixmap m_pixmap;
+ int convert(int); // value = convert(position);
+ void draw(bool pressed = TRUE);
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+
+};
+
+#endif
diff --git a/src/button.cpp b/src/button.cpp
new file mode 100644
index 000000000..8bd006ed3
--- /dev/null
+++ b/src/button.cpp
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "button.h"
+#include "skin.h"
+
+Button::Button ( QWidget *parent, uint normal, uint pressed )
+ : PixmapWidget ( parent )
+{
+ name_normal = normal;
+ name_pressed = pressed;
+ skin = Skin::getPointer();
+ setON ( FALSE );
+ connect ( skin, SIGNAL ( skinChanged() ), this, SLOT ( updateSkin() ) );
+}
+
+
+Button::~Button()
+{}
+
+void Button::updateSkin()
+{
+ setPixmap ( skin->getButton ( name_normal ) );
+}
+
+void Button::setON ( bool on )
+{
+ if ( on )
+ setPixmap ( skin->getButton ( name_pressed ) );
+ else
+ setPixmap ( skin->getButton ( name_normal ) );
+}
+void Button::mousePressEvent ( QMouseEvent* )
+{
+ setON ( TRUE );
+}
+
+void Button::mouseReleaseEvent ( QMouseEvent* )
+{
+ setON ( FALSE );
+ emit clicked();
+}
diff --git a/src/button.h b/src/button.h
new file mode 100644
index 000000000..7f2d8f314
--- /dev/null
+++ b/src/button.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include "pixmapwidget.h"
+
+class Skin;
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Button : public PixmapWidget
+{
+Q_OBJECT
+public:
+ Button(QWidget *parent, uint normal, uint pressed);
+
+ ~Button();
+
+signals:
+ void clicked();
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *skin;
+ void setON(bool);
+ uint name_normal, name_pressed;
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+};
+
+#endif
diff --git a/src/configdialog.cpp b/src/configdialog.cpp
new file mode 100644
index 000000000..e91139efc
--- /dev/null
+++ b/src/configdialog.cpp
@@ -0,0 +1,334 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QDir>
+#include <QSettings>
+#include <QFontDialog>
+#include <QTreeWidgetItem>
+#include <QHeaderView>
+#include <QCheckBox>
+#include <QRadioButton>
+#include <QMenu>
+
+#include <decoder.h>
+#include <output.h>
+#include <decoderfactory.h>
+#include <outputfactory.h>
+
+#include "skin.h"
+#include "pluginitem.h"
+#include "configdialog.h"
+
+ConfigDialog::ConfigDialog ( QWidget *parent )
+ : QDialog ( parent )
+{
+ ui.setupUi ( this );
+ connect ( ui. contentsWidget,
+ SIGNAL ( currentItemChanged ( QListWidgetItem *, QListWidgetItem * ) ),
+ this, SLOT ( changePage ( QListWidgetItem *, QListWidgetItem* ) ) );
+ connect ( ui.mainFontButton, SIGNAL ( clicked() ), SLOT ( setMainFont() ) );
+ connect ( ui.plFontButton, SIGNAL ( clicked() ), SLOT ( setPlFont() ) );
+ connect ( ui.preferencesButton, SIGNAL ( clicked() ), SLOT (showPluginSettings()));
+ connect ( ui.informationButton, SIGNAL ( clicked() ), SLOT (showPluginInfo()));
+ connect ( this, SIGNAL(accepted()),SLOT(saveSettings()));
+ ui.listWidget->setIconSize ( QSize ( 69,29 ) );
+ m_skin = Skin::getPointer();
+ readSettings();
+ loadSkins();
+ loadPluginsInfo();
+ loadFonts();
+ createMenus();
+}
+
+ConfigDialog::~ConfigDialog()
+{
+ while (!m_outputPluginItems.isEmpty())
+ delete m_outputPluginItems.takeFirst();
+ while (!m_inputPluginItems.isEmpty())
+ delete m_outputPluginItems.takeFirst();
+}
+
+void ConfigDialog::readSettings()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ ui.formatLineEdit->setText(
+ settings.value ( "PlayList/title_format", "%p - %t").toString());
+ ui.metadataCheckBox->setChecked(
+ settings.value ( "PlayList/load_metadata", TRUE).toBool());
+ ui.trayCheckBox->setChecked(
+ settings.value("Tray/enabled",TRUE).toBool());
+ ui.messageCheckBox->setChecked(
+ settings.value("Tray/show_message",TRUE).toBool());
+ ui.messageDelaySpinBox->setValue(settings.value("Tray/message_delay",
+ 2000).toInt());
+ ui.messageCheckBox->setEnabled(ui.trayCheckBox->isChecked());
+ ui.messageDelaySpinBox->setEnabled(ui.trayCheckBox->isChecked() ||
+ ui.messageCheckBox->isChecked() );
+ ui.toolTipCheckBox->setEnabled(ui.trayCheckBox->isChecked());
+ ui.toolTipCheckBox->setChecked(
+ settings.value("Tray/show_tooltip",FALSE).toBool());
+
+ ui.hideToTrayRadioButton->setChecked(settings.value("Tray/hide_on_close", FALSE).toBool());
+ ui.closeGroupBox->setEnabled(ui.trayCheckBox->isChecked());
+}
+
+void ConfigDialog::changePage ( QListWidgetItem *current, QListWidgetItem *previous )
+{
+ if ( !current )
+ current = previous;
+ ui.stackedWidget->setCurrentIndex ( ui.contentsWidget->row ( current ) );
+}
+
+void ConfigDialog::changeSkin()
+{
+ int row = ui.listWidget->currentRow();
+ QString path = m_skinList.at ( row ).canonicalFilePath();
+ m_skin->setSkin ( path );
+}
+
+void ConfigDialog::loadSkins()
+{
+ m_skinList.clear();
+ //findSkins(":/");
+
+ QFileInfo fileInfo (":/default");
+ QPixmap preview = Skin::getPixmap ("main", QDir (fileInfo.filePath()));
+ QListWidgetItem *item = new QListWidgetItem (fileInfo.fileName ());
+ item->setIcon ( preview );
+ ui.listWidget->addItem ( item );
+ m_skinList << fileInfo;
+
+ findSkins(QDir::homePath() +"/.qmmp/skins");
+ connect ( ui.listWidget, SIGNAL ( itemClicked ( QListWidgetItem* ) ),
+ this, SLOT ( changeSkin() ) );
+}
+
+void ConfigDialog::findSkins(const QString &path)
+{
+ QDir dir(path);
+ dir.setFilter ( QDir::Dirs );
+ QList <QFileInfo> fileList = dir.entryInfoList();
+ if ( fileList.count() == 2 )
+ return;
+ for ( int i = 2; i < fileList.size(); ++i )
+ {
+ QFileInfo fileInfo = fileList.at ( i );
+ QPixmap preview = Skin::getPixmap ( "main", QDir ( fileInfo.filePath() ) );
+ if ( !preview.isNull() )
+ {
+ QListWidgetItem *item = new QListWidgetItem ( fileInfo.fileName () );
+ item->setIcon ( preview );
+ ui.listWidget->addItem ( item );
+ m_skinList << fileInfo;
+ }
+ }
+}
+
+void ConfigDialog::loadPluginsInfo()
+{
+ /*
+ load input plugins information
+ */
+ QList <DecoderFactory *> *decoders = 0;
+ decoders = Decoder::decoderFactories();
+ QStringList files = Decoder::decoderFiles();
+ ui.inputPluginTable->setColumnCount ( 3 );
+ ui.inputPluginTable->verticalHeader()->hide();
+ ui.inputPluginTable->setHorizontalHeaderLabels ( QStringList()
+ << tr ( "Enabled" ) << tr ( "Description" ) << tr ( "Filename" ) );
+ ui.inputPluginTable->setRowCount ( decoders->count () );
+ for ( int i = 0; i < decoders->count (); ++i )
+ {
+ InputPluginItem *item = new InputPluginItem(this,decoders->at(i),files.at(i));
+ QCheckBox* checkBox = new QCheckBox ( ui.inputPluginTable );
+ connect(checkBox, SIGNAL(toggled(bool)), item, SLOT(setSelected(bool)));
+ checkBox->setChecked(item->isSelected());
+ ui.inputPluginTable->setCellWidget ( i, 0, checkBox );
+ ui.inputPluginTable->setItem ( i,1,
+ new QTableWidgetItem (item->factory()->name()) );
+ ui.inputPluginTable->setItem ( i,2, new QTableWidgetItem (files.at (i)) );
+ }
+ ui.inputPluginTable->resizeColumnToContents ( 0 );
+ ui.inputPluginTable->resizeColumnToContents ( 1 );
+ ui.inputPluginTable->resizeRowsToContents ();
+ /*
+ load output plugins information
+ */
+ QList <OutputFactory *> *outputs = 0;
+ outputs = Output::outputFactories();
+ files = Output::outputFiles();
+ ui.outputPluginTable->setColumnCount ( 3 );
+ ui.outputPluginTable->verticalHeader()->hide();
+ ui.outputPluginTable->setHorizontalHeaderLabels ( QStringList()
+ << tr ( "Enabled" ) << tr ( "Description" ) << tr ( "Filename" ) );
+ ui.outputPluginTable->setRowCount ( outputs->count () );
+
+ for ( int i = 0; i < outputs->count (); ++i )
+ {
+ OutputPluginItem *item = new OutputPluginItem(this,outputs->at(i),files.at(i));
+ m_outputPluginItems.append(item);
+ QRadioButton* button = new QRadioButton ( ui.outputPluginTable );
+ connect(button, SIGNAL(pressed ()), item, SLOT(select()));
+ button->setChecked ( item->isSelected() );
+ ui.outputPluginTable->setCellWidget ( i, 0, button );
+ ui.outputPluginTable->setItem (i,1,
+ new QTableWidgetItem (item->factory()->name()));
+ ui.outputPluginTable->setItem (i,2, new QTableWidgetItem (files.at(i)));
+ }
+
+ ui.outputPluginTable->resizeColumnToContents ( 0 );
+ ui.outputPluginTable->resizeColumnToContents ( 1 );
+ ui.outputPluginTable->resizeRowsToContents ();
+}
+
+void ConfigDialog::loadFonts()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ QString fontname = settings.value ( "PlayList/Font","" ).toString();
+ if ( fontname.isEmpty () )
+ fontname = QFont ( "Helvetica [Cronyx]", 10 ).toString();
+ ui.plFontLabel -> setText ( fontname );
+
+ fontname = settings.value ( "MainWindow/Font","" ).toString();
+ if ( fontname.isEmpty () )
+ fontname = QFont ( "Helvetica [Cronyx]", 9 ).toString();
+ ui.mainFontLabel -> setText ( fontname );
+}
+
+void ConfigDialog::setPlFont()
+{
+ bool ok;
+ QFont font;
+ font.fromString ( ui.plFontLabel->text() );
+ font = QFontDialog::getFont ( &ok, font, this );
+ if ( ok )
+ {
+ ui.plFontLabel -> setText ( font.toString () );
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.setValue ( "PlayList/Font", font.toString() );
+ }
+}
+
+void ConfigDialog::setMainFont()
+{
+ bool ok;
+ QFont font;
+ font.fromString ( ui.plFontLabel->text() );
+ font = QFontDialog::getFont ( &ok, font, this );
+ if ( ok )
+ {
+ ui.mainFontLabel -> setText ( font.toString () );
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.setValue ( "MainWindow/Font", font.toString() );
+ }
+}
+
+void ConfigDialog::showPluginSettings()
+{
+ switch ( ( int ) ui.pluginsTab -> currentIndex () )
+ {
+ case 0:
+ {
+ QList <DecoderFactory *> *decoders = 0;
+ decoders = Decoder::decoderFactories();
+ int row = ui.inputPluginTable->currentRow ();
+ if ( !decoders || row<0 )
+ return;
+
+ decoders->at ( row )->showSettings ( this );
+ break;
+ }
+ case 1:
+ {
+ int row = ui.outputPluginTable->currentRow ();
+ if ( m_outputPluginItems.isEmpty() || row < 0 )
+ return;
+ m_outputPluginItems.at(row)->factory()->showSettings ( this );
+ break;
+ }
+ }
+}
+
+void ConfigDialog::showPluginInfo()
+{
+ switch ( ( int ) ui.pluginsTab -> currentIndex () )
+ {
+ case 0:
+ {
+ QList <DecoderFactory *> *decoders = 0;
+ decoders = Decoder::decoderFactories();
+ int row = ui.inputPluginTable->currentRow ();
+ if ( !decoders || row<0 )
+ return;
+
+ decoders->at ( row )->showAbout ( this );
+ break;
+ }
+ case 1:
+ {
+ int row = ui.outputPluginTable->currentRow ();
+ if ( m_outputPluginItems.isEmpty() || row < 0 )
+ return;
+ m_outputPluginItems.at(row)->factory()->showAbout ( this );
+ break;
+ }
+ }
+}
+
+void ConfigDialog::createMenus()
+{
+ QMenu *menu = new QMenu(this);
+
+ menu->addAction(tr("Artist"))->setData("%p");
+ menu->addAction(tr("Album"))->setData("%a");
+ menu->addAction(tr("Title"))->setData("%t");
+ menu->addAction(tr("Tracknumber"))->setData("%n");
+ menu->addAction(tr("Genre"))->setData("%g");
+ menu->addAction(tr("Filename"))->setData("%f");
+ menu->addAction(tr("Filepath"))->setData("%F");
+ menu->addAction(tr("Date"))->setData("%d");
+ menu->addAction(tr("Year"))->setData("%y");
+ menu->addAction(tr("Comment"))->setData("%c");
+ ui.titleButton->setMenu(menu);
+ ui.titleButton->setPopupMode(QToolButton::InstantPopup);
+ connect( menu, SIGNAL(triggered ( QAction * )), SLOT(addTitleString( QAction * )));
+}
+
+void ConfigDialog::addTitleString( QAction * a)
+{
+ if (ui.formatLineEdit->cursorPosition () < 1)
+ ui.formatLineEdit->insert(a->data().toString());
+ else
+ ui.formatLineEdit->insert(" - "+a->data().toString());
+}
+
+void ConfigDialog::saveSettings()
+{
+ QSettings settings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.setValue ("PlayList/title_format", ui.formatLineEdit->text());
+ settings.setValue ("PlayList/load_metadata", ui.metadataCheckBox->isChecked());
+ settings.setValue ("MainWindow/tray_enabled", ui.trayCheckBox->isChecked());
+ settings.setValue ("Tray/enabled", ui.trayCheckBox->isChecked());
+ settings.setValue ("Tray/show_message", ui.messageCheckBox->isChecked());
+ settings.setValue ("Tray/message_delay", ui.messageDelaySpinBox->value());
+ settings.setValue ("Tray/show_tooltip", ui.toolTipCheckBox->isChecked());
+ settings.setValue ("Tray/hide_on_close",ui.hideToTrayRadioButton->isChecked());
+}
+
+
diff --git a/src/configdialog.h b/src/configdialog.h
new file mode 100644
index 000000000..879034d26
--- /dev/null
+++ b/src/configdialog.h
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef CONFIGDIALOG_H
+#define CONFIGDIALOG_H
+
+#include <QDialog>
+#include <QTreeWidgetItem>
+
+#include "ui_configdialog.h"
+
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class QFileInfo;
+
+class Skin;
+class InputPluginItem;
+class OutputPluginItem;
+
+class ConfigDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ ConfigDialog(QWidget *parent = 0);
+
+ ~ConfigDialog();
+
+private slots:
+ void changePage(QListWidgetItem *current, QListWidgetItem *previous);
+ void changeSkin();
+ void setPlFont();
+ void setMainFont();
+ void showPluginSettings();
+ void showPluginInfo();
+ void addTitleString( QAction * );
+ void saveSettings();
+
+private:
+ void readSettings();
+ void loadSkins();
+ void findSkins(const QString &path);
+ void loadPluginsInfo();
+ void loadFonts();
+ void createMenus();
+
+
+ QList <QFileInfo> m_skinList;
+ Ui::ConfigDialog ui;
+ Skin *m_skin;
+ QPixmap pixmap;
+
+ QList <InputPluginItem*> m_inputPluginItems;
+ QList <OutputPluginItem*> m_outputPluginItems;
+};
+
+#endif
diff --git a/src/configdialog.ui b/src/configdialog.ui
new file mode 100644
index 000000000..65e92eabe
--- /dev/null
+++ b/src/configdialog.ui
@@ -0,0 +1,694 @@
+<ui version="4.0" >
+ <class>ConfigDialog</class>
+ <widget class="QDialog" name="ConfigDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>521</width>
+ <height>375</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Qmmp Settings</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QListWidget" name="contentsWidget" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>7</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize" >
+ <size>
+ <width>140</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="movement" >
+ <enum>QListView::Static</enum>
+ </property>
+ <property name="flow" >
+ <enum>QListView::TopToBottom</enum>
+ </property>
+ <property name="isWrapping" stdset="0" >
+ <bool>false</bool>
+ </property>
+ <property name="resizeMode" >
+ <enum>QListView::Adjust</enum>
+ </property>
+ <property name="viewMode" >
+ <enum>QListView::IconMode</enum>
+ </property>
+ <property name="uniformItemSizes" >
+ <bool>false</bool>
+ </property>
+ <property name="batchSize" >
+ <number>100</number>
+ </property>
+ <property name="wordWrap" >
+ <bool>false</bool>
+ </property>
+ <property name="currentRow" >
+ <number>0</number>
+ </property>
+ <item>
+ <property name="text" >
+ <string>Appearance</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="images/images.qrc" >:/interface.png</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Playlist</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="images/images.qrc" >:/playlist.png</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Plugins</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="images/images.qrc" >:/plugins.png</iconset>
+ </property>
+ </item>
+ <item>
+ <property name="text" >
+ <string>Advanced</string>
+ </property>
+ <property name="icon" >
+ <iconset resource="images/images.qrc" >:/advanced.png</iconset>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QStackedWidget" name="stackedWidget" >
+ <property name="sizePolicy" >
+ <sizepolicy>
+ <hsizetype>5</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Raised</enum>
+ </property>
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="widget1" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Skins</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QListWidget" name="listWidget" >
+ <property name="movement" >
+ <enum>QListView::Static</enum>
+ </property>
+ <property name="flow" >
+ <enum>QListView::TopToBottom</enum>
+ </property>
+ <property name="viewMode" >
+ <enum>QListView::ListMode</enum>
+ </property>
+ <property name="modelColumn" >
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="plGroupBox" >
+ <property name="title" >
+ <string>Fonts</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="maximumSize" >
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>Player:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="maximumSize" >
+ <size>
+ <width>80</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text" >
+ <string>Playlist:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QLabel" name="plFontLabel" >
+ <property name="frameShape" >
+ <enum>QFrame::Panel</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text" >
+ <string>???</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QToolButton" name="plFontButton" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QLabel" name="mainFontLabel" >
+ <property name="frameShape" >
+ <enum>QFrame::Panel</enum>
+ </property>
+ <property name="frameShadow" >
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <property name="text" >
+ <string>???</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2" >
+ <widget class="QToolButton" name="mainFontButton" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="page" >
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QGroupBox" name="groupBox_3" >
+ <property name="title" >
+ <string>Metadata</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" >
+ <widget class="QCheckBox" name="metadataCheckBox" >
+ <property name="text" >
+ <string>Load metadata from files</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Song Display</string>
+ </property>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Title format:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="formatLineEdit" />
+ </item>
+ <item>
+ <widget class="QToolButton" name="titleButton" >
+ <property name="text" >
+ <string>...</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>121</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="widget" >
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="1" column="0" >
+ <widget class="QPushButton" name="preferencesButton" >
+ <property name="text" >
+ <string>Preferences</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QPushButton" name="informationButton" >
+ <property name="text" >
+ <string>Information</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>101</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0" colspan="3" >
+ <widget class="QTabWidget" name="pluginsTab" >
+ <property name="tabPosition" >
+ <enum>QTabWidget::North</enum>
+ </property>
+ <property name="tabShape" >
+ <enum>QTabWidget::Rounded</enum>
+ </property>
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="Input" >
+ <attribute name="title" >
+ <string>Input</string>
+ </attribute>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTableWidget" name="inputPluginTable" >
+ <property name="selectionMode" >
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior" >
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ <property name="rowCount" >
+ <number>0</number>
+ </property>
+ <property name="columnCount" >
+ <number>0</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="Output" >
+ <attribute name="title" >
+ <string>Output</string>
+ </attribute>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QTableWidget" name="outputPluginTable" >
+ <property name="selectionMode" >
+ <enum>QAbstractItemView::SingleSelection</enum>
+ </property>
+ <property name="selectionBehavior" >
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="page_2" >
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_4" >
+ <property name="title" >
+ <string>Tray Icon</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="2" column="0" >
+ <widget class="QCheckBox" name="toolTipCheckBox" >
+ <property name="text" >
+ <string>Show tooltip</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QCheckBox" name="messageCheckBox" >
+ <property name="text" >
+ <string>Show message</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1" >
+ <widget class="QSpinBox" name="messageDelaySpinBox" >
+ <property name="maximum" >
+ <number>10000</number>
+ </property>
+ <property name="minimum" >
+ <number>100</number>
+ </property>
+ <property name="singleStep" >
+ <number>100</number>
+ </property>
+ <property name="value" >
+ <number>1000</number>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Message delay, ms:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2" >
+ <widget class="QCheckBox" name="trayCheckBox" >
+ <property name="text" >
+ <string>Show tray icon</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="closeGroupBox" >
+ <property name="title" >
+ <string>Action On Close</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="hideToTrayRadioButton" >
+ <property name="text" >
+ <string>Hide to tray</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="quitRadioButton" >
+ <property name="text" >
+ <string>Quit</string>
+ </property>
+ <property name="checked" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line" >
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>341</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ <include location="images/images.qrc" />
+ </resources>
+ <connections>
+ <connection>
+ <sender>closeButton</sender>
+ <signal>clicked()</signal>
+ <receiver>ConfigDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>510</x>
+ <y>364</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>316</x>
+ <y>340</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>trayCheckBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>toolTipCheckBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>344</x>
+ <y>66</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>177</x>
+ <y>113</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>trayCheckBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>messageDelaySpinBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>208</x>
+ <y>55</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>469</x>
+ <y>148</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>trayCheckBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>messageCheckBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>262</x>
+ <y>55</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>258</x>
+ <y>76</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>messageCheckBox</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>messageDelaySpinBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>307</x>
+ <y>82</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>469</x>
+ <y>148</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>trayCheckBox</sender>
+ <signal>clicked(bool)</signal>
+ <receiver>closeGroupBox</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>376</x>
+ <y>56</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>436</x>
+ <y>175</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/default/balance.png b/src/default/balance.png
new file mode 100644
index 000000000..5fa10cce8
--- /dev/null
+++ b/src/default/balance.png
Binary files differ
diff --git a/src/default/cbuttons.png b/src/default/cbuttons.png
new file mode 100644
index 000000000..7a1369b59
--- /dev/null
+++ b/src/default/cbuttons.png
Binary files differ
diff --git a/src/default/eq_ex.png b/src/default/eq_ex.png
new file mode 100644
index 000000000..974004590
--- /dev/null
+++ b/src/default/eq_ex.png
Binary files differ
diff --git a/src/default/eqmain.png b/src/default/eqmain.png
new file mode 100644
index 000000000..b28b818de
--- /dev/null
+++ b/src/default/eqmain.png
Binary files differ
diff --git a/src/default/main.png b/src/default/main.png
new file mode 100644
index 000000000..6b7c8597a
--- /dev/null
+++ b/src/default/main.png
Binary files differ
diff --git a/src/default/monoster.png b/src/default/monoster.png
new file mode 100644
index 000000000..7ddb9d0e5
--- /dev/null
+++ b/src/default/monoster.png
Binary files differ
diff --git a/src/default/numbers.png b/src/default/numbers.png
new file mode 100644
index 000000000..46f1e1f63
--- /dev/null
+++ b/src/default/numbers.png
Binary files differ
diff --git a/src/default/playpaus.png b/src/default/playpaus.png
new file mode 100644
index 000000000..0cfbd6835
--- /dev/null
+++ b/src/default/playpaus.png
Binary files differ
diff --git a/src/default/pledit.png b/src/default/pledit.png
new file mode 100644
index 000000000..3c2943cea
--- /dev/null
+++ b/src/default/pledit.png
Binary files differ
diff --git a/src/default/pledit.txt b/src/default/pledit.txt
new file mode 100644
index 000000000..5435dd2fd
--- /dev/null
+++ b/src/default/pledit.txt
@@ -0,0 +1,6 @@
+[Text]
+Normal=#C0C0C0
+Current=#8080FF
+NormalBG=#400080
+SelectedBG=#408080
+Font=Tahoma Bold \ No newline at end of file
diff --git a/src/default/posbar.png b/src/default/posbar.png
new file mode 100644
index 000000000..271106557
--- /dev/null
+++ b/src/default/posbar.png
Binary files differ
diff --git a/src/default/shufrep.png b/src/default/shufrep.png
new file mode 100644
index 000000000..107fd50bb
--- /dev/null
+++ b/src/default/shufrep.png
Binary files differ
diff --git a/src/default/text.png b/src/default/text.png
new file mode 100644
index 000000000..d37241405
--- /dev/null
+++ b/src/default/text.png
Binary files differ
diff --git a/src/default/titlebar.png b/src/default/titlebar.png
new file mode 100644
index 000000000..c1e7818cd
--- /dev/null
+++ b/src/default/titlebar.png
Binary files differ
diff --git a/src/default/viscolor.txt b/src/default/viscolor.txt
new file mode 100644
index 000000000..1a88c6ed5
--- /dev/null
+++ b/src/default/viscolor.txt
@@ -0,0 +1,23 @@
+0,64,128
+0,64,128
+55,105,155
+63,111,159
+71,117,163
+79,123,167
+87,129,171
+95,135,175
+103,141,179
+111,147,183
+119,153,187
+127,159,191
+135,165,195
+143,171,199
+151,177,203
+159,183,207
+167,189,211
+175,195,215
+255,255,255
+167,189,211
+135,165,195
+119,153,187
+87,129,171
diff --git a/src/default/volume.png b/src/default/volume.png
new file mode 100644
index 000000000..b4f457453
--- /dev/null
+++ b/src/default/volume.png
Binary files differ
diff --git a/src/display.cpp b/src/display.cpp
new file mode 100644
index 000000000..f401d2f54
--- /dev/null
+++ b/src/display.cpp
@@ -0,0 +1,275 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QCoreApplication>
+#include <QPainter>
+#include <QPushButton>
+#include <QLabel>
+#include <QSettings>
+
+#include <output.h>
+#include "skin.h"
+#include "mainvisual.h"
+#include "button.h"
+#include "titlebar.h"
+#include "positionbar.h"
+#include "number.h"
+#include "togglebutton.h"
+#include "symboldisplay.h"
+#include "textscroller.h"
+#include "monostereo.h"
+#include "playstatus.h"
+#include "volumebar.h"
+#include "balancebar.h"
+#include "mainwindow.h"
+#include "timeindicator.h"
+
+#include "display.h"
+
+MainDisplay::MainDisplay ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ m_skin = Skin::getPointer();
+ setPixmap ( m_skin->getMain() );
+ setMaximumSize ( QSize ( 275,116 ) );
+ setMinimumSize ( QSize ( 275,116 ) );
+
+ m_mw = qobject_cast<MainWindow*>(parent);
+
+ Button *previous = new Button ( this,
+ Skin::BT_PREVIOUS_N, Skin::BT_PREVIOUS_P );
+ previous->move ( 16, 88 );
+ connect ( previous,SIGNAL ( clicked() ),parent,SLOT ( previous() ) );
+ Button *play = new Button ( this,
+ Skin::BT_PLAY_N, Skin::BT_PLAY_P );
+ play->move ( 39, 88 );
+ connect ( play,SIGNAL ( clicked() ),parent,SLOT ( play() ) );
+ Button *pause = new Button ( this, Skin::BT_PAUSE_N,Skin::BT_PAUSE_P );
+ pause->move ( 62, 88 );
+ connect ( pause,SIGNAL ( clicked() ),parent,SLOT ( pause() ) );
+ Button *stop = new Button ( this, Skin::BT_STOP_N,Skin::BT_STOP_P );
+ stop->move ( 85, 88 );
+ connect ( stop,SIGNAL ( clicked() ),parent,SLOT ( stop() ) );
+ connect ( stop,SIGNAL ( clicked() ),this,SLOT ( hideTimeDisplay() ) );
+ Button *next = new Button ( this, Skin::BT_NEXT_N,Skin::BT_NEXT_P );
+ next->move ( 108, 88 );
+ connect ( next,SIGNAL ( clicked() ),parent,SLOT ( next() ) );
+ Button *eject = new Button ( this, Skin::BT_EJECT_N,Skin::BT_EJECT_P );
+ eject->move ( 136, 89 );
+ connect ( eject,SIGNAL ( clicked() ),parent,SLOT ( addFile() ) );
+ connect ( m_skin, SIGNAL ( skinChanged() ), this, SLOT ( updateSkin() ) );
+ posbar = new PositionBar ( this );
+ posbar->move ( 16,72 );
+ //connect(posbar, SIGNAL(sliderMoved(int)), SLOT(setTime(int)));
+ MainVisual* vis = new MainVisual ( this,"" );
+ vis->setGeometry ( 24,39,75,20 );
+ vis->show();
+
+ m_eqButton = new ToggleButton ( this,Skin::BT_EQ_ON_N,Skin::BT_EQ_ON_P,
+ Skin::BT_EQ_OFF_N,Skin::BT_EQ_OFF_P );
+ m_eqButton->move ( 219,58 );
+ m_eqButton->show();
+ m_plButton = new ToggleButton ( this,Skin::BT_PL_ON_N,Skin::BT_PL_ON_P,
+ Skin::BT_PL_OFF_N,Skin::BT_PL_OFF_P );
+ m_plButton->move ( 241,58 );
+ m_plButton->show();
+
+ m_repeatButton = new ToggleButton ( this,Skin::REPEAT_ON_N,Skin::REPEAT_ON_P,
+ Skin::REPEAT_OFF_N,Skin::REPEAT_OFF_P );
+ connect(m_repeatButton,SIGNAL(clicked(bool)),this,SIGNAL(repeatableToggled(bool)));
+
+ m_repeatButton->move ( 210,89 );
+ m_repeatButton->show();
+
+ m_shuffleButton = new ToggleButton ( this,Skin::SHUFFLE_ON_N,Skin::SHUFFLE_ON_P,
+ Skin::SHUFFLE_OFF_N,Skin::SHUFFLE_OFF_P );
+ connect(m_shuffleButton,SIGNAL(clicked(bool)),this,SIGNAL(shuffleToggled(bool)));
+ m_shuffleButton->move ( 164,89 );
+ m_shuffleButton->show();
+
+ m_kbps = new SymbolDisplay( this,3 );
+ m_kbps -> move ( 111,43 );
+ m_kbps -> show();
+
+ m_freq = new SymbolDisplay( this,2 );
+ m_freq -> move ( 156,43 );
+ m_freq -> show();
+
+ TextScroller *m_text = new TextScroller ( this );
+ m_text->resize ( 154,15 );
+ m_text->move ( 109,23 );
+ m_text->show();
+
+ m_monoster = new MonoStereo ( this );
+ m_monoster->move ( 212,41 );
+ m_monoster->show();
+
+ m_playstatus = new PlayStatus(this);
+ m_playstatus->move(24,28);
+ m_playstatus->show();
+
+ m_volumeBar = new VolumeBar(this);
+ connect(m_volumeBar, SIGNAL(sliderMoved(int)),SLOT(updateVolume()));
+ m_volumeBar->move(107,57);
+ m_volumeBar->show();
+
+ m_balanceBar = new BalanceBar(this);
+ connect(m_balanceBar, SIGNAL(sliderMoved(int)),SLOT(updateVolume()));
+ m_balanceBar->move(177,57);
+ m_balanceBar->show();
+ m_timeIndicator = new TimeIndicator(this);
+ m_timeIndicator->move(34,26);
+ m_timeIndicator->show();
+}
+
+
+MainDisplay::~MainDisplay()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.setValue ( "Playlist/visible",m_plButton->isChecked() );
+ settings.setValue ( "Equalizer/visible",m_eqButton->isChecked() );
+}
+
+void MainDisplay::setTime ( int t )
+{
+ posbar->setValue ( t );
+ m_timeIndicator->setTime(t);
+}
+void MainDisplay::setMaxTime ( long mt ) // TODO: should be removed
+{
+ posbar->setMax ( mt );
+ m_timeIndicator->setSongDuration(mt);
+}
+
+void MainDisplay::updateSkin()
+{
+ setPixmap ( m_skin->getMain() );
+}
+
+void MainDisplay::setEQ ( QWidget* w )
+{
+ m_equlizer = w;
+ m_eqButton->setON ( m_equlizer->isVisible() );
+ connect ( m_eqButton, SIGNAL ( clicked ( bool ) ), m_equlizer, SLOT ( setVisible ( bool ) ) );
+}
+
+void MainDisplay::setPL ( QWidget* w )
+{
+ m_playlist = w;
+ m_plButton->setON ( m_playlist->isVisible() );
+ connect ( m_plButton, SIGNAL ( clicked ( bool ) ), m_playlist, SLOT ( setVisible ( bool ) ) );
+}
+
+void MainDisplay::setInfo(const OutputState &st)
+{
+
+
+ switch ( ( int ) st.type() )
+ {
+ case OutputState::Info:
+ {
+ //if ( seeking )
+ // break;
+ setTime ( st.elapsedSeconds() );
+ m_kbps->display ( st.bitrate() );
+ m_freq->display ( st.frequency() /1000 );
+ m_monoster->setChannels ( st.channels() );
+ update();
+ break;
+ }
+ case OutputState::Playing:
+ {
+ m_playstatus->setStatus(PlayStatus::PLAY);
+ m_timeIndicator->setNeedToShowTime(true);
+ break;
+ }
+ case OutputState::Buffering:
+ {
+ //ui.label->setText("Buffering");
+ break;
+ }
+ case OutputState::Paused:
+ {
+ m_playstatus->setStatus(PlayStatus::PAUSE);
+ break;
+ }
+ case OutputState::Stopped:
+ {
+ m_playstatus->setStatus(PlayStatus::STOP);
+ m_monoster->setChannels (0);
+ //m_timeIndicator->setNeedToShowTime(false);
+ break;
+ }
+ case OutputState::Volume:
+ //qDebug("volume %d, %d", st.rightVolume(), st.leftVolume());
+ int maxVol = qMax(st.leftVolume(),st.rightVolume());
+ m_volumeBar->setValue(maxVol);
+ if (maxVol && !m_volumeBar->isPressed())
+ m_balanceBar->setValue((st.rightVolume()-st.leftVolume())*100/maxVol);
+ break;
+
+ }
+}
+
+bool MainDisplay::isPlaylistVisible() const
+{
+ return m_plButton->isChecked();
+}
+
+bool MainDisplay::isEqualizerVisible() const
+{
+ return m_eqButton->isChecked();
+}
+
+void MainDisplay::updateVolume()
+{
+ m_mw->setVolume(m_volumeBar->value(), m_balanceBar->value());
+}
+
+void MainDisplay::wheelEvent (QWheelEvent *e)
+{
+ m_mw->setVolume(m_volumeBar->value()+e->delta()/10, m_balanceBar->value());
+}
+
+bool MainDisplay::isRepeatable() const
+{
+ return m_repeatButton->isChecked();
+}
+
+bool MainDisplay::isShuffle() const
+{
+ return m_shuffleButton->isChecked();
+}
+
+void MainDisplay::setIsRepeatable(bool yes)
+{
+ m_repeatButton->setON(yes);
+}
+
+void MainDisplay::setIsShuffle(bool yes)
+{
+ m_shuffleButton->setON(yes);
+}
+
+
+void MainDisplay::hideTimeDisplay()
+{
+ m_timeIndicator->setNeedToShowTime(false);
+}
+
diff --git a/src/display.h b/src/display.h
new file mode 100644
index 000000000..7ab2074bc
--- /dev/null
+++ b/src/display.h
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DISPLAY_H
+#define DISPLAY_H
+
+#include <QPixmap>
+
+class TimeIndicator;
+
+#include "pixmapwidget.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class QPushButton;
+class QLabel;
+
+class TitleBar;
+class PositionBar;
+class Number;
+class Skin;
+class ToggleButton;
+class OutputState;
+class NumberDisplay;
+class SymbolDisplay;
+class MonoStereo;
+class PlayStatus;
+class VolumeBar;
+class BalanceBar;
+class MainWindow;
+
+class MainDisplay : public PixmapWidget
+{
+ Q_OBJECT
+public:
+ MainDisplay(QWidget *parent = 0);
+
+ ~MainDisplay();
+
+ void setMaxTime(long);
+ void setEQ(QWidget*);
+ void setPL(QWidget*);
+ void setInfo(const OutputState &st);
+ bool isEqualizerVisible()const;
+ bool isPlaylistVisible()const;
+ bool isRepeatable()const;
+ bool isShuffle()const;
+ void setIsRepeatable(bool);
+ void setIsShuffle(bool);
+
+public slots:
+ void setTime(int);
+ void hideTimeDisplay();
+signals:
+ void repeatableToggled(bool);
+ void shuffleToggled(bool);
+protected:
+ void wheelEvent(QWheelEvent *);
+
+private slots:
+ void updateSkin();
+ void updateVolume();
+
+private:
+ QWidget* m_equlizer;
+ QWidget* m_playlist;
+ QPixmap pixmap;
+ QPushButton *button;
+ QLabel *label;
+ Skin *m_skin;
+ TitleBar *titleBar;
+ PositionBar *posbar;
+ ToggleButton *m_eqButton;
+ ToggleButton *m_plButton;
+ ToggleButton *m_shuffleButton;
+ ToggleButton *m_repeatButton;
+ SymbolDisplay* m_kbps;
+ SymbolDisplay* m_freq;
+ MonoStereo* m_monoster;
+ PlayStatus* m_playstatus;
+ VolumeBar* m_volumeBar;
+ BalanceBar* m_balanceBar;
+ MainWindow* m_mw;
+ TimeIndicator* m_timeIndicator;
+};
+
+#endif
diff --git a/src/dock.cpp b/src/dock.cpp
new file mode 100644
index 000000000..17edc44bf
--- /dev/null
+++ b/src/dock.cpp
@@ -0,0 +1,239 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QAction>
+
+#include "dock.h"
+
+
+Dock *Dock::pointer = 0;
+
+Dock *Dock::getPointer()
+{
+ if ( !pointer )
+ pointer = new Dock();
+ return pointer;
+}
+
+Dock::Dock ( QObject *parent )
+ : QObject ( parent )
+{
+ pointer = this;
+ m_mainWidget = 0;
+}
+
+Dock::~Dock()
+{}
+
+void Dock::setMainWidget ( QWidget *widget )
+{
+ m_mainWidget = widget;
+ m_widgetList.prepend ( widget );
+ m_dockedList.prepend ( FALSE );
+}
+
+
+QPoint Dock::snap ( QPoint npos, QWidget* mv, QWidget* st )
+{
+ int nx = npos.x() - st->x();
+ int ny = abs ( npos.y() - st->y() + mv->height() );
+
+ if ( abs ( nx ) < 15 && ny < 15 ) //above
+ npos.rx() = st->x();
+ if ( ny < 15 && nx > -mv->width() && nx < st->width() )
+ npos.ry() = st->y() - mv->height();
+ nx = abs ( npos.x() + mv->width() - st->x() - st->width() );
+ if ( nx < 15 && ny < 15 )
+ npos.rx() = st->x() + st->width() - mv->width();
+
+ /***********/
+ nx = npos.x() - st->x();
+ ny = abs ( npos.y() - st->y() - st->height() );
+
+ if ( abs ( nx ) < 15 && ny < 15 ) //near
+ npos.rx() = st->x();
+ if ( ny < 15 && nx > -mv->width() && nx < st->width() )
+ npos.ry() = st->y() + st->height();
+ nx = abs ( npos.x() + mv->width() - st->x() - st->width() );
+ if ( nx < 15 && ny < 15 )
+ npos.rx() = st->x() + st->width() - mv->width();
+ /**************/
+ nx = abs ( npos.x() - st->x() + mv->width() );
+ ny = npos.y() - st->y();
+
+ if ( nx < 15 && abs ( ny ) < 15 ) //left
+ npos.ry() = st->y();
+ if ( nx < 15 && ny > -mv->height() && ny < st->height() )
+ npos.rx() = st->x() - mv->width();
+
+ ny = abs ( npos.y() + mv->height() - st->y() - st->height() );
+ if ( nx < 15 && ny < 15 )
+ npos.ry() = st->y() + st->height() - mv->height();
+ /*****************/
+ nx = abs ( npos.x() - st->x() - st->width() );
+ ny = npos.y() - st->y();
+
+ if ( nx < 15 && abs ( ny ) < 15 ) //right
+ npos.ry() = st->y();
+ if ( nx < 15 && ny > -mv->height() && ny < st->height() )
+ npos.rx() = st->x() + st->width();
+
+ ny = abs ( npos.y() + mv->height() - st->y() - st->height() );
+ if ( nx < 15 && ny < 15 )
+ npos.ry() = st->y() + st->height() - mv->height();
+
+ return ( npos );
+}
+
+void Dock::addWidget ( QWidget *widget )
+{
+ m_widgetList.append ( widget );
+ m_dockedList.append ( FALSE );
+ widget->addActions(m_actions);
+
+}
+
+void Dock::move ( QWidget* mv, QPoint npos )
+{
+ if ( mv == m_mainWidget )
+ {
+
+ for ( int i = 1; i<m_widgetList.size(); ++i )
+ {
+ if ( !m_dockedList.at ( i ) )
+ {
+ if ( m_widgetList.at ( i )->isVisible() )
+ npos = snap ( npos, mv, m_widgetList.at ( i ) );
+
+ }
+ else
+ {
+ QPoint pos = QPoint ( npos.x() + x_list.at ( i ),
+ npos.y() + y_list.at ( i ) );
+ for ( int j = 1; j<m_widgetList.size(); ++j )
+ {
+ if ( !m_dockedList.at ( j ) && m_widgetList.at ( j )->isVisible() )
+ {
+ pos = snap ( pos, m_widgetList.at ( i ), m_widgetList.at ( j ) );
+ npos = QPoint ( pos.x() - x_list.at ( i ),
+ pos.y() - y_list.at ( i ) );
+ }
+ }
+ }
+ }
+ mv->move ( npos );
+ for ( int i = 1; i<m_widgetList.size(); ++i )
+ {
+ if ( m_dockedList.at ( i ) )
+ m_widgetList.at ( i )->move ( npos.x() + x_list.at ( i ),
+ npos.y() + y_list.at ( i ) );
+ }
+ }
+ else
+ {
+ for ( int i = 0; i<m_widgetList.size(); ++i )
+ {
+ m_dockedList[i] = FALSE;
+ if ( mv!=m_widgetList.at ( i ) && !m_dockedList.at ( i ) && m_widgetList.at ( i )->isVisible() )
+ {
+ npos = snap ( npos, mv, m_widgetList.at ( i ) );
+ }
+ }
+ mv->move ( npos );
+ }
+}
+
+void Dock::calculateDistances()
+{
+ x_list.clear();
+ y_list.clear();
+ foreach ( QWidget *w, m_widgetList )
+ {
+ if ( w!=m_mainWidget )
+ {
+ x_list.append ( - m_mainWidget->x() + w->x() );
+ y_list.append ( - m_mainWidget->y() + w->y() );
+ }
+ else
+ {
+ x_list.prepend ( 0 );
+ y_list.prepend ( 0 );
+ }
+ }
+}
+
+void Dock::updateDock()
+{
+ QWidget *mv = m_widgetList.at ( 0 );
+ for ( int j = 1; j<m_widgetList.size(); ++j )
+ {
+ QWidget *st = m_widgetList.at ( j );
+ m_dockedList[j] = isDocked ( mv, st );
+ }
+ for ( int j = 1; j<m_widgetList.size(); ++j )
+ {
+ if ( m_dockedList[j] )
+ for ( int i = 1; i<m_widgetList.size(); ++i )
+ {
+ if ( !m_dockedList[i] )
+ {
+ mv = m_widgetList.at ( j );
+ QWidget *st = m_widgetList.at ( i );
+ m_dockedList[i] = isDocked ( mv, st );
+ }
+ }
+ }
+
+}
+
+bool Dock::isDocked ( QWidget* mv, QWidget* st )
+{
+ int nx = mv->x() - st->x();
+ int ny = abs ( mv->y() - st->y() + mv->height() );
+ if ( ny < 2 && nx > -mv->width() && nx < st->width() ) //above
+ return TRUE;
+
+ /***********/
+ nx = mv->x() - st->x();
+ ny = abs ( mv->y() - st->y() - st->height() );
+ if ( ny < 2 && nx > -mv->width() && nx < st->width() ) //near
+ return TRUE;
+
+ /**************/
+ nx = abs ( mv->x() - st->x() + mv->width() );
+ ny = mv->y() - st->y();
+ if ( nx < 2 && ny > -mv->height() && ny < st->height() ) //left
+ return TRUE;
+
+ /*****************/
+ nx = abs ( mv->x() - st->x() - st->width() );
+ ny = mv->y() - st->y();
+ if ( nx < 2 && ny > -mv->height() && ny < st->height() ) //right
+ return TRUE;
+ return FALSE;
+}
+
+void Dock::addActions ( QList<QAction *> actions )
+{
+ m_actions << actions;
+ for ( int i = 0; i<m_widgetList.size(); ++i )
+ m_widgetList.at ( i )->addActions ( actions );
+}
+
diff --git a/src/dock.h b/src/dock.h
new file mode 100644
index 000000000..5622de675
--- /dev/null
+++ b/src/dock.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef DOCK_H
+#define DOCK_H
+
+#include <QObject>
+#include <QPoint>
+#include <QWidget>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QAction;
+
+class Dock : public QObject
+{
+ Q_OBJECT
+public:
+ Dock(QObject *parent = 0);
+
+ static Dock *getPointer();
+ void setMainWidget(QWidget*);
+ void addWidget(QWidget *);
+ void move(QWidget*, QPoint);
+ void calculateDistances();
+ void updateDock();
+ QPoint snap(QPoint, QWidget*, QWidget*);
+ void addActions(QList<QAction *> actions);
+
+
+ ~Dock();
+
+private:
+ bool isDocked(QWidget*, QWidget*);
+ static Dock *pointer;
+ QWidget *m_mainWidget;
+ QList <QWidget *> m_widgetList;
+ QList <bool> m_dockedList;
+ QList <int> x_list;
+ QList <int> y_list;
+ QList <QAction *> m_actions;
+
+
+};
+
+#endif
diff --git a/src/eqgraph.cpp b/src/eqgraph.cpp
new file mode 100644
index 000000000..314e504d9
--- /dev/null
+++ b/src/eqgraph.cpp
@@ -0,0 +1,163 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+
+#include "skin.h"
+#include "eqgraph.h"
+
+EQGraph::EQGraph ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ m_skin = Skin::getPointer();
+ setPixmap ( m_skin->getEqPart ( Skin::EQ_GRAPH ) );
+ clear();
+ draw();
+ connect ( m_skin, SIGNAL ( skinChanged() ), this, SLOT ( updateSkin() ) );
+}
+
+
+EQGraph::~EQGraph()
+{}
+
+void EQGraph::addValue ( int value )
+{
+ if ( m_values.size() >= 10 )
+ return;
+ m_values.append ( value );
+ if ( m_values.size() == 10 )
+ {
+ draw();
+ }
+}
+
+void EQGraph::clear ()
+{
+ m_values.clear();
+ update();
+}
+
+void EQGraph::init_spline ( double * x, double * y, int n, double * y2 )
+{
+ int i, k;
+ double p, qn, sig, un, *u;
+
+ //u = ( gfloat * ) g_malloc ( n * sizeof ( gfloat ) );
+ u = new double[n];
+
+ y2[0] = u[0] = 0.0;
+
+ for ( i = 1; i < n - 1; i++ )
+ {
+ sig = ( ( double ) x[i] - x[i - 1] ) / ( ( double ) x[i + 1] - x[i - 1] );
+ p = sig * y2[i - 1] + 2.0;
+ y2[i] = ( sig - 1.0 ) / p;
+ u[i] =
+ ( ( ( double ) y[i + 1] - y[i] ) / ( x[i + 1] - x[i] ) ) -
+ ( ( ( double ) y[i] - y[i - 1] ) / ( x[i] - x[i - 1] ) );
+ u[i] = ( 6.0 * u[i] / ( x[i + 1] - x[i - 1] ) - sig * u[i - 1] ) / p;
+ }
+ qn = un = 0.0;
+
+ y2[n - 1] = ( un - qn * u[n - 2] ) / ( qn * y2[n - 2] + 1.0 );
+ for ( k = n - 2; k >= 0; k-- )
+ y2[k] = y2[k] * y2[k + 1] + u[k];
+ //g_free ( u );
+ delete[] u;
+}
+
+double EQGraph::eval_spline ( double xa[], double ya[], double y2a[], int n, double x )
+{
+ int klo, khi, k;
+ double h, b, a;
+
+ klo = 0;
+ khi = n - 1;
+ while ( khi - klo > 1 )
+ {
+ k = ( khi + klo ) >> 1;
+ if ( xa[k] > x )
+ khi = k;
+ else
+ klo = k;
+ }
+ h = xa[khi] - xa[klo];
+ a = ( xa[khi] - x ) / h;
+ b = ( x - xa[klo] ) / h;
+ return ( a * ya[klo] + b * ya[khi] +
+ ( ( a * a * a - a ) * y2a[klo] +
+ ( b * b * b - b ) * y2a[khi] ) * ( h * h ) / 6.0 );
+}
+
+void EQGraph::draw()
+{
+ if(m_values.size()!=10)
+ {
+ setPixmap ( m_skin->getEqPart ( Skin::EQ_GRAPH ) );
+ return;
+ }
+
+ int i, y, ymin, ymax, py = 0;
+ double x[] = { 0, 11, 23, 35, 47, 59, 71, 83, 97, 109 }, yf[10];
+ double *bands = new double[10];
+
+ for ( int i = 0; i<10; ++i )
+ {
+ bands[i] = m_values.at ( i );
+ }
+ QPixmap pixmap = m_skin->getEqPart ( Skin::EQ_GRAPH );
+
+ init_spline ( x, bands, 10, yf );
+ for ( i = 0; i < 109; i++ )
+ {
+ y = 9 -
+ ( int ) ( ( eval_spline ( x, bands, yf, 10, i ) *
+ 9.0 ) / 20.0 );
+ if ( y < 0 )
+ y = 0;
+ if ( y > 18 )
+ y = 18;
+ if ( !i )
+ py = y;
+ if ( y < py )
+ {
+ ymin = y;
+ ymax = py;
+ }
+ else
+ {
+ ymin = py;
+ ymax = y;
+ }
+ py = y;
+
+ QPainter paint ( &pixmap );
+ paint.drawPixmap ( i, y, m_skin->getEqSpline ( y ) ) ;
+
+
+ }
+ setPixmap ( pixmap );
+ delete [] bands;
+}
+
+void EQGraph::updateSkin()
+{
+ draw();
+}
+
diff --git a/src/eqgraph.h b/src/eqgraph.h
new file mode 100644
index 000000000..4f6bf1882
--- /dev/null
+++ b/src/eqgraph.h
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef EQGRAPH_H
+#define EQGRAPH_H
+
+#include "pixmapwidget.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class Skin;
+
+class EQGraph : public PixmapWidget
+{
+ Q_OBJECT
+public:
+ EQGraph ( QWidget *parent = 0 );
+
+ ~EQGraph();
+
+ void addValue ( int );
+ void clear();
+
+/*protected:
+ void paintEvent ( QPaintEvent * );*/
+private slots:
+ void updateSkin();
+
+private:
+ QList <int> m_values;
+ Skin *m_skin;
+ void init_spline ( double * x, double * y, int n, double * y2 );
+ double eval_spline ( double xa[], double ya[], double y2a[], int n, double x );
+ void draw();
+
+};
+
+#endif
diff --git a/src/eqpreset.cpp b/src/eqpreset.cpp
new file mode 100644
index 000000000..760dea9d2
--- /dev/null
+++ b/src/eqpreset.cpp
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "eqpreset.h"
+
+EQPreset::EQPreset()
+ : QListWidgetItem()
+{
+ m_preamp = 0;
+ for(int i = 0; i < 10; ++i)
+ m_bands[i] = 0;
+}
+
+
+EQPreset::~EQPreset()
+{}
+
+void EQPreset::setGain(int n, int value)
+{
+ if(n > 9 || n < 0)
+ return;
+ m_bands[n] = value;
+}
+
+void EQPreset::setPreamp(int preamp)
+{
+ m_preamp = preamp;
+}
+
+int EQPreset::gain(int n)
+{
+ if(n > 9 || n < 0)
+ return 0;
+ return m_bands[n];
+}
+
+int EQPreset::preamp()
+{
+ return m_preamp;
+}
diff --git a/src/eqpreset.h b/src/eqpreset.h
new file mode 100644
index 000000000..80f31747e
--- /dev/null
+++ b/src/eqpreset.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef EQPRESET_H
+#define EQPRESET_H
+
+#include <QListWidgetItem>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class EQPreset : public QListWidgetItem
+{
+public:
+ EQPreset();
+
+ ~EQPreset();
+
+ void setGain(int n, int value);
+ void setPreamp(int);
+
+ int gain(int n);
+ int preamp();
+
+private:
+ int m_bands[10];
+ int m_preamp;
+
+};
+
+#endif
diff --git a/src/eqslider.cpp b/src/eqslider.cpp
new file mode 100644
index 000000000..bdc4ec4e2
--- /dev/null
+++ b/src/eqslider.cpp
@@ -0,0 +1,147 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QMouseEvent>
+#include <QPainter>
+#include <QWheelEvent>
+#include <math.h>
+
+#include "skin.h"
+
+#include "eqslider.h"
+
+
+EqSlider::EqSlider(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+ setPixmap(m_skin->getEqSlider(0));
+ m_moving = FALSE;
+ m_min = -20;
+ m_max = 20;
+ m_old = m_value = 0;
+ draw(FALSE);
+}
+
+
+EqSlider::~EqSlider()
+{}
+
+void EqSlider::mousePressEvent(QMouseEvent *e)
+{
+ m_moving = TRUE;
+ press_pos = e->y();
+ if (m_pos<e->y() && e->y()<m_pos+11)
+ {
+ press_pos = e->y()-m_pos;
+ }
+ else
+ {
+ m_value = convert(qMax(qMin(height()-12,e->y()-6),0));
+ press_pos = 6;
+ if (m_value!=m_old)
+ {
+ emit sliderMoved(m_value);
+ m_old = m_value;
+ //qDebug ("%d",m_value);
+ }
+ }
+ draw();
+}
+
+void EqSlider::mouseReleaseEvent(QMouseEvent*)
+{
+ m_moving = FALSE;
+ draw(FALSE);
+}
+
+void EqSlider::mouseMoveEvent(QMouseEvent* e)
+{
+ if (m_moving)
+ {
+ int po = e->y();
+ po = po - press_pos;
+
+ if (0<=po && po<=height()-12)
+ {
+ m_value = convert(po);
+ draw();
+ if (m_value!=m_old)
+ {
+
+ m_old = m_value;
+ //qDebug ("%d",-m_value);
+ emit sliderMoved(-m_value);
+ }
+ }
+ }
+}
+
+int EqSlider::value()
+{
+ return -m_value;
+}
+
+void EqSlider::setValue(int p)
+{
+ if (m_moving)
+ return;
+ m_value = -p;
+ draw(FALSE);
+}
+
+void EqSlider::setMax(int m)
+{
+ m_max = m;
+ draw(FALSE);
+}
+
+void EqSlider::updateSkin()
+{
+ draw(FALSE);
+}
+
+void EqSlider::draw(bool pressed)
+{
+ int p=int(ceil(double(m_value-m_min)*(height()-12)/(m_max-m_min)));
+ m_pixmap = m_skin->getEqSlider(27-27*(m_value-m_min)/(m_max-m_min));
+ QPainter paint(&m_pixmap);
+ if (pressed)
+ paint.drawPixmap(1,p,m_skin->getButton(Skin::EQ_BT_BAR_P));
+ else
+ paint.drawPixmap(1,p,m_skin->getButton(Skin::EQ_BT_BAR_N));
+ setPixmap(m_pixmap);
+ m_pos = p;
+}
+
+int EqSlider::convert(int p)
+{
+ return int(ceil(double(m_max-m_min)*(p)/(height()-12)+m_min));
+}
+
+void EqSlider::wheelEvent(QWheelEvent *e)
+{
+ m_value -= e->delta()/60;
+ m_value = m_value > m_max ? m_max : m_value;
+ m_value = m_value < m_min ? m_min : m_value;
+ draw(FALSE);
+ emit sliderMoved(m_value);
+}
+
diff --git a/src/eqslider.h b/src/eqslider.h
new file mode 100644
index 000000000..54dc9f285
--- /dev/null
+++ b/src/eqslider.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef EQSLIDER_H
+#define EQSLIDER_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class QMouseEvent;
+class QWheelEvent;
+
+class Skin;
+
+class EqSlider : public PixmapWidget
+{
+Q_OBJECT
+public:
+ EqSlider(QWidget *parent = 0);
+
+ ~EqSlider();
+
+ int value();
+
+public slots:
+ void setValue(int);
+ void setMax(int);
+
+signals:
+ void sliderMoved (int);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ bool m_moving;
+ int press_pos;
+ int m_max, m_min, m_pos, m_value, m_old;
+ QPixmap m_pixmap;
+ int convert(int); // value = convert(position);
+ void draw(bool pressed = TRUE);
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+ void wheelEvent(QWheelEvent *);
+
+
+};
+
+#endif
diff --git a/src/eqtitlebar.cpp b/src/eqtitlebar.cpp
new file mode 100644
index 000000000..af4547bd7
--- /dev/null
+++ b/src/eqtitlebar.cpp
@@ -0,0 +1,76 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QMouseEvent>
+#include <QMenu>
+
+#include "skin.h"
+#include "dock.h"
+#include "mainwindow.h"
+
+#include "eqtitlebar.h"
+
+EqTitleBar::EqTitleBar(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ setActive(FALSE);
+ m_eq = parentWidget();
+ m_mw = qobject_cast<MainWindow*>(m_eq->parent());
+}
+
+
+EqTitleBar::~EqTitleBar()
+{}
+
+void EqTitleBar::setActive(bool active)
+{
+ if (active)
+ setPixmap(m_skin->getEqPart(Skin::EQ_TITLEBAR_A));
+ else
+ setPixmap(m_skin->getEqPart(Skin::EQ_TITLEBAR_I));
+}
+
+void EqTitleBar::mousePressEvent(QMouseEvent* event)
+{
+ switch((int) event->button ())
+ {
+ case Qt::LeftButton:
+ {
+ m_pos = event->pos();
+ break;
+ }
+ case Qt::RightButton:
+ {
+ m_mw->menu()->exec(event->globalPos());
+ }
+ }
+}
+
+void EqTitleBar::mouseMoveEvent(QMouseEvent* event)
+{
+ QPoint npos = (event->globalPos()-m_pos);
+ //parentWidget()->move(npos);
+ Dock::getPointer()->move(m_eq, npos);
+}
+
+void EqTitleBar::mouseReleaseEvent(QMouseEvent*)
+{
+ Dock::getPointer()->updateDock();
+}
diff --git a/src/eqtitlebar.h b/src/eqtitlebar.h
new file mode 100644
index 000000000..840577272
--- /dev/null
+++ b/src/eqtitlebar.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef EQTITLEBAR_H
+#define EQTITLEBAR_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QMouseEvent;
+
+class Skin;
+class MainWindow;
+
+class EqTitleBar : public PixmapWidget
+{
+Q_OBJECT
+public:
+ EqTitleBar(QWidget *parent = 0);
+
+ ~EqTitleBar();
+
+ void setActive(bool);
+
+private:
+ Skin* m_skin;
+ bool m_active;
+ QPoint m_pos;
+ QWidget* m_eq;
+ MainWindow* m_mw;
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+
+};
+
+#endif
diff --git a/src/eqwidget.cpp b/src/eqwidget.cpp
new file mode 100644
index 000000000..627958dfd
--- /dev/null
+++ b/src/eqwidget.cpp
@@ -0,0 +1,407 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QSettings>
+#include <QEvent>
+#include <QMenu>
+#include <QInputDialog>
+#include <QFileDialog>
+
+#include "skin.h"
+#include "eqslider.h"
+#include "eqtitlebar.h"
+#include "togglebutton.h"
+#include "eqgraph.h"
+#include "button.h"
+#include "eqpreset.h"
+#include "preseteditor.h"
+#include "mainwindow.h"
+#include "playlist.h"
+#include "eqwidget.h"
+
+
+
+EqWidget::EqWidget ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ m_skin = Skin::getPointer();
+ setWindowFlags ( Qt::Dialog | Qt::FramelessWindowHint );
+ setPixmap ( m_skin->getEqPart ( Skin::EQ_MAIN ) );
+ //setPixmap(QPixmap(275,116));
+ m_titleBar = new EqTitleBar ( this );
+ m_titleBar -> move ( 0,0 );
+ m_titleBar -> show();
+ connect ( m_skin, SIGNAL ( skinChanged() ), this, SLOT ( updateSkin() ) );
+
+ m_preamp = new EqSlider ( this );
+ m_preamp->show();
+ m_preamp->move ( 21,38 );
+ connect ( m_preamp,SIGNAL ( sliderMoved ( int ) ),SLOT ( setPreamp () ) );
+
+ m_on = new ToggleButton ( this,Skin::EQ_BT_ON_N,Skin::EQ_BT_ON_P,
+ Skin::EQ_BT_OFF_N,Skin::EQ_BT_OFF_P );
+ m_on->show();
+ m_on->move ( 14,18 );
+ connect (m_on, SIGNAL (clicked(bool)), SIGNAL(valueChanged()));
+
+ m_autoButton = new ToggleButton(this, Skin::EQ_BT_AUTO_1_N, Skin::EQ_BT_AUTO_1_P,
+ Skin::EQ_BT_AUTO_0_N, Skin::EQ_BT_AUTO_0_P);
+ m_autoButton->move(39, 18);
+ m_autoButton->show();
+
+ m_eqg = new EQGraph(this);
+ m_eqg->move(87,17);
+ m_eqg->show();
+
+ m_presetsMenu = new QMenu(this);
+
+ m_presetButton = new Button ( this, Skin::EQ_BT_PRESETS_N, Skin::EQ_BT_PRESETS_P);
+ m_presetButton->move(217,18);
+ m_presetButton->show();
+
+ connect(m_presetButton, SIGNAL(clicked()), SLOT(showPresetsMenu()));
+
+ for ( int i = 0; i<10; ++i )
+ {
+ m_sliders << new EqSlider ( this );
+ m_sliders.at ( i )->move ( 78+i*18,38 );
+ m_sliders.at ( i )->show();
+ connect (m_sliders.at (i), SIGNAL ( sliderMoved (int) ),SLOT (setGain()));
+ }
+ readSettings();
+ createActions();
+}
+
+EqWidget::~EqWidget()
+{
+ while (!m_presets.isEmpty())
+ delete m_presets.takeFirst();
+ while (!m_autoPresets.isEmpty())
+ delete m_autoPresets.takeFirst();
+}
+
+int EqWidget::preamp()
+{
+ return m_preamp->value();
+}
+
+int EqWidget::gain ( int g )
+{
+ return m_sliders.at ( g )->value();
+}
+
+void EqWidget::changeEvent ( QEvent * event )
+{
+ if (event->type() == QEvent::ActivationChange)
+ {
+ m_titleBar->setActive(isActiveWindow());
+ }
+}
+
+void EqWidget::closeEvent ( QCloseEvent* )
+{
+ writeSettings();
+}
+
+void EqWidget::updateSkin()
+{
+ m_titleBar->setActive ( FALSE );
+ setPixmap ( m_skin->getEqPart ( Skin::EQ_MAIN ) );
+}
+
+void EqWidget::readSettings()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.beginGroup ( "Equalizer" );
+ //geometry
+ move ( settings.value ( "pos", QPoint ( 100, 216 ) ).toPoint() );
+ //equalizer
+ for (int i = 0; i < m_sliders.size(); ++i)
+ m_sliders.at(i)->setValue(settings.value("band_"+
+ QString("%1").arg(i), 0).toInt());
+ m_preamp->setValue(settings.value("preamp", 0).toInt());
+ m_on->setON(settings.value("enabled", FALSE).toBool());
+ settings.endGroup();
+ setGain();
+ //equalizer presets
+ QSettings eq_preset (QDir::homePath() +"/.qmmp/eq.preset", QSettings::IniFormat );
+ for (int i = 1; TRUE; ++i)
+ {
+ if (eq_preset.contains("Presets/Preset"+QString("%1").arg(i)))
+ {
+ QString name = eq_preset.value("Presets/Preset"+QString("%1").arg(i),
+ tr("preset")).toString();
+ EQPreset *preset = new EQPreset();
+ preset->setText(name);
+ eq_preset.beginGroup(name);
+ for (int j = 0; j < 10; ++j)
+ {
+ preset->setGain(j,eq_preset.value("Band"+QString("%1").arg(j),
+ 0).toInt());
+ }
+ preset->setPreamp(eq_preset.value("Preamp",0).toInt());
+ m_presets.append(preset);
+ eq_preset.endGroup();
+ }
+ else
+ break;
+ }
+ //equalizer auto-load presets
+ QSettings eq_auto (QDir::homePath() +"/.qmmp/eq.auto_preset", QSettings::IniFormat );
+ for (int i = 1; TRUE; ++i)
+ {
+ if (eq_auto.contains("Presets/Preset"+QString("%1").arg(i)))
+ {
+ QString name = eq_auto.value("Presets/Preset"+QString("%1").arg(i),
+ tr("preset")).toString();
+ EQPreset *preset = new EQPreset();
+ preset->setText(name);
+ eq_auto.beginGroup(name);
+ for (int j = 0; j < 10; ++j)
+ {
+ preset->setGain(j,eq_auto.value("Band"+QString("%1").arg(j),
+ 0).toInt());
+ }
+ preset->setPreamp(eq_auto.value("Preamp",0).toInt());
+ m_autoPresets.append(preset);
+ eq_auto.endGroup();
+ }
+ else
+ break;
+ }
+}
+
+void EqWidget::writeSettings()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.beginGroup ( "Equalizer" );
+ //geometry
+ settings.setValue ( "pos", this->pos() );
+ //equalizer
+ for (int i = 0; i < m_sliders.size(); ++i)
+ settings.setValue("band_"+QString("%1").arg(i), m_sliders.at(i)->value());
+ settings.setValue("preamp", m_preamp->value());
+ settings.setValue("enabled",m_on->isChecked());
+ settings.endGroup();
+ //equalizer presets
+ QSettings eq_preset (QDir::homePath() +"/.qmmp/eq.preset", QSettings::IniFormat );
+ eq_preset.clear ();
+ for (int i = 0; i < m_presets.size(); ++i)
+ {
+ eq_preset.setValue("Presets/Preset"+QString("%1").arg(i+1),
+ m_presets.at(i)->text());
+ eq_preset.beginGroup(m_presets.at(i)->text());
+ for (int j = 0; j < 10; ++j)
+ {
+ eq_preset.setValue("Band"+QString("%1").arg(j),m_presets.at(i)->gain(j));
+ }
+ eq_preset.setValue("Preamp",m_presets.at(i)->preamp());
+ eq_preset.endGroup();
+ }
+ //equalizer auto-load presets
+ QSettings eq_auto (QDir::homePath() +"/.qmmp/eq.auto_preset",
+ QSettings::IniFormat );
+ eq_auto.clear();
+ for (int i = 0; i < m_autoPresets.size(); ++i)
+ {
+ eq_auto.setValue("Presets/Preset"+QString("%1").arg(i+1),
+ m_autoPresets.at(i)->text());
+ eq_auto.beginGroup(m_autoPresets.at(i)->text());
+ for (int j = 0; j < 10; ++j)
+ {
+ eq_auto.setValue("Band"+QString("%1").arg(j),m_autoPresets.at(i)->gain(j));
+ }
+ eq_auto.setValue("Preamp",m_autoPresets.at(i)->preamp());
+ eq_auto.endGroup();
+ }
+}
+
+void EqWidget::setPreamp ()
+{
+ emit valueChanged();
+}
+
+void EqWidget::setGain()
+{
+ m_eqg->clear();
+ for (int i=0; i<10; ++i)
+ {
+ int value = m_sliders.at(i)->value();
+ m_eqg->addValue(value);
+ }
+ emit valueChanged();
+}
+
+bool EqWidget::isEQEnabled()
+{
+ return m_on->isChecked();
+}
+
+void EqWidget::createActions()
+{
+ m_presetsMenu->addAction(tr("&Load/Delete"),this, SLOT(showEditor()));
+ m_presetsMenu->addSeparator();
+ m_presetsMenu->addAction(tr("&Save Preset"),this,SLOT(savePreset()));
+ m_presetsMenu->addAction(tr("&Save Auto-load Preset"),this,SLOT(saveAutoPreset()));
+ m_presetsMenu->addAction(tr("&Import"),this,SLOT(importWinampEQF()));
+ m_presetsMenu->addSeparator();
+ m_presetsMenu->addAction(tr("&Clear"),this, SLOT(reset()));
+}
+
+void EqWidget::showPresetsMenu()
+{
+ m_presetsMenu->exec(m_presetButton->mapToGlobal(QPoint(0, 0)));
+}
+
+void EqWidget::reset()
+{
+ for (int i = 0; i < m_sliders.size(); ++i)
+ m_sliders.at(i)->setValue(0);
+ m_preamp->setValue(0);
+ setGain();
+}
+
+void EqWidget::showEditor()
+{
+ PresetEditor *editor = new PresetEditor(this);
+ editor->addPresets(m_presets);
+ editor->addAutoPresets(m_autoPresets);
+ connect (editor, SIGNAL(presetLoaded(EQPreset*)), SLOT(setPreset(EQPreset*)));
+ connect (editor, SIGNAL(presetDeleted(EQPreset*)), SLOT(deletePreset(EQPreset*)));
+ editor->show();
+}
+
+void EqWidget::savePreset()
+{
+ bool ok;
+ QString text = QInputDialog::getText(this, tr("Saving Preset"),
+ tr("Preset name:"), QLineEdit::Normal,
+ tr("preset #")+QString("%1").arg(m_presets.size()+1), &ok);
+ if (ok)
+ {
+ EQPreset* preset = new EQPreset;
+ preset->setText(text);
+ preset->setPreamp(m_preamp->value());
+ for (int i = 0; i<10; ++i)
+ {
+ preset->setGain(i, m_sliders.at (i)->value());
+ }
+ m_presets.append(preset);
+ }
+}
+
+void EqWidget::saveAutoPreset()
+{
+ PlayList* playlist = qobject_cast<MainWindow*>(parent())->getPLPointer();
+ if (!playlist->currentItem())
+ return;
+ //delete preset if it already exists
+ EQPreset* preset = findPreset(playlist->currentItem()->fileName());
+ if (preset)
+ deletePreset(preset);
+ //create new preset
+ preset = new EQPreset();
+ preset->setText(playlist->currentItem()->fileName());
+ preset->setPreamp(m_preamp->value());
+ for (int i = 0; i<10; ++i)
+ {
+ preset->setGain(i, m_sliders.at (i)->value());
+ }
+ m_autoPresets.append(preset);
+}
+
+void EqWidget::setPreset(EQPreset* preset)
+{
+ for (int i = 0; i<10; ++i)
+ m_sliders.at(i)->setValue(preset->gain(i));
+ m_preamp->setValue(preset->preamp());
+ setGain();
+}
+
+void EqWidget::deletePreset(EQPreset* preset)
+{
+ int p = m_presets.indexOf(preset);
+ if (p != -1)
+ {
+ delete m_presets.takeAt(p);
+ return;
+ }
+ p = m_autoPresets.indexOf(preset);
+ if (p != -1)
+ {
+ delete m_autoPresets.takeAt(p);
+ return;
+ }
+}
+
+void EqWidget::loadPreset(const QString &name)
+{
+ if (m_autoButton->isChecked())
+ {
+ EQPreset *preset = findPreset(name);
+ if (preset)
+ setPreset(preset);
+ else
+ reset();
+ }
+}
+
+EQPreset *EqWidget::findPreset(const QString &name)
+{
+ foreach(EQPreset *preset, m_autoPresets)
+ {
+ if (preset->text() == name)
+ return preset;
+ }
+ return 0;
+}
+
+void EqWidget::importWinampEQF()
+{
+ char header[31];
+ char name[257];
+ char bands[11];
+ QString path = QFileDialog::getOpenFileName(this, tr("Import Preset"),
+ "/home",
+ "Winamp EQF (*.q1)");
+ QFile file(path);
+ file.open(QIODevice::ReadOnly);
+ file.read ( header, 31);
+ if (QString::fromAscii(header).contains("Winamp EQ library file v1.1"))
+ {
+
+ while (file.read ( name, 257))
+ {
+ EQPreset* preset = new EQPreset;
+ preset->setText(QString::fromAscii(name));
+
+ file.read(bands,11);
+
+ for (int i = 0; i<10; ++i)
+ {
+ preset->setGain(i, 20 - bands[i]*40/64);
+ }
+ preset->setPreamp(20 - bands[10]*40/64);
+ m_presets.append(preset);
+ }
+
+ }
+ file.close();
+
+}
diff --git a/src/eqwidget.h b/src/eqwidget.h
new file mode 100644
index 000000000..095935fe3
--- /dev/null
+++ b/src/eqwidget.h
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef EQWIDGET_H
+#define EQWIDGET_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QMenu;
+class Skin;
+class EqTitleBar;
+class EqSlider;
+class ToggleButton;
+class EQGraph;
+class Button;
+class EQPreset;
+class MediaFile;
+
+class EqWidget : public PixmapWidget
+{
+ Q_OBJECT
+public:
+ EqWidget(QWidget *parent = 0);
+
+ ~EqWidget();
+
+ int preamp();
+ int gain(int);
+ bool isEQEnabled();
+ /*!
+ * necessary for auto-load presets
+ */
+ void loadPreset(const QString &name);
+
+signals:
+ void valueChanged();
+
+private slots:
+ void updateSkin();
+ void setPreamp();
+ void setGain();
+ void showPresetsMenu();
+ void reset();
+ void showEditor();
+ void savePreset();
+ void saveAutoPreset();
+ void setPreset(EQPreset*);
+ void deletePreset(EQPreset*);
+ void importWinampEQF();
+
+private:
+ void readSettings();
+ void writeSettings();
+ void createActions();
+ EQPreset *findPreset(const QString &name);
+ Skin *m_skin;
+ EqTitleBar *m_titleBar;
+ EqSlider *m_preamp;
+ Button *m_presetButton;
+ QList<EqSlider*> m_sliders;
+ QPoint m_pos;
+ ToggleButton *m_on;
+ ToggleButton *m_autoButton;
+ EQGraph *m_eqg;
+ QMenu *m_presetsMenu;
+ QList<EQPreset*> m_presets;
+ QList<EQPreset*> m_autoPresets;
+ QString m_autoName;
+
+protected:
+ virtual void changeEvent(QEvent*);
+ virtual void closeEvent(QCloseEvent*);
+
+};
+
+#endif
diff --git a/src/fft.c b/src/fft.c
new file mode 100644
index 000000000..7ca1978a5
--- /dev/null
+++ b/src/fft.c
@@ -0,0 +1,296 @@
+/* fft.c: Iterative implementation of a FFT
+ * Copyright (C) 1999 Richard Boulton <richard@tartarus.org>
+ * Convolution stuff by Ralph Loader <suckfish@ihug.co.nz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * TODO
+ * Remove compiling in of FFT_BUFFER_SIZE? (Might slow things down, but would
+ * be nice to be able to change size at runtime.)
+ * Finish making / checking thread-safety.
+ * More optimisations.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "fft.h"
+
+//#include <glib.h>
+#include <stdlib.h>
+#include <math.h>
+#ifndef PI
+#ifdef M_PI
+#define PI M_PI
+#else
+#define PI 3.14159265358979323846 /* pi */
+#endif
+#endif
+
+/* ########### */
+/* # Structs # */
+/* ########### */
+
+struct _struct_fft_state {
+ /* Temporary data stores to perform FFT in. */
+ float real[FFT_BUFFER_SIZE];
+ float imag[FFT_BUFFER_SIZE];
+};
+
+/* ############################# */
+/* # Local function prototypes # */
+/* ############################# */
+
+static void fft_prepare(const sound_sample * input, float *re, float *im);
+static void fft_calculate(float *re, float *im);
+static void fft_output(const float *re, const float *im, float *output);
+static int reverseBits(unsigned int initial);
+
+/* #################### */
+/* # Global variables # */
+/* #################### */
+
+/* Table to speed up bit reverse copy */
+static unsigned int bitReverse[FFT_BUFFER_SIZE];
+
+/* The next two tables could be made to use less space in memory, since they
+ * overlap hugely, but hey. */
+static float sintable[FFT_BUFFER_SIZE / 2];
+static float costable[FFT_BUFFER_SIZE / 2];
+
+/* ############################## */
+/* # Externally called routines # */
+/* ############################## */
+
+/* --------- */
+/* FFT stuff */
+/* --------- */
+
+/*
+ * Initialisation routine - sets up tables and space to work in.
+ * Returns a pointer to internal state, to be used when performing calls.
+ * On error, returns NULL.
+ * The pointer should be freed when it is finished with, by fft_close().
+ */
+fft_state *
+fft_init(void)
+{
+ fft_state *state;
+ unsigned int i;
+
+ state = (fft_state *) malloc(sizeof(fft_state));
+ if (!state)
+ return NULL;
+
+ for (i = 0; i < FFT_BUFFER_SIZE; i++) {
+ bitReverse[i] = reverseBits(i);
+ }
+ for (i = 0; i < FFT_BUFFER_SIZE / 2; i++) {
+ float j = 2 * PI * i / FFT_BUFFER_SIZE;
+ costable[i] = cos(j);
+ sintable[i] = sin(j);
+ }
+
+ return state;
+}
+
+/*
+ * Do all the steps of the FFT, taking as input sound data (as described in
+ * sound.h) and returning the intensities of each frequency as floats in the
+ * range 0 to ((FFT_BUFFER_SIZE / 2) * 32768) ^ 2
+ *
+ * FIXME - the above range assumes no frequencies present have an amplitude
+ * larger than that of the sample variation. But this is false: we could have
+ * a wave such that its maximums are always between samples, and it's just
+ * inside the representable range at the places samples get taken.
+ * Question: what _is_ the maximum value possible. Twice that value? Root
+ * two times that value? Hmmm. Think it depends on the frequency, too.
+ *
+ * The input array is assumed to have FFT_BUFFER_SIZE elements,
+ * and the output array is assumed to have (FFT_BUFFER_SIZE / 2 + 1) elements.
+ * state is a (non-NULL) pointer returned by fft_init.
+ */
+void
+fft_perform(const sound_sample * input, float *output, fft_state * state)
+{
+ /* Convert data from sound format to be ready for FFT */
+ fft_prepare(input, state->real, state->imag);
+
+ /* Do the actual FFT */
+ fft_calculate(state->real, state->imag);
+
+ /* Convert the FFT output into intensities */
+ fft_output(state->real, state->imag, output);
+}
+
+/*
+ * Free the state.
+ */
+void
+fft_close(fft_state * state)
+{
+ if (state)
+ free(state);
+}
+
+/* ########################### */
+/* # Locally called routines # */
+/* ########################### */
+
+/*
+ * Prepare data to perform an FFT on
+ */
+static void
+fft_prepare(const sound_sample * input, float *re, float *im)
+{
+ unsigned int i;
+ float *realptr = re;
+ float *imagptr = im;
+
+ /* Get input, in reverse bit order */
+ for (i = 0; i < FFT_BUFFER_SIZE; i++) {
+ *realptr++ = input[bitReverse[i]];
+ *imagptr++ = 0;
+ }
+}
+
+/*
+ * Take result of an FFT and calculate the intensities of each frequency
+ * Note: only produces half as many data points as the input had.
+ * This is roughly a consequence of the Nyquist sampling theorm thingy.
+ * (FIXME - make this comment better, and helpful.)
+ *
+ * The two divisions by 4 are also a consequence of this: the contributions
+ * returned for each frequency are split into two parts, one at i in the
+ * table, and the other at FFT_BUFFER_SIZE - i, except for i = 0 and
+ * FFT_BUFFER_SIZE which would otherwise get float (and then 4* when squared)
+ * the contributions.
+ */
+static void
+fft_output(const float *re, const float *im, float *output)
+{
+ float *outputptr = output;
+ const float *realptr = re;
+ const float *imagptr = im;
+ float *endptr = output + FFT_BUFFER_SIZE / 2;
+
+#ifdef DEBUG
+ unsigned int i, j;
+#endif
+
+ while (outputptr <= endptr) {
+ *outputptr = (*realptr * *realptr) + (*imagptr * *imagptr);
+ outputptr++;
+ realptr++;
+ imagptr++;
+ }
+ /* Do divisions to keep the constant and highest frequency terms in scale
+ * with the other terms. */
+ *output /= 4;
+ *endptr /= 4;
+
+#ifdef DEBUG
+ printf("Recalculated input:\n");
+ for (i = 0; i < FFT_BUFFER_SIZE; i++) {
+ float val_real = 0;
+ float val_imag = 0;
+ for (j = 0; j < FFT_BUFFER_SIZE; j++) {
+ float fact_real = cos(-2 * j * i * PI / FFT_BUFFER_SIZE);
+ float fact_imag = sin(-2 * j * i * PI / FFT_BUFFER_SIZE);
+ val_real += fact_real * re[j] - fact_imag * im[j];
+ val_imag += fact_real * im[j] + fact_imag * re[j];
+ }
+ printf("%5d = %8f + i * %8f\n", i,
+ val_real / FFT_BUFFER_SIZE, val_imag / FFT_BUFFER_SIZE);
+ }
+ printf("\n");
+#endif
+}
+
+/*
+ * Actually perform the FFT
+ */
+static void
+fft_calculate(float *re, float *im)
+{
+ unsigned int i, j, k;
+ unsigned int exchanges;
+ float fact_real, fact_imag;
+ float tmp_real, tmp_imag;
+ unsigned int factfact;
+
+ /* Set up some variables to reduce calculation in the loops */
+ exchanges = 1;
+ factfact = FFT_BUFFER_SIZE / 2;
+
+ /* Loop through the divide and conquer steps */
+ for (i = FFT_BUFFER_SIZE_LOG; i != 0; i--) {
+ /* In this step, we have 2 ^ (i - 1) exchange groups, each with
+ * 2 ^ (FFT_BUFFER_SIZE_LOG - i) exchanges
+ */
+ /* Loop through the exchanges in a group */
+ for (j = 0; j != exchanges; j++) {
+ /* Work out factor for this exchange
+ * factor ^ (exchanges) = -1
+ * So, real = cos(j * PI / exchanges),
+ * imag = sin(j * PI / exchanges)
+ */
+ fact_real = costable[j * factfact];
+ fact_imag = sintable[j * factfact];
+
+ /* Loop through all the exchange groups */
+ for (k = j; k < FFT_BUFFER_SIZE; k += exchanges << 1) {
+ int k1 = k + exchanges;
+ /* newval[k] := val[k] + factor * val[k1]
+ * newval[k1] := val[k] - factor * val[k1]
+ **/
+#ifdef DEBUG
+ printf("%d %d %d\n", i, j, k);
+ printf("Exchange %d with %d\n", k, k1);
+ printf("Factor %9f + i * %8f\n", fact_real, fact_imag);
+#endif
+ /* FIXME - potential scope for more optimization here? */
+ tmp_real = fact_real * re[k1] - fact_imag * im[k1];
+ tmp_imag = fact_real * im[k1] + fact_imag * re[k1];
+ re[k1] = re[k] - tmp_real;
+ im[k1] = im[k] - tmp_imag;
+ re[k] += tmp_real;
+ im[k] += tmp_imag;
+#ifdef DEBUG
+ for (k1 = 0; k1 < FFT_BUFFER_SIZE; k1++) {
+ printf("%5d = %8f + i * %8f\n", k1, real[k1], imag[k1]);
+ }
+#endif
+ }
+ }
+ exchanges <<= 1;
+ factfact >>= 1;
+ }
+}
+
+static int
+reverseBits(unsigned int initial)
+{
+ unsigned int reversed = 0, loop;
+ for (loop = 0; loop < FFT_BUFFER_SIZE_LOG; loop++) {
+ reversed <<= 1;
+ reversed += (initial & 1);
+ initial >>= 1;
+ }
+ return reversed;
+}
diff --git a/src/fft.h b/src/fft.h
new file mode 100644
index 000000000..431afa365
--- /dev/null
+++ b/src/fft.h
@@ -0,0 +1,45 @@
+/* fft.h: Header for iterative implementation of a FFT
+ * Copyright (C) 1999 Richard Boulton <richard@tartarus.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 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _FFT_H_
+#define _FFT_H_
+
+#define FFT_BUFFER_SIZE_LOG 9
+
+#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG)
+
+/* sound sample - should be an signed 16 bit value */
+typedef short int sound_sample;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* FFT library */
+ typedef struct _struct_fft_state fft_state;
+ fft_state *fft_init(void);
+ void fft_perform(const sound_sample * input, float *output,
+ fft_state * state);
+ void fft_close(fft_state * state);
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FFT_H_ */
diff --git a/src/fileloader.cpp b/src/fileloader.cpp
new file mode 100644
index 000000000..fda8efdc1
--- /dev/null
+++ b/src/fileloader.cpp
@@ -0,0 +1,108 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <decoder.h>
+
+#include "fileloader.h"
+#include "mediafile.h"
+
+FileLoader::FileLoader(QObject *parent)
+ : QThread(parent),m_files_to_load(),m_directory()
+{
+ m_filters = Decoder::nameFilters();
+ m_finished = false;
+}
+
+
+FileLoader::~FileLoader()
+{
+ qWarning("FileLoader::~FileLoader()");
+}
+
+
+void FileLoader::addFiles(const QStringList &files)
+{
+ if (files.isEmpty ())
+ return;
+
+ foreach(QString s, files)
+ {
+ if (Decoder::supports(s))
+ emit newMediaFile(new MediaFile(s));
+ if(m_finished) return;
+ }
+}
+
+
+void FileLoader::addDirectory(const QString& s)
+{
+ QDir dir(s);
+ dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
+ dir.setSorting(QDir::Name);
+ QFileInfoList l = dir.entryInfoList(m_filters);
+ for (int i = 0; i < l.size(); ++i)
+ {
+ QFileInfo fileInfo = l.at(i);
+ QString suff = fileInfo.completeSuffix();
+ list << fileInfo;
+
+ if (Decoder::supports(fileInfo.absoluteFilePath ()))
+ emit newMediaFile(new MediaFile(fileInfo.absoluteFilePath ()));
+ if(m_finished) return;
+ }
+ dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
+ dir.setSorting(QDir::Name);
+ l.clear();
+ l = dir.entryInfoList();
+ if (l.size() > 0)
+ for (int i = 0; i < l.size(); ++i)
+ {
+ QFileInfo fileInfo = l.at(i);
+ addDirectory(fileInfo.absoluteFilePath ());
+ if(m_finished) return;
+ }
+}
+
+
+void FileLoader::run()
+{
+ if(!m_files_to_load.isEmpty())
+ addFiles(m_files_to_load);
+ else if(!m_directory.isEmpty())
+ addDirectory(m_directory);
+}
+
+
+
+void FileLoader::setFilesToLoad(const QStringList & l)
+{
+ m_files_to_load = l;
+ m_directory = QString();
+}
+
+void FileLoader::setDirectoryToLoad(const QString & d)
+{
+ m_directory = d;
+ m_files_to_load.clear();
+}
+
+void FileLoader::finish()
+{
+ m_finished = true;
+}
diff --git a/src/fileloader.h b/src/fileloader.h
new file mode 100644
index 000000000..c23d1ed35
--- /dev/null
+++ b/src/fileloader.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef FILELOADER_H
+#define FILELOADER_H
+
+#include <QObject>
+#include <QDir>
+#include <QThread>
+
+class MediaFile;
+
+/*!
+ * This class represents fileloader object that
+ * processes file list in separate thread and emits
+ * \b newMediaFile(MediaFile*) signal for every newly
+ * created media file.
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class FileLoader : public QThread
+{
+Q_OBJECT
+public:
+ FileLoader(QObject *parent = 0);
+
+ ~FileLoader();
+ virtual void run();
+
+ /*!
+ * Call this method when you want to notify the thread about finishing
+ */
+ void finish();
+
+ /*!
+ * Sets filelist to load( directory to load will be cleaned )
+ */
+ void setFilesToLoad(const QStringList&);
+
+ /*!
+ * Sets directory to load( filelist to load will be cleaned )
+ */
+ void setDirectoryToLoad(const QString&);
+signals:
+ void newMediaFile(MediaFile*);
+protected:
+ void addFiles(const QStringList &files);
+ void addDirectory(const QString& s);
+private:
+ QFileInfoList list;
+ QStringList m_filters;
+ QStringList m_files_to_load;
+ QString m_directory;
+ bool m_finished;
+};
+
+#endif
diff --git a/src/guard.cpp b/src/guard.cpp
new file mode 100644
index 000000000..d9c701951
--- /dev/null
+++ b/src/guard.cpp
@@ -0,0 +1,125 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "guard.h"
+
+//#define USE_SEMAPHORE
+
+#ifdef USE_SEMAPHORE
+ #include <sys/types.h>
+ #include <sys/ipc.h>
+ #include <errno.h>
+ #include <sys/sem.h>
+#else
+ #include <QSettings>
+#endif
+
+#include <QDir>
+
+bool Guard::create(const QString& filepath)
+{
+#ifdef USE_SEMAPHORE
+ key_t key;
+ int sem_num = 1;
+ if( (key = ftok(filepath.toAscii().data(),'A')) < 0)
+ {
+ qWarning("Warning: Unable get access to key...");
+ return false;
+ }
+
+ if(semget(key,sem_num,IPC_CREAT | IPC_EXCL ) < 0)
+ {
+ if(errno == EEXIST)
+ {
+ qWarning("Warning: Semaphore with %d key already exists...",key);
+ }
+ return false;
+ }
+
+ return true;
+#else
+ Q_UNUSED(filepath)
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("Application");
+ settings.setValue("IS_RUNNING",TRUE);
+ settings.endGroup();
+ return true;
+#endif
+}
+
+bool Guard::exists(const QString& filepath)
+{
+#ifdef USE_SEMAPHORE
+ key_t key;
+ int sem_num = 1;
+ int sem_id;
+
+ if( (key = ftok(filepath.toAscii().data(),'A')) < 0)
+ {
+ qWarning("Warning: Unable get access to key");
+ return false;
+ }
+
+ if( (sem_id = semget(key,sem_num,0)) < 0 )
+ return false;
+ return true;
+#else
+ Q_UNUSED(filepath)
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("Application");
+ bool res = settings.value("IS_RUNNING",FALSE).toBool();
+ settings.endGroup();
+ return res;
+#endif
+}
+
+bool Guard::destroy(const QString& filepath)
+{
+#ifdef USE_SEMAPHORE
+ key_t key;
+ int sem_num = 1;
+ int sem_id;
+
+ if( (key = ftok(filepath.toAscii().data(),'A')) < 0)
+ {
+ qWarning("Warning: Unable get access to key");
+ return false;
+ }
+ if( (sem_id = semget(key,sem_num,0)) < 0 )
+ {
+ qWarning("Unable get semaphore with key %d",key);
+ return false;
+ }
+
+ if(semctl(sem_id,1,IPC_RMID) < 0)
+ {
+ qWarning("Unable remove semaphore with key %d",key);
+ return false;
+ }
+ return true;
+#else
+ Q_UNUSED(filepath)
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("Application");
+ settings.setValue("IS_RUNNING",FALSE);
+ settings.endGroup();
+ return true;
+#endif
+}
diff --git a/src/guard.h b/src/guard.h
new file mode 100644
index 000000000..2ae05be4a
--- /dev/null
+++ b/src/guard.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef _GUARD_H
+#define _GUARD_H
+
+#include <QString>
+
+/**
+ @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+struct Guard
+{
+ static bool create(const QString& filepath);
+ static bool exists(const QString& filepath);
+ static bool destroy(const QString& filepath);
+};
+#endif
+
diff --git a/src/html/about_en.html b/src/html/about_en.html
new file mode 100644
index 000000000..d03691cf6
--- /dev/null
+++ b/src/html/about_en.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"
+>
+<head>
+ <META content="text/html; charset=utf8">
+ <TITLE>Qt-based Multimedia Player</TITLE>
+</head>
+<div align="left">
+ <h3>
+ Qt-based Multimedia Player (Qmmp)
+ </h3>
+</div>
+<p>
+ This program is an audio-player, written with help of Qt library.
+</p>
+<h4>
+ Main opportunities:
+</h4>
+<ul type="square">
+ <li>
+ unpacked winamp skins support;
+ </li>
+ <li>
+ plugins support;
+ </li>
+ <li>
+ MPEG1 layer 1/2/3 support;
+ </li>
+ <li>
+ Ogg Vorbis support;
+ </li>
+ <li>
+ native FLAC support;
+ </li>
+ <li>
+ Musepack support;
+ </li>
+ <li>
+ WMA support;
+ </li>
+ <li>
+ ALSA sound output;
+ </li>
+ <li>
+ JACK sound output.
+ </li>
+</ul>
+<h4>
+ Requirements:
+</h4>
+<ul type="square">
+ <li>
+ OS GNU Linux;
+ </li>
+ <li>
+ <A name="Qt" href="http://www.trolltech.com/">Qt</A> версии >= 4.2;
+ </li>
+ <li>
+ <A name="MAD" href="http://www.underbit.com/products/mad/">MAD</A>;
+ </li>
+ <li>
+ <A name="Vorbis" href="http://www.vorbis.com/">Ogg Vorbis</A>;
+ </li>
+ <li>
+ <A name="FLAC" href="http://flac.sourceforge.net/">FLAC</A> >= 1.1.3;
+ </li>
+ <li>
+ <A name="ALSA" href="http://www.alsa-project.org/">ALSA</A> >= 1.0.1;
+ </li>
+ <li>
+ <A name="taglib" href="http://developer.kde.org/~wheeler/taglib.html">TagLib</A> >= 1.4.
+ </li>
+ <li>
+ <A name="libmpcdec" href="http://www.musepack.net/">libmpcdec</A> >= 1.2.6
+ </li>
+ <li>
+ <A name="JACK" href="http://jackaudio.org/">Jack</A> >= 0.102.5
+ </li>
+ <li>
+ <A name="FFmpeg" href="http://ffmpeg.mplayerhq.hu/">FFmpeg</A> >= 0.4.9-pre1
+ </li>
+</ul>
diff --git a/src/html/about_ru.html b/src/html/about_ru.html
new file mode 100644
index 000000000..26e5b9906
--- /dev/null
+++ b/src/html/about_ru.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"
+>
+<head>
+ <META content="text/html; charset=UTF-8">
+ <TITLE>Qt-based Multimedia Player</TITLE>
+</head>
+<div align="left">
+ <h3>
+ Qt-based Multimedia Player (Qmmp)
+ </h3>
+</div>
+<p>
+ Данная программа является аудио-плеером, написанным с использованием библиотеки Qt. Программа имеет интерфейс, аналогичный winamp или xmms.
+</p>
+<h4>
+ Основные возможности программы:
+</h4>
+<ul type="square">
+ <li>
+ поддержка тем winamp в распакованном виде;
+ </li>
+ <li>
+ поддержка модулей (плагинов);
+ </li>
+ <li>
+ поддержка файлов MPEG1 layer 1/2/3;
+ </li>
+ <li>
+ поддержка файлов Ogg Vorbis;
+ </li>
+ <li>
+ поддержка файлов Native FLAC;
+ </li>
+ <li>
+ поддержка файлов Musepack;
+ </li>
+ <li>
+ поддержка файлов WMA;
+ </li>
+ <li>
+ вывод звука через ALSA;
+ </li>
+ <li>
+ вывод звука через Jack.
+ </li>
+</ul>
+<h4>
+ Для работы необходимы:
+</h4>
+<ul type="square">
+ <li>
+ операционная система GNU Linux;
+ </li>
+ <li>
+ <A name="Qt" href="http://www.trolltech.com/">Qt</A> версии >= 4.2;
+ </li>
+ <li>
+ <A name="MAD" href="http://www.underbit.com/products/mad/">MAD</A>;
+ </li>
+ <li>
+ <A name="Vorbis" href="http://www.vorbis.com/">Ogg Vorbis</A>;
+ </li>
+ <li>
+ <A name="FLAC" href="http://flac.sourceforge.net/">FLAC</A> версии >= 1.1.3;
+ </li>
+ <li>
+ <A name="ALSA" href="http://www.alsa-project.org/">ALSA</A> версии >= 1.0.1;
+ </li>
+ <li>
+ <A name="taglib" href="http://developer.kde.org/~wheeler/taglib.html">TagLib</A> версии >= 1.4.
+ </li>
+ <li>
+ <A name="libmpcdec" href="http://www.musepack.net/">libmpcdec</A> версии >= 1.2.6
+ </li>
+ <li>
+ <A name="JACK" href="http://jackaudio.org/">Jack</A> версии >= 0.102.5
+ </li>
+ <li>
+ <A name="FFmpeg" href="http://ffmpeg.mplayerhq.hu/">FFmpeg</A> версии >= 0.4.9-pre1
+ </li>
+</ul>
diff --git a/src/html/about_zh_CN.html b/src/html/about_zh_CN.html
new file mode 100644
index 000000000..eadc7f78d
--- /dev/null
+++ b/src/html/about_zh_CN.html
@@ -0,0 +1,81 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"
+>
+<head>
+ <META content="text/html; charset=utf8">
+ <TITLE>基于Qt的多媒体播放器</TITLE>
+</head>
+<div align="left">
+ <h3>
+ 基于Qt的多媒体播放器(Qmmp)。
+ </h3>
+</div>
+<p>
+ 此程序是一个音乐播放器,程序的编写基于Qt库。
+</p>
+<h4>
+ 主要功能:
+</h4>
+<ul type="square">
+ <li>
+ 未压缩的winamp皮肤支持;
+ </li>
+ <li>
+ 插件支持;
+ </li>
+ <li>
+ MPEG1 1/2/3 层支持;
+ </li>
+ <li>
+ Ogg Vorbis 支持;
+ </li>
+ <li>
+ native FLAC 支持;
+ </li>
+ <li>
+ Musepack 支持;
+ </li>
+ <li>
+ WMA 支持;
+ </li>
+ <li>
+ ALSA 声音输出;
+ </li>
+ <li>
+ JACK 声音输出。
+ </li>
+</ul>
+<h4>
+ 要求:
+</h4>
+<ul type="square">
+ <li>
+ OS GNU Linux;
+ </li>
+ <li>
+ <A name="Qt" href="http://www.trolltech.com/">Qt</A> версии >= 4.2;
+ </li>
+ <li>
+ <A name="MAD" href="http://www.underbit.com/products/mad/">MAD</A>;
+ </li>
+ <li>
+ <A name="Vorbis" href="http://www.vorbis.com/">Ogg Vorbis</A>;
+ </li>
+ <li>
+ <A name="FLAC" href="http://flac.sourceforge.net/">FLAC</A> >= 1.1.3;
+ </li>
+ <li>
+ <A name="ALSA" href="http://www.alsa-project.org/">ALSA</A> >= 1.0.1;
+ </li>
+ <li>
+ <A name="taglib" href="http://developer.kde.org/~wheeler/taglib.html">TagLib</A> >= 1.4.
+ </li>
+ <li>
+ <A name="libmpcdec" href="http://www.musepack.net/">libmpcdec</A> >= 1.2.6
+ </li>
+ <li>
+ <A name="JACK" href="http://jackaudio.org/">Jack</A> >= 0.102.5
+ </li>
+ <li>
+ <A name="FFmpeg" href="http://ffmpeg.mplayerhq.hu/">FFmpeg</A> >= 0.4.9-pre1
+ </li>
+</ul>
diff --git a/src/html/authors_en.txt b/src/html/authors_en.txt
new file mode 100644
index 000000000..6c11ebd5e
--- /dev/null
+++ b/src/html/authors_en.txt
@@ -0,0 +1,12 @@
+Core Developers:
+
+ Ilya Kotov <forkotov02@hotmail.ru> (idea and base code)
+ Vladimir Kuznetsov <vovanec@gmail.com> (look&feel and many improvements)
+
+Plugin Developers:
+
+ Yuriy Zhuravlev <stalkerg@gmail.com> (jack plugin)
+
+Turkish translation:
+
+ Mustafa GUNAY <mustafagunay@gmail.com>
diff --git a/src/html/authors_ru.txt b/src/html/authors_ru.txt
new file mode 100644
index 000000000..b95c3887e
--- /dev/null
+++ b/src/html/authors_ru.txt
@@ -0,0 +1,9 @@
+Разработчики ядра:
+
+ Владимир Кузнецов <vovanec@gmail.com> (внешний вид и множество улучшений)
+ Илья Котов <forkotov02@hotmail.ru> (идея и основной код)
+
+Разработчики модулей:
+
+ Юрий Журавлёв <stalkerg@gmail.com> (модуль jack)
+
diff --git a/src/html/authors_zh_CN.txt b/src/html/authors_zh_CN.txt
new file mode 100644
index 000000000..f2d8892fd
--- /dev/null
+++ b/src/html/authors_zh_CN.txt
@@ -0,0 +1,16 @@
+核心开发:
+
+ Ilya Kotov <forkotov02@hotmail.ru> (idea and base code)
+ Vladimir Kuznetsov <vovanec@gmail.com> (look&feel and many improvements)
+
+插件开发:
+
+ Yuriy Zhuravlev <stalkerg@gmail.com> (jack plugin)
+
+土耳其语翻译:
+
+ Mustafa GUNAY <mustafagunay@gmail.com>
+
+简体中文翻译:
+
+ 李红昆 <lon83129@126.com> \ No newline at end of file
diff --git a/src/html/thanks_en.txt b/src/html/thanks_en.txt
new file mode 100644
index 000000000..3ed2fe5d5
--- /dev/null
+++ b/src/html/thanks_en.txt
@@ -0,0 +1,3 @@
+Thanks to:
+
+ Vadim Kalinnikov <moose@ylsoftware.com> (project hosting)
diff --git a/src/html/thanks_ru.txt b/src/html/thanks_ru.txt
new file mode 100644
index 000000000..79acc177d
--- /dev/null
+++ b/src/html/thanks_ru.txt
@@ -0,0 +1,3 @@
+Благодарности:
+
+ Вадиму Калинникову <moose@ylsoftware.com> (хотстинг проекта)
diff --git a/src/html/thanks_zh_CN.txt b/src/html/thanks_zh_CN.txt
new file mode 100644
index 000000000..505d05c72
--- /dev/null
+++ b/src/html/thanks_zh_CN.txt
@@ -0,0 +1,3 @@
+感谢:
+
+ Vadim Kalinnikov <moose@ylsoftware.com> (project hosting)
diff --git a/src/images/advanced.png b/src/images/advanced.png
new file mode 100644
index 000000000..30e9a7074
--- /dev/null
+++ b/src/images/advanced.png
Binary files differ
diff --git a/src/images/images.qrc b/src/images/images.qrc
new file mode 100644
index 000000000..8dcc2efe2
--- /dev/null
+++ b/src/images/images.qrc
@@ -0,0 +1,14 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>play.png</file>
+ <file>pause.png</file>
+ <file>stop.png</file>
+ <file>qmmp.xpm</file>
+ <file>interface.png</file>
+ <file>playlist.png</file>
+ <file>advanced.png</file>
+ <file>plugins.png</file>
+ <file>logo-qmmp.png</file>
+ </qresource>
+</RCC>
diff --git a/src/images/interface.png b/src/images/interface.png
new file mode 100644
index 000000000..e056d2b24
--- /dev/null
+++ b/src/images/interface.png
Binary files differ
diff --git a/src/images/logo-qmmp.png b/src/images/logo-qmmp.png
new file mode 100644
index 000000000..7a4d6ded4
--- /dev/null
+++ b/src/images/logo-qmmp.png
Binary files differ
diff --git a/src/images/pause.png b/src/images/pause.png
new file mode 100644
index 000000000..885c397da
--- /dev/null
+++ b/src/images/pause.png
Binary files differ
diff --git a/src/images/play.png b/src/images/play.png
new file mode 100644
index 000000000..9ff85ebce
--- /dev/null
+++ b/src/images/play.png
Binary files differ
diff --git a/src/images/playlist.png b/src/images/playlist.png
new file mode 100644
index 000000000..2afc1d720
--- /dev/null
+++ b/src/images/playlist.png
Binary files differ
diff --git a/src/images/plugins.png b/src/images/plugins.png
new file mode 100644
index 000000000..a88b78c2b
--- /dev/null
+++ b/src/images/plugins.png
Binary files differ
diff --git a/src/images/qmmp.xpm b/src/images/qmmp.xpm
new file mode 100644
index 000000000..85ea94fc1
--- /dev/null
+++ b/src/images/qmmp.xpm
@@ -0,0 +1,278 @@
+/* XPM */
+static char *trayicon[]={
+"32 32 243 2",
+"#N c None",
+"Qt c None",
+".# c #000000",
+".a c #050d16",
+".U c #060e16",
+".L c #06101b",
+"a9 c #091725",
+"b. c #0a0b11",
+"bU c #0b1826",
+".c c #0c2034",
+"bt c #0d1823",
+"be c #0d2034",
+"bb c #0f0f0f",
+".A c #0f253c",
+"a7 c #11263d",
+".n c #112a45",
+"a6 c #12273d",
+"aL c #122f4c",
+"bV c #131e29",
+"bi c #141b23",
+"bg c #14212e",
+".b c #16385b",
+".i c #16385c",
+"#L c #191b2b",
+"bW c #1a2b3e",
+".d c #1a4169",
+"aY c #1a416b",
+"bm c #1a426b",
+"aB c #1b1d2e",
+"aZ c #1b1d2f",
+".o c #1c1c1c",
+"bl c #1c436b",
+"aH c #1d436b",
+"ah c #1e1f25",
+"at c #1e4b7a",
+"#o c #1f2236",
+"#K c #1f272f",
+"bO c #1f2e3e",
+"aK c #1f4c7b",
+"bk c #202a35",
+"bh c #202e3e",
+"au c #212331",
+"as c #215489",
+"a8 c #21548a",
+".R c #223952",
+"br c #233242",
+"#J c #24303e",
+".3 c #253647",
+".T c #27507b",
+".e c #292929",
+"#O c #292a30",
+"aM c #2967a7",
+".x c #2968a9",
+"bN c #2a598a",
+"aJ c #2c69a9",
+"ar c #2d71b7",
+"ap c #2d71b8",
+".M c #2e2e2e",
+"bs c #2f4d6c",
+"#w c #2f547b",
+"#B c #303030",
+"ag c #303453",
+"bI c #30353a",
+"bc c #30465d",
+"bf c #307ac7",
+"bP c #314253",
+".K c #317bc7",
+"#I c #323e4b",
+"#p c #333333",
+"#8 c #34353d",
+"aq c #3484d6",
+"aA c #3484d7",
+"az c #35404c",
+"#d c #363636",
+"aN c #373b5e",
+"bu c #387ec8",
+".4 c #393939",
+"a4 c #39414a",
+"aI c #3a87d7",
+"af c #3b3b3b",
+"#j c #3b699a",
+".V c #3c3c3c",
+"#A c #3c4046",
+"bn c #3c6a9a",
+".m c #3c95f2",
+".l c #3c95f3",
+".k c #3c96f4",
+".j c #3c96f5",
+".z c #3c97f5",
+".y c #3c97f6",
+"bJ c #3f4954",
+"aX c #3f99f6",
+"bD c #408ad7",
+".J c #419af6",
+"aT c #4284c8",
+"aW c #459cf6",
+"bd c #46688c",
+"#X c #474a60",
+".I c #479df6",
+"## c #48719b",
+"bT c #4887c8",
+"ba c #494949",
+"aU c #4a97e7",
+"bE c #4b81b9",
+".S c #4b89c9",
+"aV c #4b9ff6",
+".1 c #4d4d4d",
+".p c #545454",
+"ay c #555555",
+"#M c #555b91",
+".2 c #57789c",
+"#. c #585d61",
+"a5 c #5897d9",
+"bC c #59a6f7",
+"bM c #5aa6f7",
+".f c #606060",
+"#G c #616161",
+"bv c #62abf7",
+"aC c #636785",
+"#v c #646464",
+"aF c #676767",
+"bj c #6a8aac",
+"bS c #6bb0f8",
+"#a c #6f94bb",
+"bB c #6fb2f8",
+"aO c #707175",
+"bL c #70b2f8",
+"#b c #71879d",
+"bo c #72b4f8",
+"#6 c #747474",
+"aR c #757575",
+"#7 c #777777",
+"bQ c #7798bb",
+"#x c #77b6f8",
+"#W c #787878",
+"#t c #797979",
+"#k c #7ab8f8",
+".0 c #7b7b7b",
+"ai c #7b7f9f",
+"bF c #7bb9f8",
+".h c #7e7e7e",
+"#i c #808080",
+"by c #808f9d",
+"aG c #828282",
+"#F c #838383",
+"bR c #83bdf9",
+".Q c #848484",
+"av c #8487a4",
+"#5 c #858585",
+"bz c #86a8cb",
+"ao c #878787",
+"#9 c #878aa2",
+"ac c #888888",
+"bA c #89c0f9",
+"#2 c #8a8a8a",
+"bp c #8ac1f9",
+".w c #8b8b8b",
+".q c #8c8c8c",
+"#3 c #8d8d8d",
+"#H c #8e8e8e",
+"#n c #90979e",
+"#U c #919191",
+"#Z c #9396ac",
+"aS c #969696",
+"bw c #96c7fa",
+"#4 c #989898",
+"bG c #98c8fa",
+"ad c #9a9a9a",
+"#T c #9b9b9b",
+".g c #9c9c9c",
+"#e c #9d9d9d",
+"#y c #9dcafa",
+"ae c #9f9f9f",
+"#c c #9fbddc",
+"aE c #a0a0a0",
+"bH c #a1bedc",
+".9 c #a2a2a2",
+"a. c #a2a3ad",
+"#l c #a3cefa",
+"aD c #a4a4a4",
+"#Y c #a4a5ad",
+"#u c #a5a5a5",
+"aw c #a7a7a7",
+"al c #a8a8a8",
+"#g c #a9a9a9",
+"bK c #a9d1fb",
+"bq c #aacaeb",
+"#h c #acacac",
+"am c #afafaf",
+"#Q c #afb1bd",
+"#1 c #b0b0b0",
+".Z c #b1b1b1",
+".8 c #b3b3b3",
+"#P c #b3b3b9",
+"ak c #b4b4b4",
+"bx c #b5d7fb",
+"#r c #b9b9b9",
+"aj c #bababa",
+"ax c #bbbbbb",
+"a# c #bebebe",
+"an c #bfbfbf",
+".B c #c0c0c0",
+".r c #c2c2c2",
+"#R c #c2c3cb",
+".6 c #c5c5c5",
+"#z c #c5e0fc",
+"aa c #c7c7c7",
+".Y c #c8c8c8",
+"#0 c #c9c9c9",
+"#C c #cacaca",
+"aP c #cbcbcb",
+"#S c #cccccc",
+"#E c #cecece",
+"b# c #cfcfcf",
+".X c #d0d0d0",
+".7 c #d1d1d1",
+"ab c #d2d2d2",
+"#m c #d2e7fc",
+"#q c #d5d5d5",
+"#f c #d6d6d6",
+"#D c #d6d7db",
+"aQ c #d7d7d7",
+"#s c #d9d9d9",
+".t c #dbdbdb",
+".s c #dcdcdc",
+".5 c #dddddd",
+".F c #dedede",
+".E c #e0e0e0",
+".D c #e1e1e1",
+".C c #e3e3e3",
+".u c #e5e5e5",
+".G c #e6e6e6",
+".P c #e7e7e7",
+"#V c #eaeaea",
+".v c #ebebeb",
+".H c #ececec",
+".O c #ededed",
+"a3 c #eeeeee",
+"a0 c #efefef",
+"a2 c #f0f0f0",
+"a1 c #f1f1f1",
+".W c #f7f7f7",
+".N c #f9f9f9",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQt.#.#QtQtQtQtQt.a.b.#.c.dQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQt.#.#.e.f.g.h.#QtQtQtQt.i.j.k.l.m.nQtQtQtQtQt",
+"QtQtQtQtQtQtQt.#.o.p.q.r.s.t.u.v.w.#QtQtQt.x.y.z.k.k.AQtQtQtQtQt",
+"QtQtQtQtQtQt.#.B.u.C.D.E.F.s.s.G.H.q.#Qt.#.I.J.K.c.#.LQtQtQtQtQt",
+"QtQtQtQtQtQt.M.N.O.u.C.D.E.F.s.s.P.O.Q.#.R.S.T.UQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt.V.W.N.O.u.C.F.X.r.Y.X.Z.0.1.2.3QtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt.4.H.W.N.5.6.7.s.8.9#.###a#b#c.#QtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt#d.D.H.W.Y#e#f#g#h#i#j#k#l#m#n#oQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt#p#q.D.H.X#r#s#t#u#v#w#x#y#z#AQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt#B#C#q#D#E#s.t#F#G#H.w#I#J#K#L#MQt#NQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt#O#P#Q#R#S.s.F.r#T#U.w.F#V#WQt#MQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt#X#Y#Z#C#0.F#1#2#3#4#5#6.O#7Qt#MQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQt#8#9a.a#aaabac.E.8adae.1.Eaf.##oQtagQtQtQtQtQtQtQtQt",
+"QtQtQtQtQt#Nahaiajak.6al#5am#2an.q.paoap.zaqarasat.A.AQtQtQtQtQt",
+"QtQtQtQtQtQtauavan#r.6#i#eawanaxay#4azaAapap.z.j.k.k.l.#QtQtQtQt",
+"QtQtQtQtQtQtaBaC.ra##SaD.0aE#3ayaFaGaHaIaJaK.iaLaLataMQtQtQtQtQt",
+"QtQtQtQtQtQtQtaNaOa#aPaQaR.1.paS.G.waTaUaVaWaX.y.y.zaYQtQtQtQtQt",
+"QtQtQtQtQt#NQtQtaZaF#E#5a0a1a2a3ana4a5.#.#a6a7aKa8.za9QtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtb.#2.Hb#.Qbabb.#bcbdQtQtQtQtQtbebfQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQt.#.#.#.#bgbhbibjbkQtQtQtQtQtblbmQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQt.#bnbobp#lbq.#Qt.#brbsbtbua9QtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQt.#bv#kbwbxbyQt.#bzbAbBbCbD.#QtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQt.#bEbFbGbHbIQtbJbKbpbLbMbNQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQt.#bObP.#QtQt.#bQbRbSbTbUQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt.#bVbW.#QtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt",
+"QtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQtQt"};
diff --git a/src/images/stop.png b/src/images/stop.png
new file mode 100644
index 000000000..296ef1ce3
--- /dev/null
+++ b/src/images/stop.png
Binary files differ
diff --git a/src/inlines.h b/src/inlines.h
new file mode 100644
index 000000000..3efccf0de
--- /dev/null
+++ b/src/inlines.h
@@ -0,0 +1,505 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef INLINES_H
+#define INLINES_H
+
+#include "fft.h"
+
+// *fast* convenience functions
+static inline void
+calc_freq(short* dest, short *src)
+{
+ static fft_state *state = NULL;
+ float tmp_out[257];
+ int i;
+
+ if (!state)
+ state = fft_init();
+
+ fft_perform(src, tmp_out, state);
+
+ for (i = 0; i < 256; i++)
+ dest[i] = ((int) sqrt(tmp_out[i + 1])) >> 8;
+}
+
+static inline void
+calc_mono_freq(short dest[2][256], short src[2][512], int nch)
+{
+ int i;
+ short *d, *sl, *sr, tmp[512];
+
+ if (nch == 1)
+ calc_freq(dest[0], src[0]);
+ else
+ {
+ d = tmp;
+ sl = src[0];
+ sr = src[1];
+ for (i = 0; i < 512; i++)
+ {
+ *(d++) = (*(sl++) + *(sr++)) >> 1;
+ }
+ calc_freq(dest[0], tmp);
+ }
+}
+
+static inline void stereo16_from_stereopcm8(register short *l,
+ register short *r,
+ register uchar *c,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = c[0];
+ r[0] = c[1];
+ l[1] = c[2];
+ r[1] = c[3];
+ l[2] = c[4];
+ r[2] = c[5];
+ l[3] = c[6];
+ r[3] = c[7];
+ l += 4;
+ r += 4;
+ c += 8;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = c[0];
+ r[0] = c[1];
+ if (cnt > 1l)
+ {
+ l[1] = c[2];
+ r[1] = c[3];
+ if (cnt > 2l)
+ {
+ l[2] = c[4];
+ r[2] = c[5];
+ }
+ }
+ }
+}
+
+
+static inline void stereo16_from_stereopcm16(register short *l,
+ register short *r,
+ register short *s,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = s[0];
+ r[0] = s[1];
+ l[1] = s[2];
+ r[1] = s[3];
+ l[2] = s[4];
+ r[2] = s[5];
+ l[3] = s[6];
+ r[3] = s[7];
+ l += 4;
+ r += 4;
+ s += 8;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = s[0];
+ r[0] = s[1];
+ if (cnt > 1l)
+ {
+ l[1] = s[2];
+ r[1] = s[3];
+ if (cnt > 2l)
+ {
+ l[2] = s[4];
+ r[2] = s[5];
+ }
+ }
+ }
+}
+
+
+static inline void mono16_from_monopcm8(register short *l,
+ register uchar *c,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = c[0];
+ l[1] = c[1];
+ l[2] = c[2];
+ l[3] = c[3];
+ l += 4;
+ c += 4;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = c[0];
+ if (cnt > 1l)
+ {
+ l[1] = c[1];
+ if (cnt > 2l)
+ {
+ l[2] = c[2];
+ }
+ }
+ }
+}
+
+
+static inline void mono16_from_monopcm16(register short *l,
+ register short *s,
+ long cnt)
+{
+ while (cnt >= 4l)
+ {
+ l[0] = s[0];
+ l[1] = s[1];
+ l[2] = s[2];
+ l[3] = s[3];
+ l += 4;
+ s += 4;
+ cnt -= 4l;
+ }
+
+ if (cnt > 0l)
+ {
+ l[0] = s[0];
+ if (cnt > 1l)
+ {
+ l[1] = s[1];
+ if (cnt > 2l)
+ {
+ l[2] = s[2];
+ }
+ }
+ }
+}
+
+
+static inline void fast_short_set(register short *p,
+ short v,
+ long c)
+{
+ while (c >= 4l)
+ {
+ p[0] = v;
+ p[1] = v;
+ p[2] = v;
+ p[3] = v;
+ p += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ p[0] = v;
+ if (c > 1l)
+ {
+ p[1] = v;
+ if (c > 2l)
+ {
+ p[2] = v;
+ }
+ }
+ }
+}
+
+#ifdef FFTW
+static inline void fast_real_set(register fftw_real *p,
+ fftw_real v,
+ long c)
+{
+ while (c >= 4l)
+ {
+ p[0] = v;
+ p[1] = v;
+ p[2] = v;
+ p[3] = v;
+ p += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ p[0] = v;
+ if (c > 1l)
+ {
+ p[1] = v;
+ if (c > 2l)
+ {
+ p[2] = v;
+ }
+ }
+ }
+}
+
+static inline void fast_complex_set(register fftw_complex *p,
+ fftw_complex v,
+ long c)
+{
+ while (c >= 4l)
+ {
+ p[0] = v;
+ p[1] = v;
+ p[2] = v;
+ p[3] = v;
+ p += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ p[0] = v;
+ if (c > 1l)
+ {
+ p[1] = v;
+ if (c > 2l)
+ {
+ p[2] = v;
+ }
+ }
+ }
+}
+
+
+static inline void fast_real_set_from_short(register fftw_real *d,
+ register short *s,
+ long c)
+{
+ while (c >= 4l)
+ {
+ d[0] = fftw_real(s[0]);
+ d[1] = fftw_real(s[1]);
+ d[2] = fftw_real(s[2]);
+ d[3] = fftw_real(s[3]);
+ d += 4;
+ s += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ d[0] = fftw_real(s[0]);
+ if (c > 1l)
+ {
+ d[1] = fftw_real(s[1]);
+ if (c > 2l)
+ {
+ d[2] = fftw_real(s[2]);
+ }
+ }
+ }
+}
+
+static inline void fast_complex_set_from_short(register fftw_complex *d,
+ register short *s,
+ long c)
+{
+ while (c >= 4l)
+ {
+ d[0].re = fftw_real(s[0]);
+ d[0].im = 0;
+ d[1].re = fftw_real(s[1]);
+ d[1].im = 0;
+ d[2].re = fftw_real(s[2]);
+ d[2].im = 0;
+ d[3].re = fftw_real(s[3]);
+ d[3].im = 0;
+ d += 4;
+ s += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ d[0].re = fftw_real(s[0]);
+ d[0].im = 0;
+ if (c > 1l)
+ {
+ d[1].re = fftw_real(s[1]);
+ d[1].im = 0;
+ if (c > 2l)
+ {
+ d[2].re = fftw_real(s[2]);
+ d[2].im = 0;
+ }
+ }
+ }
+}
+
+
+static inline void fast_real_avg_from_shorts(register fftw_real *d,
+ register short *s1,
+ register short *s2,
+ long c)
+{
+ fftw_real t0, t1, t2, t3;
+ while (c >= 4l)
+ {
+ t0 = (s1[0] + s2[0]) / 2;
+ t1 = (s1[1] + s2[1]) / 2;
+ t2 = (s1[2] + s2[2]) / 2;
+ t3 = (s1[3] + s2[3]) / 2;
+ d[0] = t0;
+ d[1] = t1;
+ d[2] = t2;
+ d[3] = t3;
+ d += 4;
+ s1 += 4;
+ s2 += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ d[0] = fftw_real((s1[0] + s2[0]) / 2);
+ if (c > 1l)
+ {
+ d[1] = fftw_real((s1[1] + s2[1]) / 2);
+ if (c > 2l)
+ {
+ d[2] = fftw_real((s1[2] + s2[2]) / 2);
+ }
+ }
+ }
+}
+
+static inline void fast_complex_avg_from_shorts(register fftw_complex *d,
+ register short *s1,
+ register short *s2,
+ long c)
+{
+ fftw_real t0, t1, t2, t3;
+ while (c >= 4l)
+ {
+ t0 = (s1[0] + s2[0]) / 2;
+ t1 = (s1[1] + s2[1]) / 2;
+ t2 = (s1[2] + s2[2]) / 2;
+ t3 = (s1[3] + s2[3]) / 2;
+ d[0].re = t0;
+ d[0].im = 0;
+ d[1].re = t1;
+ d[1].im = 0;
+ d[2].re = t2;
+ d[2].im = 0;
+ d[3].re = t3;
+ d[3].im = 0;
+ d += 4;
+ s1 += 4;
+ s2 += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ d[0].re = fftw_real((s1[0] + s2[0]) / 2);
+ d[0].im = 0;
+ if (c > 1l)
+ {
+ d[1].re = fftw_real((s1[1] + s2[1]) / 2);
+ d[1].im = 0;
+ if (c > 2l)
+ {
+ d[2].re = fftw_real((s1[2] + s2[2]) / 2);
+ d[2].im = 0;
+ }
+ }
+ }
+}
+
+
+static inline fftw_complex fftw_complex_from_real( fftw_real re )
+{
+ fftw_complex c;
+
+ c.re = re;
+ c.im = 0;
+
+ return c;
+}
+
+static inline void fast_reals_set(register fftw_real *p1,
+ register fftw_real *p2,
+ fftw_real v,
+ long c)
+{
+ while (c >= 4l)
+ {
+ p1[0] = v;
+ p1[1] = v;
+ p1[2] = v;
+ p1[3] = v;
+ p2[0] = v;
+ p2[1] = v;
+ p2[2] = v;
+ p2[3] = v;
+ p1 += 4;
+ p2 += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ p1[0] = v;
+ p2[0] = v;
+ if (c > 1l)
+ {
+ p1[1] = v;
+ p2[1] = v;
+ if (c > 2l)
+ {
+ p1[2] = v;
+ p2[2] = v;
+ }
+ }
+ }
+}
+
+static inline void fast_complex_set(register fftw_complex *p1,
+ register fftw_complex *p2,
+ fftw_complex v,
+ long c)
+{
+ while (c >= 4l)
+ {
+ p1[0] = v;
+ p1[1] = v;
+ p1[2] = v;
+ p1[3] = v;
+ p2[0] = v;
+ p2[1] = v;
+ p2[2] = v;
+ p2[3] = v;
+ p1 += 4;
+ p2 += 4;
+ c -= 4l;
+ }
+
+ if (c > 0l)
+ {
+ p1[0] = v;
+ p2[0] = v;
+ if (c > 1l)
+ {
+ p1[1] = v;
+ p2[1] = v;
+ if (c > 2l)
+ {
+ p1[2] = v;
+ p2[2] = v;
+ }
+ }
+ }
+}
+#endif // FFTW
+
+#endif // INLINES_H
diff --git a/src/jumptotrackdialog.cpp b/src/jumptotrackdialog.cpp
new file mode 100644
index 000000000..42069b91c
--- /dev/null
+++ b/src/jumptotrackdialog.cpp
@@ -0,0 +1,127 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "jumptotrackdialog.h"
+#include "playlistmodel.h"
+
+#include <QStringListModel>
+#include <QSortFilterProxyModel>
+#include <QShortcut>
+#include <QKeySequence>
+
+JumpToTrackDialog::JumpToTrackDialog(QWidget* parent, Qt::WFlags fl)
+: QDialog( parent, fl )
+{
+ setupUi(this);
+ m_playListModel = 0;
+ m_listModel = new QStringListModel(this);
+
+ m_proxyModel = new QSortFilterProxyModel;
+ m_proxyModel->setDynamicSortFilter(true);
+ m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
+ m_proxyModel->setSourceModel(m_listModel);
+ songsListView->setModel(m_proxyModel);
+
+ connect(songsListView,SIGNAL(doubleClicked(const QModelIndex &)),
+ this,SLOT(jumpTo(const QModelIndex&)));
+
+
+ connect(songsListView->selectionModel(),
+ SIGNAL(currentRowChanged(const QModelIndex&,const QModelIndex&)),
+ this,SLOT(queueUnqueue(const QModelIndex&,const QModelIndex&)));
+
+ new QShortcut(QKeySequence("Q"),this,SLOT(on_queuePushButton_clicked()));
+ new QShortcut(QKeySequence("J"),this,SLOT(on_jumpToPushButton_clicked()));
+ new QShortcut(QKeySequence("F5"),this,SLOT(on_refreshPushButton_clicked()));
+}
+
+JumpToTrackDialog::~JumpToTrackDialog()
+{
+}
+
+
+void JumpToTrackDialog::on_closePushButton_clicked()
+{
+ hide();
+}
+
+void JumpToTrackDialog::on_refreshPushButton_clicked()
+{
+ refresh();
+}
+
+void JumpToTrackDialog::on_queuePushButton_clicked()
+{
+ QModelIndexList mi_list = songsListView->selectionModel()->selectedRows();
+ if(!mi_list.isEmpty())
+ {
+ int selected = (m_proxyModel->mapToSource(mi_list.at(0))).row();
+ m_playListModel->setQueued(m_playListModel->item(selected));
+ if(m_playListModel->isQueued(m_playListModel->item(selected)))
+ queuePushButton->setText(tr("Unqueue"));
+ else
+ queuePushButton->setText(tr("Queue"));
+ }
+}
+
+void JumpToTrackDialog::on_jumpToPushButton_clicked()
+{
+ QModelIndexList mi_list = songsListView->selectionModel()->selectedRows();
+ if(!mi_list.isEmpty())
+ {
+ jumpTo(mi_list.at(0));
+ }
+}
+
+void JumpToTrackDialog::refresh()
+{
+ filterLineEdit->clear();
+ QStringList titles = m_playListModel->getTitles(0,m_playListModel->count());
+ m_listModel->setStringList(titles);
+ filterLineEdit->setFocus();
+}
+
+void JumpToTrackDialog::setModel(PlayListModel * model)
+{
+ m_playListModel = model;
+}
+
+void JumpToTrackDialog::on_filterLineEdit_textChanged(const QString &str)
+{
+ m_proxyModel->setFilterFixedString(str);
+}
+
+void JumpToTrackDialog::jumpTo(const QModelIndex & index)
+{
+ int selected = (m_proxyModel->mapToSource(index)).row();
+ m_playListModel->setCurrent(selected);
+ emit playRequest();
+}
+
+void JumpToTrackDialog::queueUnqueue(const QModelIndex& curr,const QModelIndex&)
+{
+ int row = m_proxyModel->mapToSource(curr).row();
+ if(m_playListModel->isQueued(m_playListModel->item(row)))
+ queuePushButton->setText(tr("Unqueue"));
+ else
+ queuePushButton->setText(tr("Queue"));
+}
+
+
diff --git a/src/jumptotrackdialog.h b/src/jumptotrackdialog.h
new file mode 100644
index 000000000..cfe629693
--- /dev/null
+++ b/src/jumptotrackdialog.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef JUMPTOTRACKDIALOG_H
+#define JUMPTOTRACKDIALOG_H
+
+#include <QDialog>
+#include "ui_jumptotrackdialog.h"
+
+/**
+ @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+
+class QStringListModel;
+class PlayListModel;
+class QSortFilterProxyModel;
+
+
+class JumpToTrackDialog : public QDialog, private Ui::JumpToTrackDialog
+{
+ Q_OBJECT
+
+public:
+ JumpToTrackDialog(QWidget* parent = 0, Qt::WFlags fl = 0 );
+ ~JumpToTrackDialog();
+ void setModel(PlayListModel* model);
+ void refresh();
+protected slots:
+ void on_closePushButton_clicked();
+ void on_refreshPushButton_clicked();
+ void on_queuePushButton_clicked();
+ void on_jumpToPushButton_clicked();
+ void on_filterLineEdit_textChanged(const QString&);
+ void jumpTo(const QModelIndex&);
+ void queueUnqueue(const QModelIndex&,const QModelIndex&);
+signals:
+ void playRequest();
+private:
+ PlayListModel* m_playListModel;
+ QStringListModel* m_listModel;
+ QSortFilterProxyModel* m_proxyModel;
+};
+
+#endif
+
diff --git a/src/jumptotrackdialog.ui b/src/jumptotrackdialog.ui
new file mode 100644
index 000000000..1418c54fd
--- /dev/null
+++ b/src/jumptotrackdialog.ui
@@ -0,0 +1,110 @@
+<ui version="4.0" >
+ <class>JumpToTrackDialog</class>
+ <widget class="QDialog" name="JumpToTrackDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>487</width>
+ <height>315</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Jump To Track</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Filter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="filterLineEdit" />
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QListView" name="songsListView" >
+ <property name="editTriggers" >
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="alternatingRowColors" >
+ <bool>true</bool>
+ </property>
+ <property name="selectionBehavior" >
+ <enum>QAbstractItemView::SelectRows</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" >
+ <property name="margin" >
+ <number>0</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="queuePushButton" >
+ <property name="text" >
+ <string>Queue</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="refreshPushButton" >
+ <property name="text" >
+ <string>Refresh</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="jumpToPushButton" >
+ <property name="text" >
+ <string>Jump To</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closePushButton" >
+ <property name="text" >
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/keyboardmanager.cpp b/src/keyboardmanager.cpp
new file mode 100644
index 000000000..5ad32fee7
--- /dev/null
+++ b/src/keyboardmanager.cpp
@@ -0,0 +1,260 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+
+#include <QKeyEvent>
+
+#include "playlist.h"
+#include "playlistmodel.h"
+#include "listwidget.h"
+#include "keyboardmanager.h"
+#include "mainwindow.h"
+
+
+KeyboardManager::KeyboardManager ( PlayList* pl )
+{
+ m_playlist = pl;
+}
+
+bool KeyboardManager::handleKeyPress ( QKeyEvent* ke )
+{
+ bool handled = TRUE;
+ switch ( ke->key() )
+ {
+ case Qt::Key_Up:
+ keyUp ( ke );
+ break;
+ case Qt::Key_Down:
+ keyDown ( ke );
+ break;
+ case Qt::Key_PageUp:
+ keyPgUp ( ke );
+ break;
+ case Qt::Key_PageDown:
+ keyPgDown ( ke );
+ break;
+ case Qt::Key_Enter:
+ case Qt::Key_Return:
+ keyEnter ( ke );
+ default:
+ handled = FALSE;
+ }
+ return handled;
+}
+
+bool KeyboardManager::handleKeyRelease ( QKeyEvent* )
+{
+ return FALSE;
+}
+
+
+
+void KeyboardManager::setModel ( PlayListModel *m )
+{
+ m_playListModel = m;
+}
+
+void KeyboardManager::keyUp ( QKeyEvent * ke )
+{
+ QList<int> rows = m_playListModel->getSelectedRows();
+ ListWidget* list_widget = m_playlist->listWidget();
+
+ if ( rows.count() > 0 )
+ {
+ if(rows[0] == 0 && rows.count() == 1)
+ return;
+
+ if ( ! ( ke->modifiers() & Qt::ShiftModifier || ke->modifiers() & Qt::AltModifier ) )
+ {
+ m_playListModel->clearSelection();
+ list_widget->setAnchorRow(-1);
+ }
+
+ bool select_top = false;
+ int first_visible = list_widget->firstVisibleRow();
+ int last_visible = list_widget->visibleRows() + first_visible - 1;
+ foreach ( int i, rows )
+ {
+ if ( i > last_visible || i < first_visible )
+ {
+ select_top = true;
+ break;
+ }
+ }
+
+ if ( !select_top || ke->modifiers() & Qt::ShiftModifier || ke->modifiers() & Qt::AltModifier )
+ {
+ if ( ke->modifiers() == Qt::AltModifier )
+ {
+ m_playListModel->moveItems ( rows[0],rows[0] - 1 );
+ list_widget->setAnchorRow ( list_widget->getAnchorRow() - 1 );
+ }
+ else
+ {
+ if ( rows.last() > list_widget->getAnchorRow() && ke->modifiers() & Qt::ShiftModifier )
+ {
+ m_playListModel->setSelected ( rows.last(),false );
+ }
+ else if ( rows[0] > 0 )
+ {
+ m_playListModel->setSelected ( rows[0] - 1,true );
+ }
+ else
+ {
+ m_playListModel->setSelected ( rows[0],true );
+ if(list_widget->getAnchorRow() == -1)
+ list_widget->setAnchorRow(rows[0]);
+ }
+
+ if ( ! ( ke->modifiers() & Qt::ShiftModifier ) && rows[0] > 0 )
+ list_widget->setAnchorRow ( rows[0] - 1 );
+ }
+ }
+ else
+ {
+ m_playListModel->setSelected ( list_widget->firstVisibleRow(),true );
+ list_widget->setAnchorRow(list_widget->firstVisibleRow());
+ }
+
+ rows = m_playListModel->getSelectedRows();
+
+ if ( rows[0] < list_widget->firstVisibleRow() && list_widget->firstVisibleRow() > 0 )
+ {
+ int r = rows.last() > list_widget->getAnchorRow() ? rows.last(): rows.first();
+ if(ke->modifiers() & Qt::ShiftModifier && (r >= list_widget->firstVisibleRow() ))
+ ;
+ else
+ list_widget->scroll ( list_widget->firstVisibleRow() - 1 );
+ }
+ }
+ else
+ {
+ //if(list_widget->getAnchorRow() == -1)
+ list_widget->setAnchorRow(list_widget->firstVisibleRow());
+ m_playListModel->setSelected ( list_widget->firstVisibleRow(),true );
+ }
+}
+
+void KeyboardManager::keyDown ( QKeyEvent * ke )
+{
+ QList<int> rows = m_playListModel->getSelectedRows();
+ ListWidget* list_widget = m_playlist->listWidget();
+ //qWarning("count: %d",rows.count());
+ if ( rows.count() > 0 )
+ {
+ if ( ! ( ke->modifiers() & Qt::ShiftModifier || ke->modifiers() & Qt::AltModifier ) )
+ {
+ m_playListModel->clearSelection();
+ list_widget->setAnchorRow(-1);
+ }
+
+ bool select_top = false;
+ int first_visible = list_widget->firstVisibleRow();
+ int last_visible = list_widget->visibleRows() + first_visible - 1;
+ foreach ( int i, rows )
+ {
+ if ( i > last_visible || i < first_visible )
+ {
+ select_top = true;
+ break;
+ }
+ }
+
+ if ( !select_top || ke->modifiers() & Qt::ShiftModifier || ke->modifiers() & Qt::AltModifier )
+ {
+ if ( ke->modifiers() == Qt::AltModifier )
+ {
+ m_playListModel->moveItems ( rows.last(),rows.last() + 1 );
+ list_widget->setAnchorRow ( list_widget->getAnchorRow() + 1 );
+ }
+ else
+ {
+ //qWarning("list_widget %d",list_widget->getAnchorRow());
+ //qWarning("model count: %d rows.last(): %d",m_playListModel->count(),rows.last());
+ if ( rows[0] < list_widget->getAnchorRow() && ke->modifiers() & Qt::ShiftModifier )
+ m_playListModel->setSelected ( rows[0],false );
+ else if ( rows.last() < m_playListModel->count() - 1 )
+ {
+ m_playListModel->setSelected ( rows.last() + 1,true );
+ }
+ else
+ {
+ m_playListModel->setSelected ( rows.last(),true );
+ if(list_widget->getAnchorRow() == -1)
+ list_widget->setAnchorRow(rows.last());
+ }
+
+ if ( ! ( ke->modifiers() & Qt::ShiftModifier ) && rows.last() < m_playListModel->count() - 1 )
+ list_widget->setAnchorRow ( rows.last() + 1 );
+ }
+ }
+ else
+ {
+ m_playListModel->setSelected ( list_widget->firstVisibleRow(),true );
+ list_widget->setAnchorRow(list_widget->firstVisibleRow());
+ }
+
+ rows = m_playListModel->getSelectedRows();
+
+ if ( !rows.isEmpty() && rows.last() >= list_widget->visibleRows() + list_widget->firstVisibleRow() )
+ {
+ int r = rows.first() < list_widget->getAnchorRow() ? rows.first(): rows.last();
+ if(ke->modifiers() & Qt::ShiftModifier &&
+ (r < list_widget->firstVisibleRow() + list_widget->visibleRows() ))
+ ;
+ else
+ list_widget->scroll ( list_widget->firstVisibleRow() + 1 );
+ }
+ }
+ else
+ {
+ m_playListModel->setSelected ( list_widget->firstVisibleRow(),true );
+ //if(list_widget->getAnchorRow() == -1)
+ list_widget->setAnchorRow(list_widget->firstVisibleRow());
+ }
+}
+
+void KeyboardManager::keyPgUp ( QKeyEvent * )
+{
+ ListWidget* list_widget = m_playlist->listWidget();
+ int page_size = list_widget->visibleRows();
+ int offset= ( list_widget->firstVisibleRow()-page_size >= 0 ) ?list_widget->firstVisibleRow()-page_size:0;
+ list_widget->scroll ( offset );
+}
+
+void KeyboardManager::keyPgDown ( QKeyEvent * )
+{
+ ListWidget* list_widget = m_playlist->listWidget();
+ int page_size = list_widget->visibleRows();
+ int offset = ( list_widget->firstVisibleRow() +page_size < m_playListModel->count() ) ?
+ list_widget->firstVisibleRow() +page_size:m_playListModel->count() - 1;
+ list_widget->scroll ( offset );
+}
+
+void KeyboardManager::keyEnter ( QKeyEvent * )
+{
+ QList<int> rows = m_playListModel->getSelectedRows();
+ MainWindow* mw = qobject_cast<MainWindow*> ( m_playlist->parentWidget() );
+ if ( mw && rows.count() > 0 )
+ {
+ m_playListModel->setCurrent ( rows[0] );
+ mw->replay();
+ }
+}
diff --git a/src/keyboardmanager.h b/src/keyboardmanager.h
new file mode 100644
index 000000000..2b33b5ef9
--- /dev/null
+++ b/src/keyboardmanager.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+
+#ifndef _KEYBOARDMANAGER_H
+#define _KEYBOARDMANAGER_H
+
+class PlayList;
+class PlayListModel;
+class QKeyEvent;
+
+
+/*!
+ * Class \b KeyboardManager represents key handler object that processes
+ * all key events passed to the \b PlayList
+ * @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+class KeyboardManager
+{
+ public:
+ /*!
+ * Constructor. Takes \b PlayList object as an argument.
+ */
+ KeyboardManager ( PlayList* );
+
+ /*!
+ * Handles key press events from \b PlayList object. Returns \b TRUE
+ * if the key was handled, otherwise \b FALSE.
+ */
+ bool handleKeyPress ( QKeyEvent* );
+
+ /*!
+ * Handles key release events from \b PlayList object. Returns \b TRUE
+ * if the key was handled, otherwise \b FALSE.
+ */
+ bool handleKeyRelease ( QKeyEvent* );
+
+ /*!
+ * Inits the \b KeyboardManager object with data model.
+ */
+ void setModel ( PlayListModel* );
+ protected:
+ void keyUp ( QKeyEvent* ke );
+ void keyDown ( QKeyEvent* ke );
+ void keyPgUp ( QKeyEvent* ke );
+ void keyPgDown ( QKeyEvent* ke );
+ void keyEnter ( QKeyEvent* ke );
+ private:
+ PlayList* m_playlist;
+ PlayListModel* m_playListModel;
+};
+
+#endif
+
diff --git a/src/listwidget.cpp b/src/listwidget.cpp
new file mode 100644
index 000000000..a21508b5f
--- /dev/null
+++ b/src/listwidget.cpp
@@ -0,0 +1,482 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPixmap>
+#include <QResizeEvent>
+#include <QPainter>
+#include <QFont>
+#include <QFontMetrics>
+#include <QSettings>
+#include <QMenu>
+#include <QUrl>
+#include <QApplication>
+
+#include "mediafile.h"
+#include "textscroller.h"
+#include "listwidget.h"
+#include "skin.h"
+#include "playlistmodel.h"
+#include "playlist.h"
+
+#define INVALID_ROW -1
+
+ListWidget::ListWidget(QWidget *parent)
+ : QWidget(parent)
+{
+ m_update = FALSE;
+ m_skin = Skin::getPointer();
+ loadColors();
+ setWindowFlags(Qt::FramelessWindowHint);
+ m_menu = new QMenu(this);
+ m_scroll_direction = NONE;
+ m_prev_y = 0;
+ m_anchor_row = INVALID_ROW;
+
+ m_first = 0;
+ m_rows = 0;
+ m_scroll = FALSE;
+ m_select_on_release = FALSE;
+ readSettings();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+ setAcceptDrops(true);
+}
+
+
+ListWidget::~ListWidget()
+{}
+
+void ListWidget::readSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ QString fontname = settings.value("PlayList/Font","").toString();
+ if (fontname.isEmpty ())
+ fontname = QFont("Helvetica [Cronyx]", 10).toString();
+ m_font.fromString(fontname);
+
+ if (m_update)
+ {
+ delete m_metrics;
+ m_metrics = new QFontMetrics(m_font);
+ m_rows = (height() - 10) / m_metrics->ascent ();
+ updateList();
+ }
+ else
+ {
+ m_update = TRUE;
+ m_metrics = new QFontMetrics(m_font);
+ }
+}
+
+void ListWidget::loadColors()
+{
+ m_normal.setNamedColor(m_skin->getPLValue("normal"));
+ m_current.setNamedColor(m_skin->getPLValue("current"));
+ m_normal_bg.setNamedColor(m_skin->getPLValue("normalbg"));
+ m_selected_bg.setNamedColor(m_skin->getPLValue("selectedbg"));
+}
+
+void ListWidget::paintEvent(QPaintEvent *)
+{
+
+ QPainter m_painter(this);
+ //m_painter.setPen(Qt::white);
+ m_painter.setFont(m_font);
+ m_painter.setBrush(QBrush(m_normal_bg));
+ m_painter.drawRect(-1,-1,width()+1,height()+1);
+
+
+
+ for (int i=0; i<m_titles.size(); ++i )
+ {
+ if (m_model->isSelected(i + m_first))
+ {
+ m_painter.setBrush(QBrush(m_selected_bg));
+ m_painter.setPen(m_selected_bg);
+ m_painter.drawRect ( 6, 15+(i-1)*m_metrics->ascent(),
+ width() - 10, m_metrics->ascent());
+ }
+
+ if (m_model->currentRow() == i + m_first)
+ m_painter.setPen(m_current);
+ else
+ m_painter.setPen(m_normal); //243,58
+
+ m_painter.drawText(10,14+i*m_metrics->ascent(),m_titles.at(i));
+
+ if (m_model->isQueued(m_model->item(i + m_first)))
+ {
+ QString queue_string = "|" +
+ QString::number(1 + m_model->queuedIndex(m_model->item(m_first + i))) + "|";
+
+ int old_size = m_font.pointSize();
+ m_font.setPointSize(old_size - 1 );
+ m_painter.setFont(m_font);
+
+ m_painter.drawText(width() - 10 - m_metrics->width(queue_string) - m_metrics->width(m_times.at(i)), 12+i*m_metrics->ascent (), queue_string);
+
+ m_font.setPointSize(old_size);
+ m_painter.setFont(m_font);
+
+
+ m_painter.setBrush(QBrush(Qt::transparent));
+ //m_painter.drawRect(width() - 10 - m_metrics->width(queue_string) - m_metrics->width(m_times.at(i)),
+ // /*14+*/i*m_metrics->ascent () + 3,10,12);
+ m_painter.setBrush(QBrush(m_normal_bg));
+ }
+
+
+ m_painter.drawText(width() - 7 - m_metrics->width(m_times.at(i)),
+ 14+i*m_metrics->ascent (), m_times.at(i));
+
+ }
+
+}
+
+void ListWidget::mouseDoubleClickEvent (QMouseEvent *e)
+{
+ int y = e->y();
+ int row = rowAt(y);
+ if (INVALID_ROW != row)
+ {
+ m_model->setCurrent(row);
+ emit selectionChanged();
+ update();
+ }
+}
+
+
+void ListWidget::mousePressEvent(QMouseEvent *e)
+{
+ m_scroll = TRUE;
+ int y = e->y();
+ int row = rowAt(y);
+
+ if (INVALID_ROW != row && m_model->count() > row)
+ {
+ if (!(Qt::ControlModifier & e->modifiers () ||
+ Qt::ShiftModifier & e->modifiers () ||
+ m_model->isSelected(row)))
+ m_model->clearSelection();
+
+ if (m_model->isSelected(row) && (e->modifiers() == Qt::NoModifier))
+ m_select_on_release = TRUE;
+
+ //qWarning("m_prev_clicked_row: %d",m_prev_clicked_row);
+
+ m_pressed_row = row;
+ if ((Qt::ShiftModifier & e->modifiers()))
+ {
+
+ if(m_pressed_row > m_anchor_row)
+ {
+ //int upper_selected = m_model->firstSelectedUpper(m_anchor_row);
+ //if (INVALID_ROW != upper_selected)
+ //{
+ /*for (int j = upper_selected;j < m_anchor_row;j++)
+ {
+ m_model->setSelected(j, false);
+ }*/
+ m_model->clearSelection();
+ for (int j = m_anchor_row;j <= m_pressed_row;j++)
+ {
+ m_model->setSelected(j, true);
+ }
+ //}
+ }
+ else
+ {
+ m_model->clearSelection();
+ for (int j = m_anchor_row;j >= m_pressed_row;j--)
+ {
+ m_model->setSelected(j, true);
+ }
+ }
+
+ /*
+ int upper_selected = m_model->firstSelectedUpper(row);
+ int lower_selected = m_model->firstSelectedLower(row);
+ if (INVALID_ROW != upper_selected)
+ {
+ for (int j = upper_selected;j <= row;j++)
+ {
+ m_model->setSelected(j, true);
+ }
+ }
+ else if (INVALID_ROW != lower_selected)
+ {
+ for (int j = row;j <= lower_selected;j++)
+ {
+ m_model->setSelected(j, true);
+ }
+ }
+ else
+ m_model->setSelected(row, true);
+ */
+ }
+ else
+ {
+ if (!m_model->isSelected(row) || (Qt::ControlModifier & e->modifiers()))
+ m_model->setSelected(row, !m_model->isSelected(row));
+ }
+
+ if(m_model->getSelection(m_pressed_row).count() == 1)
+ m_anchor_row = m_pressed_row;
+ //qWarning("m_anchor_row: %d",m_anchor_row);
+
+ update();
+ }
+ QWidget::mousePressEvent(e);
+}
+
+void ListWidget::resizeEvent(QResizeEvent *e)
+{
+ m_rows = (e->size().height() - 10) / m_metrics->ascent ();
+
+ m_scroll = TRUE;
+
+ updateList();
+ QWidget::resizeEvent(e);
+}
+
+void ListWidget::wheelEvent (QWheelEvent *e)
+{
+ if (m_model->count() <= m_rows)
+ return;
+ if ((m_first == 0 && e->delta() > 0) ||
+ ((m_first == m_model->count() - m_rows) && e->delta() < 0))
+ return;
+ m_first -= e->delta()/40; //40*3 TODO: add step to config
+ if (m_first < 0)
+ m_first = 0;
+
+ if (m_first > m_model->count() - m_rows)
+ m_first = m_model->count() - m_rows;
+
+ m_scroll = FALSE;
+ updateList();
+}
+
+void ListWidget::updateList()
+{
+ if (m_model->count() < (m_rows+m_first+1) && m_rows< m_model->count())
+ {
+ m_first = m_model->count() - m_rows;
+ }
+ if (m_model->count() < m_rows + 1)
+ {
+ m_first = 0;
+ emit positionChanged(0,0);
+ }
+ else
+ {
+ //int pos = m_first*99/(m_model->count() - m_rows);
+ //emit positionChanged(pos);
+ emit positionChanged(m_first, m_model->count() - m_rows);
+ }
+ if (m_model->count() <= m_first)
+ {
+ m_first = 0;
+ emit positionChanged(0, qMax(0, m_model->count() - m_rows));
+ }
+
+ m_titles = m_model->getTitles(m_first, m_rows );
+ m_times = m_model->getTimes(m_first, m_rows );
+ m_scroll = FALSE;
+ //add numbers
+ for (int i = 0; i < m_titles.size(); ++i)
+ {
+ QString title = m_titles.at(i);
+ m_titles.replace(i, title.prepend(QString("%1").arg(m_first+i+1)+". "));
+
+ }
+ if (m_model->currentItem())
+ {
+ TextScroller::getPointer()->setText("*** "+m_model->currentItem()->title());
+ parentWidget()->parentWidget()->setWindowTitle(m_model->currentItem()->title());
+ }
+ cut();
+ update();
+}
+
+void ListWidget::setModel(PlayListModel *model)
+{
+ m_model = model;
+ connect (m_model, SIGNAL(listChanged()), SLOT(updateList()));
+ connect (m_model, SIGNAL(currentChanged()), SLOT(recenterCurrent()));
+ updateList();
+}
+
+void ListWidget::scroll(int sc)
+{
+ if (m_model->count() <= m_rows)
+ return;
+ m_first = sc; //*(m_model->count() - m_rows)/99;
+ m_scroll = TRUE;
+ updateList();
+}
+
+void ListWidget::cut()
+{
+ bool cut;
+ for (int i=0; i<m_titles.size(); ++i )
+ {
+ QString name;
+ cut = FALSE;
+
+ int queue_number_space = 0;
+ if (m_model->isQueued(m_model->item(i + m_first)))
+ {
+ int index = m_model->queuedIndex(m_model->item(m_first + i));
+ QString queue_string = "|"+QString::number(index)+"|";
+ queue_number_space = m_metrics->width(queue_string);
+ }
+ while ( m_metrics->width(m_titles.at(i)) > (this->width() - 54 - queue_number_space))
+ {
+ cut = TRUE;
+ name = m_titles.at(i);
+ m_titles.replace(i, name.left(name.length()-1) );
+ }
+ if (cut)
+ {
+ m_titles.replace(i, name.left(name.length()-3).trimmed()+"...");
+
+ }
+ }
+}
+
+void ListWidget::updateSkin()
+{
+ loadColors();
+ update();
+}
+
+void ListWidget::dragEnterEvent(QDragEnterEvent *event)
+{
+ if (event->mimeData()->hasFormat("text/uri-list"))
+ event->acceptProposedAction();
+}
+
+
+void ListWidget::dropEvent(QDropEvent *event)
+{
+ if (event->mimeData()->hasUrls())
+ {
+ QList<QUrl> list_urls = event->mimeData()->urls();
+ event->acceptProposedAction();
+ QApplication::restoreOverrideCursor();
+
+ foreach(QUrl u,list_urls)
+ {
+ QString add_string = u.toString(QUrl::RemoveScheme);
+ if (!add_string.isEmpty())
+ processFileInfo(QFileInfo(add_string));
+ }
+ }
+
+}
+
+void ListWidget::processFileInfo(const QFileInfo& info)
+{
+ if (info.isDir())
+ {
+ m_model->addDirectory(info.absoluteFilePath());
+ }
+ else
+ {
+ m_model->addFile(info.absoluteFilePath());
+ }
+}
+
+void ListWidget::mouseMoveEvent(QMouseEvent *e)
+{
+ m_scroll = true;
+ if (m_prev_y > e->y())
+ m_scroll_direction = TOP;
+ else if (m_prev_y < e->y())
+ m_scroll_direction = DOWN;
+ else
+ m_scroll_direction = NONE;
+
+ int row = rowAt(e->y());
+
+ if (INVALID_ROW != row)
+ {
+ SimpleSelection sel = m_model->getSelection(m_pressed_row);
+ if ((sel.m_top == 0 && m_scroll_direction == TOP) && sel.count() > 1 ||
+ (sel.m_bottom == m_model->count() - 1 && m_scroll_direction == DOWN && sel.count() > 1)
+ )
+ return;
+
+ if (row + 1 == m_first + m_rows && m_scroll_direction == DOWN)
+ (m_first + m_rows < m_model->count() ) ? m_first ++ : m_first;
+ else if (row == m_first && m_scroll_direction == TOP)
+ (m_first > 0) ? m_first -- : 0;
+
+ m_model->moveItems(m_pressed_row,row);
+ m_prev_y = e->y();
+ m_scroll = false;
+ m_pressed_row = row;
+ }
+}
+
+void ListWidget::mouseReleaseEvent(QMouseEvent *e)
+{
+ if (FALSE != m_select_on_release)
+ {
+ m_model->clearSelection();
+ m_model->setSelected(m_pressed_row,true);
+ //if(e->modifiers() != Qt::ShiftModifier)
+ m_anchor_row = m_pressed_row;
+ m_select_on_release = FALSE;
+ }
+ m_pressed_row = INVALID_ROW;
+ m_scroll_direction = NONE;
+ QWidget::mouseReleaseEvent(e);
+}
+
+int ListWidget::rowAt( int y) const
+{
+ for (int i = 0; i < qMin(m_rows, m_model->count() - m_first); ++i )
+ {
+ if ((y >= 2+i*m_metrics->ascent())&&(y < 2+(i+1)*m_metrics->ascent()))
+ return m_first + i;
+ }
+ return INVALID_ROW;
+}
+
+
+void ListWidget::contextMenuEvent(QContextMenuEvent * event)
+{
+ if (menu())
+ menu()->exec(event->globalPos());
+}
+
+void ListWidget::recenterCurrent()
+{
+ if (!m_scroll)
+ {
+ if (m_first + m_rows < m_model->currentRow() + 1)
+ m_first = qMin(m_model->count() - m_rows,
+ m_model->currentRow() - m_rows/2 + 1);
+ else if (m_first > m_model->currentRow())
+ m_first = qMax (m_model->currentRow() - m_rows/2 + 1, 0);
+ }
+}
+
+
diff --git a/src/listwidget.h b/src/listwidget.h
new file mode 100644
index 000000000..2f20a3bf8
--- /dev/null
+++ b/src/listwidget.h
@@ -0,0 +1,126 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef LISTWIDGET_H
+#define LISTWIDGET_H
+
+#include <QWidget>
+#include <QDir>
+#include <QContextMenuEvent>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class QFont;
+class QFontMetrics;
+class QMenu;
+class QAction;
+
+class PlayList;
+class PlayListModel;
+class Skin;
+class MediaFile;
+
+class ListWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ ListWidget(QWidget *parent = 0);
+
+ ~ListWidget();
+
+ void setModel(PlayListModel *);
+ void readSettings();
+ /*!
+ * Returns count of currently visible rows.
+ */
+ int visibleRows()const{return m_rows;}
+
+ /*!
+ * Returns number of first visible row.
+ */
+ int firstVisibleRow()const{return m_first;}
+
+ int getAnchorRow()const{return m_anchor_row;}
+
+ void setAnchorRow(int r){m_anchor_row = r;}
+
+public slots:
+ void updateList();
+ void scroll(int); //0-99
+ void recenterCurrent();
+
+ QMenu *menu()
+ {
+ return m_menu;
+ };
+
+signals:
+ void selectionChanged();
+ void positionChanged(int, int); //current position, maximum value
+
+protected:
+ void paintEvent(QPaintEvent *);
+ void mouseDoubleClickEvent(QMouseEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void resizeEvent(QResizeEvent *);
+ void wheelEvent(QWheelEvent *);
+ int rowAt(int)const;
+ void dragEnterEvent(QDragEnterEvent *event);
+ void dropEvent(QDropEvent *event);
+ void contextMenuEvent ( QContextMenuEvent * event );
+
+private slots:
+ void updateSkin();
+
+private:
+ void cut();
+ void loadColors();
+ void processFileInfo(const QFileInfo&);
+ bool m_update;
+ bool m_scroll;
+ int m_pressed_row;
+ QMenu *m_menu;
+ PlayListModel *m_model;
+ int m_rows, m_first;
+ QList <QString> m_titles;
+ QList <QString> m_times;
+ PlayList *m_pl;
+ QFont m_font;
+ QFontMetrics *m_metrics;
+ Skin *m_skin;
+ QColor m_normal, m_current, m_normal_bg, m_selected_bg;
+ int m_anchor_row;
+
+ enum ScrollDirection
+ {
+ NONE = 0,TOP,DOWN
+ };
+
+ /*!
+ * Scroll direction that is preforming in current moment.
+ */
+ ScrollDirection m_scroll_direction;
+ int m_prev_y;
+ bool m_select_on_release;
+};
+
+#endif
diff --git a/src/logscale.cpp b/src/logscale.cpp
new file mode 100644
index 000000000..921004fd9
--- /dev/null
+++ b/src/logscale.cpp
@@ -0,0 +1,74 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#include "logscale.h"
+
+#include <math.h>
+#include <stdio.h>
+
+
+LogScale::LogScale(int maxscale, int maxrange)
+ : indices(0), s(0), r(0)
+{
+ setMax(maxscale, maxrange);
+}
+
+
+LogScale::~LogScale()
+{
+ if (indices)
+ delete [] indices;
+}
+
+
+void LogScale::setMax(int maxscale, int maxrange)
+{
+ if (maxscale == 0 || maxrange == 0)
+ return;
+
+ s = maxscale;
+ r = maxrange;
+
+ if (indices)
+ delete [] indices;
+
+ double alpha;
+ int i, scaled;
+ double domain = double(maxscale),
+ range = double(maxrange),
+ x = 1.0,
+ dx = 1.0,
+ y = 0.0,
+ yy = 0.0,
+ t = 0.0,
+ e4 = double(1.0E-8);
+
+ indices = new int[maxrange];
+ for (i = 0; i < maxrange; i++)
+ indices[i] = 0;
+
+ // initialize log scale
+ while (fabs(dx) > e4) {
+ t = log((domain + x) / x);
+ y = (x * t) - range;
+ yy = t - (domain / (x + domain));
+ dx = y / yy;
+ x -= dx;
+ }
+
+ alpha = x;
+ for (i = 1; i < (int) domain; i++) {
+ scaled = (int) floor(0.5 + (alpha * log((double(i) + alpha) / alpha)));
+ if (indices[scaled - 1] < i)
+ indices[scaled - 1] = i;
+ }
+}
+
+
+int LogScale::operator[](int index)
+{
+ return indices[index];
+}
diff --git a/src/logscale.h b/src/logscale.h
new file mode 100644
index 000000000..d74d25207
--- /dev/null
+++ b/src/logscale.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com>
+//
+// Use, modification and distribution is allowed without limitation,
+// warranty, or liability of any kind.
+//
+
+#ifndef __logscale_h
+#define __logscale_h
+
+
+class LogScale
+{
+public:
+ LogScale(int = 0, int = 0);
+ ~LogScale();
+
+ int scale() const { return s; }
+ int range() const { return r; }
+
+ void setMax(int, int);
+
+ int operator[](int);
+
+
+private:
+ int *indices;
+ int s, r;
+};
+
+
+#endif // __logscale_h
diff --git a/src/mainvisual.cpp b/src/mainvisual.cpp
new file mode 100644
index 000000000..1376457b4
--- /dev/null
+++ b/src/mainvisual.cpp
@@ -0,0 +1,921 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QTimer>
+#include <QSettings>
+#include <QPainter>
+#include <buffer.h>
+#include <constants.h>
+#include <output.h>
+#include <math.h>
+
+#include "skin.h"
+#include "fft.h"
+#include "inlines.h"
+#include "mainvisual.h"
+
+
+MainVisual *MainVisual::pointer = 0;
+
+MainVisual *MainVisual::getPointer()
+{
+ if ( !pointer )
+ qFatal ( "MainVisual: this object not created!" );
+ return pointer;
+}
+
+MainVisual::MainVisual ( QWidget *parent, const char *name )
+ : QWidget ( parent ), vis ( 0 ), playing ( FALSE ), fps ( 20 )
+{
+ //setVisual(new MonoScope);
+ setVisual ( new StereoAnalyzer );
+ timer = new QTimer ( this );
+ connect ( timer, SIGNAL ( timeout() ), this, SLOT ( timeout() ) );
+ timer->start ( 1000 / fps );
+ nodes.clear();
+ pointer = this;
+
+}
+
+MainVisual::~MainVisual()
+{
+ delete vis;
+ vis = 0;
+}
+
+/*void MainVisual::setVisual( const QString &visualname )
+{
+ VisualBase *newvis = 0;
+
+ if ( visualname == "Mono Scope" )
+ newvis = new MonoScope;
+ else if (visualname == "Stereo Scope" )
+ newvis = new StereoScope;
+#ifdef FFTW
+ else if ( visualname == "Mono Analyzer" )
+ newvis = new MonoAnalyzer;
+ else if ( visualname == "Stereo Analyzer" )
+ newvis = new StereoAnalyzer;
+ else if ( visualname == "Mono Topograph" )
+ newvis = new MonoTopograph;
+ else if ( visualname == "Stereo Topograph" )
+ newvis = new StereoTopograph;
+ else if ( visualname == "Stereo Spectroscope" )
+ newvis = new StereoSpectroscope;
+ else if ( visualname == "Mono Spectroscope" )
+ newvis = new MonoSpectroscope;
+#endif // FFTW
+
+ setVisual( newvis );
+}*/
+
+void MainVisual::setVisual ( VisualBase *newvis )
+{
+ //delete vis;
+
+ vis = newvis;
+ if ( vis )
+ vis->resize ( size() );
+
+ // force an update
+ //timer->stop();
+// timer->start( 1000 / fps );
+}
+
+void MainVisual::configChanged ( QSettings &settings )
+{
+ /*QString newvis = settings.readEntry( "/MQ3/Visual/mode", "Mono Analyzer" );
+ setVisual( newvis );*/
+
+ //fps = settings.readNumEntry("/MQ3/Visual/frameRate", 20);
+ fps = 20;
+ timer->stop();
+ timer->start ( 1000 / fps );
+ if ( vis )
+ vis->configChanged ( settings );
+}
+
+void MainVisual::prepare()
+{
+ //nodes.setAutoDelete(TRUE);
+ nodes.clear(); //TODO memory leak??
+ //nodes.setAutoDelete(FALSE);
+}
+
+void MainVisual::add ( Buffer *b, unsigned long w, int c, int p )
+{
+ long len = b->nbytes, cnt;
+ short *l = 0, *r = 0;
+
+ len /= c;
+ len /= ( p / 8 );
+ if ( len > 512 )
+ len = 512;
+ //len = 512;
+ cnt = len;
+
+ if ( c == 2 )
+ {
+ l = new short[len];
+ r = new short[len];
+
+ if ( p == 8 )
+ stereo16_from_stereopcm8 ( l, r, b->data, cnt );
+ else if ( p == 16 )
+ stereo16_from_stereopcm16 ( l, r, ( short * ) b->data, cnt );
+ }
+ else if ( c == 1 )
+ {
+ l = new short[len];
+
+ if ( p == 8 )
+ mono16_from_monopcm8 ( l, b->data, cnt );
+ else if ( p == 16 )
+ mono16_from_monopcm16 ( l, ( short * ) b->data, cnt );
+ }
+ else
+ len = 0;
+
+ nodes.append ( new VisualNode ( l, r, len, w ) );
+}
+
+void MainVisual::timeout()
+{
+ VisualNode *node = 0;
+
+ if ( /*playing &&*/ output() )
+ {
+ //output()->mutex()->lock ();
+ //long olat = output()->latency();
+ //long owrt = output()->written();
+ //output()->mutex()->unlock();
+
+ //long synctime = owrt < olat ? 0 : owrt - olat;
+
+ mutex()->lock ();
+ VisualNode *prev = 0;
+ while ( ( !nodes.isEmpty() ) )
+ {
+ node = nodes.first();
+ /*if ( node->offset > synctime )
+ break;*/
+
+ if ( prev )
+ delete prev;
+ nodes.removeFirst();
+
+ prev = node;
+
+ }
+ mutex()->unlock();
+ node = prev;
+ }
+
+ bool stop = TRUE;
+ if ( vis )
+ stop = vis->process ( node );
+ delete node;
+
+ if ( vis )
+ {
+ pixmap.fill ( Qt::transparent );
+ QPainter p ( &pixmap );
+ //vis->draw( &p, backgroundColor() );
+ vis->draw ( &p, "Green" );
+ }
+ else
+ //pixmap.fill( backgroundColor() );
+ pixmap.fill ( "Red" );
+ //bitBlt(this, 0, 0, &pixmap);
+// QPainter painter(this);
+// painter.drawPixmap(0,0,pixmap);
+ update();
+ /*if (! playing && stop)
+ timer->stop();*/
+}
+
+void MainVisual::paintEvent ( QPaintEvent * )
+{
+ //bitBlt(this, 0, 0, &pixmap);
+ QPainter painter ( this );
+ painter.drawPixmap ( 0,0,pixmap );
+}
+
+void MainVisual::resizeEvent ( QResizeEvent *event )
+{
+ pixmap = QPixmap ( event->size() );
+ /*pixmap.resize(event->size());
+ pixmap.fill(backgroundColor());
+ QWidget::resizeEvent( event );*/
+ if ( vis )
+ vis->resize ( size() );
+}
+
+/*void MainVisual::customEvent(QCustomEvent *event)
+{
+ switch (event->type()) {
+ case OutputEvent::Playing:
+ playing = TRUE;
+ // fall through intended
+
+ case OutputEvent::Info:
+ case OutputEvent::Buffering:
+ case OutputEvent::Paused:
+ if (! timer->isActive())
+ timer->start(1000 / fps);
+
+ break;
+
+ case OutputEvent::Stopped:
+ case OutputEvent::Error:
+ playing = FALSE;
+ break;
+
+ default:
+ ;
+ }
+}*/
+
+QStringList MainVisual::visuals()
+{
+ QStringList list;
+ list << "Stereo Scope";
+ list << "Mono Scope";
+#ifdef FFTW
+ list << "Stereo Analyzer";
+ list << "Mono Analyzer";
+ list << "Stereo Topograph";
+ list << "Mono Topograph";
+ list << "Stereo Spectroscope";
+ list << "Mono Spectroscope";
+#endif // FFTW
+
+ return list;
+}
+
+
+MonoScope::MonoScope()
+{}
+
+MonoScope::~MonoScope()
+{}
+
+bool MonoScope::process ( VisualNode *node )
+{
+ bool allZero = TRUE;
+ int i;
+ long s, index, indexTo, step = 512 / size.width();
+ double *magnitudesp = magnitudes.data();
+ double val, tmp;
+
+ if ( node )
+ {
+
+ index = 0;
+ for ( i = 0; i < size.width(); i++ )
+ {
+ indexTo = index + step;
+
+ if ( rubberband )
+ {
+ val = magnitudesp[ i ];
+ if ( val < 0. )
+ {
+ val += falloff;
+ if ( val > 0. )
+ val = 0.;
+ }
+ else
+ {
+ val -= falloff;
+ if ( val < 0. )
+ val = 0.;
+ }
+ }
+ else
+ val = 0.;
+
+ for ( s = index; s < indexTo && s < node->length; s++ )
+ {
+ tmp = ( double ( node->left[s] ) +
+ ( node->right ? double ( node->right[s] ) : 0 ) *
+ double ( size.height() / 2 ) ) / 65536.;
+ if ( tmp > 0 )
+ val = ( tmp > val ) ? tmp : val;
+ else
+ val = ( tmp < val ) ? tmp : val;
+ }
+
+ if ( val != 0. )
+ allZero = FALSE;
+ magnitudesp[ i ] = val;
+ index = indexTo;
+ }
+ }
+ else if ( rubberband )
+ {
+ for ( i = 0; i < size.width(); i++ )
+ {
+ val = magnitudesp[ i ];
+ if ( val < 0 )
+ {
+ val += 2;
+ if ( val > 0. )
+ val = 0.;
+ }
+ else
+ {
+ val -= 2;
+ if ( val < 0. )
+ val = 0.;
+ }
+
+ if ( val != 0. )
+ allZero = FALSE;
+ magnitudesp[ i ] = val;
+ }
+ }
+ else
+ {
+ for ( i = 0; i < size.width(); i++ )
+ magnitudesp[ i ] = 0.;
+ }
+
+ return allZero;
+}
+
+void MonoScope::draw ( QPainter *p, const QColor &back )
+{
+ double *magnitudesp = magnitudes.data();
+ double r, g, b, per;
+
+ p->fillRect ( 0, 0, size.width(), size.height(), back );
+ for ( int i = 1; i < size.width(); i++ )
+ {
+ per = double ( magnitudesp[ i ] ) /
+ double ( size.height() / 4 );
+ if ( per < 0.0 )
+ per = -per;
+ if ( per > 1.0 )
+ per = 1.0;
+ else if ( per < 0.0 )
+ per = 0.0;
+
+ r = startColor.red() + ( targetColor.red() -
+ startColor.red() ) * ( per * per );
+ g = startColor.green() + ( targetColor.green() -
+ startColor.green() ) * ( per * per );
+ b = startColor.blue() + ( targetColor.blue() -
+ startColor.blue() ) * ( per * per );
+
+ if ( r > 255.0 )
+ r = 255.0;
+ else if ( r < 0.0 )
+ r = 0;
+
+ if ( g > 255.0 )
+ g = 255.0;
+ else if ( g < 0.0 )
+ g = 0;
+
+ if ( b > 255.0 )
+ b = 255.0;
+ else if ( b < 0.0 )
+ b = 0;
+
+ p->setPen ( QColor ( int ( r ), int ( g ), int ( b ) ) );
+ p->drawLine ( i - 1, size.height() / 2 + int ( magnitudesp[ i - 1 ] ),
+ i, int ( size.height() / 2 + magnitudesp[ i ] ) );
+ //qDebug("draw %d", int(magnitudesp[ i ]));
+ }
+}
+
+StereoScope::StereoScope()
+ : rubberband ( true ), falloff ( 1.0 ), fps ( 20 )
+{
+ startColor = Qt::white;
+
+ /*val = settings.readEntry("/MQ3/Scope/targetColor");
+ if (! val.isEmpty())
+ targetColor = QColor(val);
+ else*/
+ targetColor = Qt::red;
+}
+
+StereoScope::~StereoScope()
+{}
+
+void StereoScope::resize ( const QSize &newsize )
+{
+ size = newsize;
+
+ uint os = magnitudes.size();
+ magnitudes.resize ( size.width() * 2 );
+ for ( ; os < magnitudes.size(); os++ )
+ magnitudes[os] = 0.0;
+}
+
+void StereoScope::configChanged ( QSettings &settings )
+{
+ QString val;
+
+ // need the framerate for the fall off speed
+ //fps = settings.readNumEntry("/MQ3/Visual/frameRate", 20);
+ fps = 20;
+ /*val = settings.readEntry("/MQ3/Scope/startColor");
+ if (! val.isEmpty())
+ startColor = QColor(val);
+ else*/
+ startColor = Qt::green;
+
+ /*val = settings.readEntry("/MQ3/Scope/targetColor");
+ if (! val.isEmpty())
+ targetColor = QColor(val);
+ else*/
+ targetColor = Qt::red;
+
+ //rubberband = settings.readBoolEntry( "/MQ3/Scope/enableRubberBand", true );
+ rubberband = TRUE;
+ //val = settings.readEntry( "/MQ3/Scope/fallOffSpeed", "Normal" );
+ val == "Normal";
+ if ( val == "Slow" )
+ falloff = .125;
+ else if ( val == "Fast" )
+ falloff = .5;
+ else
+ falloff = .25;
+
+ falloff *= ( 80. / double ( fps ) );
+
+ resize ( size );
+}
+
+bool StereoScope::process ( VisualNode *node )
+{
+ bool allZero = TRUE;
+ int i;
+ long s, index, indexTo, step = 256 / size.width();
+ double *magnitudesp = magnitudes.data();
+ double valL, valR, tmpL, tmpR;
+
+ if ( node )
+ {
+ index = 0;
+ for ( i = 0; i < size.width(); i++ )
+ {
+ indexTo = index + step;
+
+ if ( rubberband )
+ {
+ valL = magnitudesp[ i ];
+ valR = magnitudesp[ i + size.width() ];
+ if ( valL < 0. )
+ {
+ valL += falloff;
+ if ( valL > 0. )
+ valL = 0.;
+ }
+ else
+ {
+ valL -= falloff;
+ if ( valL < 0. )
+ valL = 0.;
+ }
+ if ( valR < 0. )
+ {
+ valR += falloff;
+ if ( valR > 0. )
+ valR = 0.;
+ }
+ else
+ {
+ valR -= falloff;
+ if ( valR < 0. )
+ valR = 0.;
+ }
+ }
+ else
+ valL = valR = 0.;
+
+ for ( s = index; s < indexTo && s < node->length; s++ )
+ {
+ tmpL = ( ( node->left ?
+ double ( node->left[s] ) : 0. ) *
+ double ( size.height() / 4 ) ) / 32768.;
+ tmpR = ( ( node->right ?
+ double ( node->right[s] ) : 0. ) *
+ double ( size.height() / 4 ) ) / 32768.;
+ if ( tmpL > 0 )
+ valL = ( tmpL > valL ) ? tmpL : valL;
+ else
+ valL = ( tmpL < valL ) ? tmpL : valL;
+ if ( tmpR > 0 )
+ valR = ( tmpR > valR ) ? tmpR : valR;
+ else
+ valR = ( tmpR < valR ) ? tmpR : valR;
+ }
+
+ if ( valL != 0. || valR != 0. )
+ allZero = FALSE;
+
+ magnitudesp[ i ] = valL;
+ magnitudesp[ i + size.width() ] = valR;
+
+ index = indexTo;
+ }
+ }
+ else if ( rubberband )
+ {
+ for ( i = 0; i < size.width(); i++ )
+ {
+ valL = magnitudesp[ i ];
+ if ( valL < 0 )
+ {
+ valL += 2;
+ if ( valL > 0. )
+ valL = 0.;
+ }
+ else
+ {
+ valL -= 2;
+ if ( valL < 0. )
+ valL = 0.;
+ }
+
+ valR = magnitudesp[ i + size.width() ];
+ if ( valR < 0. )
+ {
+ valR += falloff;
+ if ( valR > 0. )
+ valR = 0.;
+ }
+ else
+ {
+ valR -= falloff;
+ if ( valR < 0. )
+ valR = 0.;
+ }
+
+ if ( valL != 0. || valR != 0. )
+ allZero = FALSE;
+
+ magnitudesp[ i ] = valL;
+ magnitudesp[ i + size.width() ] = valR;
+ }
+ }
+ else
+ {
+ for ( i = 0; ( unsigned ) i < magnitudes.size(); i++ )
+ magnitudesp[ i ] = 0.;
+ }
+
+ return allZero;
+}
+
+void StereoScope::draw ( QPainter *p, const QColor &back )
+{
+ double *magnitudesp = magnitudes.data();
+ double r, g, b, per;
+
+ p->fillRect ( 0, 0, size.width(), size.height(), back );
+ for ( int i = 1; i < size.width(); i++ )
+ {
+ // left
+ per = double ( magnitudesp[ i ] * 2 ) /
+ double ( size.height() / 4 );
+ if ( per < 0.0 )
+ per = -per;
+ if ( per > 1.0 )
+ per = 1.0;
+ else if ( per < 0.0 )
+ per = 0.0;
+
+ r = startColor.red() + ( targetColor.red() -
+ startColor.red() ) * ( per * per );
+ g = startColor.green() + ( targetColor.green() -
+ startColor.green() ) * ( per * per );
+ b = startColor.blue() + ( targetColor.blue() -
+ startColor.blue() ) * ( per * per );
+
+ if ( r > 255.0 )
+ r = 255.0;
+ else if ( r < 0.0 )
+ r = 0;
+
+ if ( g > 255.0 )
+ g = 255.0;
+ else if ( g < 0.0 )
+ g = 0;
+
+ if ( b > 255.0 )
+ b = 255.0;
+ else if ( b < 0.0 )
+ b = 0;
+
+ p->setPen ( QColor ( int ( r ), int ( g ), int ( b ) ) );
+ /*p->drawLine ( i - 1, ( size.height() / 4 ) + int ( magnitudesp[ i - 1 ] ),
+ i, ( size.height() / 4 ) + int ( magnitudesp[ i ] ) );*/
+
+ //right
+ per = double ( magnitudesp[ i + size.width() ] * 2 ) /
+ double ( size.height() / 4 );
+ if ( per < 0.0 )
+ per = -per;
+ if ( per > 1.0 )
+ per = 1.0;
+ else if ( per < 0.0 )
+ per = 0.0;
+
+ r = startColor.red() + ( targetColor.red() -
+ startColor.red() ) * ( per * per );
+ g = startColor.green() + ( targetColor.green() -
+ startColor.green() ) * ( per * per );
+ b = startColor.blue() + ( targetColor.blue() -
+ startColor.blue() ) * ( per * per );
+
+ if ( r > 255.0 )
+ r = 255.0;
+ else if ( r < 0.0 )
+ r = 0;
+
+ if ( g > 255.0 )
+ g = 255.0;
+ else if ( g < 0.0 )
+ g = 0;
+
+ if ( b > 255.0 )
+ b = 255.0;
+ else if ( b < 0.0 )
+ b = 0;
+
+ p->setPen ( QColor ( int ( r ), int ( g ), int ( b ) ) );
+ /*p->drawLine ( i - 1, ( size.height() * 3 / 4 ) +
+ int ( magnitudesp[ i + size.width() - 1 ] ),
+ i, ( size.height() * 3 / 4 ) + int ( magnitudesp[ i + size.width() ] ) );*/
+ }
+}
+
+StereoAnalyzer::StereoAnalyzer()
+ : scaleFactor ( 1.0 ), falloff ( 1.0 ), analyzerBarWidth ( 4 ), fps ( 20 )
+{
+ //plan = rfftw_create_plan(512, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE);
+ startColor = Qt::green;
+ targetColor = Qt::red;
+ falloff = .5;
+ for ( int i = 0; i< 19; ++i )
+ intern_vis_data[i] = 0;
+ m_skin = Skin::getPointer();
+}
+
+StereoAnalyzer::~StereoAnalyzer()
+{
+ //rfftw_destroy_plan(plan);
+}
+
+void StereoAnalyzer::resize ( const QSize &newsize )
+{
+ size = newsize;
+
+ scale.setMax ( 192, size.width() / analyzerBarWidth );
+
+ rects.resize ( scale.range() );
+ int i = 0, w = 0;
+ for ( ; ( unsigned ) i < rects.count(); i++, w += analyzerBarWidth )
+ rects[i].setRect ( w, size.height() / 2, analyzerBarWidth - 1, 1 );
+
+ int os = magnitudes.size();
+ magnitudes.resize ( scale.range() * 2 );
+ for ( ; ( unsigned ) os < magnitudes.size(); os++ )
+ magnitudes[os] = 0.0;
+
+ // scaleFactor = double( size.height() / 2 ) / log( 512.0 );
+}
+
+void StereoAnalyzer::configChanged ( QSettings &settings )
+{
+ QString val;
+
+ // need the framerate for the fall off speed
+ /*fps = settings.readNumEntry("/MQ3/Visual/frameRate", 20);
+
+ val = settings.readEntry("/MQ3/Analyzer/startColor");
+ if (! val.isEmpty())
+ startColor = QColor(val);
+ else
+ startColor = Qt::green;
+
+ val = settings.readEntry("/MQ3/Analyzer/targetColor");
+ if (! val.isEmpty())
+ targetColor = QColor(val);
+ else
+ targetColor = Qt::red;
+
+ analyzerBarWidth = settings.readNumEntry( "/MQ3/Analyzer/barWidth", 4 );
+
+ val = settings.readEntry( "/MQ3/Analyzer/fallOffSpeed", "Normal" );
+ if ( val == "Slow" )
+ falloff = .25;
+ else if ( val == "Fast" )
+ falloff = 1.;
+ else
+ falloff = .5;
+
+ falloff *= ( 80. / double( fps ) );
+
+ resize( size );*/
+}
+
+bool StereoAnalyzer::process ( VisualNode *node )
+{
+ bool allZero = TRUE;
+ uint i;
+ long w = 0, index;
+ QRect *rectsp = rects.data();
+ double *magnitudesp = magnitudes.data();
+ double magL, magR, tmp;
+ static fft_state *state = 0;
+ if ( !state )
+ state = fft_init();
+ //if(node)
+ //float tmp_out[512];
+ short dest[256];
+
+ const int xscale[] =
+ {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 15, 20, 27,
+ 36, 47, 62, 82, 107, 141, 184, 255
+ };
+
+ if ( node )
+ {
+ i = node->length;
+ //fast_real_set_from_short(lin, node->left, node->length);
+ //if (node->right)
+ //fast_real_set_from_short(rin, node->right, node->length);
+ //fft_perform ( node->left, tmp_out, state );
+ /*for(int j = 0; j<256; ++j )
+ dest*/
+
+ //calc_mono_freq ( short dest[2][256], short src[2][512], int nch )
+
+ calc_freq ( dest, node->left );
+
+ }
+ else
+ return FALSE;
+ const double y_scale = 3.60673760222; /* 20.0 / log(256) */
+ int max = 19, y, j;
+ //int intern_vis_data[19];
+
+ for ( int i = 0; i < max; i++ )
+ {
+ for ( j = xscale[i], y = 0; j < xscale[i + 1]; j++ )
+ {
+ if ( dest[j] > y )
+ y = dest[j];
+ }
+ y >>= 7;
+ if ( y != 0 )
+ {
+ intern_vis_data[i] = log ( y ) * y_scale;
+ //qDebug("%d",y);
+ if ( intern_vis_data[i] > 15 )
+ intern_vis_data[i] = 15;
+ if ( intern_vis_data[i] < 0 )
+ intern_vis_data[i] = 0;
+ }
+ }
+ return TRUE;
+
+// fast_reals_set(lin + i, rin + i, 0, 512 - i);
+
+ //rfftw_one(plan, lin, lout);
+ //rfftw_one(plan, rin, rout);
+
+ /*index = 1;
+ for ( i = 0; i < rects.count(); i++, w += analyzerBarWidth )
+ {
+ magL = (log(lout[index] * lout[index] +
+ lout[512 - index] * lout[512 - index]) - 22.0) *
+ scaleFactor;
+ magR = (log(rout[index] * rout[index] +
+ rout[512 - index] * rout[512 - index]) - 22.0) *
+ scaleFactor;*/
+ //magL = fftperform()*/
+ /*magL = ( log ( tmp_out[index] * tmp_out[index] +
+ tmp_out[512 - index] * tmp_out[512 - index] ) - 22.0 ) *
+ scaleFactor;
+
+ if ( magL > size.height() )
+ magL = size.height() ;
+ if ( magL < magnitudesp[i] )
+ {
+ tmp = magnitudesp[i] - falloff;
+ if ( tmp < magL )
+ tmp = magL;
+ magL = tmp;
+ }
+ if ( magL < 1. )
+ magL = 1.;
+
+ if ( magR > size.height() )
+ magR = size.height();
+ if ( magR < magnitudesp[i + scale.range() ] )
+ {
+ tmp = magnitudesp[i + scale.range() ] - falloff;
+ if ( tmp < magR )
+ tmp = magR;
+ magR = tmp;
+ }
+ if ( magR < 1. )
+ magR = 1.;
+
+ if ( magR != 1 || magL != 1 )
+ allZero = FALSE;
+
+ magnitudesp[i] = magL;
+ magnitudesp[i + scale.range() ] = magR;
+
+ //rectsp[i].setTop ( size.height() / 2 - int ( magL ) );
+ //rectsp[i].setBottom ( size.height() / 2 + int ( magR ) );
+ rectsp[i].setTop ( size.height() - int ( magL ) );
+ rectsp[i].setBottom ( size.height() );
+
+ index = scale[i];
+ }
+
+ return allZero;*/
+}
+
+void StereoAnalyzer::draw ( QPainter *p, const QColor &back )
+{
+ p->setPen ( "Cyan" );
+ //p->fillRect ( 0, 0, size.width(), size.height(), Qt::transparent );
+ //int intern_vis_data[19];
+ //qDebug("%d",int(intern_vis_data[3]));
+ //p->fill(Qt::transparent);
+ for ( int j= 0; j<19; ++j )
+ {
+ for ( int i = 0; i<=intern_vis_data[j]; ++i )
+ {
+ p->setPen ( m_skin->getVisBarColor ( i ) );
+ p->drawLine ( j*4,size.height()-i, ( j+1 ) *4-2,size.height()-i );
+ }
+ }
+ for ( int i = 0; i< 19; ++i )
+ intern_vis_data[i] = 0;
+ //update();
+ /*QRect *rectsp = rects.data();
+ double r, g, b, per;
+
+ p->fillRect ( 0, 0, size.width(), size.height(), back );
+ for ( uint i = 0; i < rects.count(); i++ )
+ {
+ per = double ( rectsp[i].height() - 2 ) / double ( size.height() );
+ if ( per > 1.0 )
+ per = 1.0;
+ else if ( per < 0.0 )
+ per = 0.0;
+
+ r = startColor.red() + ( targetColor.red() -
+ startColor.red() ) * ( per * per );
+ g = startColor.green() + ( targetColor.green() -
+ startColor.green() ) * ( per * per );
+ b = startColor.blue() + ( targetColor.blue() -
+ startColor.blue() ) * ( per * per );
+
+ if ( r > 255.0 )
+ r = 255.0;
+ else if ( r < 0.0 )
+ r = 0;
+
+ if ( g > 255.0 )
+ g = 255.0;
+ else if ( g < 0.0 )
+ g = 0;
+
+ if ( b > 255.0 )
+ b = 255.0;
+ else if ( b < 0.0 )
+ b = 0;
+
+ p->fillRect ( rectsp[i], QColor ( int ( r ), int ( g ), int ( b ) ) );
+ }*/
+
+}
diff --git a/src/mainvisual.h b/src/mainvisual.h
new file mode 100644
index 000000000..043a0154b
--- /dev/null
+++ b/src/mainvisual.h
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MAINVISUAL_H
+#define MAINVISUAL_H
+
+#include <QWidget>
+#include <QResizeEvent>
+#include <visualization.h>
+#include <constants.h>
+
+#include "logscale.h"
+
+class QSettings;
+class QTimer;
+class Buffer;
+
+
+class VisualNode
+{
+public:
+ VisualNode(short *l, short *r, unsigned long n, unsigned long o)
+ : left(l), right(r), length(n), offset(o)
+ {
+ // left and right are allocated and then passed to this class
+ // the code that allocated left and right should give up all ownership
+ }
+
+ ~VisualNode()
+ {
+ delete [] left;
+ delete [] right;
+ }
+
+ short *left, *right;
+ long length, offset;
+};
+
+class VisualBase
+{
+public:
+ virtual ~VisualBase() {}
+ // return true if the output should stop
+ virtual bool process( VisualNode *node ) = 0;
+ virtual void draw( QPainter *, const QColor & ) = 0;
+ virtual void resize( const QSize &size ) = 0;
+ virtual void configChanged( QSettings & ) = 0;
+};
+
+class StereoScope : public VisualBase
+{
+public:
+ StereoScope();
+ virtual ~StereoScope();
+
+ void resize( const QSize &size );
+ void configChanged(QSettings &settings);
+ bool process( VisualNode *node );
+ void draw( QPainter *p, const QColor &back );
+
+protected:
+ QColor startColor, targetColor;
+ //QMemArray<double> magnitudes;
+ QVector <double> magnitudes;
+ QSize size;
+ bool rubberband;
+ double falloff;
+ int fps;
+};
+
+class MonoScope : public StereoScope
+{
+public:
+ MonoScope();
+ virtual ~MonoScope();
+
+ bool process( VisualNode *node );
+ void draw( QPainter *p, const QColor &back );
+};
+
+
+
+
+class MainVisual : public QWidget, public Visualization
+{
+ Q_OBJECT
+
+public:
+ MainVisual( QWidget *parent = 0, const char * = 0 );
+ virtual ~MainVisual();
+
+ //static Prefs *createPrefs( const QString &visualname,
+ // QWidget *parent, const char *name );
+ static MainVisual *getPointer();
+
+ VisualBase *visual() const { return vis; }
+ void setVisual( VisualBase *newvis );
+ //void setVisual( const QString &visualname );
+
+ void add(Buffer *, unsigned long, int, int);
+ void prepare();
+
+ void configChanged(QSettings &settings);
+
+ QSize minimumSizeHint() const { return sizeHint(); }
+ QSize sizeHint() const { return QSize(4*4*4*2, 3*3*3*2); }
+
+ void paintEvent( QPaintEvent * );
+ void resizeEvent( QResizeEvent * );
+ //void customEvent( QCustomEvent * );
+
+ static QStringList visuals();
+
+ void setFrameRate( int newfps );
+ int frameRate() const { return fps; }
+
+public slots:
+void timeout();
+
+private:
+ static MainVisual *pointer;
+ VisualBase *vis;
+ QPixmap pixmap;
+ //QPtrList<VisualNode> nodes;
+ QList <VisualNode*> nodes;
+ QTimer *timer;
+ bool playing;
+ int fps;
+};
+
+class Skin;
+
+class StereoAnalyzer : public VisualBase
+{
+public:
+ StereoAnalyzer();
+ virtual ~StereoAnalyzer();
+
+
+ void resize( const QSize &size );
+ void configChanged(QSettings &settings);
+ bool process( VisualNode *node );
+ void draw( QPainter *p, const QColor &back );
+
+protected:
+ QColor startColor, targetColor;
+ //QMemArray<QRect> rects;
+ QVector <QRect> rects;
+ QVector <double> magnitudes;
+ //QMemArray<double> magnitudes;
+ QSize size;
+ LogScale scale;
+ double scaleFactor, falloff;
+ int analyzerBarWidth, fps;
+ //rfftw_plan plan;
+ //fftw_real lin[512], rin[512], lout[1024], rout[1024];
+ int intern_vis_data[19];
+
+private:
+ Skin *m_skin;
+};
+
+#endif
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
new file mode 100644
index 000000000..645bbecdb
--- /dev/null
+++ b/src/mainwindow.cpp
@@ -0,0 +1,677 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QtGui>
+#include <QFileDialog>
+#include <QDir>
+#include <QAction>
+#include <QMenu>
+
+#include <math.h>
+
+#include <soundcore.h>
+
+#include "textscroller.h"
+#include "mainwindow.h"
+#include "constants.h"
+#include "fileloader.h"
+#include "skin.h"
+#include "playlist.h"
+#include "playlistmodel.h"
+#include "configdialog.h"
+#include "dock.h"
+#include "eqwidget.h"
+#include "mainvisual.h"
+#include "playlistformat.h"
+#include "tcpserver.h"
+#include "jumptotrackdialog.h"
+#include "aboutdialog.h"
+
+MainWindow::MainWindow(const QStringList& args, QWidget *parent)
+ : QMainWindow(parent)
+{
+ m_vis = 0;
+ seeking = FALSE;
+ m_update = FALSE;
+ m_paused = FALSE;
+
+ setWindowIcon( QIcon(":/qmmp.xpm") );
+
+ m_skin = new Skin(this);
+ Dock *dock = new Dock(this);
+ dock->setMainWidget(this);
+
+ setWindowFlags(Qt::FramelessWindowHint);
+ setFixedSize (275,116);
+
+ display = new MainDisplay(this);
+ setCentralWidget(display);
+ display->show();
+ display->setFocus ();
+
+ m_playlistName = tr("Default");
+
+ m_playlist = new PlayList(this);
+
+ connect (m_playlist,SIGNAL(next()),SLOT(next()));
+ connect (m_playlist,SIGNAL(prev()),SLOT(previous()));
+ connect (m_playlist,SIGNAL(play()),SLOT(play()));
+ connect (m_playlist,SIGNAL(pause()),SLOT(pause()));
+ connect (m_playlist,SIGNAL(stop()),SLOT(stop()));
+ connect (m_playlist,SIGNAL(eject()),SLOT(addFile()));
+
+ connect (m_playlist,SIGNAL(newPlaylist()),SLOT(newPlaylist()));
+ connect (m_playlist,SIGNAL(loadPlaylist()),SLOT(loadPlaylist()));
+ connect (m_playlist,SIGNAL(savePlaylist()),SLOT(savePlaylist()));
+
+ m_playListModel = new PlayListModel(this);
+
+ connect(display,SIGNAL(shuffleToggled(bool)),m_playListModel,SLOT(prepareForShufflePlaying(bool)));
+ connect(display,SIGNAL(repeatableToggled(bool)),m_playListModel,SLOT(prepareForRepeatablePlaying(bool)));
+
+ dock->addWidget(m_playlist);
+
+ m_equalizer = new EqWidget(this);
+ dock->addWidget(m_equalizer);
+ connect(m_equalizer, SIGNAL(valueChanged()), SLOT(updateEQ()));
+
+ m_playlist->setModel(m_playListModel);
+
+ m_jumpDialog = new JumpToTrackDialog(this);
+ m_jumpDialog->setModel(m_playListModel);
+ connect(m_jumpDialog,SIGNAL(playRequest()),this,SLOT(play()));
+ m_jumpDialog->hide();
+
+ createActions();
+
+ m_titlebar = new TitleBar(this);
+ m_titlebar->move(0,0);
+ m_titlebar->show();
+ m_titlebar->setActive(TRUE);
+
+ m_tray = new QSystemTrayIcon( this );
+ m_tray->setIcon ( QIcon(":/stop.png") );
+ m_tray->setContextMenu( m_mainMenu );
+ connect(m_tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
+
+ readSettings();
+ dock->updateDock();
+
+ display->setEQ(m_equalizer);
+ display->setPL(m_playlist);
+
+ m_vis = MainVisual::getPointer();
+
+ m_core = new SoundCore(this);
+ m_core -> addVisualization(m_vis);
+
+ connect(m_core, SIGNAL(outputStateChanged(const OutputState&)),
+ SLOT(showOutputState(const OutputState&)));
+ connect(m_core, SIGNAL(decoderStateChanged(const DecoderState&)),
+ SLOT(showDecoderState(const DecoderState&)));
+
+ connect ( m_skin, SIGNAL ( skinChanged() ), this, SLOT ( updateSkin() ) );
+ updateEQ();
+ updateSkin();
+
+ // Starting the TcpServer
+
+ new TcpServer(this);
+
+ m_playListModel->readSettings();
+ char buf[PATH_MAX + 1];
+ QString cwd = QString::fromLocal8Bit(getcwd(buf,PATH_MAX));
+ processCommandArgs(args,cwd);
+}
+
+
+MainWindow::~MainWindow()
+{
+ stop();
+}
+
+void MainWindow::play()
+{
+ m_playListModel->doCurrentVisibleRequest();
+
+ if (m_core->isPaused())
+ {
+ pause();
+ return;
+ }
+ stop();
+ if (m_playListModel->count() == 0)
+ return;
+
+ m_equalizer->loadPreset(m_playListModel->currentItem()->fileName());
+ QString s = m_playListModel->currentItem()->path();
+ if (s.isEmpty())
+ return;
+ if (m_core->play(s))
+ {
+ display->setTime(0);
+ display->setMaxTime(m_core->length());
+ }
+ else
+ {
+ //find out the reason why the playback failed
+ switch ((int) m_core->error())
+ {
+ case SoundCore::OutputError:
+ {
+ stop();
+ return; //unrecovable error in output, so abort playing
+ }
+ case SoundCore::DecoderError:
+ {
+ //error in decoder, so we should try to play next song
+ if (!m_playListModel->isEmptyQueue())
+ {
+ m_playListModel->setCurrentToQueued();
+ }
+ else if (!m_playListModel->next())
+ {
+ stop();
+ display->hideTimeDisplay();
+ return;
+ }
+ m_playlist->update();
+ play();
+ break;
+ }
+ }
+ }
+}
+
+void MainWindow::replay()
+{
+ stop();
+ play();
+}
+
+void MainWindow::seek(int pos)
+{
+ m_core->seek(pos);
+}
+
+void MainWindow::setVolume(int volume, int balance)
+{
+ m_core->setVolume(volume-qMax(balance,0)*volume/100,
+ volume+qMin(balance,0)*volume/100);
+}
+
+void MainWindow::pause(void)
+{
+ m_core->pause();
+}
+
+void MainWindow::stop()
+{
+ display->setTime(0);
+ m_core->stop();
+}
+void MainWindow::next()
+{
+ if (!m_playListModel->isEmptyQueue())
+ {
+ m_playListModel->setCurrentToQueued();
+ }
+ else if (!m_playListModel->next())
+ {
+ stop();
+ display->hideTimeDisplay();
+ return;
+ }
+ m_playlist->update();
+ if (m_core->isInitialized())
+ {
+ stop();
+ play();
+ }
+ else
+ display->hideTimeDisplay();
+}
+void MainWindow::previous()
+{
+ if (!m_playListModel->previous())
+ {
+ display->hideTimeDisplay();
+ return;
+ }
+
+ m_playlist->update();
+ if (m_core->isInitialized())
+ {
+ stop();
+ play();
+ }
+ else
+ display->hideTimeDisplay();
+}
+
+void MainWindow::updateEQ()
+{
+ int b[10];
+ for (int i=0; i<10; ++i)
+ b[i] = m_equalizer->gain(i);
+ m_core->setEQ(b, m_equalizer->preamp());
+ m_core->setEQEnabled(m_equalizer->isEQEnabled());
+}
+
+void MainWindow::updatePreset()
+{
+ //if(m_playListModel->currentItem())
+ // m_equalizer->setPresetName(m_playListModel->currentItem()->fileName());
+}
+
+void MainWindow::showOutputState(const OutputState &st)
+
+{
+ if (seeking)
+ return;
+
+ display->setInfo(st);
+ m_playlist->setInfo(st, m_core->length(), m_playListModel->totalLength());
+ switch ((int) st.type())
+ {
+ case OutputState::Playing:
+ {
+ m_tray->setIcon ( QIcon(":/play.png") );
+ if (m_showMessage && m_playListModel->currentItem())
+ m_tray->showMessage ( tr("Now Playing"),
+ m_playListModel->currentItem()->title(),
+ QSystemTrayIcon::Information, m_messageDelay );
+ if (m_showToolTip && m_playListModel->currentItem())
+ m_tray->setToolTip (m_playListModel->currentItem()->title());
+ break;
+ }
+ case OutputState::Paused:
+ {
+ m_tray->setIcon ( QIcon(":/pause.png") );
+ break;
+ }
+ case OutputState::Stopped:
+ {
+ m_tray->setIcon ( QIcon(":/stop.png") );
+ break;
+ }
+ }
+
+}
+void MainWindow::showDecoderState(const DecoderState &st)
+{
+ switch ((int) st.type())
+ {
+ case DecoderState::Finished:
+ {
+ next();
+ break;
+ }
+ }
+}
+
+void MainWindow::closeEvent ( QCloseEvent *)
+{
+ writeSettings();
+ m_playlist->close();
+ m_equalizer->close();
+ QApplication::quit ();
+}
+
+void MainWindow::addDir()
+{
+ QString s = QFileDialog::getExistingDirectory(
+ this,
+ tr("Choose a directory"),
+ m_lastDir,
+ QFileDialog::DontResolveSymlinks | QFileDialog::ShowDirsOnly);
+
+ if (s.isEmpty())
+ return;
+ m_playListModel->addDirectory(s);
+ m_lastDir = s+"../";
+}
+
+void MainWindow::addFile()
+{
+ QStringList files = QFileDialog::getOpenFileNames(
+ this,
+ tr("Select one or more files to open"),
+ m_lastDir,
+ Decoder::filter());
+ if (files.isEmpty ())
+ return;
+ /*
+ foreach(QString s, files)
+ m_playListModel->load(new MediaFile(s));
+ */
+ m_playListModel->addFiles(files);
+ m_lastDir = files.at(0);
+}
+
+void MainWindow::clear()
+{
+ m_playListModel->clear();
+}
+
+void MainWindow::startSeek()
+{
+ seeking = TRUE;
+}
+
+void MainWindow::endSeek()
+{
+ seeking = FALSE;
+}
+
+void MainWindow::changeEvent ( QEvent * event )
+{
+ if (event->type() == QEvent::ActivationChange)
+ {
+ m_titlebar->setActive(isActiveWindow());
+ }
+}
+
+void MainWindow::readSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ if (!m_update)
+ {
+ settings.beginGroup("MainWindow");
+ //geometry
+ move(settings.value("pos", QPoint(100, 100)).toPoint());
+ //last directory
+ m_lastDir = settings.value("last_dir","/").toString();
+ settings.endGroup();
+ show();
+ //visibility
+ m_playlist->setVisible(settings.value("Playlist/visible",TRUE).toBool());
+ m_equalizer->setVisible(settings.value("Equalizer/visible",TRUE).toBool());
+ bool val = settings.value("Playlist/repeatable",FALSE).toBool();
+
+ // Repeat/Shuffle
+ m_playListModel->prepareForRepeatablePlaying(val);
+ display->setIsRepeatable(val);
+ val = settings.value("Playlist/shuffle",FALSE).toBool();
+ display->setIsShuffle(val);
+ m_playListModel->prepareForShufflePlaying(val);
+
+ // Playlist name
+ m_playlistName = settings.value("Playlist/playlist_name","Default").toString();
+
+ m_update = TRUE;
+ }
+ //tray
+ settings.beginGroup("Tray");
+ m_tray->setVisible(settings.value("enabled",TRUE).toBool());
+ m_showMessage = settings.value("show_message",TRUE).toBool();
+ m_messageDelay = settings.value("message_delay",2000).toInt();
+ m_showToolTip = settings.value("show_tooltip", FALSE).toBool();
+ m_hide_on_titlebar_close = settings.value("hide_on_close",FALSE).toBool();
+ if (!m_showToolTip)
+ m_tray->setToolTip(QString());
+ settings.endGroup();
+
+}
+
+void MainWindow::writeSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("MainWindow");
+ //geometry
+ settings.setValue("pos", this->pos());
+ //last directory
+ settings.setValue("last_dir",m_lastDir);
+ settings.endGroup();
+
+ // Repeat/Shuffle
+ settings.beginGroup("Playlist");
+ settings.setValue("repeatable",display->isRepeatable());
+ settings.setValue("shuffle",display->isShuffle());
+
+ // Playlist name
+ settings.setValue("playlist_name",m_playlistName);
+ settings.endGroup();
+}
+
+void MainWindow::showSettings()
+{
+ m_confDialog = new ConfigDialog(this);
+ if (m_confDialog->exec() == QDialog::Accepted)
+ {
+ readSettings();
+ m_playlist->readSettings();
+ TextScroller::getPointer()->readSettings();
+ m_core->updateConfig();
+ }
+ delete m_confDialog;
+}
+
+void MainWindow::toggleVisibility()
+{
+ if (isHidden())
+ {
+ show();
+ m_playlist->setVisible(display->isPlaylistVisible());
+ m_equalizer->setVisible(display->isEqualizerVisible());
+ if (isMinimized())
+ {
+ if (isMaximized())
+ showMaximized();
+ else
+ showNormal();
+ }
+ raise();
+ activateWindow();
+ }
+ else
+ {
+ hide();
+ if (m_playlist->isVisible())
+ m_playlist->hide();
+ if (m_equalizer->isVisible())
+ m_equalizer->hide();
+ }
+}
+
+void MainWindow::trayActivated(QSystemTrayIcon::ActivationReason reason)
+{
+ if (reason == QSystemTrayIcon::Trigger)
+ toggleVisibility();
+}
+
+void MainWindow::createActions()
+{
+ m_mainMenu = new QMenu(this);
+ m_mainMenu->addAction(tr("&Play"),this, SLOT(play()), tr("X"));
+ m_mainMenu->addAction(tr("&Pause"),this, SLOT(pause()), tr("C"));
+ m_mainMenu->addAction(tr("&Stop"),this, SLOT(stop()), tr("V"));
+ m_mainMenu->addAction(tr("&Previous"),this, SLOT(previous()), tr("Z"));
+ m_mainMenu->addAction(tr("&Next"),this, SLOT(next()), tr("B"));
+ m_mainMenu->addAction(tr("&Queue"),m_playListModel, SLOT(addToQueue()), tr("Q"));
+ m_mainMenu->addSeparator();
+ m_mainMenu->addAction(tr("&Jump To File"),this, SLOT(jumpToFile()), tr("J"));
+ m_mainMenu->addSeparator();
+ m_mainMenu->addAction(tr("&Settings"),this, SLOT(showSettings()), tr("Ctrl+P"));
+ m_mainMenu->addSeparator();
+ m_mainMenu->addAction(tr("&About"),this, SLOT(about()));
+ Dock::getPointer()->addActions(m_mainMenu->actions());
+ m_mainMenu->addSeparator();
+ m_mainMenu->addAction(tr("&Exit"),this, SLOT(close ()), tr("Ctrl+Q"));
+ Dock::getPointer()->addActions(m_mainMenu->actions());
+}
+
+
+void MainWindow::about()
+{
+ AboutDialog dlg(this);
+ dlg.exec();
+}
+
+QMenu* MainWindow::menu()
+{
+ return m_mainMenu;
+}
+
+void MainWindow::updateSkin()
+{
+ clearMask();
+ m_equalizer->clearMask();
+ /*qt bug workarround */
+ setMask(QRegion(0,0,275,116));
+ m_equalizer->setMask(QRegion(0,0,275,116));
+ update();
+ m_equalizer->update();
+
+ QRegion region = m_skin->getMWRegion();
+ if (!region.isEmpty())
+ setMask(region);
+
+ region = m_skin->getPLRegion();
+ if (!region.isEmpty())
+ m_equalizer->setMask(region);
+}
+
+void MainWindow::newPlaylist()
+{
+ m_playListModel->clear();
+ m_playlistName = tr("Default");
+}
+
+void MainWindow::loadPlaylist()
+{
+ QStringList l;
+ QList<PlaylistFormat*> p_list = m_playListModel->registeredPlaylistFormats();
+ if (!p_list.isEmpty())
+ {
+ foreach(PlaylistFormat* fmt,p_list)
+ l << fmt->getExtensions();
+
+ QString mask = tr("Playlist Files")+" (" + l.join(" *.").prepend("*.") + ")";
+ QString f_name = QFileDialog::getOpenFileName(this,tr("Open Playlist"),m_lastDir,mask);
+ if (!f_name.isEmpty())
+ {
+ m_playListModel->loadPlaylist(f_name);
+ m_playlistName = QFileInfo(f_name).baseName();
+ }
+ m_lastDir = QFileInfo(f_name).absoluteDir().path();
+ }
+ else
+ qWarning("Error: There is no registered playlist parsers");
+}
+
+void MainWindow::savePlaylist()
+{
+ QStringList l;
+ QList<PlaylistFormat*> p_list = m_playListModel->registeredPlaylistFormats();
+ if (!p_list.isEmpty())
+ {
+ foreach(PlaylistFormat* fmt,p_list)
+ l << fmt->getExtensions();
+
+ QString mask = tr("Playlist Files")+" (" + l.join(" *.").prepend("*.") + ")";
+ QString f_name = QFileDialog::getSaveFileName(this, tr("Save Playlist"),m_lastDir + "/" +
+ m_playlistName + "." + l[0],mask);
+
+ if (!f_name.isEmpty())
+ {
+ m_playListModel->savePlaylist(f_name);
+ m_playlistName = QFileInfo(f_name).baseName();
+ }
+ m_lastDir = QFileInfo(f_name).absoluteDir().path();
+ }
+ else
+ qWarning("Error: There is no registered playlist parsers");
+}
+
+void MainWindow::setFileList(const QStringList & l)
+{
+ if (!m_playListModel->setFileList(l))
+ addFile();
+}
+
+void MainWindow::playPause()
+{
+ if (m_core->isInitialized())
+ pause();
+ else
+ play();
+}
+
+bool MainWindow::processCommandArgs(const QStringList &slist,const QString& cwd)
+{
+ if (slist.count() > 0)
+ {
+ QString str = slist[0];
+ if (str.startsWith("--")) // is it a command?
+ {
+ if (str == "--play")
+ play();
+ else if (str == "--stop")
+ {
+ stop();
+ display->hideTimeDisplay();
+ }
+ else if (str == "--pause")
+ pause();
+ else if (str == "--next")
+ next();
+ else if (str == "--previous")
+ previous();
+ else if (str == "--play-pause")
+ playPause();
+ else
+ return false;
+ }
+ else// maybe it is a list of files or dirs
+ {
+ QStringList full_path_list;
+ foreach(QString s,slist)
+ {
+ if (s.left(1) == "/") //is it absolute path?
+ full_path_list << s;
+ else
+ full_path_list << cwd + "/" + s;
+ //qWarning("Current working dir: %s",qPrintable(workingDir));
+ //qWarning("Full path to play: %s",qPrintable(workingDir + "/" + s));
+ }
+ setFileList(full_path_list);
+ }
+ }
+ return true;
+}
+
+void MainWindow::jumpToFile()
+{
+ if (m_jumpDialog->isHidden())
+ {
+ m_jumpDialog->show();
+ m_jumpDialog->refresh();
+ }
+}
+
+void MainWindow::handleCloseRequest()
+{
+ if (m_hide_on_titlebar_close && m_tray->isVisible())
+ toggleVisibility();
+ else
+ QApplication::closeAllWindows();
+}
+
+
diff --git a/src/mainwindow.h b/src/mainwindow.h
new file mode 100644
index 000000000..751f20939
--- /dev/null
+++ b/src/mainwindow.h
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QMainWindow>
+#include <QSystemTrayIcon>
+
+#include "output.h"
+#include "decoder.h"
+#include "display.h"
+#include "mediafile.h"
+#include "decoderfactory.h"
+#include "titlebar.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class PlayList;
+class PlayListModel;
+class ConfigDialog;
+class EqWidget;
+class MainVisual;
+class Skin;
+class SoundCore;
+class JumpToTrackDialog;
+
+class QMenu;
+class QKeyEvent;
+
+
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+public:
+ MainWindow(const QStringList& args, QWidget *parent);
+
+ ~MainWindow();
+
+ PlayList *getPLPointer()
+ {
+ return m_playlist;
+ }
+
+ void seek(int);
+ QMenu* menu();
+ void setVolume(int volume, int balance);
+
+ bool processCommandArgs(const QStringList &slist,const QString& cwd);
+
+public slots:
+ void previous();
+ void play();
+ void pause();
+ void playPause();
+ void stop();
+ void next();
+ void replay();
+
+ void newPlaylist();
+ void loadPlaylist();
+ void savePlaylist();
+
+ void setFileList(const QStringList&);
+
+protected:
+ virtual void closeEvent ( QCloseEvent *);
+ virtual void changeEvent ( QEvent * event );
+
+private slots:
+ void showOutputState(const OutputState&);
+ void showDecoderState(const DecoderState&);
+ void clear();
+ void startSeek();
+ void endSeek();
+ void showSettings();
+ void addDir();
+ void addFile();
+ void updateEQ();
+ void updatePreset();
+ void updateSkin();
+
+ void jumpToFile();
+
+ void toggleVisibility();
+ void trayActivated(QSystemTrayIcon::ActivationReason);
+ void about();
+
+ void handleCloseRequest();
+
+private:
+ void readSettings();
+ void writeSettings();
+ void createActions();
+ double seeking;
+ SoundCore *m_core;
+ QMenu *m_mainMenu;
+ MainDisplay *display;
+ PlayList *m_playlist;
+ PlayListModel *m_playListModel;
+ TitleBar *m_titlebar;
+ ConfigDialog *m_confDialog;
+ int m_preamp;
+ EqWidget *m_equalizer;
+ MainVisual *m_vis;
+ QString m_lastDir;
+ QSystemTrayIcon *m_tray;
+ bool m_update;
+ bool m_showMessage;
+ int m_messageDelay;
+ bool m_paused;
+ bool m_showToolTip;
+ Skin *m_skin;
+ QString m_playlistName;
+ JumpToTrackDialog* m_jumpDialog;
+ bool m_hide_on_titlebar_close;
+};
+
+#endif
diff --git a/src/mediafile.cpp b/src/mediafile.cpp
new file mode 100644
index 000000000..305f4c32d
--- /dev/null
+++ b/src/mediafile.cpp
@@ -0,0 +1,109 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QSettings>
+#include <QDir>
+
+#include <decoder.h>
+
+#include "mediafile.h"
+
+MediaFile::MediaFile(QString path)
+{
+ m_selected = FALSE;
+ m_current = FALSE;
+ m_path = path;
+ m_tag = Decoder::createTag(path);
+ //format
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ QString format = settings.value("PlayList/title_format", "%p - %t").toString();
+ bool use_meta = settings.value ("PlayList/load_metadata", TRUE).toBool();
+ if (use_meta && m_tag && !m_tag->isEmpty())
+ {
+ m_year = m_tag->year();
+ //m_title = m_tag->artist()+" - "+m_tag->title();
+ m_title = format;
+ m_title.replace("%p",m_tag->artist());
+ m_title.replace("%a",m_tag->album());
+ m_title.replace("%t",m_tag->title());
+ m_title.replace("%n",QString("%1").arg(m_tag->track()));
+ m_title.replace("%g",m_tag->genre ());
+ m_title.replace("%f",m_path.section('/',-1));
+ m_title.replace("%F",m_path);
+ //m_title.replace("%d",);
+ m_title.replace("%y",QString("%1").arg(m_tag->year ()));
+ //m_title.replace("%c",);
+ }
+ else
+ m_title = m_path.section('/',-1);
+}
+
+
+MediaFile::~MediaFile()
+{
+ if (m_tag)
+ delete m_tag;
+}
+
+const QString MediaFile::path()const
+{
+ return m_path;
+}
+const QString MediaFile::fileName() const
+{
+ return m_path.section('/',-1);
+}
+
+const QString MediaFile::title()const
+{
+ return m_title;
+}
+
+int MediaFile::length()const
+{
+ if (m_tag)
+ return m_tag->length();
+ else
+ return 0;
+}
+
+void MediaFile::setSelected(bool yes)
+{
+ m_selected = yes;
+}
+
+bool MediaFile::isSelected()const
+{
+ return m_selected;
+}
+
+uint MediaFile::year()const
+{
+ return m_year;
+}
+
+bool MediaFile::isCurrent()
+{
+ return m_current;
+}
+
+void MediaFile::setCurrent(bool cur)
+{
+ m_current = cur;
+}
diff --git a/src/mediafile.h b/src/mediafile.h
new file mode 100644
index 000000000..d7e566145
--- /dev/null
+++ b/src/mediafile.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MEDIAFILE_H
+#define MEDIAFILE_H
+
+#include <QString>
+
+class FileTag;
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+
+class MediaFile
+{
+public:
+ MediaFile()
+ {};
+ MediaFile(QString);
+
+ ~MediaFile();
+ MediaFile &operator=(const MediaFile &other);
+
+ const QString path()const;
+ const QString title()const;
+ const QString fileName()const;
+ uint year()const;
+ int length()const;
+ void setSelected(bool);
+ bool isSelected()const;
+ bool isCurrent();
+ void setCurrent(bool);
+
+
+private:
+ QString m_path;
+ QString m_title;
+ uint m_year;
+ FileTag *m_tag;
+ bool m_selected;
+ bool m_current;
+
+};
+
+#endif
diff --git a/src/monostereo.cpp b/src/monostereo.cpp
new file mode 100644
index 000000000..659fec7be
--- /dev/null
+++ b/src/monostereo.cpp
@@ -0,0 +1,68 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+
+#include "skin.h"
+#include "monostereo.h"
+
+MonoStereo::MonoStereo ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ m_skin = Skin::getPointer();
+ m_pixmap = QPixmap ( 54,12 );
+ setChannels ( 0 );
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+
+MonoStereo::~MonoStereo()
+{}
+
+void MonoStereo::setChannels ( int c )
+{
+ m_channels = c;
+ QPainter paint ( &m_pixmap );
+ switch ( ( int ) c )
+ {
+ case 0:
+ {
+ paint.drawPixmap ( 0,0,m_skin->getMSPart ( Skin::MONO_I ) );
+ paint.drawPixmap ( 27,0,m_skin->getMSPart ( Skin::STEREO_I ) );
+ break;
+ }
+ case 1:
+ {
+ paint.drawPixmap ( 0,0,m_skin->getMSPart ( Skin::MONO_A ) );
+ paint.drawPixmap ( 27,0,m_skin->getMSPart ( Skin::STEREO_I ) );
+ break;
+ }
+ }
+ if ( c > 1 )
+ {
+ paint.drawPixmap ( 0,0,m_skin->getMSPart ( Skin::MONO_I ) );
+ paint.drawPixmap ( 27,0,m_skin->getMSPart ( Skin::STEREO_A ) );
+ }
+ setPixmap ( m_pixmap );
+}
+
+void MonoStereo::updateSkin()
+{
+ setChannels ( m_channels );
+}
diff --git a/src/monostereo.h b/src/monostereo.h
new file mode 100644
index 000000000..be1419c5c
--- /dev/null
+++ b/src/monostereo.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef MONOSTEREO_H
+#define MONOSTEREO_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Skin;
+
+class MonoStereo : public PixmapWidget
+{
+Q_OBJECT
+public:
+ MonoStereo(QWidget *parent = 0);
+
+ ~MonoStereo();
+
+ void setChannels(int);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ QPixmap m_pixmap;
+ int m_channels;
+
+};
+
+#endif
diff --git a/src/mp3player.cpp b/src/mp3player.cpp
new file mode 100644
index 000000000..cf6b27535
--- /dev/null
+++ b/src/mp3player.cpp
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <QApplication>
+#include <QTranslator>
+#include <QLocale>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mainwindow.h"
+#include "playlist.h"
+#include "qmmpstarter.h"
+
+int main(int argc, char *argv[])
+{
+ QApplication a (argc, argv );
+ QTranslator translator;
+ QString locale = QLocale::system().name();
+ translator.load(QString(":/qmmp_") + locale);
+ a.installTranslator(&translator);
+
+ QMMPStarter starter(argc,argv);
+ Q_UNUSED(starter)
+
+ return a.exec();
+}
diff --git a/src/number.cpp b/src/number.cpp
new file mode 100644
index 000000000..9f127965e
--- /dev/null
+++ b/src/number.cpp
@@ -0,0 +1,45 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "number.h"
+#include "skin.h"
+
+Number::Number(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ //TODO default value??
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+
+Number::~Number()
+{
+}
+
+void Number::setValue(int n)
+{
+ setPixmap(m_skin->getNumber(n));
+ m_value = n;
+}
+
+void Number::updateSkin(void)
+{
+ setValue(m_value);
+}
diff --git a/src/number.h b/src/number.h
new file mode 100644
index 000000000..1c89f71d4
--- /dev/null
+++ b/src/number.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef NUMBER_H
+#define NUMBER_H
+
+#include "pixmapwidget.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Skin;
+
+class Number : public PixmapWidget
+{
+Q_OBJECT
+public:
+ Number(QWidget *parent = 0);
+
+ ~Number();
+
+ void setValue(int);
+
+private slots:
+ void updateSkin(void);
+
+private:
+ Skin *m_skin;
+ int m_value;
+
+};
+
+#endif
diff --git a/src/pixmapwidget.cpp b/src/pixmapwidget.cpp
new file mode 100644
index 000000000..a0f4ff7fd
--- /dev/null
+++ b/src/pixmapwidget.cpp
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPixmap>
+#include <QPainter>
+#include <QPaintEvent>
+
+#include "pixmapwidget.h"
+
+PixmapWidget::PixmapWidget(QWidget *parent)
+ : QWidget(parent)
+{}
+
+
+PixmapWidget::~PixmapWidget()
+{}
+
+void PixmapWidget::setPixmap(const QPixmap pixmap)
+{
+ m_pixmap = pixmap;
+ resize(m_pixmap.size());
+ update();
+}
+
+void PixmapWidget::paintEvent ( QPaintEvent *)
+{
+ QPainter paint(this);
+ paint.drawPixmap(0,0, m_pixmap);
+}
+
diff --git a/src/pixmapwidget.h b/src/pixmapwidget.h
new file mode 100644
index 000000000..24d34260e
--- /dev/null
+++ b/src/pixmapwidget.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PIXMAPWIDGET_H
+#define PIXMAPWIDGET_H
+
+#include <QWidget>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class QPixmap;
+
+class PixmapWidget : public QWidget
+{
+Q_OBJECT
+public:
+ PixmapWidget(QWidget *parent = 0);
+
+ ~PixmapWidget();
+
+ virtual void setPixmap(const QPixmap);
+
+protected:
+ void paintEvent ( QPaintEvent * event );
+
+private:
+ QPixmap m_pixmap;
+
+
+
+};
+
+#endif
diff --git a/src/playlist.cpp b/src/playlist.cpp
new file mode 100644
index 000000000..d65debb85
--- /dev/null
+++ b/src/playlist.cpp
@@ -0,0 +1,462 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+#include <QResizeEvent>
+#include <QSettings>
+#include <QMenu>
+#include <QAction>
+#include <QSignalMapper>
+#include <QHBoxLayout>
+
+#include "dock.h"
+#include "fileloader.h"
+#include "playlist.h"
+#include "skin.h"
+#include "listwidget.h"
+#include "button.h"
+#include "mediafile.h"
+#include "playlistmodel.h"
+#include "playlisttitlebar.h"
+#include "playlistslider.h"
+#include "pixmapwidget.h"
+#include "symboldisplay.h"
+#include "playlistcontrol.h"
+#include "keyboardmanager.h"
+#include <output.h>
+
+PlayList::PlayList ( QWidget *parent )
+ : QWidget ( parent )
+{
+ setWindowFlags ( Qt::Dialog | Qt::FramelessWindowHint );
+
+ m_update = FALSE;
+ m_resize = FALSE;
+ m_anchor_row = -1;
+
+ createMenus();
+
+
+ resize ( 275,116 );
+ setMinimumSize ( 275,116 );
+ setBaseSize ( 275,116 );
+ m_titleBar = new PlayListTitleBar ( this );
+ m_titleBar->show();
+ m_titleBar->move ( 0,0 );
+ m_listWidget = new ListWidget ( this );
+ m_listWidget->show();
+ m_listWidget->setGeometry ( 12,20,243,58 );
+
+ m_plslider = new PlayListSlider ( this );
+ m_plslider->show();
+
+ setSizeIncrement ( 25,29 );
+ m_skin = Skin::getPointer();
+
+ m_buttonAdd = new Button ( this,Skin::PL_BT_ADD,Skin::PL_BT_ADD );
+ m_buttonAdd->move ( 11,86 );
+ m_buttonSub = new Button ( this,Skin::PL_BT_SUB,Skin::PL_BT_SUB );
+ m_buttonSub->move ( 40,86 );
+ m_selectButton = new Button ( this,Skin::PL_BT_SEL,Skin::PL_BT_SEL );
+ m_selectButton->move ( 70,86 );
+ m_sortButton= new Button ( this,Skin::PL_BT_SORT,Skin::PL_BT_SORT );
+ m_sortButton->move ( 99,86 );
+ m_playlistButton = new Button ( this,Skin::PL_BT_LST,Skin::PL_BT_LST );
+
+ m_pl_control = new PlaylistControl ( this );
+ m_pl_control->move ( 0,0 );
+ m_pl_control->show();
+
+ m_length_totalLength = new SymbolDisplay ( this,14 );
+ m_length_totalLength->setAlignment ( Qt::AlignLeft );
+ m_length_totalLength -> show();
+
+ m_current_time = new SymbolDisplay ( this,6 );
+ m_current_time->show();
+
+ m_keyboardManager = new KeyboardManager ( this );
+
+ connect ( m_listWidget, SIGNAL ( selectionChanged() ), parent, SLOT ( replay() ) );
+
+ connect ( m_plslider, SIGNAL ( sliderMoved ( int ) ), m_listWidget, SLOT ( scroll ( int ) ) );
+ connect ( m_listWidget, SIGNAL ( positionChanged ( int, int ) ), m_plslider,
+ SLOT ( setPos ( int, int ) ) );
+ connect ( m_skin, SIGNAL ( skinChanged() ), this, SLOT ( update() ) );
+ connect ( m_buttonAdd, SIGNAL ( clicked() ), SLOT ( showAddMenu() ) );
+ connect ( m_buttonSub, SIGNAL ( clicked() ), SLOT ( showSubMenu() ) );
+ connect ( m_selectButton, SIGNAL ( clicked() ), SLOT ( showSelectMenu() ) );
+ connect ( m_sortButton, SIGNAL ( clicked() ), SLOT ( showSortMenu() ) );
+ connect ( m_playlistButton, SIGNAL ( clicked() ), SLOT ( showPlaylistMenu() ) );
+
+ connect ( m_pl_control, SIGNAL ( nextClicked() ), SIGNAL ( next() ) );
+ connect ( m_pl_control, SIGNAL ( previousClicked() ), SIGNAL ( prev() ) );
+ connect ( m_pl_control, SIGNAL ( playClicked() ), SIGNAL ( play() ) );
+ connect ( m_pl_control, SIGNAL ( pauseClicked() ), SIGNAL ( pause() ) );
+ connect ( m_pl_control, SIGNAL ( stopClicked() ), SIGNAL ( stop() ) );
+ connect ( m_pl_control, SIGNAL ( ejectClicked() ), SIGNAL ( eject() ) );
+ readSettings();
+}
+
+
+PlayList::~PlayList()
+{}
+
+void PlayList::createMenus()
+{
+ m_addMenu = new QMenu ( this );
+ m_subMenu = new QMenu ( this );
+ m_selectMenu = new QMenu ( this );
+ m_sortMenu = new QMenu ( this );
+ m_playlistMenu = new QMenu ( this );
+}
+
+void PlayList::createActions()
+{ //add menu
+ QAction *addFileAct = new QAction ( tr ( "&Add File" ),this );
+ addFileAct->setShortcut ( tr ( "F" ) );
+ m_addMenu->addAction ( addFileAct );
+ connect ( addFileAct, SIGNAL ( triggered() ), parent(), SLOT ( addFile () ) );
+ m_actions << addFileAct;
+
+ QAction *addDirAct = new QAction ( tr ( "&Add Directory" ),this );
+ addDirAct->setShortcut ( tr ( "D" ) );
+ m_addMenu->addAction ( addDirAct );
+ connect ( addDirAct, SIGNAL ( triggered() ), parent(), SLOT ( addDir () ) );
+ m_actions << addDirAct;
+ //remove menu
+ QAction *remSelAct = new QAction ( tr ( "&Remove Selected" ),this );
+ remSelAct->setShortcut ( tr ( "Del" ) );
+ m_subMenu->addAction ( remSelAct );
+ connect ( remSelAct, SIGNAL ( triggered() ),
+ m_playListModel, SLOT ( removeSelected () ) );
+ this->addAction ( remSelAct );
+
+ QAction *remAllAct = new QAction ( tr ( "&Remove All" ),this );
+ //remAllAct->setShortcut(tr("D")); FIXME: add correct shortcat
+ m_subMenu->addAction ( remAllAct );
+ connect ( remAllAct, SIGNAL ( triggered() ), m_playListModel, SLOT ( clear () ) );
+ m_actions << remAllAct;
+
+ QAction *remUnselAct = new QAction ( tr ( "&Remove Unselected" ),this );
+ m_subMenu->addAction ( remUnselAct );
+ connect ( remUnselAct, SIGNAL ( triggered() ),
+ m_playListModel, SLOT ( removeUnselected () ) );
+
+ //listwidget menu
+ QAction *detailsAct = new QAction ( tr ( "&View Track Details" ),this );
+ detailsAct->setShortcut ( tr ( "Alt+I" ) );
+ m_listWidget->menu()->addAction ( detailsAct );
+ connect ( detailsAct, SIGNAL ( triggered() ), m_playListModel, SLOT ( showDetails () ) );
+
+ // sort menu
+ m_sortMenu->addAction ( detailsAct );
+ m_sortMenu->addSeparator();
+
+ QMenu* sort_mode_menu = new QMenu ( tr ( "Sort List" ),m_sortMenu );
+
+ QSignalMapper* signalMapper = new QSignalMapper ( this );
+ QAction* titleAct = sort_mode_menu->addAction ( tr ( "By Title" ) );
+ connect ( titleAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( titleAct, PlayListModel::TITLE );
+
+ QAction* nameAct = sort_mode_menu->addAction ( tr ( "By Filename" ) );
+ connect ( nameAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( nameAct, PlayListModel::FILENAME );
+
+ QAction* pathnameAct = sort_mode_menu->addAction ( tr ( "By Path + Filename" ) );
+ connect ( pathnameAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( pathnameAct, PlayListModel::PATH_AND_FILENAME );
+
+ QAction* dateAct = sort_mode_menu->addAction ( tr ( "By Date" ) );
+ connect ( dateAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( dateAct, PlayListModel::DATE );
+
+ connect ( signalMapper, SIGNAL ( mapped ( int ) ),
+ m_playListModel, SLOT ( sort ( int ) ) );
+
+ m_sortMenu->addMenu ( sort_mode_menu );
+
+ sort_mode_menu = new QMenu ( tr ( "Sort Selection" ),m_sortMenu );
+ signalMapper = new QSignalMapper ( this );
+ titleAct = sort_mode_menu->addAction ( tr ( "By Title" ) );
+ connect ( titleAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( titleAct, PlayListModel::TITLE );
+
+ nameAct = sort_mode_menu->addAction ( tr ( "By Filename" ) );
+ connect ( nameAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( nameAct, PlayListModel::FILENAME );
+
+ pathnameAct = sort_mode_menu->addAction ( tr ( "By Path + Filename" ) );
+ connect ( pathnameAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( pathnameAct, PlayListModel::PATH_AND_FILENAME );
+
+ dateAct = sort_mode_menu->addAction ( tr ( "By Date" ) );
+ connect ( dateAct, SIGNAL ( triggered ( bool ) ), signalMapper, SLOT ( map() ) );
+ signalMapper->setMapping ( dateAct, PlayListModel::DATE );
+
+ connect ( signalMapper, SIGNAL ( mapped ( int ) ),
+ m_playListModel, SLOT ( sortSelection ( int ) ) );
+
+ m_sortMenu->addMenu ( sort_mode_menu );
+
+ m_sortMenu->addSeparator();
+ m_sortMenu->addAction ( tr ( "Randomize List" ),m_playListModel,SLOT ( randomizeList() ) );
+ m_sortMenu->addAction ( tr ( "Reverse List" ),m_playListModel,SLOT ( reverseList() ) );
+
+ m_listWidget->menu()->addSeparator();
+ m_listWidget->menu()->addActions ( m_subMenu->actions() );
+ m_actions << detailsAct;
+
+ //select menu
+ QAction *invSelAct = new QAction ( tr ( "Invert Selection" ),this );
+ m_selectMenu->addAction ( invSelAct );
+ connect ( invSelAct, SIGNAL ( triggered() ),
+ m_playListModel, SLOT ( invertSelection () ) );
+
+ m_selectMenu->addSeparator();
+
+ QAction *selNoneAct = new QAction ( tr ( "&Select None" ),this );
+ //selNoneAct->setShortcut(tr("Ctrl+Shift+A"));
+ m_selectMenu->addAction ( selNoneAct );
+ connect ( selNoneAct, SIGNAL ( triggered() ),
+ m_playListModel, SLOT ( clearSelection () ) );
+ this->addAction ( selNoneAct );
+
+ QAction *selAllAct = new QAction ( tr ( "&Select All" ),this );
+ selAllAct->setShortcut ( tr ( "Ctrl+A" ) );
+ m_selectMenu->addAction ( selAllAct );
+ connect ( selAllAct, SIGNAL ( triggered() ),
+ m_playListModel, SLOT ( selectAll () ) );
+ this->addAction ( selAllAct );
+
+// Playlist Menu
+ QAction *newListAct = new QAction ( tr ( "&New List" ),this );
+ newListAct->setShortcut ( tr ( "Shift+N" ) );
+ m_playlistMenu->addAction ( newListAct );
+ connect ( newListAct, SIGNAL ( triggered() ), this, SIGNAL ( newPlaylist() ) );
+ m_playlistMenu->addSeparator();
+
+ QAction *loadListAct = new QAction ( tr ( "&Load List" ),this );
+ loadListAct->setShortcut ( tr ( "O" ) );
+ m_playlistMenu->addAction ( loadListAct );
+ connect ( loadListAct, SIGNAL ( triggered() ), this, SIGNAL ( loadPlaylist() ) );
+
+ QAction *saveListAct = new QAction ( tr ( "&Save List" ),this );
+ saveListAct->setShortcut ( tr ( "Shift+S" ) );
+ m_playlistMenu->addAction ( saveListAct );
+ connect ( saveListAct, SIGNAL ( triggered() ), this, SIGNAL ( savePlaylist() ) );
+ this->addActions ( m_playlistMenu->actions() );
+
+ Dock::getPointer()->addActions ( m_actions );
+}
+
+void PlayList::closeEvent ( QCloseEvent* )
+{
+ writeSettings();
+}
+
+void PlayList::paintEvent ( QPaintEvent * )
+{
+ int m_sx = ( width()-275 ) /25;
+ int m_sy = ( height()-116 ) /29;
+ drawPixmap ( m_sx, m_sy );
+}
+
+void PlayList::drawPixmap ( int sx, int sy )
+{
+ QPainter paint;
+ paint.begin ( this );
+ paint.drawPixmap ( 0,20,m_skin->getPlPart ( Skin::PL_LFILL ) );
+ for ( int i = 1; i<sy+2; i++ )
+ {
+ paint.drawPixmap ( 0,20+29*i,m_skin->getPlPart ( Skin::PL_LFILL ) );
+ }
+ paint.drawPixmap ( 0,78+29*sy,m_skin->getPlPart ( Skin::PL_LSBAR ) );
+ for ( int i = 0; i<sx; i++ )
+ {
+ paint.drawPixmap ( 125+i*25,78+sy*29,m_skin->getPlPart ( Skin::PL_SFILL1 ) );
+ }
+
+ paint.drawPixmap ( 125+sx*25,78+sy*29,m_skin->getPlPart ( Skin::PL_RSBAR ) );
+ paint.end();
+
+}
+
+void PlayList::resizeEvent ( QResizeEvent *e )
+{
+ int sx = ( e->size().width()-275 ) /25;
+ int sy = ( e->size().height()-116 ) /29;
+
+ m_titleBar->resize ( 275+25*sx,20 );
+ m_plslider->resize ( 20,58+sy*29 );
+
+ m_listWidget->resize ( 243+25*sx,58+29*sy );
+
+ m_buttonAdd->move ( 11,86+29*sy );
+ m_buttonSub->move ( 40,86+29*sy );
+ m_selectButton->move ( 70,86+29*sy );
+ m_sortButton->move ( 99,86+29*sy );
+
+ m_pl_control->move ( 128+sx*25,100+29*sy );
+ m_playlistButton->move ( 228+sx*25,86+29*sy );
+
+ m_length_totalLength -> move ( 131+sx*25,88+29*sy );
+ m_current_time->move ( 190+sx*25,101+29*sy );
+
+ m_plslider->move ( 255+sx*25,20 );
+}
+void PlayList::mousePressEvent ( QMouseEvent *e )
+{
+ m_pos = e->pos ();
+ if ( ( m_pos.x() > width()-25 ) && ( m_pos.y() > height()-25 ) )
+ {
+ m_resize = TRUE;
+ setCursor ( Qt::SizeFDiagCursor );
+ }
+ else
+ m_resize = FALSE;
+}
+void PlayList::mouseMoveEvent ( QMouseEvent *e )
+{
+ if ( m_resize )
+ {
+ resize ( e->x() +25, e->y() +25 );
+ //usleep(32000);
+ }
+}
+void PlayList::mouseReleaseEvent ( QMouseEvent * )
+{
+ setCursor ( Qt::ArrowCursor );
+ /*if (m_resize)
+ m_listWidget->updateList();*/
+ m_resize = FALSE;
+ Dock::getPointer()->updateDock();
+}
+void PlayList::setModel ( PlayListModel *model )
+{
+ m_playListModel = model;
+ m_listWidget->setModel ( model );
+ m_keyboardManager->setModel ( model );
+ createActions();
+}
+
+void PlayList::changeEvent ( QEvent * event )
+{
+ if ( event->type() == QEvent::ActivationChange )
+ {
+ m_titleBar->setActive ( isActiveWindow() );
+ }
+}
+
+void PlayList::readSettings()
+{
+ if ( m_update )
+ {
+ m_listWidget->readSettings();
+ }
+ else
+ {
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.beginGroup ( "PlayList" );
+ //geometry
+ resize ( settings.value ( "size", QSize ( 275, 116 ) ).toSize() );
+ move ( settings.value ( "pos", QPoint ( 100, 332 ) ).toPoint() );
+ settings.endGroup();
+ m_update = TRUE;
+ }
+
+}
+
+void PlayList::writeSettings()
+{
+ QSettings settings ( QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat );
+ settings.beginGroup ( "PlayList" );
+ //geometry
+ settings.setValue ( "size", size() );
+ settings.setValue ( "pos", this->pos() );
+ settings.endGroup();
+}
+
+void PlayList::showAddMenu()
+{
+ m_addMenu->exec ( m_buttonAdd->mapToGlobal ( QPoint ( 0,0 ) ) );
+}
+
+void PlayList::showSubMenu()
+{
+ m_subMenu->exec ( m_buttonSub->mapToGlobal ( QPoint ( 0,0 ) ) );
+}
+
+void PlayList::showSelectMenu()
+{
+ m_selectMenu->exec ( m_selectButton->mapToGlobal ( QPoint ( 0,0 ) ) );
+}
+
+void PlayList::showSortMenu()
+{
+ m_sortMenu->exec ( m_sortButton->mapToGlobal ( QPoint ( 0,0 ) ) );
+}
+
+
+
+QString PlayList::formatTime ( int sec )
+{
+ int minutes = sec / 60;
+ int seconds = sec % 60;
+
+ QString str_minutes = QString::number ( minutes );
+ QString str_seconds = QString::number ( seconds );
+
+ if ( minutes < 10 ) str_minutes.prepend ( "0" );
+ if ( seconds < 10 ) str_seconds.prepend ( "0" );
+
+ return str_minutes + ":" + str_seconds;
+}
+
+void PlayList::setInfo ( const OutputState &st,int length_current, int length_total )
+{
+ if ( st.type() == OutputState::Info )
+ {
+ m_current_time->display ( formatTime ( st.elapsedSeconds() ) );
+ m_current_time->update();
+
+ QString str_length = formatTime ( length_current ) + "/" + formatTime ( length_total );
+ m_length_totalLength->display ( str_length );
+ m_length_totalLength->update();
+ }
+}
+
+MediaFile *PlayList::currentItem()
+{
+ if ( m_playListModel )
+ return m_playListModel->currentItem();
+ else
+ return 0;
+}
+
+void PlayList::showPlaylistMenu()
+{
+ m_playlistMenu->exec ( m_playlistButton->mapToGlobal ( QPoint ( 0,0 ) ) );
+}
+
+void PlayList::keyPressEvent ( QKeyEvent *ke )
+{
+ if ( m_keyboardManager->handleKeyPress ( ke ) )
+ update();
+}
diff --git a/src/playlist.h b/src/playlist.h
new file mode 100644
index 000000000..d8b4fba2c
--- /dev/null
+++ b/src/playlist.h
@@ -0,0 +1,123 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include <QWidget>
+
+class KeyboardManager;
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QMenu;
+
+class Skin;
+class ListWidget;
+class MediaFile;
+class Button;
+class PlayListModel;
+class PlayListTitleBar;
+class PlayListSlider;
+class MainWindow;
+class SymbolDisplay;
+class OutputState;
+class PixmapWidget;
+class PlaylistControl;
+
+class PlayList : public QWidget
+{
+ Q_OBJECT
+ public:
+ PlayList ( QWidget *parent = 0 );
+
+ ~PlayList();
+ void load ( MediaFile * );
+ void setModel ( PlayListModel * );
+ void readSettings();
+ void setInfo ( const OutputState &,int,int );
+ MediaFile *currentItem();
+ ListWidget* listWidget() const{return m_listWidget;}
+
+ signals:
+ void play();
+ void next();
+ void prev();
+ void pause();
+ void stop();
+ void eject();
+ void loadPlaylist();
+ void savePlaylist();
+ void newPlaylist();
+
+ private slots:
+ void showAddMenu();
+ void showSubMenu();
+ void showSelectMenu();
+ void showSortMenu();
+ void showPlaylistMenu();
+
+
+ private:
+ QString formatTime ( int sec );
+ void drawPixmap ( int, int );
+ void writeSettings();
+ void createMenus();
+ void createActions();
+ QMenu *m_addMenu;
+ QMenu *m_subMenu;
+ QMenu *m_selectMenu;
+ QMenu *m_sortMenu;
+ QMenu *m_playlistMenu;
+ Button *m_buttonAdd;
+ Button *m_buttonSub;
+ Button *m_selectButton;
+ Button *m_sortButton;
+ Button* m_playlistButton;
+
+ PlaylistControl* m_pl_control;
+ SymbolDisplay* m_length_totalLength;
+ SymbolDisplay* m_current_time;
+
+ Skin *m_skin;
+ ListWidget *m_listWidget;
+ PlayListModel *m_playListModel;
+ PlayListTitleBar *m_titleBar;
+ PlayListSlider *m_plslider;
+ QList <QAction *> m_actions;
+ QPoint m_pos;
+ bool m_resize;
+ bool m_update;
+ int m_anchor_row;
+ KeyboardManager* m_keyboardManager;
+
+ protected:
+ virtual void paintEvent ( QPaintEvent * );
+ virtual void resizeEvent ( QResizeEvent * );
+ virtual void mouseMoveEvent ( QMouseEvent * );
+ virtual void mousePressEvent ( QMouseEvent * );
+ virtual void mouseReleaseEvent ( QMouseEvent * );
+ virtual void changeEvent ( QEvent* );
+ virtual void closeEvent ( QCloseEvent* );
+ virtual void keyPressEvent ( QKeyEvent* );
+};
+
+#endif
diff --git a/src/playlistcontrol.cpp b/src/playlistcontrol.cpp
new file mode 100644
index 000000000..8f111fe4e
--- /dev/null
+++ b/src/playlistcontrol.cpp
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QPainter>
+#include <QPaintEvent>
+#include <QMouseEvent>
+
+#include "playlistcontrol.h"
+#include "skin.h"
+
+PlaylistControl::PlaylistControl(QWidget* parent) : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(update()));
+}
+
+void PlaylistControl::paintEvent(QPaintEvent *)
+{
+ QPainter painter(this);
+ painter.drawPixmap(0,0,m_skin->getPlPart(Skin::PL_CONTROL));
+}
+
+void PlaylistControl::mouseReleaseEvent(QMouseEvent *me)
+{
+ QPoint pt = me->pos();
+ if(QRect(4,1,7,7).contains(pt))
+ emit previousClicked();
+ else if(QRect(12,1,7,7).contains(pt))
+ emit playClicked();
+ else if(QRect(21,1,7,7).contains(pt))
+ emit pauseClicked();
+ else if(QRect(31,1,7,7).contains(pt))
+ emit stopClicked();
+ else if(QRect(40,1,7,7).contains(pt))
+ emit nextClicked();
+ else if(QRect(49,1,7,7).contains(pt))
+ emit ejectClicked();
+}
diff --git a/src/playlistcontrol.h b/src/playlistcontrol.h
new file mode 100644
index 000000000..26c3871d4
--- /dev/null
+++ b/src/playlistcontrol.h
@@ -0,0 +1,52 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+ /**
+ @author Vladimir Kuznetsov <vovanec@gmail.ru>
+ */
+
+#ifndef _PALYLISTCONTROL_H
+#define _PALYLISTCONTROL_H
+
+#include "pixmapwidget.h"
+
+class PaintEvent;
+class Skin;
+class QMouseEvent;
+
+class PlaylistControl : public PixmapWidget
+{
+Q_OBJECT
+public:
+ PlaylistControl(QWidget* parent = 0);
+ void paintEvent(QPaintEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+signals:
+ void previousClicked();
+ void nextClicked();
+ void pauseClicked();
+ void playClicked();
+ void stopClicked();
+ void ejectClicked();
+protected:
+ Skin* m_skin;
+};
+
+#endif
diff --git a/src/playlistformat.cpp b/src/playlistformat.cpp
new file mode 100644
index 000000000..505805c88
--- /dev/null
+++ b/src/playlistformat.cpp
@@ -0,0 +1,289 @@
+/***************************************************************************
+* Copyright (C) 2006 by Ilya Kotov *
+* forkotov02@hotmail.ru *
+* *
+* This program is free software; you can redistribute it and/or modify *
+* it under the terms of the GNU General Public License as published by *
+* the Free Software Foundation; either version 2 of the License, or *
+* (at your option) any later version. *
+* *
+* This program is distributed in the hope that it will be useful, *
+* but WITHOUT ANY WARRANTY; without even the implied warranty of *
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+* GNU General Public License for more details. *
+* *
+* You should have received a copy of the GNU General Public License *
+* along with this program; if not, write to the *
+* Free Software Foundation, Inc., *
+* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QFileInfo>
+
+#ifndef XSPF_PLUGIN
+ #include <QDomDocument>
+ #include <QDomElement>
+ #include <QUrl>
+ #include "version.h"
+#endif
+
+#include "playlistformat.h"
+
+#include "mediafile.h"
+
+bool PLSPlaylistFormat::hasFormat(const QString & f)
+{
+ foreach(QString s,m_supported_formats)
+ if(f == s)
+ return true;
+
+ return false;
+}
+
+QStringList PLSPlaylistFormat::getExtensions() const
+{
+ return m_supported_formats;
+}
+
+PLSPlaylistFormat::PLSPlaylistFormat()
+{
+ m_supported_formats << "pls";
+}
+
+QString PLSPlaylistFormat::name() const
+{
+ return "PLSPlaylistFormat";
+}
+
+
+QStringList PLSPlaylistFormat::decode(const QString & contents)
+{
+ QStringList out;
+ QStringList splitted = contents.split("\n");
+ if(!splitted.isEmpty())
+ {
+ if(splitted.takeAt(0).toLower().contains("[playlist]"))
+ {
+ foreach(QString str, splitted)
+ {
+ if(str.startsWith("File"))
+ {
+ QString unverified = str.remove(0,str.indexOf(QChar('=')) + 1);
+ if(QFileInfo(unverified).exists())
+ out << QFileInfo(unverified).absoluteFilePath();
+ else
+ qWarning("File %s does not exist",unverified.toLocal8Bit().data());
+ }
+ }
+ return out;
+ }
+ }
+ else
+ qWarning("Error parsing PLS format");
+
+ return QStringList();
+}
+
+QString PLSPlaylistFormat::encode(const QList< MediaFile * > & contents)
+{
+ QStringList out;
+ out << QString("[playlist]");
+ int counter = 1;
+ foreach(MediaFile* f,contents)
+ {
+ QString begin = "File" + QString::number(counter) + "=";
+ out.append(begin + f->path());
+ begin = "Title" + QString::number(counter) + "=";
+ out.append(begin + f->title());
+ begin = "Length" + QString::number(counter) + "=";
+ out.append(begin + QString::number(f->length()));
+ counter ++;
+ }
+ out << "NumberOfEntries=" + QString::number(contents.count());
+ return out.join("\n");
+}
+
+
+
+
+bool M3UPlaylistFormat::hasFormat(const QString & f)
+{
+ foreach(QString s,m_supported_formats)
+ if(f == s)
+ return true;
+
+ return false;
+}
+
+QStringList M3UPlaylistFormat::getExtensions() const
+{
+ return m_supported_formats;
+}
+
+M3UPlaylistFormat::M3UPlaylistFormat()
+{
+ m_supported_formats << "m3u";
+}
+
+QStringList M3UPlaylistFormat::decode(const QString & contents)
+{
+ QStringList out;
+ QStringList splitted = contents.split("\n");
+ if(!splitted.isEmpty())
+ {
+ if(splitted.takeAt(0).contains("#EXTM3U"))
+ {
+ foreach(QString str, splitted)
+ {
+ if(str.startsWith("#EXTINF:"))
+ ;//TODO: Let's skip it for now...
+ else if(QFileInfo(str).exists())
+ out << QFileInfo(str).absoluteFilePath();
+ else
+ qWarning("File %s does not exist",str.toLocal8Bit().data());
+ }
+ return out;
+ }
+ }
+ else
+ qWarning("Error parsing M3U format");
+
+ return QStringList();
+}
+
+QString M3UPlaylistFormat::encode(const QList< MediaFile * > & contents)
+{
+ QStringList out;
+ out << QString("#EXTM3U");
+ foreach(MediaFile* f,contents)
+ {
+ QString info = "#EXTINF:" + QString::number(f->length()) + "," + f->title();
+ out.append(info);
+ out.append(f->path());
+ }
+ return out.join("\n");
+}
+
+QString M3UPlaylistFormat::name() const
+{
+ return "M3UPlaylistFormat";
+}
+
+#ifndef XSPF_PLUGIN
+
+// Needs more work - it's better use libSpiff there and put it as plugin.
+
+QStringList XSPFPlaylistFormat::decode(const QString & contents)
+{
+ QStringList out;
+ QDomDocument doc;
+ QString errorMsg;
+ int errorCol;
+ int errorRow;
+ bool ok = doc.setContent(contents, &errorMsg, &errorRow, &errorCol);
+
+ if(!ok)
+ qDebug("Parse Error: %s\tRow:%d\tCol%d",
+ qPrintable(errorMsg), errorRow, errorCol );
+
+ QDomElement rootElement = doc.firstChildElement("playlist");
+ if(rootElement.isNull())
+ qWarning("Error parsing XSPF: can't find 'playlist' element");
+
+ QDomElement tracklistElement = rootElement.firstChildElement("trackList");
+ if(tracklistElement.isNull())
+ qWarning("Error parsing XSPF: can't find 'trackList' element");
+
+ QDomElement child = tracklistElement.firstChildElement("track");
+
+ while (!child.isNull())
+ {
+ QString str = QUrl(child.firstChildElement("location").text()).toString(QUrl::RemoveScheme);
+ out << str;
+ child = child.nextSiblingElement();
+ }
+
+ return out;
+}
+
+// Needs more work - it's better use libSpiff there and put it as plugin.
+
+QString XSPFPlaylistFormat::encode(const QList< MediaFile * > & files)
+{
+ QDomDocument doc;
+ QDomElement root = doc.createElement("playlist");
+ root.setAttribute("version",QString("1"));
+ root.setAttribute("xmlns",QString("http://xspf.org/ns/0"));
+
+ QDomElement creator = doc.createElement("creator");
+ QDomText text = doc.createTextNode("qmmp-" + QString(QMMP_STR_VERSION));
+ creator.appendChild(text);
+ root.appendChild(creator);
+
+ QDomElement tracklist = doc.createElement("trackList");
+
+ int counter = 1;
+ foreach(MediaFile* f,files)
+ {
+ QDomElement track = doc.createElement("track");
+
+ QDomElement ch = doc.createElement("location");
+ QDomText text = doc.createTextNode(/*QString("file://") + */QFileInfo(f->path()).absoluteFilePath());
+ ch.appendChild(text);
+ track.appendChild(ch);
+
+ ch = doc.createElement("title");
+ text = doc.createTextNode(f->title());
+ ch.appendChild(text);
+ track.appendChild(ch);
+
+ ch = doc.createElement("trackNum");
+ text = doc.createTextNode(QString::number(counter));
+ ch.appendChild(text);
+ track.appendChild(ch);
+
+ ch = doc.createElement("year");
+ text = doc.createTextNode(QString::number(f->year()));
+ ch.appendChild(text);
+ track.appendChild(ch);
+
+ tracklist.appendChild(track);
+ counter ++;
+ }
+
+ root.appendChild(tracklist);
+ doc.appendChild( root );
+ QString xml_header("<?xml version='1.0' encoding='UTF-8'?>\n");
+ return doc.toString().prepend(xml_header);
+}
+
+XSPFPlaylistFormat::XSPFPlaylistFormat()
+{
+ m_supported_formats << "xspf";
+}
+
+bool XSPFPlaylistFormat::hasFormat(const QString & f)
+{
+ foreach(QString s,m_supported_formats)
+ if(f == s)
+ return true;
+
+ return false;
+}
+
+QStringList XSPFPlaylistFormat::getExtensions() const
+{
+ return m_supported_formats;
+}
+
+
+QString XSPFPlaylistFormat::name() const
+{
+ return "XSPFPlaylistFormat";
+}
+
+#endif
+
+
+
+
diff --git a/src/playlistformat.h b/src/playlistformat.h
new file mode 100644
index 000000000..70af82764
--- /dev/null
+++ b/src/playlistformat.h
@@ -0,0 +1,120 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef _PALYLISTFORMAT_H
+#define _PALYLISTFORMAT_H
+#include <QStringList>
+
+
+class MediaFile;
+/*!
+ * Abstract interface for playlist formats.
+ *
+ * @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+class PlaylistFormat
+{
+public:
+ virtual ~PlaylistFormat(){;}
+ /*!
+ * Takes raw contents of playlist file, should return string list of
+ * ready file pathes to fill the playlist.
+ */
+ virtual QStringList decode(const QString& contents) = 0;
+
+ /*!
+ * Takes the list of MediaFile objects, should return string of
+ * encoded playlist file
+ */
+ virtual QString encode(const QList<MediaFile*>& contents) = 0;
+
+ /*!
+ * Returns list of file extensions that current format supports
+ */
+ virtual QStringList getExtensions()const = 0;
+
+ /*!
+ * Verifies is the \b ext file extension supported by current playlist format.
+ */
+ virtual bool hasFormat(const QString& ext) = 0;
+
+ /// Unique name of playlist format.
+ virtual QString name()const = 0;
+};
+
+Q_DECLARE_INTERFACE(PlaylistFormat,"PlaylistFormatInterface/1.0");
+
+/*!
+ * Class for PLS playlist format parsing
+ */
+class PLSPlaylistFormat : public PlaylistFormat
+{
+public:
+ PLSPlaylistFormat();
+ virtual QStringList getExtensions()const;
+ virtual bool hasFormat(const QString&);
+ virtual QStringList decode(const QString& contents);
+ virtual QString encode(const QList<MediaFile*>& contents);
+ virtual QString name()const;
+protected:
+ QStringList m_supported_formats;
+
+};
+
+
+
+/*!
+ * Class for M3U playlist format parsing
+ */
+class M3UPlaylistFormat : public PlaylistFormat
+{
+ public:
+ M3UPlaylistFormat();
+ virtual QStringList getExtensions()const;
+ virtual bool hasFormat(const QString&);
+ virtual QStringList decode(const QString& contents);
+ virtual QString encode(const QList<MediaFile*>& contents);
+ virtual QString name()const;
+protected:
+ QStringList m_supported_formats;
+};
+
+
+// Format below is made also as plugin - experimental. To enable it
+// uncomment 'CONFIG += XSPF_PLUGIN' line in qmmp.pri
+#ifndef XSPF_PLUGIN
+/*!
+ * Class for XSPF playlist format parsing
+ */
+class XSPFPlaylistFormat : public PlaylistFormat
+{
+ public:
+ XSPFPlaylistFormat();
+ virtual QStringList getExtensions()const;
+ virtual bool hasFormat(const QString&);
+ virtual QStringList decode(const QString& contents);
+ virtual QString encode(const QList<MediaFile*>& contents);
+ virtual QString name()const;
+ protected:
+ QStringList m_supported_formats;
+};
+#endif
+
+#endif
diff --git a/src/playlistmodel.cpp b/src/playlistmodel.cpp
new file mode 100644
index 000000000..f50eca956
--- /dev/null
+++ b/src/playlistmodel.cpp
@@ -0,0 +1,821 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QWidget>
+#include <QFile>
+#include <QDir>
+#include <QtAlgorithms>
+#include <QFileInfo>
+#include <QTextStream>
+#include <QPluginLoader>
+#include <QApplication>
+#include <QTimer>
+
+#include <time.h>
+
+#include <decoder.h>
+#include <decoderfactory.h>
+
+#include "fileloader.h"
+#include "playlistmodel.h"
+#include "mediafile.h"
+#include "playlistformat.h"
+#include "playstate.h"
+
+#include <QMetaType>
+
+#define INVALID_ROW -1
+
+PlayListModel::PlayListModel ( QObject *parent )
+ : QObject ( parent ) , m_selection()
+{
+ qsrand(time(0));
+ m_total_length = 0;
+ m_current = 0;
+ m_block_update_signals = false;
+ is_repeatable_list = false;
+ m_play_state = new NormalPlayState(this);
+ //readSettings();
+
+ registerPlaylistFormat( new PLSPlaylistFormat);
+ registerPlaylistFormat( new M3UPlaylistFormat);
+#ifndef XSPF_PLUGIN
+ registerPlaylistFormat( new XSPFPlaylistFormat);
+#endif
+ loadExternalPlaylistFormats();
+
+ //qRegisterMetaType<MediaFile*>("MediaFileStar");
+}
+
+PlayListModel::~PlayListModel()
+{
+ writeSettings();
+ clear();
+ delete m_play_state;
+ qDeleteAll(m_registered_pl_formats);
+
+ foreach(GuardedFileLoader l,m_running_loaders)
+ {
+ if (!l.isNull())
+ {
+ l->finish();
+ l->wait();
+ }
+ }
+}
+
+void PlayListModel::load ( MediaFile *file )
+{
+ if (m_files.isEmpty())
+ m_currentItem = file;
+
+ m_total_length += file->length();
+ m_files << file;
+
+ //if (!m_block_update_signals)
+ emit listChanged();
+}
+
+int PlayListModel::count()
+{
+ return m_files.size();
+}
+
+MediaFile* PlayListModel::currentItem()
+{
+ if ( m_files.isEmpty() )
+ return 0;
+ else
+ return m_files.at ( m_current );
+}
+
+int PlayListModel::currentRow()
+{
+ return m_current;
+}
+
+bool PlayListModel::setCurrent ( int c )
+{
+ if ( c > count()-1 || c < 0)
+ return FALSE;
+ m_current = c;
+ m_currentItem = m_files.at(c);
+ emit currentChanged();
+ emit listChanged();
+ return TRUE;
+}
+
+
+bool PlayListModel::next()
+{
+ if (isFileLoaderRunning())
+ m_play_state->prepare();
+
+ return m_play_state->next();
+}
+
+bool PlayListModel::previous()
+{
+ if (isFileLoaderRunning())
+ m_play_state->prepare();
+
+ return m_play_state->previous();//)
+}
+
+void PlayListModel::clear()
+{
+ foreach(GuardedFileLoader l,m_running_loaders)
+ {
+ if (!l.isNull())
+ {
+ qWarning("void PlayListModel::clear()");
+ l->finish();
+ l->wait();
+ }
+ }
+
+ m_running_loaders.clear();
+
+ m_current = 0;
+ while ( !m_files.isEmpty() )
+ delete m_files.takeFirst();
+
+ m_total_length = 0;
+ m_play_state->resetState();
+ emit listChanged();
+}
+
+void PlayListModel::clearSelection()
+{
+ for ( int i = 0; i<m_files.size(); ++i )
+ m_files.at ( i )->setSelected ( FALSE );
+ emit listChanged();
+}
+
+QList <QString> PlayListModel::getTitles ( int b,int l )
+{
+ QList <QString> m_titles;
+ for ( int i = b; ( i < b + l ) && ( i < m_files.size() ); ++i )
+ m_titles << m_files.at ( i )->title();
+ return m_titles;
+}
+
+QList <QString> PlayListModel::getTimes ( int b,int l )
+{
+ QList <QString> m_times;
+ for ( int i = b; ( i < b + l ) && ( i < m_files.size() ); ++i )
+ m_times << QString ( "%1" ).arg ( m_files.at ( i )->length() /60 ) +":"
+ +QString ( "%1" ).arg ( m_files.at ( i )->length() %60/10 ) +
+ QString ( "%1" ).arg ( m_files.at ( i )->length() %60%10 );
+ return m_times;
+}
+
+bool PlayListModel::isSelected ( int row )
+{
+ if(m_files.count() > row && row >= 0)
+ return m_files.at ( row )->isSelected();
+
+ return false;
+}
+
+void PlayListModel::setSelected ( int row, bool yes )
+{
+ if(m_files.count() > row && row >= 0)
+ m_files.at ( row )->setSelected ( yes );
+}
+
+void PlayListModel::removeSelected()
+{
+ int i = 0;
+ while ( !m_files.isEmpty() && i<m_files.size() )
+ {
+ if ( m_files.at ( i )->isSelected() )
+ {
+ MediaFile* f = m_files.takeAt ( i );
+ m_total_length -= f->length();
+ if (m_total_length < 0)
+ m_total_length = 0;
+ delete f;
+
+ if ( m_current >= i && m_current!=0 )
+ m_current--;
+ }
+ else
+ i++;
+ }
+ if (!m_files.isEmpty())
+ m_currentItem = m_files.at(m_current);
+
+ m_play_state->prepare();
+
+ emit listChanged();
+}
+
+void PlayListModel::removeUnselected()
+{
+ int i = 0;
+ while ( !m_files.isEmpty() &&i<m_files.size() )
+ {
+ if ( !m_files.at ( i )->isSelected() )
+ {
+ MediaFile* f = m_files.takeAt ( i );
+ m_total_length -= f->length();
+ if (m_total_length < 0)
+ m_total_length = 0;
+ delete f;
+
+ if ( m_current >= i && m_current!=0 )
+ m_current--;
+ }
+ else
+ i++;
+ }
+
+ if (!m_files.isEmpty())
+ m_currentItem = m_files.at(m_current);
+
+ m_play_state->prepare();
+
+ emit listChanged();
+}
+
+void PlayListModel::invertSelection()
+{
+ for ( int i = 0; i<m_files.size(); ++i )
+ m_files.at ( i )->setSelected ( !m_files.at ( i )->isSelected() );
+ emit listChanged();
+}
+
+void PlayListModel::selectAll()
+{
+ for ( int i = 0; i<m_files.size(); ++i )
+ m_files.at ( i )->setSelected ( TRUE );
+ emit listChanged();
+}
+
+void PlayListModel::showDetails()
+{
+ for ( int i = 0; i<m_files.size(); ++i )
+ {
+ if ( m_files.at ( i )->isSelected() )
+ {
+ DecoderFactory *fact = Decoder::findFactory ( m_files.at ( i )->path() );
+ if ( fact )
+ fact->showDetails ( 0, m_files.at ( i )->path() );
+
+ return;
+ }
+ }
+
+}
+
+
+void PlayListModel::readSettings()
+{
+ QFile file ( QDir::homePath() +"/.qmmp/playlist.txt" );
+ file.open ( QIODevice::ReadOnly );
+
+ QStringList files;
+ QByteArray line;
+ m_files.clear();
+
+ while ( !file.atEnd () )
+ {
+ line = file.readLine();
+ files << QString::fromUtf8 ( line ).trimmed ();
+ }
+
+ file.close ();
+
+ int preload = (files.count() < 100) ? files.count() : 100;
+
+ for (int i = 0;i < preload;i++)
+ {
+ load(new MediaFile(files.takeAt(0)));
+ }
+
+
+ if (files.isEmpty())
+ return;
+
+ FileLoader* f_loader = createFileLoader();
+
+ f_loader->setFilesToLoad(files);
+ //f_loader->start(QThread::IdlePriority);
+ QTimer::singleShot(1000,f_loader,SLOT(start()));
+ //m_play_state->prepare();
+}
+
+void PlayListModel::writeSettings()
+{
+ QFile file ( QDir::homePath() +"/.qmmp/playlist.txt" );
+ file.open ( QIODevice::WriteOnly );
+ foreach ( MediaFile* m, m_files )
+ file.write ( m->path().toUtf8 () +"\n" );
+ file.close ();
+}
+
+void PlayListModel::addFile(const QString& path)
+{
+ if (path.isEmpty ())
+ return;
+ if (Decoder::supports(path))
+ load(new MediaFile(path));
+
+ m_play_state->prepare();
+}
+
+FileLoader * PlayListModel::createFileLoader()
+{
+ FileLoader* f_loader = new FileLoader(this);
+// f_loader->setStackSize(20 * 1024 * 1024);
+ m_running_loaders << f_loader;
+ connect(f_loader,SIGNAL(newMediaFile(MediaFile*)),this,SLOT(load(MediaFile*)),Qt::QueuedConnection);
+ connect(f_loader,SIGNAL(finished()),this,SLOT(preparePlayState()));
+ connect(f_loader,SIGNAL(finished()),f_loader,SLOT(deleteLater()));
+ return f_loader;
+}
+
+void PlayListModel::addFiles(const QStringList &files)
+{
+ FileLoader* f_loader = createFileLoader();
+ f_loader->setFilesToLoad(files);
+ f_loader->start(QThread::IdlePriority);
+}
+
+void PlayListModel::addDirectory(const QString& s)
+{
+ FileLoader* f_loader = createFileLoader();
+ f_loader->setDirectoryToLoad(s);
+ f_loader->start(QThread::IdlePriority);
+}
+
+bool PlayListModel::setFileList(const QStringList & l)
+{
+ bool model_cleared = FALSE;
+ foreach(QString str,l)
+ {
+ QFileInfo f_info(str);
+ if (f_info.exists())
+ {
+ if (!model_cleared)
+ {
+ clear();
+ model_cleared = TRUE;
+ }
+ if (f_info.isDir())
+ addDirectory(str);
+ else
+ addFile(str);
+ }
+ // Do the processing the rest of events to avoid GUI freezing
+ QApplication::processEvents(QEventLoop::AllEvents,10);
+ }
+
+ return model_cleared;
+}
+
+int PlayListModel::firstSelectedUpper(int row)
+{
+ for (int i = row - 1;i >= 0;i--)
+ {
+ if (isSelected(i))
+ return i;
+ }
+ return -1;
+}
+
+int PlayListModel::firstSelectedLower(int row)
+{
+ for (int i = row + 1;i < count() ;i++)
+ {
+ if (isSelected(i))
+ return i;
+ }
+ return -1;
+}
+
+void PlayListModel::moveItems( int from, int to )
+{
+ // Get rid of useless work
+ if (from == to)
+ return;
+
+ QList<int> selected_rows = getSelectedRows();
+
+ if (! (bottommostInSelection(from) == INVALID_ROW ||
+ from == INVALID_ROW ||
+ topmostInSelection(from) == INVALID_ROW)
+ )
+ {
+ if (from > to)
+ foreach(int i, selected_rows)
+ if (i + to - from < 0)
+ break;
+ else
+ m_files.move(i,i + to - from);
+ else
+ for (int i = selected_rows.count() - 1; i >= 0; i--)
+ if (selected_rows[i] + to -from >= m_files.count())
+ break;
+ else
+ m_files.move(selected_rows[i],selected_rows[i] + to - from);
+
+ m_current = m_files.indexOf(m_currentItem);
+
+ emit listChanged();
+ }
+}
+
+
+
+int PlayListModel::topmostInSelection( int row)
+{
+ if ( row == 0)
+ return 0;
+
+ for (int i = row - 1;i >= 0;i--)
+ {
+ if (isSelected(i))
+ continue;
+ else
+ return i + 1;
+ }
+ return 0;
+}
+
+int PlayListModel::bottommostInSelection( int row )
+{
+ if (row >= m_files.count() - 1)
+ return row;
+
+ for (int i = row + 1;i < count() ;i++)
+ {
+ if (isSelected(i))
+ continue;
+ else
+ return i - 1;
+ }
+ return count() - 1;
+}
+
+const SimpleSelection& PlayListModel::getSelection(int row )
+{
+ m_selection.m_top = topmostInSelection( row );
+ m_selection.m_anchor = row;
+ m_selection.m_bottom = bottommostInSelection( row );
+ m_selection.m_selected_rows = getSelectedRows();
+ return m_selection;
+}
+
+QList<int> PlayListModel::getSelectedRows() const
+{
+ QList<int>selected_rows;
+ for (int i = 0;i<m_files.count();i++)
+ {
+ if (m_files[i]->isSelected())
+ {
+ selected_rows.append(i);
+ }
+ }
+ return selected_rows;
+}
+
+QList< MediaFile * > PlayListModel::getSelectedItems() const
+{
+ QList<MediaFile*>selected_items;
+ for (int i = 0;i<m_files.count();i++)
+ {
+ if (m_files[i]->isSelected())
+ {
+ selected_items.append(m_files[i]);
+ }
+ }
+ return selected_items;
+}
+
+void PlayListModel::addToQueue()
+{
+ QList<MediaFile*> selected_items = getSelectedItems();
+ foreach(MediaFile* file,selected_items)
+ {/*
+ if (isQueued(file))
+ m_queued_songs.removeAt(m_queued_songs.indexOf(file));
+ else
+ m_queued_songs.append(file);
+ */
+ setQueued(file);
+ }
+ emit listChanged();
+}
+
+void PlayListModel::setQueued(MediaFile* file)
+{
+ if (isQueued(file))
+ m_queued_songs.removeAt(m_queued_songs.indexOf(file));
+ else
+ m_queued_songs.append(file);
+
+ emit listChanged();
+}
+
+bool PlayListModel::isQueued(MediaFile* f) const
+{
+ return m_queued_songs.contains(f);
+}
+
+void PlayListModel::setCurrentToQueued()
+{
+ setCurrent(row(m_queued_songs.at(0)));
+ m_queued_songs.pop_front();
+}
+
+bool PlayListModel::isEmptyQueue() const
+{
+ return m_queued_songs.isEmpty();
+}
+
+void PlayListModel::randomizeList()
+{
+ for (int i = 0;i < m_files.size();i++)
+ m_files.swap(qrand()%m_files.size(),qrand()%m_files.size());
+
+ m_current = m_files.indexOf(m_currentItem);
+ emit listChanged();
+}
+
+void PlayListModel::reverseList()
+{
+ for (int i = 0;i < m_files.size()/2;i++)
+ m_files.swap(i,m_files.size() - i - 1);
+
+ m_current = m_files.indexOf(m_currentItem);
+ emit listChanged();
+}
+
+////===============THE BEGINNING OF SORT IMPLEMENTATION =======================////
+
+// First we'll implement bundle of static compare procedures
+// to sort items in different ways
+static bool _titleLessComparator(MediaFile* s1,MediaFile* s2)
+{
+ return s1->title() < s2->title();
+}
+
+static bool _titleGreaterComparator(MediaFile* s1,MediaFile* s2)
+{
+ return s1->title() > s2->title();
+}
+
+static bool _pathAndFilenameLessComparator(MediaFile* s1,MediaFile* s2)
+{
+ return s1->path() < s2->path();
+}
+
+static bool _pathAndFilenameGreaterComparator(MediaFile* s1,MediaFile* s2)
+{
+ return s1->path() > s2->path();
+}
+
+static bool _filenameLessComparator(MediaFile* s1,MediaFile* s2)
+{
+ QFileInfo i_s1(s1->path());
+ QFileInfo i_s2(s2->path());
+ return i_s1.baseName() < i_s2.baseName();
+}
+
+static bool _filenameGreaterComparator(MediaFile* s1,MediaFile* s2)
+{
+ QFileInfo i_s1(s1->path());
+ QFileInfo i_s2(s2->path());
+ return i_s1.baseName() > i_s2.baseName();
+}
+
+static bool _dateLessComparator(MediaFile* s1,MediaFile* s2)
+{
+ return s1->year() < s2->year();
+}
+
+static bool _dateGreaterComparator(MediaFile* s1,MediaFile* s2)
+{
+ return s1->year() > s2->year();
+}
+
+// This is main sort method
+void PlayListModel::doSort(int sort_mode,QList<MediaFile*>& list_to_sort)
+{
+ QList<MediaFile*>::iterator begin;
+ QList<MediaFile*>::iterator end;
+
+ begin = list_to_sort.begin();
+ end = list_to_sort.end();
+
+ bool (*compareLessFunc)(MediaFile*,MediaFile*) = 0;
+ bool (*compareGreaterFunc)(MediaFile*,MediaFile*) = 0;
+
+ switch (sort_mode)
+ {
+ case TITLE:
+ compareLessFunc = _titleLessComparator;
+ compareGreaterFunc = _titleGreaterComparator;
+ break;
+ case FILENAME:
+ compareLessFunc = _filenameLessComparator;
+ compareGreaterFunc = _filenameGreaterComparator;
+ break;
+ case PATH_AND_FILENAME:
+ compareLessFunc = _pathAndFilenameLessComparator;
+ compareGreaterFunc = _pathAndFilenameGreaterComparator;
+ break;
+ case DATE:
+ compareLessFunc = _dateLessComparator;
+ compareGreaterFunc = _dateGreaterComparator;
+ break;
+ //qWarning("TODO Sort by Date: %s\t%d",__FILE__,__LINE__);
+ default:
+ compareLessFunc = _titleLessComparator;
+ compareGreaterFunc = _titleGreaterComparator;
+ }
+
+ static bool sorted_asc = false;
+ if (!sorted_asc)
+ {
+ qSort(begin,end,compareLessFunc);
+ sorted_asc = true;
+ }
+ else
+ {
+ qSort(begin,end,compareGreaterFunc);
+ sorted_asc = false;
+ }
+
+ m_current = m_files.indexOf(m_currentItem);
+}
+
+void PlayListModel::sortSelection(int mode)
+{
+ QList<MediaFile*>selected_items = getSelectedItems();
+ QList<int>selected_rows = getSelectedRows();
+
+ doSort(mode,selected_items);
+
+ for (int i = 0;i < selected_rows.count();i++)
+ m_files.replace(selected_rows[i],selected_items[i]);
+
+ m_current = m_files.indexOf(m_currentItem);
+ emit listChanged();
+}
+
+void PlayListModel::sort(int mode)
+{
+ doSort(mode,m_files);
+ emit listChanged();
+}
+
+////=============== THE END OF SORT IMPLEMENTATION =======================////
+
+void PlayListModel::prepareForShufflePlaying(bool val)
+{
+ if (m_play_state)
+ delete m_play_state;
+
+ if (val)
+ m_play_state = new ShufflePlayState(this);
+ else
+ m_play_state = new NormalPlayState(this);
+
+}
+
+void PlayListModel::prepareForRepeatablePlaying(bool val)
+{
+ is_repeatable_list = val;
+}
+
+void PlayListModel::doCurrentVisibleRequest()
+{
+ emit currentChanged();
+ emit listChanged();
+}
+
+void PlayListModel::setUpdatesEnabled(bool yes)
+{
+ if (yes)
+ {
+ m_block_update_signals = false;
+ emit listChanged();
+ }
+ else
+ {
+ m_block_update_signals = true;
+ }
+}
+
+void PlayListModel::loadPlaylist(const QString & f_name)
+{
+
+ foreach(PlaylistFormat* prs,m_registered_pl_formats.values())
+ {
+ if (prs->hasFormat(QFileInfo(f_name).completeSuffix().toLower()))
+ {
+ QFile file(f_name);
+ if (file.open(QIODevice::ReadOnly))
+ {
+ clear();
+ addFiles(prs->decode(QTextStream(&file).readAll()));
+ file.close();
+ }
+ else
+ qWarning("Error opening %s",f_name.toLocal8Bit().data());
+ }
+ }
+}
+
+void PlayListModel::savePlaylist(const QString & f_name)
+{
+ foreach(PlaylistFormat* prs,m_registered_pl_formats.values())
+ {
+ if (prs->hasFormat(QFileInfo(f_name).completeSuffix().toLower()))
+ {
+ QFile file(f_name);
+ if (file.open(QIODevice::WriteOnly))
+ {
+ QTextStream ts(&file);
+ ts << prs->encode(m_files);
+ file.close();
+ }
+ else
+ qWarning("Error opening %s",f_name.toLocal8Bit().data());
+ }
+ }
+}
+
+
+void PlayListModel::loadExternalPlaylistFormats()
+{
+ QDir pluginsDir (qApp->applicationDirPath());
+ pluginsDir.cdUp();
+ pluginsDir.cd("Plugins/PlaylistFormats");
+ foreach (QString fileName, pluginsDir.entryList(QDir::Files))
+ {
+ QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
+ QObject *plugin = loader.instance();
+ if (loader.isLoaded())
+ qDebug("PlaylistFormat: plugin loaded - %s", qPrintable(fileName));
+
+ PlaylistFormat *fmt = 0;
+ if (plugin)
+ fmt = qobject_cast<PlaylistFormat *>(plugin);
+
+ if (fmt)
+ if (!registerPlaylistFormat(fmt))
+ qDebug("Warning: Plugin with name %s is already registered...",
+ qPrintable(fmt->name()));
+ }
+}
+
+bool PlayListModel::registerPlaylistFormat(PlaylistFormat* p)
+{
+ QString name = p->name();
+ if (!m_registered_pl_formats.contains(name))
+ {
+ m_registered_pl_formats.insert(name,p);
+ return true;
+ }
+ return false;
+}
+
+bool PlayListModel::isFileLoaderRunning() const
+{
+ foreach(FileLoader* l,m_running_loaders)
+ if (l && l->isRunning())
+ return TRUE;
+
+ return FALSE;
+}
+
+void PlayListModel::preparePlayState()
+{
+ m_play_state->prepare();
+}
+
+
+
+
+
+
diff --git a/src/playlistmodel.h b/src/playlistmodel.h
new file mode 100644
index 000000000..8b520f7c0
--- /dev/null
+++ b/src/playlistmodel.h
@@ -0,0 +1,331 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PLAYLISTMODEL_H
+#define PLAYLISTMODEL_H
+
+#include <QObject>
+#include <QString>
+#include <QStringList>
+#include <QMap>
+#include <QPointer>
+#include <QVector>
+
+//#include "fileloader.h"
+class FileLoader;
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class MediaFile;
+class PlayState;
+class PlaylistFormat;
+class PlayListModel;
+
+struct SimpleSelection
+{
+ SimpleSelection()
+ {
+ ;
+ }
+ inline bool isValid()const
+ {
+ return (m_bottom != -1) && (m_anchor != -1) && (m_top != -1);
+ }
+ inline void dump()const
+ {
+ qWarning("top: %d\tbotom: %d\tanchor: %d",m_top,m_bottom,m_anchor);
+ }
+ inline int count()const
+ {
+ return m_bottom - m_top + 1;
+ }
+ int m_bottom;
+ int m_top;
+ int m_anchor;
+ QList<int>m_selected_rows;
+};
+
+class PlayListModel : public QObject
+{
+ Q_OBJECT
+public:
+ PlayListModel(QObject *parent = 0);
+
+ ~PlayListModel();
+
+ int count();
+ MediaFile* currentItem();
+ int row(MediaFile* f)const
+ {
+ return m_files.indexOf(f);
+ }
+ MediaFile* item(int row)const
+ {
+ return m_files.at(row);
+ }
+ int currentRow();
+ bool setCurrent (int);
+ bool isSelected(int);
+ void setSelected(int, bool);
+
+ bool next();
+ bool previous();
+
+ QList <QString> getTitles(int,int);
+ QList <QString> getTimes(int,int);
+
+ void addFile(const QString&);
+
+ /*!
+ * Adds the list \b l of files to the model.
+ */
+ void addFiles(const QStringList& l);
+
+ /*!
+ * Adds \b dir to the model.
+ */
+ void addDirectory(const QString& dir);
+
+ /*!
+ * Loads list of files (regular files or directories),
+ * returns \b TRUE if at least one file has been successfully loaded,
+ * otherwise \b FALSE
+ */
+ bool setFileList(const QStringList&);
+
+ void moveItems(int from,int to);
+
+ /*!
+ * Returns \b true if \b f file is in play queue, else return \b false
+ */
+ bool isQueued(MediaFile* f) const;
+
+ bool isRepeatableList()const{return is_repeatable_list;}
+
+ /*!
+ * Sets current song to the file that is nex in queue, if queue is empty - does nothing
+ */
+ void setCurrentToQueued();
+
+ /*!
+ * Returns \b true if play queue is empty,otherwise - \b false.
+ */
+ bool isEmptyQueue()const;
+
+ /*!
+ * Returns index of \b f file in queue.e
+ */
+ int queuedIndex(MediaFile* f)const
+ {
+ return m_queued_songs.indexOf(f);
+ }
+
+ /*!
+ * Returns current selection(playlist can contain a lot of selections,
+ * this method returns selection which \b row belongs to)
+ */
+ const SimpleSelection& getSelection(int row);
+
+ /*!
+ * Returns vector with selected rows indexes.
+ */
+ QList<int> getSelectedRows()const;
+ /*!
+ * Returns vector of \b MediaFile pointers that are selected.
+ */
+ QList<MediaFile*> getSelectedItems()const;
+
+ QList<MediaFile*> items()const{return m_files;}
+
+ /*!
+ * Returns number of first item that selected upper the \b row item.
+ */
+ int firstSelectedUpper(int row);
+
+ /*!
+ * Returns number of first item that selected lower the \b row item.
+ */
+ int firstSelectedLower(int row);
+
+ /*!
+ * Returns total lenght in seconds of all songs.
+ */
+ int totalLength()const{return m_total_length;}
+
+ /*!
+ * Registers playlist format parser.
+ */
+ bool registerPlaylistFormat(PlaylistFormat* p);
+
+ /*!
+ * Checks and loads external playlist format plugins
+ */
+ void loadExternalPlaylistFormats();
+
+ /*!
+ * Returns vector of reistered format parsers.
+ */
+ const QList<PlaylistFormat*> registeredPlaylistFormats()const{return m_registered_pl_formats.values();}
+
+ const QStringList registeredPlaylistFormatNames()const{return m_registered_pl_formats.keys();}
+
+ /*!
+ * Loads playlist with \b f_name name.
+ */
+ void loadPlaylist(const QString& f_name);
+
+ /*!
+ * Saves current songs to the playlist with \b f_name name.
+ */
+ void savePlaylist(const QString& f_name);
+
+ /*!
+ * Enum of available sort modes
+ */
+ enum SortMode{ TITLE,FILENAME,PATH_AND_FILENAME,DATE };
+
+signals:
+ void listChanged();
+ void currentChanged();
+
+public slots:
+ void load(MediaFile *);
+ void clear();
+ void clearSelection();
+ void removeSelected();
+ void removeUnselected();
+ void invertSelection();
+ void selectAll();
+ void showDetails();
+ void doCurrentVisibleRequest();
+
+
+ void randomizeList();
+ void reverseList();
+
+ /*!
+ * Prepares model for shuffle playing. \b yes parameter is true - model iterates in shuffle mode.
+ */
+ void prepareForShufflePlaying(bool yes);
+
+ /*!
+ * Prepares model for shuffle playing. \b yes parameter is true - model iterates in repeat mode.
+ */
+ void prepareForRepeatablePlaying(bool);
+
+ /*!
+ * Sorts selected items in \b mode sort mode.
+ */
+ void sortSelection(int mode);
+
+ /*!
+ * Sorts items in \b mode sort mode.
+ */
+ void sort(int mode);
+
+ /*!
+ * Adds selected items to play queue.
+ */
+ void addToQueue();
+
+ /*!
+ * Sets \b f media file to queue.
+ */
+ void setQueued(MediaFile* f);
+
+ void preparePlayState();
+
+private:
+
+ /*!
+ * This internal method performs sorting of \b list_to_sort list of items.
+ */
+ void doSort(int mode,QList<MediaFile*>& list_to_sort);
+ /*!
+ * Returns topmost row in current selection
+ */
+ int topmostInSelection(int);
+
+ /*!
+ * Returns bottommost row in current selection
+ */
+ int bottommostInSelection(int);
+
+ /*!
+ * Creates and initializes file loader object.
+ */
+ FileLoader* createFileLoader();
+
+
+ /*!
+ * Is someone of file loaders is running?
+ */
+ bool isFileLoaderRunning()const;
+
+private:
+ QList <MediaFile*> m_files;
+ MediaFile* m_currentItem;
+
+ int m_current;
+ void readSettings();
+ void writeSettings();
+
+ void setUpdatesEnabled(bool);
+
+ bool updatesEnabled()const{return !m_block_update_signals;}
+
+ /*!
+ * This flyweight object represents current selection.
+ */
+ SimpleSelection m_selection;
+
+ /*!
+ * Songs in play queue.
+ */
+ QList<MediaFile*>m_queued_songs;
+
+ QMap<QString,PlaylistFormat* > m_registered_pl_formats;
+
+ /*!
+ * Is playlist repeatable?
+ */
+ bool is_repeatable_list;
+
+ /// Current playing state (Normal or Shuffle)
+ PlayState* m_play_state;
+
+ bool m_block_update_signals;
+
+ int m_total_length;
+
+ typedef QPointer<FileLoader> GuardedFileLoader;
+
+ /*! Vector of currently running file loaders.
+ * All loaders are automatically sheduled for deletion
+ * when finished.
+ */
+ QVector<GuardedFileLoader> m_running_loaders;
+
+ friend class MainWindow;
+};
+
+
+#endif
diff --git a/src/playlistslider.cpp b/src/playlistslider.cpp
new file mode 100644
index 000000000..098b3bcee
--- /dev/null
+++ b/src/playlistslider.cpp
@@ -0,0 +1,135 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+#include <QResizeEvent>
+#include <math.h>
+
+#include "skin.h"
+#include "playlistslider.h"
+#include "pixmapwidget.h"
+
+PlayListSlider::PlayListSlider(QWidget *parent)
+ : QWidget(parent)
+{
+ m_skin = Skin::getPointer();
+
+ m_moving = FALSE;
+ m_pressed = FALSE;
+ m_min = 0;
+ m_max = 0;
+ m_value = 0;
+ pos = 0;
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+
+PlayListSlider::~PlayListSlider()
+{}
+
+void PlayListSlider::paintEvent(QPaintEvent *)
+{
+ int sy = (height()-58)/29;
+ int p=int(ceil(double(m_value-m_min)*(height()-18)/(m_max-m_min)));
+ QPainter paint(this);
+ paint.drawPixmap(0,0,m_skin->getPlPart(Skin::PL_RFILL));
+ paint.drawPixmap(0,29,m_skin->getPlPart(Skin::PL_RFILL));
+
+ for (int i = 0; i<sy; i++)
+ {
+ paint.drawPixmap(0,58+i*29,m_skin->getPlPart(Skin::PL_RFILL));
+ }
+ if (m_pressed)
+ paint.drawPixmap(5,p,m_skin->getButton(Skin::PL_BT_SCROLL_P));
+ else
+ paint.drawPixmap(5,p,m_skin->getButton(Skin::PL_BT_SCROLL_N));
+ m_pos = p;
+}
+
+void PlayListSlider::mousePressEvent(QMouseEvent *e)
+{
+
+ m_moving = TRUE;
+ press_pos = e->y();
+ if (m_pos<e->y() && e->y()<m_pos+18)
+ {
+ press_pos = e->y()-m_pos;
+ }
+ else
+ {
+ m_value = convert(qMax(qMin(height()-18,e->y()-9),0));
+ press_pos = 9;
+ if (m_value!=m_old)
+ {
+ emit sliderMoved(m_value);
+ m_old = m_value;
+ //qDebug ("%d",m_value);
+ }
+ }
+ m_pressed = TRUE;
+ update();
+}
+
+void PlayListSlider::mouseReleaseEvent(QMouseEvent*)
+{
+ m_moving = FALSE;
+ m_pressed = FALSE;
+ update();
+}
+
+void PlayListSlider::mouseMoveEvent(QMouseEvent* e)
+{
+ if (m_moving)
+ {
+ int po = e->y();
+ po = po - press_pos;
+
+ if (0<=po && po<=height()-18)
+ {
+ m_value = convert(po);
+ update();
+ if (m_value!=m_old)
+ {
+
+ m_old = m_value;
+ emit sliderMoved(m_value);
+ }
+ }
+ }
+}
+
+void PlayListSlider::setPos(int p, int max)
+{
+ m_max = max;
+ m_value = p;
+ if(m_moving)
+ return;
+ update();
+}
+
+void PlayListSlider::updateSkin()
+{
+ update();
+}
+
+int PlayListSlider::convert(int p)
+{
+ return int(floor(double(m_max-m_min)*(p)/(height()-18)+m_min));
+}
+
diff --git a/src/playlistslider.h b/src/playlistslider.h
new file mode 100644
index 000000000..a8eb45c66
--- /dev/null
+++ b/src/playlistslider.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PLAYLISTSLIDER_H
+#define PLAYLISTSLIDER_H
+
+#include <QWidget>
+
+class Skin;
+class PixmapWidget;
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class PlayListSlider : public QWidget
+{
+Q_OBJECT
+public:
+ PlayListSlider(QWidget *parent = 0);
+
+ ~PlayListSlider();
+
+public slots:
+ void setPos(int pos, int max);
+
+signals:
+ void sliderMoved (int);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ PixmapWidget *m_scroll;
+ int m_old;
+ bool m_moving, m_pressed;
+ int press_pos;
+ int m_min, m_max, m_value, pos, m_pos;
+ int convert(int); // value = convert(position);
+
+protected:
+ void paintEvent(QPaintEvent*);
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+};
+
+#endif
diff --git a/src/playlisttitlebar.cpp b/src/playlisttitlebar.cpp
new file mode 100644
index 000000000..9aaed74ca
--- /dev/null
+++ b/src/playlisttitlebar.cpp
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+#include <QResizeEvent>
+#include <QMenu>
+
+#include "dock.h"
+#include "playlisttitlebar.h"
+#include "skin.h"
+
+PlayListTitleBar::PlayListTitleBar(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_active = FALSE;
+ m_skin = Skin::getPointer();
+ resize(275,20);
+ setSizeIncrement(25,1);
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+ m_pl = qobject_cast<PlayList*>(parent);
+ m_mw = qobject_cast<MainWindow*>(m_pl->parent());
+}
+
+
+PlayListTitleBar::~PlayListTitleBar()
+{}
+void PlayListTitleBar::drawPixmap(int sx)
+{
+ QPixmap pixmap(275+sx*25,20);
+ pixmap.fill("black");
+ QPainter paint;
+ paint.begin(&pixmap);
+ if (m_active)
+ {
+ paint.drawPixmap(0,0,m_skin->getPlPart(Skin::PL_CORNER_UL_A));
+ for (int i = 1; i<sx+10; i++)
+ {
+ paint.drawPixmap(25*i,0,m_skin->getPlPart(Skin::PL_TFILL1_A));
+ }
+ paint.drawPixmap(100-12+12*sx,0,m_skin->getPlPart(Skin::PL_TITLEBAR_A));
+ paint.drawPixmap(250+sx*25,0,m_skin->getPlPart(Skin::PL_CORNER_UR_A));
+ }
+ else
+ {
+ paint.drawPixmap(0,0,m_skin->getPlPart(Skin::PL_CORNER_UL_I));
+ for (int i = 1; i<sx+10; i++)
+ {
+ paint.drawPixmap(25*i,0,m_skin->getPlPart(Skin::PL_TFILL1_I));
+ }
+ paint.drawPixmap(100-12+12*sx,0,m_skin->getPlPart(Skin::PL_TITLEBAR_I));
+ paint.drawPixmap(250+sx*25,0,m_skin->getPlPart(Skin::PL_CORNER_UR_I));
+ }
+ paint.end();
+ setPixmap(pixmap);
+}
+
+void PlayListTitleBar::resizeEvent(QResizeEvent *e)
+{
+ int m_sx = (e->size().width()-275)/25;
+ drawPixmap(m_sx);
+
+}
+
+void PlayListTitleBar::mousePressEvent(QMouseEvent* event)
+{
+ switch((int) event->button ())
+ {
+ case Qt::LeftButton:
+ {
+ pos = event->pos();
+ break;
+ }
+ case Qt::RightButton:
+ {
+ m_mw->menu()->exec(event->globalPos());
+ }
+ }
+}
+
+void PlayListTitleBar::mouseReleaseEvent(QMouseEvent*)
+{
+ Dock::getPointer()->updateDock();
+}
+
+void PlayListTitleBar::mouseMoveEvent(QMouseEvent* event)
+{
+ QPoint npos = (event->globalPos()-pos);
+ QPoint oldpos = npos;
+ Dock::getPointer()->move(m_pl, npos);
+}
+
+void PlayListTitleBar::setActive(bool a)
+{
+ m_active = a;
+ int m_sx = (width()-275)/25;
+ drawPixmap(m_sx);
+}
+
+void PlayListTitleBar::updateSkin()
+{
+ drawPixmap((width()-275)/25);
+}
diff --git a/src/playlisttitlebar.h b/src/playlisttitlebar.h
new file mode 100644
index 000000000..bea68c946
--- /dev/null
+++ b/src/playlisttitlebar.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PLAYLISTTITLEBAR_H
+#define PLAYLISTTITLEBAR_H
+
+#include "playlist.h"
+#include "pixmapwidget.h"
+#include "mainwindow.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Skin;
+class MainWindow;
+
+class PlayListTitleBar : public PixmapWidget
+{
+Q_OBJECT
+public:
+ PlayListTitleBar(QWidget *parent = 0);
+
+ ~PlayListTitleBar();
+
+ void setActive(bool);
+
+private slots:
+ void updateSkin();
+
+private:
+ void drawPixmap(int);
+ Skin *m_skin;
+ QPoint pos;
+ bool m_active;
+ PlayList* m_pl;
+ MainWindow* m_mw;
+
+protected:
+ void resizeEvent(QResizeEvent*);
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+};
+
+#endif
diff --git a/src/playstate.cpp b/src/playstate.cpp
new file mode 100644
index 000000000..373619574
--- /dev/null
+++ b/src/playstate.cpp
@@ -0,0 +1,138 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <playstate.h>
+
+ShufflePlayState::ShufflePlayState(PlayListModel * model) : PlayState(model)
+{
+ prepare();
+}
+
+bool ShufflePlayState::next()
+{
+ int itm_count = m_model->items().count();
+
+ if (itm_count > 0)
+ {
+ if (m_shuffled_current >= m_shuffled_indexes.count() -1 )
+ {
+ if (!m_model->isRepeatableList())
+ return FALSE;
+ else
+ prepare();
+ }
+
+ if (m_shuffled_current < m_shuffled_indexes.count() - 1)m_shuffled_current++;
+
+ return m_model->setCurrent(m_shuffled_indexes.at(m_shuffled_current));
+ }
+ return FALSE;
+}
+
+bool ShufflePlayState::previous()
+{
+ int itm_count = m_model->items().count();
+
+ if (itm_count > 0)
+ {
+ if (m_shuffled_current <= 0)
+ {
+ if (!m_model->isRepeatableList())
+ return FALSE;
+ else
+ {
+ prepare();
+ m_shuffled_current = m_shuffled_indexes.count() - 1;
+ }
+ }
+
+ if (itm_count > 1) m_shuffled_current --;
+
+ m_model->setCurrent(m_shuffled_indexes.at(m_shuffled_current));
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void ShufflePlayState::prepare()
+{
+ resetState();
+ for (int i = 0;i < m_model->items().count();i++)
+ {
+ if (i != m_model->currentRow())
+ m_shuffled_indexes << i;
+ }
+
+ for (int i = 0;i < m_shuffled_indexes.count();i++)
+ m_shuffled_indexes.swap(qrand()%m_shuffled_indexes.size(),qrand()%m_shuffled_indexes.size());
+
+ m_shuffled_indexes.prepend(m_model->currentRow());
+}
+
+void ShufflePlayState::resetState()
+{
+ m_shuffled_indexes.clear();
+ m_shuffled_current = 0;
+}
+
+
+
+
+
+NormalPlayState::NormalPlayState(PlayListModel * model) : PlayState(model)
+{}
+
+
+bool NormalPlayState::next()
+{
+ int itm_count = m_model->items().count();
+
+ if (itm_count > 0)
+ {
+ if ( m_model->currentRow() == itm_count - 1)
+ {
+ if (m_model->isRepeatableList())
+ return m_model->setCurrent(0);
+ else
+ return FALSE;
+ }
+ return m_model->setCurrent(m_model->currentRow() + 1);
+ }
+ else
+ return FALSE;
+}
+
+bool NormalPlayState::previous()
+{
+ int itm_count = m_model->items().count();
+
+ if (itm_count > 0)
+ {
+ if ( m_model->currentRow() < 1 && !m_model->isRepeatableList())
+ return FALSE;
+ else if (m_model->setCurrent(m_model->currentRow() - 1))
+ return TRUE;
+ else if (m_model->isRepeatableList())
+ return m_model->setCurrent(m_model->items().count() - 1);
+ }
+
+ return FALSE;
+}
+
diff --git a/src/playstate.h b/src/playstate.h
new file mode 100644
index 000000000..e4af7fa6f
--- /dev/null
+++ b/src/playstate.h
@@ -0,0 +1,109 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef _PLAYSTATE_H
+#define _PLAYSTATE_H
+
+/**
+ @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+
+#include <playlistmodel.h>
+
+/*!
+ * Abstract class that represents data model playing states
+ */
+class PlayState
+{
+public:
+ /*! Makes single step forward through songs list.
+ * If the step has done returns \b true, otherwise \b false
+ */
+ virtual bool next() = 0;
+
+ /*! Makes single step back through songs list.
+ * If the step has done returns \b true, otherwise \b false
+ */
+ virtual bool previous() = 0;
+
+ /*!
+ * Service method, resets state to it's defaults.
+ */
+ virtual void resetState()
+ {
+ ;
+ };
+
+ /*!
+ * Service method, can be used for state initializing.
+ */
+ virtual void prepare()
+ {
+ ;
+ }
+ virtual ~PlayState()
+ {
+ ;
+ }
+ PlayState(PlayListModel* model) : m_model(model)
+ {
+ ;
+ }
+protected:
+
+ /// Data model
+ PlayListModel* m_model;
+};
+
+/*!
+ * Represents normal playing state.
+ * @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+class NormalPlayState : public PlayState
+{
+public:
+ virtual bool next();
+ virtual bool previous();
+ NormalPlayState(PlayListModel* model);
+};
+
+/*!
+ * Represents shuffle playing state.
+ * @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+class ShufflePlayState : public PlayState
+{
+public:
+ virtual bool next();
+ virtual bool previous();
+ virtual void prepare();
+ ShufflePlayState(PlayListModel* model);
+ virtual void resetState();
+private:
+
+ /// Current shuffled index.
+ int m_shuffled_current;
+
+ /// List of indexes used for shuffled playing.
+ QList<int> m_shuffled_indexes;
+};
+
+
+#endif
diff --git a/src/playstatus.cpp b/src/playstatus.cpp
new file mode 100644
index 000000000..913199c4f
--- /dev/null
+++ b/src/playstatus.cpp
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "skin.h"
+#include "playstatus.h"
+
+PlayStatus::PlayStatus ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ m_skin = Skin::getPointer();
+ setStatus ( STOP );
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+
+PlayStatus::~PlayStatus()
+{}
+
+void PlayStatus::setStatus ( Type st )
+{
+ m_status = st;
+ switch ( ( uint ) st )
+ {
+ case PLAY:
+ {
+ setPixmap ( m_skin->getItem ( Skin::PLAY ));
+ break;
+ }
+ case STOP:
+ {
+ setPixmap ( m_skin->getItem ( Skin::STOP ));
+ break;
+ }
+ case PAUSE:
+ {
+ setPixmap ( m_skin->getItem ( Skin::PAUSE ));
+ break;
+ }
+ }
+}
+
+void PlayStatus::updateSkin()
+{
+ setStatus ( m_status );
+}
+
+
diff --git a/src/playstatus.h b/src/playstatus.h
new file mode 100644
index 000000000..b050de1ca
--- /dev/null
+++ b/src/playstatus.h
@@ -0,0 +1,55 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PLAYSTATUS_H
+#define PLAYSTATUS_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Skin;
+
+class PlayStatus : public PixmapWidget
+{
+Q_OBJECT
+public:
+ PlayStatus(QWidget *parent = 0);
+
+ ~PlayStatus();
+
+ enum Type
+ {
+ PLAY,
+ STOP,
+ PAUSE,
+ };
+
+void setStatus(Type);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ Type m_status;
+};
+
+#endif
diff --git a/src/pluginitem.cpp b/src/pluginitem.cpp
new file mode 100644
index 000000000..a0498df34
--- /dev/null
+++ b/src/pluginitem.cpp
@@ -0,0 +1,91 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QSettings>
+#include <QDir>
+
+#include <decoderfactory.h>
+#include <outputfactory.h>
+
+#include "pluginitem.h"
+
+/*Input*/
+InputPluginItem::InputPluginItem(QObject *parent, DecoderFactory *fact,
+ const QString &filePath)
+ : QObject(parent)
+{
+ m_fileName = filePath.section('/',-1);
+ m_factory = fact;
+}
+
+InputPluginItem::~InputPluginItem()
+{}
+
+bool InputPluginItem::isSelected()
+{
+ QSettings settings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat);
+ QStringList blacklist = settings.value("Decoder/disabled_plugins").toStringList();
+ return !blacklist.contains(m_fileName);
+}
+
+DecoderFactory* InputPluginItem::factory()
+{
+ return m_factory;
+}
+
+void InputPluginItem::setSelected(bool select)
+{
+ QSettings settings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat);
+ QStringList blacklist = settings.value("Decoder/disabled_plugins").toStringList();
+ if (select)
+ blacklist.removeAll (m_fileName);
+ else
+ blacklist.append (m_fileName);
+ settings.setValue("Decoder/disabled_plugins", blacklist);
+}
+
+/*Output*/
+OutputPluginItem::OutputPluginItem(QObject *parent, OutputFactory *fact,
+ const QString &filePath): QObject(parent)
+{
+ m_fileName = filePath.section('/',-1);
+ m_factory = fact;
+}
+
+
+OutputPluginItem::~OutputPluginItem()
+{}
+
+void OutputPluginItem::select()
+{
+ QSettings settings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.setValue("Output/plugin_file", m_fileName);
+}
+
+bool OutputPluginItem::isSelected()
+{
+ QSettings settings (QDir::homePath() +"/.qmmp/qmmprc", QSettings::IniFormat);
+ return m_fileName == settings.value("Output/plugin_file","libalsa.so").toString();
+}
+
+OutputFactory *OutputPluginItem::factory()
+{
+ return m_factory;
+}
diff --git a/src/pluginitem.h b/src/pluginitem.h
new file mode 100644
index 000000000..a8877e61c
--- /dev/null
+++ b/src/pluginitem.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PLUGINITEM_H
+#define PLUGINITEM_H
+
+#include <QObject>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class DecoderFactory;
+class OutputFactory;
+
+class InputPluginItem : public QObject
+{
+ Q_OBJECT
+public:
+ InputPluginItem(QObject *parent, DecoderFactory *fact, const QString &filePath);
+
+ ~InputPluginItem();
+
+ bool isSelected();
+ DecoderFactory * factory();
+
+public slots:
+ void setSelected(bool);
+
+private:
+ QString m_fileName;
+ DecoderFactory *m_factory;
+
+};
+
+class OutputPluginItem : public QObject
+{
+ Q_OBJECT
+public:
+ OutputPluginItem(QObject *parent, OutputFactory *fact, const QString &filePath);
+
+ ~OutputPluginItem();
+
+ bool isSelected();
+ OutputFactory * factory();
+
+public slots:
+ void select();
+
+private:
+ QString m_fileName;
+ OutputFactory *m_factory;
+
+};
+
+#endif
diff --git a/src/positionbar.cpp b/src/positionbar.cpp
new file mode 100644
index 000000000..5431fd4f3
--- /dev/null
+++ b/src/positionbar.cpp
@@ -0,0 +1,137 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QMouseEvent>
+#include <QPainter>
+#include <math.h>
+
+#include "skin.h"
+#include "button.h"
+#include "mainwindow.h"
+
+#include "positionbar.h"
+
+
+PositionBar::PositionBar(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+ setPixmap(m_skin->getPosBar());
+ mw = qobject_cast<MainWindow*>(window());
+ m_moving = FALSE;
+ m_min = 0;
+ m_max = 50;
+ m_old = m_value = 0;
+ draw(FALSE);
+}
+
+
+PositionBar::~PositionBar()
+{}
+
+void PositionBar::mousePressEvent(QMouseEvent *e)
+{
+
+ m_moving = TRUE;
+ press_pos = e->x();
+ if(m_pos<e->x() && e->x()<m_pos+29)
+ {
+ press_pos = e->x()-m_pos;
+ }
+ else
+ {
+ m_value = convert(qMax(qMin(width()-30,e->x()-15),0));
+ press_pos = 15;
+ if (m_value!=m_old)
+ {
+ emit sliderMoved(m_value);
+
+ }
+ }
+ draw();
+}
+
+void PositionBar::mouseMoveEvent (QMouseEvent *e)
+{
+ if(m_moving)
+ {
+ int po = e->x();
+ po = po - press_pos;
+
+ if(0<=po && po<=width()-30)
+ {
+ m_value = convert(po);
+ draw();
+ emit sliderMoved(m_value);
+ }
+ }
+}
+
+void PositionBar::mouseReleaseEvent(QMouseEvent*)
+{
+ m_moving = FALSE;
+ draw(FALSE);
+ if (m_value!=m_old)
+ {
+ m_old = m_value;
+ mw->seek(m_value);
+ }
+
+}
+
+void PositionBar::setValue(int v)
+{
+ if (m_moving || m_max == 0)
+ return;
+ m_value = v;
+ draw(FALSE);
+}
+
+void PositionBar::setMax(int max)
+{
+ m_max = max;
+ draw(FALSE);
+}
+
+void PositionBar::updateSkin()
+{
+ draw(FALSE);
+ //setPixmap(m_skin->getPosBar());
+ //setButtonPixmap(Skin::BT_POSBAR_N);
+}
+
+void PositionBar::draw(bool pressed)
+{
+ int p=int(ceil(double(m_value-m_min)*(width()-30)/(m_max-m_min)));
+ m_pixmap = m_skin->getPosBar();
+ QPainter paint(&m_pixmap);
+ if(pressed)
+ paint.drawPixmap(p,0,m_skin->getButton(Skin::BT_POSBAR_P));
+ else
+ paint.drawPixmap(p,0,m_skin->getButton(Skin::BT_POSBAR_N));
+ setPixmap(m_pixmap);
+ m_pos = p;
+}
+
+int PositionBar::convert(int p)
+{
+ return int(ceil(double(m_max-m_min)*(p)/(width()-30)+m_min));
+}
diff --git a/src/positionbar.h b/src/positionbar.h
new file mode 100644
index 000000000..7bb822f6e
--- /dev/null
+++ b/src/positionbar.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef POSITIONBAR_H
+#define POSITIONBAR_H
+
+#include "pixmapwidget.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QMouseEvent;
+
+class MainWindow;
+class Skin;
+
+
+class PositionBar : public PixmapWidget
+{
+ Q_OBJECT
+public:
+ PositionBar(QWidget *parent = 0);
+
+ ~PositionBar();
+
+public slots:
+ void setValue(int);
+ void setMax(int);
+
+signals:
+ void sliderMoved (int);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ bool m_moving;
+ int press_pos;
+ int m_max, m_min, m_pos, m_value, m_old;
+ QPixmap m_pixmap;
+ MainWindow *mw;
+ int convert(int); // value = convert(position);
+ void draw(bool pressed = TRUE);
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+
+
+};
+
+#endif
diff --git a/src/preseteditor.cpp b/src/preseteditor.cpp
new file mode 100644
index 000000000..1b55a229d
--- /dev/null
+++ b/src/preseteditor.cpp
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include "eqpreset.h"
+
+#include "preseteditor.h"
+
+PresetEditor::PresetEditor(QWidget *parent)
+ : QDialog(parent)
+{
+ ui.setupUi(this);
+ setAttribute(Qt::WA_DeleteOnClose);
+ connect(ui.loadButton,SIGNAL(clicked()),SLOT(loadPreset()));
+ connect(ui.deleteButton,SIGNAL(clicked()),SLOT(deletePreset()));
+}
+
+
+PresetEditor::~PresetEditor()
+{
+ while (ui.presetListWidget->count () !=0)
+ ui.presetListWidget->takeItem (0);
+
+ while (ui.autoPresetListWidget->count () !=0)
+ ui.autoPresetListWidget->takeItem (0);
+}
+
+void PresetEditor::addPresets(const QList<EQPreset*> &presets)
+{
+ foreach(QListWidgetItem *item, presets)
+ {
+ ui.presetListWidget->addItem(item);
+ }
+}
+
+void PresetEditor::addAutoPresets(const QList<EQPreset*> &presets)
+{
+ foreach(QListWidgetItem *item, presets)
+ {
+ ui.autoPresetListWidget->addItem(item);
+ }
+}
+
+void PresetEditor::loadPreset()
+{
+ EQPreset* preset = 0;
+ if (ui.tabWidget->currentIndex () == 0)
+ preset = (EQPreset *) ui.presetListWidget->currentItem ();
+ if (ui.tabWidget->currentIndex () == 1)
+ preset = (EQPreset *) ui.autoPresetListWidget->currentItem ();
+ if (preset)
+ emit presetLoaded(preset);
+}
+
+void PresetEditor::deletePreset()
+{
+ EQPreset* preset = 0;
+ if (ui.tabWidget->currentIndex () == 0)
+ preset = (EQPreset *) ui.presetListWidget->currentItem ();
+ if (ui.tabWidget->currentIndex () == 1)
+ preset = (EQPreset *) ui.autoPresetListWidget->currentItem ();
+ if (preset)
+ emit presetDeleted(preset);
+}
diff --git a/src/preseteditor.h b/src/preseteditor.h
new file mode 100644
index 000000000..35302b185
--- /dev/null
+++ b/src/preseteditor.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef PRESETEDITOR_H
+#define PRESETEDITOR_H
+
+#include <QDialog>
+
+#include "ui_preseteditor.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class EQPreset;
+
+class PresetEditor : public QDialog
+{
+Q_OBJECT
+public:
+ PresetEditor(QWidget *parent = 0);
+
+ ~PresetEditor();
+
+ void addPresets(const QList<EQPreset*>&);
+ void addAutoPresets(const QList<EQPreset*>&);
+
+signals:
+ void presetLoaded(EQPreset*);
+ void presetDeleted(EQPreset*);
+
+private slots:
+ void loadPreset();
+ void deletePreset();
+
+private:
+ Ui::PresetEditor ui;
+
+};
+
+#endif
diff --git a/src/preseteditor.ui b/src/preseteditor.ui
new file mode 100644
index 000000000..2f88d6d53
--- /dev/null
+++ b/src/preseteditor.ui
@@ -0,0 +1,88 @@
+<ui version="4.0" >
+ <class>PresetEditor</class>
+ <widget class="QDialog" name="PresetEditor" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>225</width>
+ <height>248</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Preset Editor</string>
+ </property>
+ <property name="modal" >
+ <bool>false</bool>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="1" column="0" >
+ <widget class="QPushButton" name="loadButton" >
+ <property name="text" >
+ <string>Load</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QPushButton" name="deleteButton" >
+ <property name="text" >
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" colspan="2" >
+ <widget class="QTabWidget" name="tabWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Preset</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QListWidget" name="presetListWidget" />
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <attribute name="title" >
+ <string>Auto-preset</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QListWidget" name="autoPresetListWidget" >
+ <property name="editTriggers" >
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qmmpstarter.cpp b/src/qmmpstarter.cpp
new file mode 100644
index 000000000..4aab25da6
--- /dev/null
+++ b/src/qmmpstarter.cpp
@@ -0,0 +1,156 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QApplication>
+#include <QTcpSocket>
+
+#include <unistd.h>
+
+#include "mainwindow.h"
+#include "version.h"
+#include "qmmpstarter.h"
+#include "guard.h"
+
+QMMPStarter::QMMPStarter(int argc,char ** argv,QObject* parent) : QObject(parent),mw(0)
+{
+ QStringList tmp;
+ for(int i = 1;i < argc;i++)
+ tmp << QString::fromLocal8Bit(argv[i]);
+
+ argString = tmp.join("\n");
+
+ if(argString == "--help")
+ {
+ printUsage();
+ exit(0);
+ }
+ else if(argString == "--version")
+ {
+ printVersion();
+ exit(0);
+ }
+
+ if(argString.startsWith("--") && // command?
+ argString != "--play" &&
+ argString != "--previous" &&
+ argString != "--next" &&
+ argString != "--stop" &&
+ argString != "--pause" &&
+ argString != "--play-pause"
+ )
+ {
+ qFatal("QMMP: Unknown command...");
+ exit(1);
+ }
+
+ if(Guard::exists(QApplication::applicationFilePath()))
+ {
+ m_tcpSocket = new QTcpSocket(this);
+ connect(m_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(displayError(QAbstractSocket::SocketError)));
+ connect(m_tcpSocket, SIGNAL(connected()),this, SLOT(writeCommand()));
+
+ m_tcpSocket->connectToHost("127.0.0.1",TCPSERVER_PORT_NUMBER + getuid());
+
+ }
+ else
+ {
+ Guard::create(QApplication::applicationFilePath());
+ QStringList arg_l = argString.split("\n", QString::SkipEmptyParts);
+ mw = new MainWindow(arg_l,0);
+ }
+}
+
+void QMMPStarter::displayError(QAbstractSocket::SocketError socketError)
+{
+ switch (socketError)
+ {
+ case QAbstractSocket::RemoteHostClosedError:
+ break;
+ case QAbstractSocket::HostNotFoundError:
+ qWarning("The host was not found");
+ break;
+ case QAbstractSocket::ConnectionRefusedError:
+ qWarning("The connection was refused by the peer. ");
+ break;
+ default:
+ qWarning("The following error: %s:",qPrintable(m_tcpSocket->errorString()));
+ }
+
+ Guard::create(QApplication::applicationFilePath());
+ mw = new MainWindow(argString.split("\n", QString::SkipEmptyParts),0);
+}
+
+QMMPStarter::~ QMMPStarter()
+{
+ if(mw)
+ {
+ Guard::destroy(QApplication::applicationFilePath());
+ delete mw;
+ }
+}
+
+void QMMPStarter::writeCommand()
+{
+ if(!argString.isEmpty())
+ {
+ char buf[PATH_MAX + 1];
+ QString workingDir = QString(getcwd(buf,PATH_MAX)) + "\n";
+
+ QByteArray barray;
+ barray.append(workingDir);
+ barray.append(argString);
+
+ m_tcpSocket->write(barray);
+ m_tcpSocket->flush();
+ }
+ else
+ {
+ qWarning("It seems that another version of application is already running ...\n");
+ printUsage();
+ }
+
+ m_tcpSocket->close();
+ QApplication::quit();
+}
+
+void QMMPStarter::printUsage()
+{
+ qWarning(
+ "Usage: qmmp [options] [files] \n"
+ "Options:\n"
+ "--------\n"
+ "--help Display this text and exit.\n"
+ "--previous Skip backwards in playlist\n"
+ "--play Start playing current playlist\n"
+ "--pause Pause current song\n"
+ "--play-pause Pause if playing, play otherwise\n"
+ "--stop Stop current song\n"
+ "--next Skip forward in playlist\n"
+ "--version Print version number and exit.\n\n"
+ "Ideas, patches, bugreports send to forkotov02@hotmail.ru\n"
+ );
+}
+
+void QMMPStarter::printVersion()
+{
+ qWarning("QMMP version: %s",QMMP_STR_VERSION);
+}
+
diff --git a/src/qmmpstarter.h b/src/qmmpstarter.h
new file mode 100644
index 000000000..b3094eb69
--- /dev/null
+++ b/src/qmmpstarter.h
@@ -0,0 +1,72 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef _QMMPSTARTER_H
+#define _QMMPSTARTER_H
+
+#include <QObject>
+#include <QAbstractSocket>
+#include <QStringList>
+
+class QTcpSocket;
+class MainWindow;
+
+
+/*!
+ * QMMPStarter represents wrapper object that is responsible
+ * for proper QMMP initialization(only one instance of running
+ * MainWindow) and passing command line args to the TcpServer.
+ * @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+class QMMPStarter : public QObject
+{
+Q_OBJECT
+public:
+ QMMPStarter(int argc,char ** argv,QObject* parent = 0);
+ ~QMMPStarter();
+protected slots:
+ /*!
+ * Displays error message.
+ */
+ void displayError(QAbstractSocket::SocketError socketError);
+
+ /*!
+ * Passes command args to the running TCP server
+ */
+ void writeCommand();
+private:
+ /*!
+ * Prints usage
+ */
+ void printUsage();
+
+ /*!
+ * Prints version of program
+ */
+ void printVersion();
+private:
+ MainWindow* mw;
+ QTcpSocket* m_tcpSocket;
+ QString argString;
+};
+
+#endif
+
+
diff --git a/src/skin.cpp b/src/skin.cpp
new file mode 100644
index 000000000..37e393b25
--- /dev/null
+++ b/src/skin.cpp
@@ -0,0 +1,697 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QCoreApplication>
+#include <QDir>
+#include <QSettings>
+#include <QPainter>
+#include <QPolygon>
+
+#include "skin.h"
+
+
+
+
+Skin *Skin::pointer = 0;
+
+Skin *Skin::getPointer()
+{
+ if ( !pointer )
+ pointer = new Skin();
+ return pointer;
+}
+
+QPixmap Skin::getPixmap ( const QString& name, QDir dir )
+{
+ dir.setFilter ( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
+ QFileInfoList f = dir.entryInfoList();
+ for ( int j = 0; j < f.size(); ++j )
+ {
+ QFileInfo fileInfo = f.at ( j );
+ QString fn = fileInfo.fileName().toLower();
+ if ( fn.section ( ".",0,0 ) == name )
+ {
+ return QPixmap ( fileInfo.filePath() );
+ }
+ }
+ return QPixmap();
+}
+
+Skin::Skin ( QObject *parent )
+ : QObject ( parent )
+{
+ pointer = this;
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ QString path = settings.value("skin_path","").toString();
+ if (path.isEmpty() || !QDir(path).exists ())
+ path = ":/default";
+ setSkin (QDir::cleanPath(path));
+ /* skin directory */
+ QDir skinDir(QDir::homePath()+"/.qmmp");
+ skinDir.mkdir ("skins");
+}
+
+
+Skin::~Skin()
+{}
+
+void Skin::setSkin ( const QString& path )
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.setValue("skin_path",path);
+
+ qDebug ( path.toAscii() ); //TODO don't clear lists
+ m_skin_dir = QDir ( path );
+
+ m_pledit_txt.clear();
+ loadPLEdit();
+ loadMain();
+ buttons.clear();
+ loadButtons();
+ loadShufRep();
+ titlebar.clear();
+ loadTitleBar();
+ loadPosBar();
+ m_numbers.clear();
+ loadNumbers();
+ m_pl_parts.clear();
+ loadPlayList();
+ m_eq_parts.clear();
+ m_eq_bar.clear();
+ m_eq_spline.clear();
+ loadEqMain();
+ loadVisColor();
+ loadLetters();
+ loadMonoSter();
+ loadVolume();
+ loadBalance();
+ loadRegion();
+
+ emit skinChanged();
+}
+
+void Skin::loadMain()
+{
+ QPixmap *pixmap = getPixmap ("main");
+ if (!pixmap)
+ pixmap = getDummyPixmap("main");
+
+ m_main = pixmap->copy ( 0,0,275,116 );
+ delete pixmap;
+}
+
+void Skin::loadButtons()
+{
+
+ QPixmap *pixmap = getPixmap ("cbuttons");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("cbuttons");
+
+ buttons[BT_PREVIOUS_N] = pixmap->copy ( 0, 0,23,18 );
+ buttons[BT_PREVIOUS_P] = pixmap->copy ( 0,18,23,18 );
+
+ buttons[BT_PLAY_N] = pixmap->copy ( 23, 0,23,18 );
+ buttons[BT_PLAY_P] = pixmap->copy ( 23,18,23,18 );
+
+ buttons[BT_PAUSE_N] = pixmap->copy ( 46, 0,23,18 );
+ buttons[BT_PAUSE_P] = pixmap->copy ( 46,18,23,18 );
+
+ buttons[BT_STOP_N] = pixmap->copy ( 69, 0,23,18 );
+ buttons[BT_STOP_P] = pixmap->copy ( 69,18,23,18 );
+
+ buttons[BT_NEXT_N] = pixmap->copy ( 92, 0,22,18 );
+ buttons[BT_NEXT_P] = pixmap->copy ( 92,16,22,18 );
+
+ buttons[BT_EJECT_N] = pixmap->copy ( 114, 0,22,16 );
+ buttons[BT_EJECT_P] = pixmap->copy ( 114,16,22,16 );
+ delete pixmap;
+
+}
+
+void Skin::loadTitleBar()
+{
+
+ QPixmap *pixmap = getPixmap ("titlebar");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("titlebar");
+
+ buttons[BT_MENU_N] = pixmap->copy ( 0,0,9,9 );
+ buttons[BT_MENU_P] = pixmap->copy ( 0,9,9,9 );
+ buttons[BT_MINIMIZE_N] = pixmap->copy ( 9,0,9,9 );
+ buttons[BT_MINIMIZE_P] = pixmap->copy ( 9,9,9,9 );
+ buttons[BT_CLOSE_N] = pixmap->copy ( 18,0,9,9 );
+ buttons[BT_CLOSE_P] = pixmap->copy ( 18,9,9,9 );
+ buttons[BT_SHADE1_N] = pixmap->copy ( 0,18,9,9 );
+ buttons[BT_SHADE1_P] = pixmap->copy ( 9,18,9,9 );
+ buttons[BT_SHADE2_N] = pixmap->copy ( 0,27,9,9 );
+ buttons[BT_SHADE2_P] = pixmap->copy ( 9,27,9,9 );
+ titlebar[TITLEBAR_A] = pixmap->copy ( 27, 0,275,14 );
+ titlebar[TITLEBAR_I] = pixmap->copy ( 27,15,275,14 );
+ titlebar[STATUSBAR_A] = pixmap->copy ( 27,29,275,14 );
+ titlebar[STATUSBAR_I] = pixmap->copy ( 27,42,275,14 );
+ delete pixmap;
+
+}
+
+void Skin::loadPosBar()
+{
+
+ QPixmap *pixmap = getPixmap ("posbar");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("posbar");
+
+ buttons[BT_POSBAR_N] = pixmap->copy ( 248,0,29, pixmap->height());
+ buttons[BT_POSBAR_P] = pixmap->copy ( 278,0,29, pixmap->height());
+ posbar = pixmap->copy ( 0,0,248,pixmap->height() );
+ delete pixmap;
+
+}
+
+void Skin::loadNumbers()
+{
+ QPixmap *pixmap = getPixmap ( "numbers" );
+ if ( !pixmap )
+ {
+ pixmap = getPixmap ( "nums_ex" );
+ }
+ for ( uint i = 0; i < 10; i++ )
+ {
+ m_numbers << pixmap->copy ( i*9, 0, 9, 13 );
+ }
+ if (pixmap->width() > 107)
+ {
+ m_numbers << pixmap->copy(99, 0, 9,13 );
+ }
+ else
+ { // We didn't find "-" symbol. So we have to extract it from "2".
+ // Winamp uses this method too.
+ QPixmap pix = pixmap->copy(90,0,9,13);
+ QPixmap minus = pixmap->copy(18,6,9,1);
+ QPainter paint(&pix);
+ paint.drawPixmap(0,6, minus);
+ m_numbers << pix;
+ }
+ delete pixmap;
+}
+
+void Skin::loadPlayList()
+{
+
+ QPixmap *pixmap = getPixmap ("pledit");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("pledit");
+
+ m_pl_parts[PL_CORNER_UL_A] = pixmap->copy ( 0,0,25,20 );
+ m_pl_parts[PL_CORNER_UL_I] = pixmap->copy ( 0,21,25,20 );
+
+ m_pl_parts[PL_CORNER_UR_A] = pixmap->copy ( 153,0,25,20 );
+ m_pl_parts[PL_CORNER_UR_I] = pixmap->copy ( 153,21,25,20 );
+
+ m_pl_parts[PL_TITLEBAR_A] = pixmap->copy ( 26,0,100,20 );
+ m_pl_parts[PL_TITLEBAR_I] = pixmap->copy ( 26,21,100,20 );
+
+ m_pl_parts[PL_TFILL1_A] = pixmap->copy ( 127,0,25,20 );
+ m_pl_parts[PL_TFILL1_I] = pixmap->copy ( 127,21,25,20 );
+
+ //m_pl_parts[PL_TFILL2_A] = pixmap->copy();//FIXME: �����
+ //m_pl_parts[PL_TFILL2_I] = pixmap->copy();
+
+ m_pl_parts[PL_LFILL] = pixmap->copy ( 0,42,12,29 );
+ m_pl_parts[PL_RFILL] = pixmap->copy ( 31,42,20,29 ); //???
+
+ m_pl_parts[PL_LSBAR] = pixmap->copy ( 0,72,125,38 );
+ m_pl_parts[PL_RSBAR] = pixmap->copy ( 126,72,150,38 );
+ m_pl_parts[PL_SFILL1] = pixmap->copy ( 179,0,25,38 );
+ m_pl_parts[PL_SFILL2] = pixmap->copy ( 250,21,75,38 );
+
+ m_pl_parts[PL_CONTROL] = pixmap->copy(129,94,60,8);
+
+ buttons[PL_BT_ADD] = pixmap->copy ( 11,80,25,18 );
+ buttons[PL_BT_SUB] = pixmap->copy ( 40,80,25,18 );
+ buttons[PL_BT_SEL] = pixmap->copy ( 69,80,25,18 );
+ buttons[PL_BT_SORT] = pixmap->copy ( 98,80,25,18 );
+ buttons[PL_BT_LST] = pixmap->copy(229, 80, 25, 18);
+ buttons[PL_BT_SCROLL_N] = pixmap->copy ( 52,53,8,18 );
+ buttons[PL_BT_SCROLL_P] = pixmap->copy ( 61,53,8,18 );
+
+}
+
+QPixmap *Skin::getPixmap ( const QString& name )
+{
+ m_skin_dir.setFilter ( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
+ QFileInfoList f = m_skin_dir.entryInfoList();
+ for ( int j = 0; j < f.size(); ++j )
+ {
+ QFileInfo fileInfo = f.at ( j );
+ QString fn = fileInfo.fileName().toLower();
+ if ( fn.section ( ".",0,0 ) == name )
+ {
+ return new QPixmap ( fileInfo.filePath() );
+ }
+ }
+ return 0;
+}
+
+void Skin::loadPLEdit()
+{
+ m_skin_dir.setFilter ( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
+ QString path;
+ QFileInfoList list = m_skin_dir.entryInfoList();
+ for ( int i = 0; i < list.size(); ++i )
+ {
+ QFileInfo fileInfo = list.at ( i );
+ if ( fileInfo.fileName().toLower() == "pledit.txt" )
+ {
+ path = fileInfo.filePath ();
+ break;
+ }
+ }
+
+ if ( path.isNull () )
+ {
+ qDebug ( "Skin: Cannot find pledit.txt" );
+ return;
+ }
+
+
+ QFile file ( path );
+
+ if ( !file.open ( QIODevice::ReadOnly | QIODevice::Text ) )
+ return;
+
+ while ( !file.atEnd () )
+ {
+ QByteArray line = file.readLine ();
+ QList<QByteArray> l = line.split ( '=' );
+ if ( l.count () == 2 )
+ {
+ m_pledit_txt[l[0].toLower () ] = l[1].trimmed();
+ }
+ else if ( line.length() == 0 )
+ {
+ break;
+ }
+ }
+
+}
+
+void Skin::loadEqMain()
+{
+ QPixmap *pixmap = getPixmap ("eqmain");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("eqmain");
+
+ m_eq_parts[ EQ_MAIN ] = pixmap->copy ( 0,0,275,116 );
+ m_eq_parts[ EQ_TITLEBAR_A ] = pixmap->copy ( 0,134,275,14 );
+ m_eq_parts[ EQ_TITLEBAR_I ] = pixmap->copy ( 0,149,275,14 );
+ m_eq_parts[ EQ_GRAPH ] = pixmap->copy ( 0,294,113,19 );
+ for ( int i = 0; i < 14; ++i )
+ {
+ m_eq_bar << pixmap->copy ( 13 + i*15,164,14,63 );
+ }
+ for ( int i = 0; i < 14; ++i )
+ {
+ m_eq_bar << pixmap->copy ( 13 + i*15,229,14,63 );
+ }
+ buttons[ EQ_BT_BAR_N ] = pixmap->copy ( 0,164,11,11 );
+ buttons[ EQ_BT_BAR_P ] = pixmap->copy ( 0,164+12,11,11 );
+
+ buttons[ EQ_BT_ON_N ] = pixmap->copy ( 69,119,28,12 );
+ buttons[ EQ_BT_ON_P ] = pixmap->copy ( 128,119,28,12 );
+ buttons[ EQ_BT_OFF_N ] = pixmap->copy ( 10, 119,28,12 );
+ buttons[ EQ_BT_OFF_P ] = pixmap->copy ( 187,119,28,12 );
+
+ buttons[ EQ_BT_PRESETS_N ] = pixmap->copy ( 224,164,44,12 );
+ buttons[ EQ_BT_PRESETS_P ] = pixmap->copy ( 224,176,44,12 );
+
+ buttons[ EQ_BT_AUTO_1_N ] = pixmap->copy ( 94,119,33,12 );
+ buttons[ EQ_BT_AUTO_1_P ] = pixmap->copy ( 153,119,33,12 );
+ buttons[ EQ_BT_AUTO_0_N ] = pixmap->copy ( 35, 119,33,12 );
+ buttons[ EQ_BT_AUTO_0_P ] = pixmap->copy ( 212,119,33,12 );
+
+ for ( int i = 0; i < 19; ++i )
+ {
+ m_eq_spline << pixmap->copy ( 115, 294+i, 1, 1 );
+ }
+ delete pixmap;
+
+}
+
+void Skin::loadVisColor()
+{
+ QList <QColor> colors;
+ m_skin_dir.setFilter ( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
+ QString path;
+ QFileInfoList list = m_skin_dir.entryInfoList();
+ for ( int i = 0; i < list.size(); ++i )
+ {
+ QFileInfo fileInfo = list.at ( i );
+ if ( fileInfo.fileName().toLower() == "viscolor.txt" )
+ {
+ path = fileInfo.filePath ();
+ break;
+ }
+ }
+
+ if ( path.isNull () )
+ {
+ qDebug ( "Skin: Cannot find viscolor.txt" );
+ return;
+ }
+
+
+ QFile file ( path );
+
+ if ( !file.open ( QIODevice::ReadOnly | QIODevice::Text ) )
+ return;
+
+ int j = 0;
+ while ( !file.atEnd () && j<24 )
+ {
+ j++;
+ QByteArray line = file.readLine ();
+ QString tmp = QString::fromAscii ( line );
+ tmp = tmp.trimmed ();
+ int i = tmp.indexOf ( "//" );
+ if ( i>0 )
+ tmp.truncate ( tmp.indexOf ( "//" ) );
+ QStringList list = tmp.split ( "," );
+ if ( list.count () >= 3 )
+ {
+ //colors
+ int r = list.at ( 0 ).toInt();
+ int g = list.at ( 1 ).toInt();
+ int b = list.at ( 2 ).toInt();
+ colors << QColor ( r,g,b );
+ }
+ else if ( line.length() == 0 )
+ {
+ break;
+ }
+ }
+ if ( colors.size() <24 )
+ {
+ qWarning ( "Skin: cannot parse viscolor.txt" );
+ while ( colors.size() <24 )
+ colors << QColor ( 0,0,0 );
+ }
+ m_vis_bars.clear();
+ for ( j = 17; j > 1; --j )
+ m_vis_bars << colors.at ( j );
+
+}
+
+void Skin::loadShufRep()
+{
+ QPixmap *pixmap = getPixmap ("shufrep");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("shufrep");
+
+ buttons[ BT_EQ_ON_N ] = pixmap->copy ( 0,73,23,12 );
+ buttons[ BT_EQ_ON_P ] = pixmap->copy ( 46,73,23,12 );
+ buttons[ BT_EQ_OFF_N ] = pixmap->copy ( 0,61,23,12 );
+ buttons[ BT_EQ_OFF_P ] = pixmap->copy ( 46,61,23,12 );
+
+ buttons[ BT_PL_ON_N ] = pixmap->copy ( 23,73,23,12 );
+ buttons[ BT_PL_ON_P ] = pixmap->copy ( 69,73,23,12 );
+ buttons[ BT_PL_OFF_N ] = pixmap->copy ( 23,61,23,12 );
+ buttons[ BT_PL_OFF_P ] = pixmap->copy ( 69,61,23,12 );
+
+ buttons[REPEAT_ON_N] = pixmap->copy ( 0,30, 28, 15 );
+ buttons[REPEAT_ON_P] = pixmap->copy ( 0,45, 28, 15 );
+
+ buttons[REPEAT_OFF_N] = pixmap->copy ( 0, 0,28,15 );
+ buttons[REPEAT_OFF_P] = pixmap->copy ( 0,15,28,15 );
+
+ buttons[SHUFFLE_ON_N] = pixmap->copy ( 28,30,46,15 );
+ buttons[SHUFFLE_ON_P] = pixmap->copy ( 28,45,46,15 );
+
+ buttons[SHUFFLE_OFF_N] = pixmap->copy ( 28, 0,46,15 );
+ buttons[SHUFFLE_OFF_P] = pixmap->copy ( 28,15,46,15 );
+
+ delete pixmap;
+
+}
+
+void Skin::loadLetters ( void )
+{
+ QPixmap *img = getPixmap("text");
+
+ if (!img)
+ img = getDummyPixmap("text");
+
+ QList<QList<QPixmap> > ( letters );
+ for ( int i = 0; i < 3; i++ )
+ {
+ QList<QPixmap> ( l );
+ for ( int j = 0; j < 31; j++ )
+ {
+ l.append ( img->copy ( j*5, i*6, 5, 6 ) );
+ }
+ letters.append ( l );
+ }
+
+ delete img;
+
+
+ /* alphabet */
+ for ( uint i = 97; i < 123; i++ )
+ {
+ m_letters.insert(i, letters[0][i-97]);
+ }
+
+ /* digits */
+ for ( uint i = 0; i <= 9; i++ )
+ {
+ m_letters.insert ( i+48, letters[1][i] );
+ }
+
+ /* special characters */
+ m_letters.insert('"', letters[0][27]);
+ m_letters.insert('@', letters[0][28]);
+ m_letters.insert(':', letters[1][12]);
+ m_letters.insert('(', letters[1][13]);
+ m_letters.insert(')', letters[1][14]);
+ m_letters.insert('-', letters[1][15]);
+ m_letters.insert('\'', letters[1][16]);
+ m_letters.insert('`', letters[1][16]);
+ m_letters.insert('!', letters[1][17]);
+ m_letters.insert('_', letters[1][18]);
+ m_letters.insert('+', letters[1][19]);
+ m_letters.insert('\\', letters[1][20]);
+ m_letters.insert('/', letters[1][21]);
+ m_letters.insert('[', letters[1][22]);
+ m_letters.insert(']', letters[1][23]);
+ m_letters.insert('^', letters[1][24]);
+ m_letters.insert('&', letters[1][25]);
+ m_letters.insert('%', letters[1][26]);
+ m_letters.insert('.', letters[1][27]);
+ m_letters.insert(',', letters[1][27]);
+ m_letters.insert('=', letters[1][28]);
+ m_letters.insert('$', letters[1][29]);
+ m_letters.insert('#', letters[1][30]);
+
+ m_letters.insert(229, letters[2][0]);
+ m_letters.insert(246, letters[2][1]);
+ m_letters.insert(228, letters[2][2]);
+ m_letters.insert('?', letters[2][3]);
+ m_letters.insert('*', letters[2][4]);
+ m_letters.insert(' ', letters[2][5]);
+
+ /* text background */
+ //m_items->insert (TEXTBG, letters[2][6]);
+}
+
+void Skin::loadMonoSter()
+{
+ QPixmap *pixmap = getPixmap("monoster");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("monoster");
+
+ m_ms_parts.clear();
+ m_ms_parts[ MONO_A ] = pixmap->copy ( 29,0,27,12 );
+ m_ms_parts[ MONO_I ] = pixmap->copy ( 29,12,27,12 );
+ m_ms_parts[ STEREO_A ] = pixmap->copy ( 0,0,27,12 );
+ m_ms_parts[ STEREO_I ] = pixmap->copy ( 0,12,27,12 );
+
+ delete pixmap;
+
+ m_parts.clear();
+ QPainter paint;
+ pixmap = getPixmap("playpaus");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("playpaus");
+
+ QPixmap part(11, 9);
+ paint.begin(&part);
+ paint.drawPixmap (0, 0, 3, 9, *pixmap, 36, 0, 3, 9);
+ paint.drawPixmap (3, 0, 8, 9, *pixmap, 1, 0, 8, 9);
+ paint.end();
+ m_parts [PLAY] = part.copy();
+
+ part = QPixmap(11, 9);
+ paint.begin(&part);
+ paint.drawPixmap (0, 0, 2, 9, *pixmap, 27, 0, 2, 9);
+ paint.drawPixmap (2, 0, 9, 9, *pixmap, 9, 0, 9, 9);
+ paint.end();
+ m_parts [PAUSE] = part.copy();
+
+ part = QPixmap(11, 9);
+ paint.begin(&part);
+ paint.drawPixmap (0, 0, 2, 9, *pixmap, 27, 0, 2, 9);
+ paint.drawPixmap (2, 0, 9, 9, *pixmap, 18, 0, 9, 9);
+ paint.end();
+ m_parts [STOP] = part.copy();
+
+ delete pixmap;
+}
+
+void Skin::loadVolume()
+{
+ QPixmap *pixmap = getPixmap("volume");
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("volume");
+
+ m_volume.clear();
+ for (int i = 0; i < 28; ++i)
+ m_volume.append(pixmap->copy ( 0,i*15,66,13 ));
+ if (pixmap->height() > 425)
+ {
+ buttons [BT_VOL_N] = pixmap->copy (15,422,14, pixmap->height() - 422);
+ buttons [BT_VOL_P] = pixmap->copy (0, 422,14, pixmap->height() - 422);
+ }
+ else
+ {
+ buttons [BT_VOL_N] = QPixmap();
+ buttons [BT_VOL_P] = QPixmap();
+ }
+ delete pixmap;
+}
+
+void Skin::loadBalance()
+{
+ QPixmap *pixmap = getPixmap ( "balance" );
+ if (!pixmap)
+ pixmap = getPixmap ( "volume" );
+
+ if (!pixmap)
+ pixmap = getDummyPixmap("balance");
+
+ m_balance.clear();
+ for (int i = 0; i < 28; ++i)
+ m_balance.append(pixmap->copy ( 9,i*15,38,13 ));
+ if (pixmap->height() > 427)
+ {
+ buttons [BT_BAL_N] = pixmap->copy (15, 422,14,pixmap->height()-422);
+ buttons [BT_BAL_P] = pixmap->copy (0,422,14,pixmap->height()-422);
+ }
+ else
+ {
+ buttons [BT_BAL_N] = QPixmap();
+ buttons [BT_BAL_P] = QPixmap();
+ }
+ delete pixmap;
+}
+
+void Skin::loadRegion()
+{
+ m_mwRegion = QRegion();
+ m_plRegion = QRegion();
+ m_skin_dir.setFilter ( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
+ QString path;
+ QFileInfoList list = m_skin_dir.entryInfoList();
+ for ( int i = 0; i < list.size(); ++i )
+ {
+ QFileInfo fileInfo = list.at ( i );
+ if ( fileInfo.fileName().toLower() == "region.txt" )
+ {
+ path = fileInfo.filePath ();
+ break;
+ }
+ }
+
+ if ( path.isNull () )
+ {
+ qDebug ( "Skin: cannot find region.txt. Transparenty disabled" );
+ return;
+ }
+ m_mwRegion = createRegion(path, "Normal");
+ m_plRegion = createRegion(path, "Equalizer");
+}
+
+QRegion Skin::createRegion(const QString &path, const QString &key)
+{
+ QRegion region;
+ QSettings settings(path, QSettings::IniFormat);
+ QStringList numPoints = settings.value(key+"/NumPoints").toStringList();
+ QStringList value = settings.value(key+"/PointList").toStringList();
+ QStringList numbers;
+ foreach(QString str, value)
+ numbers << str.split(" ", QString::SkipEmptyParts);
+
+ QList <QRegion> regions;
+
+ QList<QString>::iterator n;
+ n = numbers.begin();
+ for (int i = 0; i < numPoints.size(); ++i)
+ {
+ QList <int> lp;
+ for (int j = 0; j < numPoints.at(i).toInt()*2; j++)
+ {
+ lp << n->toInt();
+ n ++;
+ }
+ QVector<QPoint> points;
+
+ for (int l = 0; l < lp.size(); l+=2)
+ {
+ points << QPoint(lp.at(l), lp.at(l+1));
+ }
+ region = region.united(QRegion(QPolygon(points)));
+ }
+ return region;
+}
+
+QPixmap * Skin::getDummyPixmap(const QString& name)
+{
+ QDir dir (":/default");
+ dir.setFilter ( QDir::Files | QDir::Hidden | QDir::NoSymLinks );
+ QFileInfoList f = dir.entryInfoList();
+ for ( int j = 0; j < f.size(); ++j )
+ {
+ QFileInfo fileInfo = f.at ( j );
+ QString fn = fileInfo.fileName().toLower();
+ if ( fn.section ( ".",0,0 ) == name )
+ {
+ return new QPixmap ( fileInfo.filePath() );
+ }
+ }
+ qFatal("Skin:: default skin corrupted");
+ return 0;
+}
+
diff --git a/src/skin.h b/src/skin.h
new file mode 100644
index 000000000..47ca70fb7
--- /dev/null
+++ b/src/skin.h
@@ -0,0 +1,308 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef SKIN_H
+#define SKIN_H
+
+#include <QObject>
+#include <QMap>
+#include <QPixmap>
+#include <QDir>
+#include <QRegion>
+
+/*
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+
+class Skin : public QObject
+{
+ Q_OBJECT
+public:
+ Skin(QObject *parent = 0);
+
+ ~Skin();
+
+ static Skin *getPointer();
+ static QPixmap getPixmap(const QString&, QDir);
+ void setSkin(const QString& path);
+ const QPixmap getMain() const
+ {
+ return m_main;
+ };
+ const QPixmap getButton(uint bt) const
+ {
+ return buttons[bt];
+ };
+ const QPixmap getTitleBar(uint tb) const
+ {
+ return titlebar[tb];
+ };
+ const QPixmap getPosBar() const
+ {
+ return posbar;
+ };
+ const QPixmap getNumber(uint n) const
+ {
+ return m_numbers[n];
+ };
+ /*!
+ * Returns count of numbers in number list.
+ * We need this to check if we have "-" in pixmaps.
+ * if no we should draw it manually.
+ */
+ const uint getNumCount(void) const
+ {
+ return m_numbers.count();
+ }
+ const QPixmap getPlPart(uint p) const
+ {
+ return m_pl_parts[p];
+ };
+ const QPixmap getEqPart(uint p) const
+ {
+ return m_eq_parts[p];
+ };
+ const QPixmap getEqSlider(uint n) const
+ {
+ return m_eq_bar[n];
+ };
+ const QPixmap getEqSpline(uint n) const
+ {
+ return m_eq_spline[n];
+ };
+ const QPixmap getMSPart(uint n) const
+ {
+ return m_ms_parts[n];
+ };
+ const QPixmap getLetter(const QChar& ch)
+ {
+ return m_letters[ch];
+ };
+ const QPixmap getItem(uint n) const
+ {
+ return m_parts[n];
+ };
+ const QPixmap getVolumeBar(int n) const
+ {
+ return m_volume[n];
+ };
+ const QPixmap getBalanceBar(int n) const
+ {
+ return m_balance[n];
+ };
+ const QByteArray getPLValue (QByteArray c) const
+ {
+ return m_pledit_txt[c];
+ };
+ const QColor getVisBarColor(int n) const
+ {
+ return m_vis_bars[n];
+ };
+ const QRegion getMWRegion() const
+ {
+ return m_mwRegion;
+ };
+ const QRegion getPLRegion() const
+ {
+ return m_plRegion;
+ };
+
+ enum Buttons
+ {
+ BT_PREVIOUS_N,
+ BT_PREVIOUS_P,
+ BT_PLAY_N,
+ BT_PLAY_P,
+ BT_PAUSE_N,
+ BT_PAUSE_P,
+ BT_STOP_N,
+ BT_STOP_P,
+ BT_NEXT_N,
+ BT_NEXT_P,
+ BT_EJECT_N,
+ BT_EJECT_P,
+ /*titlebar.* */
+ BT_MENU_N,
+ BT_MENU_P,
+ BT_MINIMIZE_N,
+ BT_MINIMIZE_P,
+ BT_CLOSE_N,
+ BT_CLOSE_P,
+ BT_SHADE1_N,
+ BT_SHADE1_P,
+ BT_SHADE2_N,
+ BT_SHADE2_P,
+ /* posbar.* */
+ BT_POSBAR_N,
+ BT_POSBAR_P,
+ /* pledit.* */
+ PL_BT_ADD,
+ PL_BT_SUB,
+ PL_BT_SEL,
+ PL_BT_SORT,
+ PL_BT_LST,
+ PL_BT_SCROLL_N,
+ PL_BT_SCROLL_P,
+ /* eqmain.* */
+ EQ_BT_BAR_N,
+ EQ_BT_BAR_P,
+ EQ_BT_ON_N,
+ EQ_BT_ON_P,
+ EQ_BT_OFF_N,
+ EQ_BT_OFF_P,
+ EQ_BT_PRESETS_N,
+ EQ_BT_PRESETS_P,
+ EQ_BT_AUTO_1_N,
+ EQ_BT_AUTO_1_P,
+ EQ_BT_AUTO_0_N,
+ EQ_BT_AUTO_0_P,
+ /* shufrep.* */
+ BT_EQ_ON_N,
+ BT_EQ_ON_P,
+ BT_EQ_OFF_N,
+ BT_EQ_OFF_P,
+ BT_PL_ON_N,
+ BT_PL_ON_P,
+ BT_PL_OFF_N,
+ BT_PL_OFF_P,
+ REPEAT_ON_N,
+ REPEAT_ON_P,
+ REPEAT_OFF_N,
+ REPEAT_OFF_P,
+ SHUFFLE_ON_N,
+ SHUFFLE_ON_P,
+ SHUFFLE_OFF_N,
+ SHUFFLE_OFF_P,
+ /* volume.* */
+ BT_VOL_N,
+ BT_VOL_P,
+ /* balance.* */
+ BT_BAL_N,
+ BT_BAL_P,
+ // Playlist play/stop buttons
+ /*BT_PL_PLAY,
+ BT_PL_STOP,
+ BT_PL_PAUSE,
+ BT_PL_NEXT,
+ BT_PL_PREV,
+ BT_PL_EJECT*/
+ };
+ enum TitleBar
+ {
+ TITLEBAR_A,
+ TITLEBAR_I,
+ STATUSBAR_A,
+ STATUSBAR_I
+ };
+ enum PlayList
+ {
+ PL_CORNER_UL_A,
+ PL_CORNER_UL_I,
+ PL_CORNER_UR_A,
+ PL_CORNER_UR_I,
+ PL_TITLEBAR_A,
+ PL_TITLEBAR_I,
+ PL_TFILL1_A,
+ PL_TFILL1_I,
+ PL_TFILL2_A,
+ PL_TFILL2_I,
+ PL_LFILL,
+ PL_RFILL,
+ PL_LSBAR,
+ PL_RSBAR,
+ PL_SFILL1,
+ PL_SFILL2,
+ PL_CONTROL
+ };
+ enum Equalizer
+ {
+ EQ_MAIN,
+ EQ_TITLEBAR_A,
+ EQ_TITLEBAR_I,
+ EQ_GRAPH,
+ };
+ enum MonoSter
+ {
+ MONO_A,
+ MONO_I,
+ STEREO_A,
+ STEREO_I,
+ };
+ enum OtherParts
+ {
+ PLAY,
+ PAUSE,
+ STOP,
+ };
+
+signals:
+ void skinChanged();
+
+private:
+ QPixmap *getPixmap(const QString&);
+
+ /*!
+ * As far as there is no standard in skin making we cannot be sure
+ * that all needful images we can find in skin :( This will cause
+ * segfaults and asserts. So to prevent this we need such method
+ * to load pixmap from default skin.
+ */
+ QPixmap *getDummyPixmap(const QString&);
+ static Skin *pointer;
+ QDir m_skin_dir;
+ QMap<uint, QPixmap> buttons;
+ QMap<uint, QPixmap> titlebar;
+ QMap<uint, QPixmap> m_pl_parts;
+ QMap<uint, QPixmap> m_eq_parts;
+ QMap<uint, QPixmap> m_ms_parts;
+ QMap<uint, QPixmap> m_parts;
+ QMap<QChar, QPixmap> m_letters;
+ QMap<QByteArray, QByteArray> m_pledit_txt;
+ QPixmap m_main;
+ QPixmap posbar;
+ QList<QPixmap> m_numbers;
+ QList<QPixmap> m_eq_bar;
+ QList<QPixmap> m_eq_spline;
+ QList<QPixmap> m_volume;
+ QList<QPixmap> m_balance;
+ QList<QColor> m_vis_bars;
+ QRegion m_mwRegion;
+ QRegion m_plRegion;
+
+ void loadMain();
+ void loadButtons();
+ void loadTitleBar();
+ void loadPosBar();
+ void loadNumbers();
+ void loadPlayList();
+ void loadPLEdit();
+ void loadEqMain();
+ void loadVisColor();
+ void loadShufRep();
+ void loadLetters();
+ void loadMonoSter();
+ void loadVolume();
+ void loadBalance();
+ void loadRegion();
+ QRegion createRegion(const QString &path, const QString &key);
+
+};
+
+#endif
diff --git a/src/src.pro b/src/src.pro
new file mode 100644
index 000000000..908b0a133
--- /dev/null
+++ b/src/src.pro
@@ -0,0 +1,134 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./src
+# ???? - ??????????: ../bin/mp3player
+
+include(../qmmp.pri)
+
+FORMS += configdialog.ui \
+ preseteditor.ui \
+ jumptotrackdialog.ui \
+ aboutdialog.ui
+HEADERS += mainwindow.h \
+ fileloader.h \
+ button.h \
+ display.h \
+ skin.h \
+ titlebar.h \
+ positionbar.h \
+ number.h \
+ playlist.h \
+ mediafile.h \
+ listwidget.h \
+ playlistmodel.h \
+ pixmapwidget.h \
+ playlisttitlebar.h \
+ configdialog.h \
+ playlistslider.h \
+ dock.h \
+ eqwidget.h \
+ eqtitlebar.h \
+ eqslider.h \
+ togglebutton.h \
+ eqgraph.h \
+ mainvisual.h \
+ inlines.h \
+ fft.h \
+ logscale.h \
+ textscroller.h \
+ monostereo.h \
+ playstatus.h \
+ pluginitem.h \
+ volumebar.h \
+ balancebar.h \
+ playstate.h \
+ symboldisplay.h \
+ playlistformat.h \
+ playlistcontrol.h \
+ version.h \
+ tcpserver.h \
+ qmmpstarter.h \
+ guard.h \
+ eqpreset.h \
+ preseteditor.h \
+ jumptotrackdialog.h \
+ aboutdialog.h \
+ timeindicator.h \
+ keyboardmanager.h
+SOURCES += mainwindow.cpp \
+ mp3player.cpp \
+ fileloader.cpp \
+ button.cpp \
+ display.cpp \
+ skin.cpp \
+ titlebar.cpp \
+ positionbar.cpp \
+ number.cpp \
+ playlist.cpp \
+ mediafile.cpp \
+ listwidget.cpp \
+ playlistmodel.cpp \
+ pixmapwidget.cpp \
+ playlisttitlebar.cpp \
+ configdialog.cpp \
+ playlistslider.cpp \
+ dock.cpp \
+ eqwidget.cpp \
+ eqtitlebar.cpp \
+ eqslider.cpp \
+ togglebutton.cpp \
+ eqgraph.cpp \
+ mainvisual.cpp \
+ fft.c \
+ logscale.cpp \
+ textscroller.cpp \
+ monostereo.cpp \
+ playstatus.cpp \
+ pluginitem.cpp \
+ volumebar.cpp \
+ balancebar.cpp \
+ playstate.cpp \
+ symboldisplay.cpp \
+ playlistformat.cpp \
+ playlistcontrol.cpp \
+ qmmpstarter.cpp \
+ tcpserver.cpp \
+ guard.cpp \
+ eqpreset.cpp \
+ preseteditor.cpp \
+ jumptotrackdialog.cpp \
+ aboutdialog.cpp \
+ timeindicator.cpp \
+ keyboardmanager.cpp
+contains(CONFIG,XSPF_PLUGIN){
+ message(*********************************************)
+ message(* XSPF support will be compiled as plugin *)
+ message(*********************************************)
+ DEFINES += XSPF_PLUGIN
+}else {
+ DEFINES -= XSPF_PLUGIN
+ message(*******************************************)
+ message(* XSPF support will be compiled in QMMP *)
+ message(*******************************************)
+ QT += xml
+}
+
+QT += network
+TARGET = ../bin/qmmp.real
+CONFIG += thread release \
+warn_on
+QMAKE_LIBDIR += ../lib
+LIBS += -Wl,-rpath,../lib -lqmmp
+INCLUDEPATH += ../lib
+RESOURCES = images/images.qrc \
+ stuff.qrc
+# translations/qmmp_locales.qrc
+
+#TRANSLATIONS = translations/qmmp_ru.ts \
+# translations/qmmp_tr.ts \
+# translations/qmmp_zh_CN.ts
+TEMPLATE = app
+script.files += ../bin/qmmp
+script.path = /bin
+target.path = /bin
+INSTALLS += target script
diff --git a/src/stuff.qrc b/src/stuff.qrc
new file mode 100644
index 000000000..dfffed67d
--- /dev/null
+++ b/src/stuff.qrc
@@ -0,0 +1,28 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>../COPYING</file>
+ <file>html/about_en.html</file>
+ <file>html/about_ru.html</file>
+ <file>html/authors_en.txt</file>
+ <file>html/thanks_en.txt</file>
+ <file>html/authors_ru.txt</file>
+ <file>html/thanks_ru.txt</file>
+ <file>default/balance.png</file>
+ <file>default/eqmain.png</file>
+ <file>default/numbers.png</file>
+ <file>default/pledit.txt</file>
+ <file>default/text.png</file>
+ <file>default/volume.png</file>
+ <file>default/cbuttons.png</file>
+ <file>default/main.png</file>
+ <file>default/playpaus.png</file>
+ <file>default/posbar.png</file>
+ <file>default/titlebar.png</file>
+ <file>default/eq_ex.png</file>
+ <file>default/monoster.png</file>
+ <file>default/pledit.png</file>
+ <file>default/shufrep.png</file>
+ <file>default/viscolor.txt</file>
+ </qresource>
+</RCC>
diff --git a/src/symboldisplay.cpp b/src/symboldisplay.cpp
new file mode 100644
index 000000000..df55057f2
--- /dev/null
+++ b/src/symboldisplay.cpp
@@ -0,0 +1,87 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+#include <math.h>
+
+#include "skin.h"
+
+#include "symboldisplay.h"
+
+SymbolDisplay::SymbolDisplay ( QWidget *parent, int digits )
+ : PixmapWidget ( parent ), m_digits ( digits ), m_text(), m_max(0)
+{
+ m_alignment = Qt::AlignRight;
+ m_skin = Skin::getPointer();
+ connect ( m_skin, SIGNAL ( skinChanged() ), this, SLOT (draw()));
+ draw();
+ for(int i=0; i<m_digits; ++i)
+ m_max += 9 * (int) exp10(i);
+}
+
+
+SymbolDisplay::~SymbolDisplay()
+{}
+
+void SymbolDisplay::display (const QString& str)
+{
+ m_text = str;
+ if (!str.isEmpty())
+ draw();
+}
+
+void SymbolDisplay::draw()
+{
+ QString str = m_text;
+ QPixmap bg = m_skin->getLetter ( ' ' );
+ int w = bg.size().width();
+ int h = bg.size().height();
+ QPixmap tmp ( m_digits*w,h );
+ QPainter paint ( &tmp );
+ int j;
+ for ( int i = 0; i < m_digits; ++i )
+ {
+ if (m_alignment == Qt::AlignRight) // TODO: add align Center
+ {
+ j = str.size() -1 - i;
+ if ( j >= 0 )
+ paint.drawPixmap ( ( m_digits-1-i ) *w,0,m_skin->getLetter ( str.at ( j ) ) );
+ else
+ paint.drawPixmap ( ( m_digits-1-i ) *w,0,m_skin->getLetter ( ' ' ) );
+ }
+ else
+ {
+ if (i < str.size())
+ paint.drawPixmap ( i * w,0,m_skin->getLetter ( str.at ( i ) ) );
+ else
+ paint.drawPixmap ( i * w,0,m_skin->getLetter ( ' ' ) );
+ ;
+ }
+ }
+ setPixmap(tmp);
+}
+
+void SymbolDisplay::display(int val)
+{
+ if(val < m_max)
+ display(QString::number(val));
+ else
+ display(QString("%1h").arg(val/100));
+}
+
diff --git a/src/symboldisplay.h b/src/symboldisplay.h
new file mode 100644
index 000000000..065579b7a
--- /dev/null
+++ b/src/symboldisplay.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef SYMBOLDISPLAY_H
+#define SYMBOLDISPLAY_H
+
+#include <QPixmap>
+
+#include "pixmapwidget.h"
+
+/**
+ @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+
+class Skin;
+
+class SymbolDisplay : public PixmapWidget
+{
+ Q_OBJECT
+public:
+ SymbolDisplay(QWidget *parent = 0, int digits = 3);
+
+ ~SymbolDisplay();
+ void display(const QString&);
+ void display(int);
+ void setAlignment(Qt::Alignment a)
+ {
+ m_alignment = a;
+ }
+ Qt::Alignment alignment()const
+ {
+ return m_alignment;
+ }
+
+private slots:
+ void draw();
+
+private:
+ Skin* m_skin;
+ QPixmap m_pixmap;
+ int m_digits;
+ QString m_text;
+ Qt::Alignment m_alignment;
+ int m_max;
+
+};
+
+#endif
diff --git a/src/tcpserver.cpp b/src/tcpserver.cpp
new file mode 100644
index 000000000..735df2716
--- /dev/null
+++ b/src/tcpserver.cpp
@@ -0,0 +1,69 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QStringList>
+#include <QApplication>
+#include <QTcpSocket>
+#include <QRegExp>
+#include <QTimer>
+
+#include <unistd.h>
+
+#include "mainwindow.h"
+#include "tcpserver.h"
+#include "version.h"
+
+TcpServer::TcpServer(MainWindow* parent) : QTcpServer(parent) , m_mainWindow(parent)
+{
+ if (!listen(QHostAddress::LocalHost,TCPSERVER_PORT_NUMBER + getuid()))
+ {
+ qFatal("Unable to start the server: %s ",qPrintable(errorString()));
+ QApplication::exit(1);
+ }
+
+ connect(this, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
+ qDebug("TcpServer running at localost port %d",TCPSERVER_PORT_NUMBER + getuid());
+}
+
+void TcpServer::handleNewConnection()
+{
+ clientConnection = nextPendingConnection();
+ connect(clientConnection, SIGNAL(readyRead()),this, SLOT(readCommand()));
+}
+
+
+void TcpServer::readCommand()
+{
+ QByteArray inputArray(clientConnection->readAll());
+ QStringList slist = QString(inputArray).split("\n",QString::SkipEmptyParts);
+ QString cwd = slist.takeAt(0);
+ m_mainWindow->processCommandArgs(slist,cwd);
+
+ if(clientConnection)
+ {
+ clientConnection->disconnectFromHost();
+ if(clientConnection)
+ delete clientConnection;
+ }
+}
+
+
+
+
diff --git a/src/tcpserver.h b/src/tcpserver.h
new file mode 100644
index 000000000..4438f712c
--- /dev/null
+++ b/src/tcpserver.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef _TCPSERVER_H
+#define _TCPSERVER_H
+
+#include <QTcpServer>
+#include <QPointer>
+
+class TcpSocket;
+class MainWindow;
+
+/*!
+ * Class TcpServer represents server for frocessing external command line args.
+ @author Vladimir Kuznetsov <vovanec@gmail.com>
+ */
+
+class TcpServer : public QTcpServer
+{
+Q_OBJECT
+public:
+ TcpServer(MainWindow* parent = 0);
+protected slots:
+ void handleNewConnection();
+ /*!
+ * Reads external commands and dispatches it to the MainWindow
+ */
+ void readCommand();
+private:
+ MainWindow* m_mainWindow;
+ QPointer <QTcpSocket>clientConnection;
+};
+
+#endif
diff --git a/src/textscroller.cpp b/src/textscroller.cpp
new file mode 100644
index 000000000..98460b62b
--- /dev/null
+++ b/src/textscroller.cpp
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+#include <QTimer>
+#include <QSettings>
+
+#include "skin.h"
+#include "textscroller.h"
+#include "version.h"
+
+TextScroller *TextScroller::pointer = 0;
+
+TextScroller *TextScroller::getPointer()
+{
+ return pointer;
+}
+
+
+TextScroller::TextScroller ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ pointer = this;
+ m_skin = Skin::getPointer();
+ m_pixmap = QPixmap ( 154,15 );
+ x = 0;
+ m_text = "Qt-based Multimedia Player (Qmmp " + QString(QMMP_STR_VERSION) + ")";
+ m_update = FALSE;
+ readSettings();
+ m_timer = new QTimer ( this );
+ connect ( m_timer, SIGNAL ( timeout() ),SLOT ( addOffset() ) );
+ m_timer -> start ( 50 );
+ updateSkin();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+
+TextScroller::~TextScroller()
+{}
+
+void TextScroller::addOffset()
+{
+ m_pixmap.fill ( Qt::transparent );
+ QPainter paint ( &m_pixmap );
+ x--;
+ paint.setPen(m_color);
+ paint.setFont(m_font);
+ paint.drawText ( 154+x,12, m_text );
+ paint.drawText ( 154+x+m_metrics->width ( m_text ) + 15,12, m_text );
+ if ( 154 + x < - m_metrics->width ( m_text ) - 15 +1)
+ {
+ x=-154;
+ }
+ setPixmap ( m_pixmap );
+}
+
+void TextScroller::setText(const QString& text)
+{
+ if (m_text != text)
+ {
+ m_text = text;
+ x = -50;
+ }
+}
+
+void TextScroller::updateSkin()
+{
+ m_color.setNamedColor(m_skin->getPLValue("normal"));
+}
+
+void TextScroller::readSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ QString fontname = settings.value("MainWindow/Font","").toString();
+ if (fontname.isEmpty ())
+ fontname = QFont("Helvetica [Cronyx]", 9).toString();
+ m_font.fromString(fontname);
+
+ if (m_update)
+ {
+ delete m_metrics;
+ m_metrics = new QFontMetrics(m_font);
+ }
+ else
+ {
+ m_update = TRUE;
+ m_metrics = new QFontMetrics(m_font);
+ }
+}
diff --git a/src/textscroller.h b/src/textscroller.h
new file mode 100644
index 000000000..ef469cd15
--- /dev/null
+++ b/src/textscroller.h
@@ -0,0 +1,62 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef TEXTSCROLLER_H
+#define TEXTSCROLLER_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class QTimer;
+
+class Skin;
+
+class TextScroller : public PixmapWidget
+{
+Q_OBJECT
+public:
+ TextScroller(QWidget *parent = 0);
+
+ ~TextScroller();
+
+ static TextScroller *getPointer();
+ void setText(const QString&);
+ void readSettings();
+
+private slots:
+ void addOffset();
+ void updateSkin();
+
+private:
+ bool m_update;
+ static TextScroller *pointer;
+ QPixmap m_pixmap;
+ int x;
+ QFont m_font;
+ QFontMetrics *m_metrics;
+ QString m_text;
+ Skin *m_skin;
+ QColor m_color;
+ QTimer *m_timer;
+};
+
+#endif
diff --git a/src/timeindicator.cpp b/src/timeindicator.cpp
new file mode 100644
index 000000000..3e2b04ee0
--- /dev/null
+++ b/src/timeindicator.cpp
@@ -0,0 +1,125 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QPainter>
+#include <QSettings>
+
+#include "skin.h"
+#include "timeindicator.h"
+
+TimeIndicator::TimeIndicator ( QWidget *parent )
+ : PixmapWidget ( parent )
+{
+ m_skin = Skin::getPointer();
+ m_pixmap = QPixmap ( 65,20 );
+ m_elapsed = true;
+ m_time = m_songDuration = 0;
+ readSettings();
+ m_needToShowTime = false;
+ updateSkin();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+void TimeIndicator::setTime ( int t )
+{
+ m_time = t;
+ m_pixmap.fill ( Qt::transparent );
+ QPainter paint ( &m_pixmap );
+
+ if (!m_elapsed)
+ {
+ t = m_songDuration - t;
+ paint.drawPixmap(QPoint(2,0),m_skin->getNumber( 10 ));
+ }
+ if(t < 0)
+ t = 0;
+
+ paint.drawPixmap(QPoint(13,0),m_skin->getNumber( t/600%10 ));
+ paint.drawPixmap(QPoint(26,0),m_skin->getNumber( t/60%10 ));
+ paint.drawPixmap(QPoint(43,0),m_skin->getNumber( t%60/10 ));
+ paint.drawPixmap(QPoint(56,0),m_skin->getNumber( t%60%10 ));
+
+ setPixmap ( m_pixmap );
+
+}
+
+void TimeIndicator::reset()
+{
+ m_pixmap.fill ( Qt::transparent );
+ QPainter paint ( &m_pixmap );
+ setPixmap ( m_pixmap );
+}
+
+void TimeIndicator::mousePressEvent(QMouseEvent* e )
+{
+ if (m_needToShowTime)
+ {
+ m_elapsed = m_elapsed ? false : true;
+ setTime(m_time);
+ PixmapWidget::mousePressEvent(e);
+ }
+}
+
+void TimeIndicator::setSongDuration(int d)
+{
+ m_songDuration = d;
+}
+
+TimeIndicator::~TimeIndicator()
+{
+ writeSettings();
+}
+
+
+void TimeIndicator::updateSkin()
+{
+ if (m_needToShowTime)
+ setTime(m_time);
+}
+
+void TimeIndicator::readSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("Display");
+ m_elapsed = settings.value("Elapsed",true).toBool();
+ settings.endGroup();
+}
+
+
+void TimeIndicator::writeSettings()
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("Display");
+ settings.setValue("Elapsed",m_elapsed);
+ settings.endGroup();
+}
+
+
+void TimeIndicator::setNeedToShowTime(bool need)
+{
+ m_needToShowTime = need;
+ if (!need) reset();
+}
+
+void TimeIndicator::mouseMoveEvent(QMouseEvent *)
+{}
+
+void TimeIndicator::mouseReleaseEvent(QMouseEvent *)
+{}
+
diff --git a/src/timeindicator.h b/src/timeindicator.h
new file mode 100644
index 000000000..19981a4b3
--- /dev/null
+++ b/src/timeindicator.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef TIMEINDICATOR_H
+#define TIMEINDICATOR_H
+
+#include <pixmapwidget.h>
+
+class QMouseEvent;
+
+class Skin;
+
+
+/** Class TimeIndicator
+ * @author Vladimir Kuznetsov <vovanec@gmail.com>
+ *
+ * Represents time indicator in the main display. Can show elapsed
+ * and rest time of song (mouse press on indicator changes mode)
+ */
+class TimeIndicator : public PixmapWidget
+{
+ Q_OBJECT
+public:
+ TimeIndicator(QWidget *parent = 0);
+ ~TimeIndicator();
+ void setTime ( int t );
+ void setSongDuration(int);
+ void setNeedToShowTime(bool);
+protected:
+ virtual void mousePressEvent(QMouseEvent*);
+ virtual void mouseMoveEvent(QMouseEvent*);
+ virtual void mouseReleaseEvent(QMouseEvent*);
+ void writeSettings();
+ void readSettings();
+ void reset();
+private slots:
+ void updateSkin();
+private:
+ QPixmap m_pixmap;
+ Skin *m_skin;
+ int m_time;
+ int m_songDuration;
+ bool m_elapsed;
+ bool m_needToShowTime;
+};
+
+#endif
diff --git a/src/titlebar.cpp b/src/titlebar.cpp
new file mode 100644
index 000000000..db9b272fc
--- /dev/null
+++ b/src/titlebar.cpp
@@ -0,0 +1,111 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QMainWindow>
+#include <QApplication>
+#include <QMouseEvent>
+#include <QMenu>
+
+#include "titlebar.h"
+#include "skin.h"
+#include "button.h"
+#include "dock.h"
+
+TitleBar::TitleBar(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ skin = Skin::getPointer();
+ setPixmap(skin->getTitleBar(Skin::TITLEBAR_A));
+ mw = qobject_cast<MainWindow*>(parent);
+ //buttons
+ menu = new Button(this,Skin::BT_MENU_N,Skin::BT_MENU_P);
+ menu->move(6,3);
+ minimize = new Button(this,Skin::BT_MINIMIZE_N,Skin::BT_MINIMIZE_P);
+ minimize->move(244,3);
+ connect(minimize, SIGNAL(clicked()), mw, SLOT(showMinimized()));
+ shade = new Button(this,Skin::BT_SHADE1_N,Skin::BT_SHADE1_P);
+ shade->move(254,3);
+ close = new Button(this,Skin::BT_CLOSE_N,Skin::BT_CLOSE_P);
+ close->move(264,3);
+ connect(close, SIGNAL(clicked()), mw, SLOT(handleCloseRequest()));
+ setActive(FALSE);
+ connect(skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+}
+
+
+TitleBar::~TitleBar()
+{}
+
+
+
+void TitleBar::mousePressEvent(QMouseEvent* event)
+{
+ switch((int) event->button ())
+ {
+ case Qt::LeftButton:
+ {
+ pos = event->pos();
+ PlayList *pl = mw->getPLPointer();
+ Dock::getPointer()->calculateDistances();
+ x_diff = - mw->x() + pl->x();
+ y_diff = - mw->y() + pl->y();
+ break;
+ }
+ case Qt::RightButton:
+ {
+ mw->menu()->exec(event->globalPos());
+ }
+ }
+}
+
+void TitleBar::mouseReleaseEvent(QMouseEvent*)
+{
+ Dock::getPointer()->updateDock();
+}
+void TitleBar::mouseMoveEvent(QMouseEvent* event)
+{
+ QPoint npos = (event->globalPos()-pos);
+ Dock::getPointer()->move(mw, npos);
+}
+
+void TitleBar::setActive(bool a)
+{
+ if(a)
+ {
+ setPixmap(skin->getTitleBar(Skin::TITLEBAR_A));
+ menu->show();
+ minimize->show();
+ shade->show();
+ close->show();
+ }
+ else
+ {
+ setPixmap(skin->getTitleBar(Skin::TITLEBAR_I));
+ menu->hide();
+ minimize->hide();
+ shade->hide();
+ close->hide();
+ }
+}
+
+void TitleBar::updateSkin()
+{
+ setActive(FALSE);
+}
diff --git a/src/titlebar.h b/src/titlebar.h
new file mode 100644
index 000000000..7b8874f17
--- /dev/null
+++ b/src/titlebar.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef TITLEBAR_H
+#define TITLEBAR_H
+
+#include <QMainWindow>
+#include "pixmapwidget.h"
+
+#include "playlist.h"
+#include <QPoint>
+#include "mainwindow.h"
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class MainWindow;
+class QMouseEvent;
+
+class Skin;
+class Button;
+
+
+class TitleBar : public PixmapWidget
+{
+Q_OBJECT
+public:
+ TitleBar(QWidget *parent = 0);
+
+ ~TitleBar();
+
+ void setActive(bool);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *skin;
+ QPoint pos;
+ MainWindow *mw;
+ Button *menu;
+ Button *minimize;
+ Button *shade;
+ Button *close;
+ int x_diff, y_diff;
+
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+};
+
+
+
+#endif
diff --git a/src/togglebutton.cpp b/src/togglebutton.cpp
new file mode 100644
index 000000000..0405ab344
--- /dev/null
+++ b/src/togglebutton.cpp
@@ -0,0 +1,74 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "skin.h"
+#include "togglebutton.h"
+
+
+ToggleButton::ToggleButton ( QWidget *parent,uint on_n,uint on_p,uint off_n,uint off_p )
+ : PixmapWidget ( parent )
+{
+ m_on_n = on_n;
+ m_on_p = on_p;
+ m_off_n = off_n;
+ m_off_p = off_p;
+ m_on = FALSE;
+ skin = Skin::getPointer();
+ setON ( FALSE );
+ connect ( skin, SIGNAL ( skinChanged() ), this, SLOT ( updateSkin() ) );
+}
+
+
+ToggleButton::~ToggleButton()
+{}
+
+bool ToggleButton::isChecked()
+{
+ return m_on;
+}
+
+void ToggleButton::updateSkin()
+{
+ //setPixmap ( skin->getButton ( name_normal ) );
+ setON ( m_on );
+}
+
+void ToggleButton::setON ( bool on )
+{
+ m_on = on;
+ if ( on )
+ setPixmap ( skin->getButton ( m_on_n ) );
+ else
+ setPixmap ( skin->getButton ( m_off_n ) );
+}
+void ToggleButton::mousePressEvent ( QMouseEvent* )
+{
+ if ( m_on )
+ setPixmap ( skin->getButton ( m_off_p ) );
+ else
+ setPixmap ( skin->getButton ( m_on_p ) );
+}
+
+void ToggleButton::mouseReleaseEvent ( QMouseEvent* )
+{
+ m_on = !m_on;
+ setON ( m_on );
+ emit clicked( m_on );
+}
diff --git a/src/togglebutton.h b/src/togglebutton.h
new file mode 100644
index 000000000..07a7ec452
--- /dev/null
+++ b/src/togglebutton.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef TOGGLEBUTTON_H
+#define TOGGLEBUTTON_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class Skin;
+
+class ToggleButton : public PixmapWidget
+{
+Q_OBJECT
+public:
+ ToggleButton( QWidget *parent, uint on_n, uint on_p, uint off_n, uint off_p );
+
+ ~ToggleButton();
+
+ bool isChecked();
+ void setON(bool);
+
+signals:
+ void clicked(bool);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *skin;
+ uint m_on_n, m_on_p, m_off_n, m_off_p;
+ bool m_on;
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+};
+
+
+#endif
diff --git a/src/translations/qmmp_locales.qrc b/src/translations/qmmp_locales.qrc
new file mode 100644
index 000000000..64ac204fa
--- /dev/null
+++ b/src/translations/qmmp_locales.qrc
@@ -0,0 +1,8 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>qmmp_ru.qm</file>
+ <file>qmmp_tr.qm</file>
+ <file>qmmp_zh_CN.qm</file>
+ </qresource>
+</RCC>
diff --git a/src/translations/qmmp_ru.qm b/src/translations/qmmp_ru.qm
new file mode 100644
index 000000000..0b543721d
--- /dev/null
+++ b/src/translations/qmmp_ru.qm
Binary files differ
diff --git a/src/translations/qmmp_ru.ts b/src/translations/qmmp_ru.ts
new file mode 100644
index 000000000..7e4a8499d
--- /dev/null
+++ b/src/translations/qmmp_ru.ts
@@ -0,0 +1,644 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="ru">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../aboutdialog.ui" line="13"/>
+ <source>About Qmmp</source>
+ <translation>О Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="42"/>
+ <source>About</source>
+ <translation>О программе</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="102"/>
+ <source>License Agreement</source>
+ <translation>Лицензия</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.cpp" line="46"/>
+ <source>:/html/about_en.html</source>
+ <translation>:/html/about_ru.html</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.cpp" line="47"/>
+ <source>:/html/authors_en.txt</source>
+ <translation>:/html/authors_ru.txt</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.cpp" line="48"/>
+ <source>:/html/thanks_en.txt</source>
+ <translation>:/html/thanks_ru.txt</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="62"/>
+ <source>Authors</source>
+ <translation>Авторы</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="82"/>
+ <source>Thanks To</source>
+ <translation>Благодарности</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <location filename="../configdialog.cpp" line="179"/>
+ <source>Enabled</source>
+ <translation>Включён</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="179"/>
+ <source>Description</source>
+ <translation>Описание</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="303"/>
+ <source>Filename</source>
+ <translation>Имя файла</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="298"/>
+ <source>Artist</source>
+ <translation>Исполнитель</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="299"/>
+ <source>Album</source>
+ <translation>Альбом</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="300"/>
+ <source>Title</source>
+ <translation>Название</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="301"/>
+ <source>Tracknumber</source>
+ <translation>Номер трека</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="302"/>
+ <source>Genre</source>
+ <translation>Жанр</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="304"/>
+ <source>Filepath</source>
+ <translation>Путь к файлу</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="305"/>
+ <source>Date</source>
+ <translation>Дата</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="306"/>
+ <source>Year</source>
+ <translation>Год</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="307"/>
+ <source>Comment</source>
+ <translation>Комментарий</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="13"/>
+ <source>Qmmp Settings</source>
+ <translation>Настройки Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="134"/>
+ <source>Skins</source>
+ <translation>Обложки</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="165"/>
+ <source>Fonts</source>
+ <translation>Шрифты</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="183"/>
+ <source>Player:</source>
+ <translation>Плеер:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="199"/>
+ <source>Playlist:</source>
+ <translation>Список:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="235"/>
+ <source>???</source>
+ <translation>???</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="309"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="262"/>
+ <source>Metadata</source>
+ <translation>Метаданные</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="274"/>
+ <source>Load metadata from files</source>
+ <translation>Считывать метаданные из файлов</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="284"/>
+ <source>Song Display</source>
+ <translation>Список песен</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="296"/>
+ <source>Title format:</source>
+ <translation>Формат названия:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="379"/>
+ <source>Input</source>
+ <translation>Ввод</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="408"/>
+ <source>Output</source>
+ <translation>Вывод</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="342"/>
+ <source>Preferences</source>
+ <translation>Настройки</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="349"/>
+ <source>Information</source>
+ <translation>Информация</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="444"/>
+ <source>Tray Icon</source>
+ <translation>Системный значок</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="456"/>
+ <source>Show tooltip</source>
+ <translation>Всплывающая подсказка</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="463"/>
+ <source>Show message</source>
+ <translation>Показывать сообщение</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="486"/>
+ <source>Message delay, ms:</source>
+ <translation>Задержка сообщение, мс:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="496"/>
+ <source>Show tray icon</source>
+ <translation>Показывать системный значок</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="75"/>
+ <source>Appearance</source>
+ <translation>Внешний вид</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="83"/>
+ <source>Playlist</source>
+ <translation>Список</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="91"/>
+ <source>Plugins</source>
+ <translation>Модули</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="99"/>
+ <source>Advanced</source>
+ <translation>Дополнительно</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="585"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="506"/>
+ <source>Action On Close</source>
+ <translation>Реакция на закрытие</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="518"/>
+ <source>Hide to tray</source>
+ <translation>Свернуть в системный лоток</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="525"/>
+ <source>Quit</source>
+ <translation>Выход</translation>
+ </message>
+</context>
+<context>
+ <name>EqWidget</name>
+ <message>
+ <location filename="../eqwidget.cpp" line="172"/>
+ <source>preset</source>
+ <translation>предустановка</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="258"/>
+ <source>&amp;Load/Delete</source>
+ <translation>&amp;Загрузить/Удалить</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="260"/>
+ <source>&amp;Save Preset</source>
+ <translation>&amp;Сохранить предустановку</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="261"/>
+ <source>&amp;Save Auto-load Preset</source>
+ <translation>&amp;Сохранить авто-предустановку</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="264"/>
+ <source>&amp;Clear</source>
+ <translation>&amp;Очистить</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="293"/>
+ <source>Saving Preset</source>
+ <translation>Сохранение предустановки</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="294"/>
+ <source>Preset name:</source>
+ <translation>Имя предустановки:</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="295"/>
+ <source>preset #</source>
+ <translation>предустановка #</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="262"/>
+ <source>&amp;Import</source>
+ <translation>&amp;Импортировать</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="380"/>
+ <source>Import Preset</source>
+ <translation>Импорт предустановки</translation>
+ </message>
+</context>
+<context>
+ <name>JumpToTrackDialog</name>
+ <message>
+ <location filename="../jumptotrackdialog.cpp" line="122"/>
+ <source>Unqueue</source>
+ <translation>Снять с очереди</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="79"/>
+ <source>Queue</source>
+ <translation>В очередь</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="13"/>
+ <source>Jump To Track</source>
+ <translation>Перейти к треку</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="33"/>
+ <source>Filter</source>
+ <translation>Фильтр</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="86"/>
+ <source>Refresh</source>
+ <translation>Обновить</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="93"/>
+ <source>Jump To</source>
+ <translation>Перейти к</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="100"/>
+ <source>Close</source>
+ <translation>Закрыть</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../mainwindow.cpp" line="554"/>
+ <source>Default</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="297"/>
+ <source>Now Playing</source>
+ <translation>Сейчас играет</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="341"/>
+ <source>Choose a directory</source>
+ <translation>Выберите директорию</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="355"/>
+ <source>Select one or more files to open</source>
+ <translation>Выберите один или несколько файлов</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="502"/>
+ <source>&amp;Play</source>
+ <translation>&amp;Воспроизвести</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="502"/>
+ <source>X</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="503"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Приостановить</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="503"/>
+ <source>C</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="504"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Стоп</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="504"/>
+ <source>V</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="505"/>
+ <source>&amp;Previous</source>
+ <translation>&amp;Назад</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="505"/>
+ <source>Z</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="506"/>
+ <source>&amp;Next</source>
+ <translation>&amp;Вперёд</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="506"/>
+ <source>B</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="507"/>
+ <source>&amp;Queue</source>
+ <translation>&amp;В очередь</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="507"/>
+ <source>Q</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="509"/>
+ <source>&amp;Jump To File</source>
+ <translation>&amp;Перейти к файлу</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="509"/>
+ <source>J</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="511"/>
+ <source>&amp;Settings</source>
+ <translation>&amp;Настройки</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="511"/>
+ <source>Ctrl+P</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="516"/>
+ <source>&amp;Exit</source>
+ <translation>&amp;Выход</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="516"/>
+ <source>Ctrl+Q</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="567"/>
+ <source>Open Playlist</source>
+ <translation>Открыть список</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="589"/>
+ <source>Save Playlist</source>
+ <translation>Сохранить список</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="513"/>
+ <source>&amp;About</source>
+ <translation>&amp;О программе</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="588"/>
+ <source>Playlist Files</source>
+ <translation>Файлы списков</translation>
+ </message>
+</context>
+<context>
+ <name>PlayList</name>
+ <message>
+ <location filename="../playlist.cpp" line="132"/>
+ <source>F</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="138"/>
+ <source>D</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="163"/>
+ <source>Alt+I</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="242"/>
+ <source>Ctrl+A</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="256"/>
+ <source>O</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="131"/>
+ <source>&amp;Add File</source>
+ <translation>&amp;Добавить файл</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="137"/>
+ <source>&amp;Add Directory</source>
+ <translation>&amp;Добавить директорию</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="143"/>
+ <source>&amp;Remove Selected</source>
+ <translation>&amp;Удалить выделенное</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="150"/>
+ <source>&amp;Remove All</source>
+ <translation>&amp;Удалить всё</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="156"/>
+ <source>&amp;Remove Unselected</source>
+ <translation>&amp;Удалить невыделенное</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="162"/>
+ <source>&amp;View Track Details</source>
+ <translation>&amp;Информация</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="171"/>
+ <source>Sort List</source>
+ <translation>Сортировать</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="197"/>
+ <source>By Title</source>
+ <translation>По названию</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="201"/>
+ <source>By Filename</source>
+ <translation>По имени файла</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="205"/>
+ <source>By Path + Filename</source>
+ <translation>По пути и файлу</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="209"/>
+ <source>By Date</source>
+ <translation>По дате</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="195"/>
+ <source>Sort Selection</source>
+ <translation>Сортировать выделенное</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="219"/>
+ <source>Randomize List</source>
+ <translation>Перемешать</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="220"/>
+ <source>Reverse List</source>
+ <translation>Перевернуть</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="227"/>
+ <source>Invert Selection</source>
+ <translation>Инвертировать выделение</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="234"/>
+ <source>&amp;Select None</source>
+ <translation>&amp;Снять выделение</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="241"/>
+ <source>&amp;Select All</source>
+ <translation>&amp;Выделить всё</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="249"/>
+ <source>&amp;New List</source>
+ <translation>&amp;Новый лист</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="250"/>
+ <source>Shift+N</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="255"/>
+ <source>&amp;Load List</source>
+ <translation>&amp;Загрузить лист</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="260"/>
+ <source>&amp;Save List</source>
+ <translation>&amp;Сохранить лист</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="261"/>
+ <source>Shift+S</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="144"/>
+ <source>Del</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PresetEditor</name>
+ <message>
+ <location filename="../preseteditor.ui" line="13"/>
+ <source>Preset Editor</source>
+ <translation>Редактор предустановок</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="28"/>
+ <source>Load</source>
+ <translation>Загрузить</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="35"/>
+ <source>Delete</source>
+ <translation>Удалить</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="46"/>
+ <source>Preset</source>
+ <translation>Предустановка</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="62"/>
+ <source>Auto-preset</source>
+ <translation>Авто-предустановка</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/translations/qmmp_tr.qm b/src/translations/qmmp_tr.qm
new file mode 100644
index 000000000..1180e41d5
--- /dev/null
+++ b/src/translations/qmmp_tr.qm
Binary files differ
diff --git a/src/translations/qmmp_tr.ts b/src/translations/qmmp_tr.ts
new file mode 100644
index 000000000..73f8206d6
--- /dev/null
+++ b/src/translations/qmmp_tr.ts
@@ -0,0 +1,644 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../aboutdialog.ui" line="13"/>
+ <source>About Qmmp</source>
+ <translation>Qmmp Hakkında</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="42"/>
+ <source>About</source>
+ <translation>Hakkında</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="102"/>
+ <source>License Agreement</source>
+ <translation>Lisans Anlaşması</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.cpp" line="46"/>
+ <source>:/html/about_en.html</source>
+ <translation>:/html/about_en.html</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.cpp" line="47"/>
+ <source>:/html/authors_en.txt</source>
+ <translation>:/html/authors_en.txt</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.cpp" line="48"/>
+ <source>:/html/thanks_en.txt</source>
+ <translation>:/html/thanks_en.txt</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="62"/>
+ <source>Authors</source>
+ <translation>Yazarlar</translation>
+ </message>
+ <message>
+ <location filename="../aboutdialog.ui" line="82"/>
+ <source>Thanks To</source>
+ <translation>Teşekkürler</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <location filename="../configdialog.cpp" line="179"/>
+ <source>Enabled</source>
+ <translation>Etkinleştirildi</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="179"/>
+ <source>Description</source>
+ <translation>Açıklama</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="303"/>
+ <source>Filename</source>
+ <translation>Dosya adı</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="298"/>
+ <source>Artist</source>
+ <translation>Sanatçı</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="299"/>
+ <source>Album</source>
+ <translation>Albüm</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="300"/>
+ <source>Title</source>
+ <translation>Başlık</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="301"/>
+ <source>Tracknumber</source>
+ <translation>Parça Numarası</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="302"/>
+ <source>Genre</source>
+ <translation>Tarz</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="304"/>
+ <source>Filepath</source>
+ <translation>Dosya yolu</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="305"/>
+ <source>Date</source>
+ <translation>Tarih</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="306"/>
+ <source>Year</source>
+ <translation>Yıl</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.cpp" line="307"/>
+ <source>Comment</source>
+ <translation>Yorum</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="13"/>
+ <source>Qmmp Settings</source>
+ <translation>Qmmp Ayarları</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="134"/>
+ <source>Skins</source>
+ <translation>Kabuklar</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="165"/>
+ <source>Fonts</source>
+ <translation>Fontlar</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="183"/>
+ <source>Player:</source>
+ <translation>Oynatıcı:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="199"/>
+ <source>Playlist:</source>
+ <translation>Çalma Listesi:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="235"/>
+ <source>???</source>
+ <translation>???</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="309"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="262"/>
+ <source>Metadata</source>
+ <translation>Veri bilgisi</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="274"/>
+ <source>Load metadata from files</source>
+ <translation>Veri bilgisini dosyadan yükle</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="284"/>
+ <source>Song Display</source>
+ <translation>Şarkı Göstergesi</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="296"/>
+ <source>Title format:</source>
+ <translation>Başlık formatı:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="379"/>
+ <source>Input</source>
+ <translation>Giriş</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="408"/>
+ <source>Output</source>
+ <translation>Çıkış</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="342"/>
+ <source>Preferences</source>
+ <translation>Tercihler</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="349"/>
+ <source>Information</source>
+ <translation>Bilgi</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="444"/>
+ <source>Tray Icon</source>
+ <translation>Sistem Çekmecesi Simgesi</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="456"/>
+ <source>Show tooltip</source>
+ <translation>İpuçlarını göster</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="463"/>
+ <source>Show message</source>
+ <translation>Mesaj göster</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="486"/>
+ <source>Message delay, ms:</source>
+ <translation>Mesaj görüntü süresi, ms:</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="496"/>
+ <source>Show tray icon</source>
+ <translation>Sistem çekmecesi simgesini göster</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="75"/>
+ <source>Appearance</source>
+ <translation>Görünüm</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="83"/>
+ <source>Playlist</source>
+ <translation>Çalma Listesi</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="91"/>
+ <source>Plugins</source>
+ <translation>Eklentiler</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="99"/>
+ <source>Advanced</source>
+ <translation>Gelişmiş</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="585"/>
+ <source>Close</source>
+ <translation>Kapat</translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="506"/>
+ <source>Action On Close</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="518"/>
+ <source>Hide to tray</source>
+ <translation type="unfinished"></translation>
+ </message>
+ <message>
+ <location filename="../configdialog.ui" line="525"/>
+ <source>Quit</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>EqWidget</name>
+ <message>
+ <location filename="../eqwidget.cpp" line="172"/>
+ <source>preset</source>
+ <translation>tanımlanmış ayar</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="258"/>
+ <source>&amp;Load/Delete</source>
+ <translation>&amp;Yükle/Sil</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="260"/>
+ <source>&amp;Save Preset</source>
+ <translation>Tanımlanmış &amp;Ayarları Kaydet</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="261"/>
+ <source>&amp;Save Auto-load Preset</source>
+ <translation>&amp;Otomatik Tanımlanmış Ayarları Kaydet</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="264"/>
+ <source>&amp;Clear</source>
+ <translation>&amp;Temizle</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="293"/>
+ <source>Saving Preset</source>
+ <translation>Tanımlanmış Ayarla Kaydediliyor</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="294"/>
+ <source>Preset name:</source>
+ <translation>Tanımlanmış ayar adı:</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="295"/>
+ <source>preset #</source>
+ <translation>tanımlanmış ayar #</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="262"/>
+ <source>&amp;Import</source>
+ <translation>&amp;İçe Aktar</translation>
+ </message>
+ <message>
+ <location filename="../eqwidget.cpp" line="380"/>
+ <source>Import Preset</source>
+ <translation>Tanımlanmış Ayarları Al</translation>
+ </message>
+</context>
+<context>
+ <name>JumpToTrackDialog</name>
+ <message>
+ <location filename="../jumptotrackdialog.cpp" line="122"/>
+ <source>Unqueue</source>
+ <translation>Kuyrukta Değil</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="79"/>
+ <source>Queue</source>
+ <translation>Kuyruk</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="13"/>
+ <source>Jump To Track</source>
+ <translation>Parçaya Git</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="33"/>
+ <source>Filter</source>
+ <translation>Filtre</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="86"/>
+ <source>Refresh</source>
+ <translation>Yenile</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="93"/>
+ <source>Jump To</source>
+ <translation>Git</translation>
+ </message>
+ <message>
+ <location filename="../jumptotrackdialog.ui" line="100"/>
+ <source>Close</source>
+ <translation>Kapat</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../mainwindow.cpp" line="554"/>
+ <source>Default</source>
+ <translation>Öntanımlı</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="297"/>
+ <source>Now Playing</source>
+ <translation>Şimdi Çalınıyor</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="341"/>
+ <source>Choose a directory</source>
+ <translation>Bir dizin seçin</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="355"/>
+ <source>Select one or more files to open</source>
+ <translation>Açmak için bir yada daha çok dosya seçin</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="502"/>
+ <source>&amp;Play</source>
+ <translation>&amp;Çal</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="502"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="503"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Duraklat</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="503"/>
+ <source>C</source>
+ <translation>C</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="504"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Durdur</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="504"/>
+ <source>V</source>
+ <translation>V</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="505"/>
+ <source>&amp;Previous</source>
+ <translation>&amp;Önceki</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="505"/>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="506"/>
+ <source>&amp;Next</source>
+ <translation>&amp;Sonraki</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="506"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="507"/>
+ <source>&amp;Queue</source>
+ <translation>&amp;Kuyruğa ekle</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="507"/>
+ <source>Q</source>
+ <translation>Q</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="509"/>
+ <source>&amp;Jump To File</source>
+ <translation>&amp;Parçaya Git</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="509"/>
+ <source>J</source>
+ <translation>J</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="511"/>
+ <source>&amp;Settings</source>
+ <translation>&amp;Ayarlar</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="511"/>
+ <source>Ctrl+P</source>
+ <translation>Ctrl+P</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="516"/>
+ <source>&amp;Exit</source>
+ <translation>&amp;Çıkış</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="516"/>
+ <source>Ctrl+Q</source>
+ <translation>Ctrl+Q</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="567"/>
+ <source>Open Playlist</source>
+ <translation>Çalma Listesini Aç</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="589"/>
+ <source>Save Playlist</source>
+ <translation>Çalma Listesini Kaydet</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="513"/>
+ <source>&amp;About</source>
+ <translation>&amp;Hakkında</translation>
+ </message>
+ <message>
+ <location filename="../mainwindow.cpp" line="588"/>
+ <source>Playlist Files</source>
+ <translation>Çalma Listesi Dosyaları</translation>
+ </message>
+</context>
+<context>
+ <name>PlayList</name>
+ <message>
+ <location filename="../playlist.cpp" line="132"/>
+ <source>F</source>
+ <translation>F</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="138"/>
+ <source>D</source>
+ <translation>D</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="163"/>
+ <source>Alt+I</source>
+ <translation>Alt+I</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="242"/>
+ <source>Ctrl+A</source>
+ <translation>Ctrl+A</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="256"/>
+ <source>O</source>
+ <translation>O</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="131"/>
+ <source>&amp;Add File</source>
+ <translation>&amp;Dosya Ekle</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="137"/>
+ <source>&amp;Add Directory</source>
+ <translation>&amp;Dizin Ekle</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="143"/>
+ <source>&amp;Remove Selected</source>
+ <translation>&amp;Seçileni Kaldır</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="150"/>
+ <source>&amp;Remove All</source>
+ <translation>&amp;Hepsini Kaldır</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="156"/>
+ <source>&amp;Remove Unselected</source>
+ <translation>&amp;Seçilmemişleri Kaldır</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="162"/>
+ <source>&amp;View Track Details</source>
+ <translation>&amp;Parça Detaylarını Göster</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="171"/>
+ <source>Sort List</source>
+ <translation>Listeyi Sınıflandır</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="197"/>
+ <source>By Title</source>
+ <translation>Başlığa Göre</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="201"/>
+ <source>By Filename</source>
+ <translation>Dosya Adına Göre</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="205"/>
+ <source>By Path + Filename</source>
+ <translation>Dosya Yolu + Dosya Adına Göre</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="209"/>
+ <source>By Date</source>
+ <translation>Tarihe Göre</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="195"/>
+ <source>Sort Selection</source>
+ <translation>Seçilenleri Sınıflandır</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="219"/>
+ <source>Randomize List</source>
+ <translation>Rastgele Listele</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="220"/>
+ <source>Reverse List</source>
+ <translation>Listeyi Ters Çevir</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="227"/>
+ <source>Invert Selection</source>
+ <translation>Seçimi Tersine Çevir</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="234"/>
+ <source>&amp;Select None</source>
+ <translation>&amp;Hiçbirini Seçme</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="241"/>
+ <source>&amp;Select All</source>
+ <translation>&amp;Tümünü Seç</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="249"/>
+ <source>&amp;New List</source>
+ <translation>&amp;Yeni Liste</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="250"/>
+ <source>Shift+N</source>
+ <translation>Shift+N</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="255"/>
+ <source>&amp;Load List</source>
+ <translation>&amp;Liste Yükle</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="260"/>
+ <source>&amp;Save List</source>
+ <translation>&amp;Listeyi Kaydet</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="261"/>
+ <source>Shift+S</source>
+ <translation>Shift+S</translation>
+ </message>
+ <message>
+ <location filename="../playlist.cpp" line="144"/>
+ <source>Del</source>
+ <translation type="unfinished"></translation>
+ </message>
+</context>
+<context>
+ <name>PresetEditor</name>
+ <message>
+ <location filename="../preseteditor.ui" line="13"/>
+ <source>Preset Editor</source>
+ <translation>Tanımlanmış Ayar Düzenleyici</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="28"/>
+ <source>Load</source>
+ <translation>Yükle</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="35"/>
+ <source>Delete</source>
+ <translation>Sil</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="46"/>
+ <source>Preset</source>
+ <translation>Tanımlanmış Ayar</translation>
+ </message>
+ <message>
+ <location filename="../preseteditor.ui" line="62"/>
+ <source>Auto-preset</source>
+ <translation>Ayarları Otomatik Tanımla</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/translations/qmmp_zh_CN.qm b/src/translations/qmmp_zh_CN.qm
new file mode 100644
index 000000000..2e3e3f2ea
--- /dev/null
+++ b/src/translations/qmmp_zh_CN.qm
Binary files differ
diff --git a/src/translations/qmmp_zh_CN.ts b/src/translations/qmmp_zh_CN.ts
new file mode 100644
index 000000000..3aeefd569
--- /dev/null
+++ b/src/translations/qmmp_zh_CN.ts
@@ -0,0 +1,507 @@
+<!DOCTYPE TS><TS>
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <source>About Qmmp</source>
+ <translation>关于 Qmmp</translation>
+ </message>
+ <message>
+ <source>About</source>
+ <translation>关于</translation>
+ </message>
+ <message>
+ <source>License Agreement</source>
+ <translation>许可协议</translation>
+ </message>
+ <message>
+ <source>:/html/about_en.html</source>
+ <translation>:/html/about_zh_CN.html</translation>
+ </message>
+ <message>
+ <source>:/html/authors_en.txt</source>
+ <translation>:/html/authors_zh_CN.txt</translation>
+ </message>
+ <message>
+ <source>:/html/thanks_en.txt</source>
+ <translation>:/html/thanks_zh_CN.txt</translation>
+ </message>
+ <message>
+ <source>Authors</source>
+ <translation>作者</translation>
+ </message>
+ <message>
+ <source>Thanks To</source>
+ <translation>感谢</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigDialog</name>
+ <message>
+ <source>Enabled</source>
+ <translation>启用</translation>
+ </message>
+ <message>
+ <source>Description</source>
+ <translation>描述</translation>
+ </message>
+ <message>
+ <source>Filename</source>
+ <translation>文件名</translation>
+ </message>
+ <message>
+ <source>Artist</source>
+ <translation>艺术家</translation>
+ </message>
+ <message>
+ <source>Album</source>
+ <translation>专辑</translation>
+ </message>
+ <message>
+ <source>Title</source>
+ <translation>标题</translation>
+ </message>
+ <message>
+ <source>Tracknumber</source>
+ <translation>轨迹</translation>
+ </message>
+ <message>
+ <source>Genre</source>
+ <translation>流派</translation>
+ </message>
+ <message>
+ <source>Filepath</source>
+ <translation>文件路径</translation>
+ </message>
+ <message>
+ <source>Date</source>
+ <translation>日期</translation>
+ </message>
+ <message>
+ <source>Year</source>
+ <translation>年</translation>
+ </message>
+ <message>
+ <source>Comment</source>
+ <translation>注释</translation>
+ </message>
+ <message>
+ <source>Qmmp Settings</source>
+ <translation>Qmmp 设置</translation>
+ </message>
+ <message>
+ <source>Skins</source>
+ <translation>皮肤</translation>
+ </message>
+ <message>
+ <source>Fonts</source>
+ <translation>字体</translation>
+ </message>
+ <message>
+ <source>Player:</source>
+ <translation>播放器:</translation>
+ </message>
+ <message>
+ <source>Playlist:</source>
+ <translation>播放列表:</translation>
+ </message>
+ <message>
+ <source>???</source>
+ <translation>???</translation>
+ </message>
+ <message>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <source>Metadata</source>
+ <translation>元数据</translation>
+ </message>
+ <message>
+ <source>Load metadata from files</source>
+ <translation>从文件载入元数据</translation>
+ </message>
+ <message>
+ <source>Song Display</source>
+ <translation>显示歌曲</translation>
+ </message>
+ <message>
+ <source>Title format:</source>
+ <translation>标题格式:</translation>
+ </message>
+ <message>
+ <source>Input</source>
+ <translation>输入</translation>
+ </message>
+ <message>
+ <source>Output</source>
+ <translation>输出</translation>
+ </message>
+ <message>
+ <source>Preferences</source>
+ <translation>参数设置</translation>
+ </message>
+ <message>
+ <source>Information</source>
+ <translation>信息</translation>
+ </message>
+ <message>
+ <source>Tray Icon</source>
+ <translation>托盘图标</translation>
+ </message>
+ <message>
+ <source>Show tooltip</source>
+ <translation>显示工具栏</translation>
+ </message>
+ <message>
+ <source>Show message</source>
+ <translation>显示通知</translation>
+ </message>
+ <message>
+ <source>Message delay, ms:</source>
+ <translation>消息延迟,毫秒:</translation>
+ </message>
+ <message>
+ <source>Show tray icon</source>
+ <translation>显示托盘图标</translation>
+ </message>
+ <message>
+ <source>Appearance</source>
+ <translation>外观</translation>
+ </message>
+ <message>
+ <source>Playlist</source>
+ <translation>播放列表</translation>
+ </message>
+ <message>
+ <source>Plugins</source>
+ <translation>插件</translation>
+ </message>
+ <message>
+ <source>Advanced</source>
+ <translation>高级</translation>
+ </message>
+ <message>
+ <source>Close</source>
+ <translation>关闭</translation>
+ </message>
+</context>
+<context>
+ <name>EqWidget</name>
+ <message>
+ <source>preset</source>
+ <translation>预设</translation>
+ </message>
+ <message>
+ <source>&amp;Load/Delete</source>
+ <translation>载入/删除(&amp;L)</translation>
+ </message>
+ <message>
+ <source>&amp;Save Preset</source>
+ <translation>保存预设(&amp;S)</translation>
+ </message>
+ <message>
+ <source>&amp;Save Auto-load Preset</source>
+ <translation>保存自动载入预设(&amp;S)</translation>
+ </message>
+ <message>
+ <source>&amp;Clear</source>
+ <translation>清除(&amp;C)</translation>
+ </message>
+ <message>
+ <source>Saving Preset</source>
+ <translation>保存预设</translation>
+ </message>
+ <message>
+ <source>Preset name:</source>
+ <translation>预设名字:</translation>
+ </message>
+ <message>
+ <source>preset #</source>
+ <translation>预设 #</translation>
+ </message>
+ <message>
+ <source>&amp;Import</source>
+ <translation>导入(&amp;I)</translation>
+ </message>
+ <message>
+ <source>Import Preset</source>
+ <translation>导入预设</translation>
+ </message>
+</context>
+<context>
+ <name>JumpToTrackDialog</name>
+ <message>
+ <source>Unqueue</source>
+ <translation>移出队列</translation>
+ </message>
+ <message>
+ <source>Queue</source>
+ <translation>加入队列</translation>
+ </message>
+ <message>
+ <source>Jump To Track</source>
+ <translation>跳跃至音轨</translation>
+ </message>
+ <message>
+ <source>Filter</source>
+ <translation>过滤</translation>
+ </message>
+ <message>
+ <source>Refresh</source>
+ <translation>刷新</translation>
+ </message>
+ <message>
+ <source>Jump To</source>
+ <translation>跳跃至</translation>
+ </message>
+ <message>
+ <source>Close</source>
+ <translation>关闭</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <source>Default</source>
+ <translation>默认</translation>
+ </message>
+ <message>
+ <source>Now Playing</source>
+ <translation>正在播放</translation>
+ </message>
+ <message>
+ <source>Choose a directory</source>
+ <translation>选择一个目录</translation>
+ </message>
+ <message>
+ <source>Select one or more files to open</source>
+ <translation>选择打开一个或更多文件</translation>
+ </message>
+ <message>
+ <source>&amp;Play</source>
+ <translation>播放(&amp;P)</translation>
+ </message>
+ <message>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <source>&amp;Pause</source>
+ <translation>暂停(&amp;P)</translation>
+ </message>
+ <message>
+ <source>C</source>
+ <translation>C</translation>
+ </message>
+ <message>
+ <source>&amp;Stop</source>
+ <translation>停止(&amp;S)</translation>
+ </message>
+ <message>
+ <source>V</source>
+ <translation>V</translation>
+ </message>
+ <message>
+ <source>&amp;Previous</source>
+ <translation>上一个(&amp;P)</translation>
+ </message>
+ <message>
+ <source>Z</source>
+ <translation>Z</translation>
+ </message>
+ <message>
+ <source>&amp;Next</source>
+ <translation>下一个(&amp;N)</translation>
+ </message>
+ <message>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <source>&amp;Queue</source>
+ <translation>队列&amp;Q)</translation>
+ </message>
+ <message>
+ <source>Q</source>
+ <translation>Q</translation>
+ </message>
+ <message>
+ <source>&amp;Jump To File</source>
+ <translation>跳跃至文件(&amp;J)</translation>
+ </message>
+ <message>
+ <source>J</source>
+ <translation>J</translation>
+ </message>
+ <message>
+ <source>&amp;Settings</source>
+ <translation>设置(&amp;S)</translation>
+ </message>
+ <message>
+ <source>Ctrl+P</source>
+ <translation>Ctrl+P</translation>
+ </message>
+ <message>
+ <source>&amp;Exit</source>
+ <translation>退出(&amp;E)</translation>
+ </message>
+ <message>
+ <source>Ctrl+Q</source>
+ <translation>Q</translation>
+ </message>
+ <message>
+ <source>Open Playlist</source>
+ <translation>打开播放列表</translation>
+ </message>
+ <message>
+ <source>Save Playlist</source>
+ <translation>保存播放列表</translation>
+ </message>
+ <message>
+ <source>&amp;About</source>
+ <translation>关于(&amp;A)</translation>
+ </message>
+ <message>
+ <source>Playlist Files</source>
+ <translation>播放列表文件</translation>
+ </message>
+</context>
+<context>
+ <name>PlayList</name>
+ <message>
+ <source>F</source>
+ <translation>F</translation>
+ </message>
+ <message>
+ <source>D</source>
+ <translation>D</translation>
+ </message>
+ <message>
+ <source>Delete</source>
+ <translation>删除</translation>
+ </message>
+ <message>
+ <source>Alt+I</source>
+ <translation>Alt+I</translation>
+ </message>
+ <message>
+ <source>Ctrl+A</source>
+ <translation>Ctrl+A</translation>
+ </message>
+ <message>
+ <source>O</source>
+ <translation>O</translation>
+ </message>
+ <message>
+ <source>&amp;Add File</source>
+ <translation>添加文件(&amp;A)</translation>
+ </message>
+ <message>
+ <source>&amp;Add Directory</source>
+ <translation>添加文件夹(&amp;A)</translation>
+ </message>
+ <message>
+ <source>&amp;Remove Selected</source>
+ <translation>移除所选(&amp;R)</translation>
+ </message>
+ <message>
+ <source>&amp;Remove All</source>
+ <translation>移除全部(&amp;R)</translation>
+ </message>
+ <message>
+ <source>&amp;Remove Unselected</source>
+ <translation>移除未选(&amp;R)</translation>
+ </message>
+ <message>
+ <source>&amp;View Track Details</source>
+ <translation>查看音轨详细信息(&amp;V)</translation>
+ </message>
+ <message>
+ <source>Sort List</source>
+ <translation>列表排序</translation>
+ </message>
+ <message>
+ <source>By Title</source>
+ <translation>按标题</translation>
+ </message>
+ <message>
+ <source>By Filename</source>
+ <translation>按文件名</translation>
+ </message>
+ <message>
+ <source>By Path + Filename</source>
+ <translation>按路径+文件名</translation>
+ </message>
+ <message>
+ <source>By Date</source>
+ <translation>按日期</translation>
+ </message>
+ <message>
+ <source>Sort Selection</source>
+ <translation>选择排序</translation>
+ </message>
+ <message>
+ <source>Randomize List</source>
+ <translation>随机产生列表</translation>
+ </message>
+ <message>
+ <source>Reverse List</source>
+ <translation>逆序列表</translation>
+ </message>
+ <message>
+ <source>Invert Selection</source>
+ <translation>反选</translation>
+ </message>
+ <message>
+ <source>&amp;Select None</source>
+ <translation>选择无(&amp;S)</translation>
+ </message>
+ <message>
+ <source>&amp;Select All</source>
+ <translation>全选(&amp;S)</translation>
+ </message>
+ <message>
+ <source>&amp;New List</source>
+ <translation>新建列表(&amp;N)</translation>
+ </message>
+ <message>
+ <source>Shift+N</source>
+ <translation>Shift+N</translation>
+ </message>
+ <message>
+ <source>&amp;Load List</source>
+ <translation>载入列表(&amp;L)</translation>
+ </message>
+ <message>
+ <source>&amp;Save List</source>
+ <translation>保存列表(&amp;S)</translation>
+ </message>
+ <message>
+ <source>Shift+S</source>
+ <translation>Shift+S</translation>
+ </message>
+</context>
+<context>
+ <name>PresetEditor</name>
+ <message>
+ <source>Preset Editor</source>
+ <translation>预设编辑器</translation>
+ </message>
+ <message>
+ <source>Load</source>
+ <translation>装入</translation>
+ </message>
+ <message>
+ <source>Delete</source>
+ <translation>删除</translation>
+ </message>
+ <message>
+ <source>Preset</source>
+ <translation>预设</translation>
+ </message>
+ <message>
+ <source>Auto-preset</source>
+ <translation>预设自动</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/version.h b/src/version.h
new file mode 100644
index 000000000..8d6fabe5b
--- /dev/null
+++ b/src/version.h
@@ -0,0 +1,10 @@
+#ifndef _QMMP_VERSION_H
+#define _QMMP_VERSION_H
+
+#define QMMP_VERSION 0.1.2
+
+#define QMMP_STR_VERSION "0.1.2"
+
+#define TCPSERVER_PORT_NUMBER 33000
+
+#endif
diff --git a/src/volumebar.cpp b/src/volumebar.cpp
new file mode 100644
index 000000000..c29794033
--- /dev/null
+++ b/src/volumebar.cpp
@@ -0,0 +1,135 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QMouseEvent>
+#include <QPainter>
+#include <math.h>
+
+#include "skin.h"
+#include "button.h"
+#include "mainwindow.h"
+
+#include "volumebar.h"
+
+
+VolumeBar::VolumeBar(QWidget *parent)
+ : PixmapWidget(parent)
+{
+ m_skin = Skin::getPointer();
+ connect(m_skin, SIGNAL(skinChanged()), this, SLOT(updateSkin()));
+ setPixmap(m_skin->getVolumeBar(0));
+ mw = qobject_cast<MainWindow*>(window());
+ m_moving = FALSE;
+ m_min = 0;
+ m_max = 100;
+ m_old = m_value = 0;
+ draw(FALSE);
+}
+
+
+VolumeBar::~VolumeBar()
+{}
+
+void VolumeBar::mousePressEvent(QMouseEvent *e)
+{
+
+ m_moving = TRUE;
+ press_pos = e->x();
+ if(m_pos<e->x() && e->x()<m_pos+11)
+ {
+ press_pos = e->x()-m_pos;
+ }
+ else
+ {
+ m_value = convert(qMax(qMin(width()-18,e->x()-6),0));
+ press_pos = 6;
+ if (m_value!=m_old)
+ {
+ emit sliderMoved(m_value);
+
+ }
+ }
+ draw();
+}
+
+void VolumeBar::mouseMoveEvent (QMouseEvent *e)
+{
+ if(m_moving)
+ {
+ int po = e->x();
+ po = po - press_pos;
+
+ if(0<=po && po<=width()-18)
+ {
+ m_value = convert(po);
+ draw();
+ emit sliderMoved(m_value);
+ }
+ }
+}
+
+void VolumeBar::mouseReleaseEvent(QMouseEvent*)
+{
+ m_moving = FALSE;
+ draw(FALSE);
+ if (m_value!=m_old)
+ {
+ m_old = m_value;
+ //mw->seek(m_value);
+ }
+
+}
+
+void VolumeBar::setValue(int v)
+{
+ if (m_moving || m_max == 0)
+ return;
+ m_value = v;
+ draw(FALSE);
+}
+
+void VolumeBar::setMax(int max)
+{
+ m_max = max;
+ draw(FALSE);
+}
+
+void VolumeBar::updateSkin()
+{
+ draw(FALSE);
+}
+
+void VolumeBar::draw(bool pressed)
+{
+ int p=int(ceil(double(m_value-m_min)*(width()-18)/(m_max-m_min)));
+ m_pixmap = m_skin->getVolumeBar(27*(m_value-m_min)/(m_max-m_min));
+ QPainter paint(&m_pixmap);
+ if(pressed)
+ paint.drawPixmap(p,1,m_skin->getButton(Skin::BT_VOL_P));
+ else
+ paint.drawPixmap(p,1,m_skin->getButton(Skin::BT_VOL_N));
+ setPixmap(m_pixmap);
+ m_pos = p;
+}
+
+int VolumeBar::convert(int p)
+{
+ return int(ceil(double(m_max-m_min)*(p)/(width()-18)+m_min));
+}
diff --git a/src/volumebar.h b/src/volumebar.h
new file mode 100644
index 000000000..51bc7ac46
--- /dev/null
+++ b/src/volumebar.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef VOLUMEBAR_H
+#define VOLUMEBAR_H
+
+#include <pixmapwidget.h>
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+
+class Skin;
+class MainWindow;
+
+class VolumeBar : public PixmapWidget
+{
+Q_OBJECT
+public:
+ VolumeBar(QWidget *parent = 0);
+
+ ~VolumeBar();
+
+ int value() { return m_value; };
+ int isPressed() {return m_moving; }
+
+public slots:
+ void setValue(int);
+ void setMax(int);
+
+signals:
+ void sliderMoved (int);
+
+private slots:
+ void updateSkin();
+
+private:
+ Skin *m_skin;
+ bool m_moving;
+ int press_pos;
+ int m_max, m_min, m_pos, m_value, m_old;
+ QPixmap m_pixmap;
+ MainWindow *mw;
+ int convert(int); // value = convert(position);
+ void draw(bool pressed = TRUE);
+
+protected:
+ void mousePressEvent(QMouseEvent*);
+ void mouseReleaseEvent(QMouseEvent*);
+ void mouseMoveEvent(QMouseEvent*);
+
+
+};
+
+#endif