summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am58
-rwxr-xr-xsrc/amarokscript/Makefile.am3
-rw-r--r--src/amarokscript/README43
-rwxr-xr-xsrc/amarokscript/soundKonverter.rb296
-rwxr-xr-xsrc/audiocd_extract_with_soundkonverter.desktop11
-rwxr-xr-xsrc/cddb.cpp669
-rwxr-xr-xsrc/cddb.h79
-rwxr-xr-xsrc/cdmanager.cpp190
-rwxr-xr-xsrc/cdmanager.h95
-rwxr-xr-xsrc/cdopener.cpp781
-rwxr-xr-xsrc/cdopener.h139
-rwxr-xr-xsrc/combobutton.cpp114
-rwxr-xr-xsrc/combobutton.h110
-rwxr-xr-xsrc/config.cpp1516
-rwxr-xr-xsrc/config.h354
-rwxr-xr-xsrc/configbackendspage.cpp493
-rwxr-xr-xsrc/configbackendspage.h81
-rwxr-xr-xsrc/configdialog.cpp174
-rwxr-xr-xsrc/configdialog.h79
-rwxr-xr-xsrc/configenvironmentpage.cpp223
-rwxr-xr-xsrc/configenvironmentpage.h62
-rwxr-xr-xsrc/configgeneralpage.cpp323
-rwxr-xr-xsrc/configgeneralpage.h69
-rwxr-xr-xsrc/configpagebase.cpp20
-rwxr-xr-xsrc/configpagebase.h38
-rwxr-xr-xsrc/configpluginspage.cpp639
-rwxr-xr-xsrc/configpluginspage.h82
-rwxr-xr-xsrc/conversionoptions.cpp86
-rwxr-xr-xsrc/conversionoptions.h65
-rwxr-xr-xsrc/convert.cpp1615
-rwxr-xr-xsrc/convert.h281
-rwxr-xr-xsrc/cuesheeteditor.cpp325
-rwxr-xr-xsrc/cuesheeteditor.h50
-rwxr-xr-xsrc/dcopinterface.h58
-rwxr-xr-xsrc/dirdialog.cpp154
-rwxr-xr-xsrc/dirdialog.h65
-rwxr-xr-xsrc/filelist.cpp1445
-rwxr-xr-xsrc/filelist.h235
-rwxr-xr-xsrc/hi16-app-soundkonverter.pngbin0 -> 782 bytes
-rwxr-xr-xsrc/hi16-app-soundkonverter_replaygain.pngbin0 -> 855 bytes
-rwxr-xr-xsrc/hi22-app-soundkonverter.pngbin0 -> 1200 bytes
-rwxr-xr-xsrc/hi22-app-soundkonverter_replaygain.pngbin0 -> 1313 bytes
-rwxr-xr-xsrc/hi32-app-soundkonverter.pngbin0 -> 2101 bytes
-rwxr-xr-xsrc/hi32-app-soundkonverter_replaygain.pngbin0 -> 2023 bytes
-rwxr-xr-xsrc/hi48-app-soundkonverter.pngbin0 -> 3976 bytes
-rwxr-xr-xsrc/hi48-app-soundkonverter_replaygain.pngbin0 -> 3676 bytes
-rwxr-xr-xsrc/hi64-app-soundkonverter.pngbin0 -> 6144 bytes
-rwxr-xr-xsrc/hi64-app-soundkonverter_replaygain.pngbin0 -> 5759 bytes
-rwxr-xr-xsrc/logger.cpp149
-rwxr-xr-xsrc/logger.h100
-rwxr-xr-xsrc/logviewer.cpp204
-rwxr-xr-xsrc/logviewer.h111
-rwxr-xr-xsrc/main.cpp75
-rw-r--r--src/metadata/Makefile.am37
-rw-r--r--src/metadata/aac/Makefile.am12
-rw-r--r--src/metadata/aac/aacfiletyperesolver.cpp38
-rw-r--r--src/metadata/aac/aacfiletyperesolver.h36
-rwxr-xr-xsrc/metadata/ape/Makefile.am6
-rwxr-xr-xsrc/metadata/ape/taglib_monkeysaudiofiletyperesolver.cpp21
-rwxr-xr-xsrc/metadata/ape/taglib_monkeysaudiofiletyperesolver.h19
-rw-r--r--src/metadata/asf/Makefile.am20
-rw-r--r--src/metadata/asf/asfattribute.cpp304
-rw-r--r--src/metadata/asf/asfattribute.h176
-rw-r--r--src/metadata/asf/asffile.cpp547
-rw-r--r--src/metadata/asf/asffile.h114
-rw-r--r--src/metadata/asf/asfproperties.cpp95
-rw-r--r--src/metadata/asf/asfproperties.h69
-rw-r--r--src/metadata/asf/asftag.cpp202
-rw-r--r--src/metadata/asf/asftag.h181
-rw-r--r--src/metadata/asf/taglib_asffiletyperesolver.cpp47
-rw-r--r--src/metadata/asf/taglib_asffiletyperesolver.h42
-rw-r--r--src/metadata/audible/Makefile.am17
-rw-r--r--src/metadata/audible/audibleproperties.cpp86
-rw-r--r--src/metadata/audible/audibleproperties.h85
-rw-r--r--src/metadata/audible/audibletag.cpp163
-rw-r--r--src/metadata/audible/audibletag.h185
-rw-r--r--src/metadata/audible/taglib_audiblefile.cpp121
-rw-r--r--src/metadata/audible/taglib_audiblefile.h93
-rw-r--r--src/metadata/audible/taglib_audiblefiletyperesolver.cpp44
-rw-r--r--src/metadata/audible/taglib_audiblefiletyperesolver.h36
-rw-r--r--src/metadata/m4a/Makefile.am84
-rw-r--r--src/metadata/m4a/boxfactory.cpp150
-rw-r--r--src/metadata/m4a/boxfactory.h45
-rw-r--r--src/metadata/m4a/itunesalbbox.cpp89
-rw-r--r--src/metadata/m4a/itunesalbbox.h50
-rw-r--r--src/metadata/m4a/itunesartbox.cpp89
-rw-r--r--src/metadata/m4a/itunesartbox.h50
-rw-r--r--src/metadata/m4a/itunescmtbox.cpp89
-rw-r--r--src/metadata/m4a/itunescmtbox.h50
-rw-r--r--src/metadata/m4a/itunescvrbox.cpp89
-rw-r--r--src/metadata/m4a/itunescvrbox.h50
-rw-r--r--src/metadata/m4a/itunesdatabox.cpp63
-rw-r--r--src/metadata/m4a/itunesdatabox.h53
-rw-r--r--src/metadata/m4a/itunesdaybox.cpp89
-rw-r--r--src/metadata/m4a/itunesdaybox.h50
-rw-r--r--src/metadata/m4a/itunesdiskbox.cpp93
-rw-r--r--src/metadata/m4a/itunesdiskbox.h50
-rw-r--r--src/metadata/m4a/itunesgenbox.cpp89
-rw-r--r--src/metadata/m4a/itunesgenbox.h50
-rw-r--r--src/metadata/m4a/itunesgrpbox.cpp89
-rw-r--r--src/metadata/m4a/itunesgrpbox.h50
-rw-r--r--src/metadata/m4a/itunesnambox.cpp89
-rw-r--r--src/metadata/m4a/itunesnambox.h50
-rw-r--r--src/metadata/m4a/itunestmpobox.cpp93
-rw-r--r--src/metadata/m4a/itunestmpobox.h50
-rw-r--r--src/metadata/m4a/itunestrknbox.cpp93
-rw-r--r--src/metadata/m4a/itunestrknbox.h50
-rw-r--r--src/metadata/m4a/ituneswrtbox.cpp89
-rw-r--r--src/metadata/m4a/ituneswrtbox.h50
-rw-r--r--src/metadata/m4a/mp4audioproperties.cpp75
-rw-r--r--src/metadata/m4a/mp4audioproperties.h73
-rw-r--r--src/metadata/m4a/mp4audiosampleentry.cpp146
-rw-r--r--src/metadata/m4a/mp4audiosampleentry.h57
-rw-r--r--src/metadata/m4a/mp4file.cpp377
-rw-r--r--src/metadata/m4a/mp4file.h169
-rw-r--r--src/metadata/m4a/mp4fourcc.cpp84
-rw-r--r--src/metadata/m4a/mp4fourcc.h63
-rw-r--r--src/metadata/m4a/mp4hdlrbox.cpp75
-rw-r--r--src/metadata/m4a/mp4hdlrbox.h53
-rw-r--r--src/metadata/m4a/mp4ilstbox.cpp97
-rw-r--r--src/metadata/m4a/mp4ilstbox.h49
-rw-r--r--src/metadata/m4a/mp4isobox.cpp76
-rw-r--r--src/metadata/m4a/mp4isobox.h67
-rw-r--r--src/metadata/m4a/mp4isofullbox.cpp67
-rw-r--r--src/metadata/m4a/mp4isofullbox.h57
-rw-r--r--src/metadata/m4a/mp4itunestag.cpp197
-rw-r--r--src/metadata/m4a/mp4itunestag.h95
-rw-r--r--src/metadata/m4a/mp4mdiabox.cpp111
-rw-r--r--src/metadata/m4a/mp4mdiabox.h49
-rw-r--r--src/metadata/m4a/mp4metabox.cpp86
-rw-r--r--src/metadata/m4a/mp4metabox.h49
-rw-r--r--src/metadata/m4a/mp4minfbox.cpp104
-rw-r--r--src/metadata/m4a/mp4minfbox.h51
-rw-r--r--src/metadata/m4a/mp4moovbox.cpp86
-rw-r--r--src/metadata/m4a/mp4moovbox.h49
-rw-r--r--src/metadata/m4a/mp4mvhdbox.cpp140
-rw-r--r--src/metadata/m4a/mp4mvhdbox.h65
-rw-r--r--src/metadata/m4a/mp4propsproxy.cpp89
-rw-r--r--src/metadata/m4a/mp4propsproxy.h65
-rw-r--r--src/metadata/m4a/mp4sampleentry.cpp59
-rw-r--r--src/metadata/m4a/mp4sampleentry.h54
-rw-r--r--src/metadata/m4a/mp4skipbox.cpp50
-rw-r--r--src/metadata/m4a/mp4skipbox.h50
-rw-r--r--src/metadata/m4a/mp4stblbox.cpp105
-rw-r--r--src/metadata/m4a/mp4stblbox.h51
-rw-r--r--src/metadata/m4a/mp4stsdbox.cpp91
-rw-r--r--src/metadata/m4a/mp4stsdbox.h51
-rw-r--r--src/metadata/m4a/mp4tagsproxy.cpp168
-rw-r--r--src/metadata/m4a/mp4tagsproxy.h99
-rw-r--r--src/metadata/m4a/mp4trakbox.cpp86
-rw-r--r--src/metadata/m4a/mp4trakbox.h49
-rw-r--r--src/metadata/m4a/mp4udtabox.cpp95
-rw-r--r--src/metadata/m4a/mp4udtabox.h49
-rw-r--r--src/metadata/m4a/taglib_mp4filetyperesolver.cpp42
-rw-r--r--src/metadata/m4a/taglib_mp4filetyperesolver.h36
-rw-r--r--src/metadata/mp4/Makefile.am18
-rwxr-xr-xsrc/metadata/optimfrog/Makefile.am6
-rwxr-xr-xsrc/metadata/optimfrog/taglib_optimfrogfiletyperesolver.cpp21
-rwxr-xr-xsrc/metadata/optimfrog/taglib_optimfrogfiletyperesolver.h19
-rw-r--r--src/metadata/rmff/Makefile.am15
-rw-r--r--src/metadata/rmff/rmff.cpp998
-rw-r--r--src/metadata/rmff/rmff.h348
-rw-r--r--src/metadata/rmff/taglib_realmediafile.cpp213
-rw-r--r--src/metadata/rmff/taglib_realmediafile.h133
-rw-r--r--src/metadata/rmff/taglib_realmediafiletyperesolver.cpp53
-rw-r--r--src/metadata/rmff/taglib_realmediafiletyperesolver.h39
-rw-r--r--src/metadata/speex/Makefile.am16
-rw-r--r--src/metadata/speex/speexfile.cpp111
-rw-r--r--src/metadata/speex/speexfile.h93
-rw-r--r--src/metadata/speex/speexproperties.cpp171
-rw-r--r--src/metadata/speex/speexproperties.h83
-rw-r--r--src/metadata/speex/taglib_speexfiletyperesolver.cpp44
-rw-r--r--src/metadata/speex/taglib_speexfiletyperesolver.h36
-rwxr-xr-xsrc/metadata/tagengine.cpp419
-rwxr-xr-xsrc/metadata/tagengine.h79
-rwxr-xr-xsrc/metadata/tplugins.cpp151
-rwxr-xr-xsrc/metadata/tplugins.h27
-rw-r--r--src/metadata/trueaudio/Makefile.am16
-rw-r--r--src/metadata/trueaudio/combinedtag.h171
-rw-r--r--src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.cpp44
-rw-r--r--src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.h36
-rw-r--r--src/metadata/trueaudio/ttafile.cpp307
-rw-r--r--src/metadata/trueaudio/ttafile.h178
-rw-r--r--src/metadata/trueaudio/ttaproperties.cpp134
-rw-r--r--src/metadata/trueaudio/ttaproperties.h86
-rw-r--r--src/metadata/wav/Makefile.am15
-rw-r--r--src/metadata/wav/wavfile.cpp115
-rw-r--r--src/metadata/wav/wavfile.h92
-rw-r--r--src/metadata/wav/wavfiletyperesolver.cpp44
-rw-r--r--src/metadata/wav/wavfiletyperesolver.h36
-rw-r--r--src/metadata/wav/wavproperties.cpp108
-rw-r--r--src/metadata/wav/wavproperties.h85
-rw-r--r--src/metadata/wavpack/Makefile.am16
-rw-r--r--src/metadata/wavpack/combinedtag.h171
-rw-r--r--src/metadata/wavpack/taglib_wavpackfiletyperesolver.cpp44
-rw-r--r--src/metadata/wavpack/taglib_wavpackfiletyperesolver.h36
-rw-r--r--src/metadata/wavpack/wvfile.cpp311
-rw-r--r--src/metadata/wavpack/wvfile.h160
-rw-r--r--src/metadata/wavpack/wvproperties.cpp141
-rw-r--r--src/metadata/wavpack/wvproperties.h86
-rwxr-xr-xsrc/options.cpp273
-rwxr-xr-xsrc/options.h123
-rwxr-xr-xsrc/optionsdetailed.cpp911
-rwxr-xr-xsrc/optionsdetailed.h145
-rwxr-xr-xsrc/optionseditor.cpp729
-rwxr-xr-xsrc/optionseditor.h146
-rwxr-xr-xsrc/optionsrequester.cpp98
-rwxr-xr-xsrc/optionsrequester.h54
-rwxr-xr-xsrc/optionssimple.cpp574
-rwxr-xr-xsrc/optionssimple.h107
-rwxr-xr-xsrc/outputdirectory.cpp571
-rwxr-xr-xsrc/outputdirectory.h92
-rwxr-xr-xsrc/paranoia.cpp257
-rwxr-xr-xsrc/paranoia.h59
-rwxr-xr-xsrc/pics/Makefile.am7
-rwxr-xr-xsrc/pics/ledgreen.pngbin0 -> 544 bytes
-rwxr-xr-xsrc/pics/ledgreen_legend.pngbin0 -> 533 bytes
-rwxr-xr-xsrc/pics/ledred.pngbin0 -> 522 bytes
-rwxr-xr-xsrc/pics/ledred_legend.pngbin0 -> 515 bytes
-rwxr-xr-xsrc/pics/ledyellow.pngbin0 -> 514 bytes
-rwxr-xr-xsrc/pics/ledyellow_legend.pngbin0 -> 509 bytes
-rwxr-xr-xsrc/pluginloader/Makefile.am9
-rwxr-xr-xsrc/pluginloader/convertpluginloader.cpp265
-rwxr-xr-xsrc/pluginloader/convertpluginloader.h190
-rwxr-xr-xsrc/pluginloader/formatinfoloader.cpp88
-rwxr-xr-xsrc/pluginloader/formatinfoloader.h67
-rwxr-xr-xsrc/pluginloader/pluginloaderbase.cpp17
-rwxr-xr-xsrc/pluginloader/pluginloaderbase.h86
-rwxr-xr-xsrc/pluginloader/replaygainpluginloader.cpp116
-rwxr-xr-xsrc/pluginloader/replaygainpluginloader.h89
-rwxr-xr-xsrc/pluginloader/ripperpluginloader.cpp111
-rwxr-xr-xsrc/pluginloader/ripperpluginloader.h84
-rwxr-xr-xsrc/plugins/Makefile.am5
-rwxr-xr-xsrc/plugins/format_infos/3gp.xml4
-rw-r--r--src/plugins/format_infos/Makefile.am5
-rwxr-xr-xsrc/plugins/format_infos/aac.xml4
-rwxr-xr-xsrc/plugins/format_infos/ac3.xml4
-rw-r--r--src/plugins/format_infos/aiff.xml4
-rwxr-xr-xsrc/plugins/format_infos/amr.xml4
-rwxr-xr-xsrc/plugins/format_infos/ape.xml4
-rwxr-xr-xsrc/plugins/format_infos/au.xml4
-rwxr-xr-xsrc/plugins/format_infos/avi.xml4
-rwxr-xr-xsrc/plugins/format_infos/bonk.xml4
-rwxr-xr-xsrc/plugins/format_infos/flac.xml4
-rwxr-xr-xsrc/plugins/format_infos/la.xml4
-rwxr-xr-xsrc/plugins/format_infos/m4a.xml4
-rwxr-xr-xsrc/plugins/format_infos/mid.xml4
-rwxr-xr-xsrc/plugins/format_infos/mod.xml4
-rwxr-xr-xsrc/plugins/format_infos/mp2.xml4
-rwxr-xr-xsrc/plugins/format_infos/mp3.xml4
-rwxr-xr-xsrc/plugins/format_infos/mpc.xml4
-rwxr-xr-xsrc/plugins/format_infos/ofc.xml4
-rwxr-xr-xsrc/plugins/format_infos/ofr.xml4
-rwxr-xr-xsrc/plugins/format_infos/ofs.xml4
-rwxr-xr-xsrc/plugins/format_infos/ogg.xml4
-rwxr-xr-xsrc/plugins/format_infos/pac.xml4
-rwxr-xr-xsrc/plugins/format_infos/qt.xml4
-rwxr-xr-xsrc/plugins/format_infos/ra.xml4
-rwxr-xr-xsrc/plugins/format_infos/shn.xml4
-rwxr-xr-xsrc/plugins/format_infos/spx.xml4
-rwxr-xr-xsrc/plugins/format_infos/tta.xml4
-rwxr-xr-xsrc/plugins/format_infos/wma.xml4
-rwxr-xr-xsrc/plugins/format_infos/wv.xml4
-rwxr-xr-xsrc/plugins/format_infos/wvc.xml4
-rwxr-xr-xsrc/plugins/mime_types/Makefile.am11
-rwxr-xr-xsrc/plugins/mime_types/amr.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-ape.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-bonk.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-flv.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-la.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-ofc.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-ofr.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-ofs.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-pac.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-shorten.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-tta.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-wavpack-correction.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/mime_types/x-wavpack.soundkonverter.desktop8
-rwxr-xr-xsrc/plugins/plugins/110.oggvorbis.soundkonverter.xml18
-rwxr-xr-xsrc/plugins/plugins/120.lame.soundkonverter.xml22
-rwxr-xr-xsrc/plugins/plugins/122.twolame.soundkonverter.xml15
-rwxr-xr-xsrc/plugins/plugins/123.toolame.soundkonverter.xml15
-rwxr-xr-xsrc/plugins/plugins/125.gogo.soundkonverter.xml18
-rwxr-xr-xsrc/plugins/plugins/127.faac.soundkonverter.xml15
-rwxr-xr-xsrc/plugins/plugins/130.musepack.soundkonverter.xml11
-rwxr-xr-xsrc/plugins/plugins/132.aften.soundkonverter.xml13
-rwxr-xr-xsrc/plugins/plugins/135.flac.soundkonverter.xml11
-rwxr-xr-xsrc/plugins/plugins/136.flake.soundkonverter.xml8
-rwxr-xr-xsrc/plugins/plugins/137.mac.soundkonverter.xml10
-rwxr-xr-xsrc/plugins/plugins/140.mplayer.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/145.ffmpeg.soundkonverter.xml16
-rwxr-xr-xsrc/plugins/plugins/146.ffmpeg-lossless.soundkonverter.xml10
-rwxr-xr-xsrc/plugins/plugins/150.shorten.soundkonverter.xml9
-rwxr-xr-xsrc/plugins/plugins/151.tta.soundkonverter.xml8
-rwxr-xr-xsrc/plugins/plugins/153.bonk.soundkonverter.xml12
-rwxr-xr-xsrc/plugins/plugins/155.optimfrog.soundkonverter.xml11
-rwxr-xr-xsrc/plugins/plugins/156.optimfrog-dualstream.soundkonverter.xml15
-rwxr-xr-xsrc/plugins/plugins/158.wavpack.soundkonverter.xml11
-rwxr-xr-xsrc/plugins/plugins/160.lac.soundkonverter.xml9
-rwxr-xr-xsrc/plugins/plugins/165.lpac.soundkonverter.xml9
-rwxr-xr-xsrc/plugins/plugins/170.speex.soundkonverter.xml16
-rwxr-xr-xsrc/plugins/plugins/180.timidity.soundkonverter.xml5
-rw-r--r--src/plugins/plugins/190.sox.soundkonverter.xml8
-rwxr-xr-xsrc/plugins/plugins/210.vorbisgain.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/220.mp3gain.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/225.aacgain.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/230.replaygain.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/240.metaflac.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/250.wvgain.soundkonverter.xml5
-rwxr-xr-xsrc/plugins/plugins/310.cdda2wav.soundkonverter.xml7
-rwxr-xr-xsrc/plugins/plugins/320.cdparanoia.soundkonverter.xml7
-rw-r--r--src/plugins/plugins/Makefile.am14
-rwxr-xr-xsrc/plugins/rohling/Makefile.am6
-rwxr-xr-xsrc/progressindicator.cpp144
-rwxr-xr-xsrc/progressindicator.h61
-rwxr-xr-xsrc/replaygain.cpp96
-rwxr-xr-xsrc/replaygain.h58
-rwxr-xr-xsrc/replaygainfilelist.cpp1262
-rwxr-xr-xsrc/replaygainfilelist.h242
-rwxr-xr-xsrc/replaygainscanner.cpp233
-rwxr-xr-xsrc/replaygainscanner.h77
-rwxr-xr-xsrc/soundkonverter.cpp780
-rwxr-xr-xsrc/soundkonverter.desktop15
-rwxr-xr-xsrc/soundkonverter.h206
-rwxr-xr-xsrc/soundkonverterapp.cpp133
-rwxr-xr-xsrc/soundkonverterapp.h37
-rwxr-xr-xsrc/soundkonverterui.rc40
-rwxr-xr-xsrc/userscript.sh22
328 files changed, 35394 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..c758fb3
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,58 @@
+# set the include path for X, qt and KDE
+INCLUDES = $(all_includes) -I$(top_srcdir)/src/metadata/ \
+ -I$(top_srcdir)/src/pluginloader/
+
+# these are the headers for your project
+noinst_HEADERS = cdmanager.h cdopener.h combobutton.h config.h \
+ configbackendspage.h configdialog.h configenvironmentpage.h configgeneralpage.h \
+ configpagebase.h configpluginspage.h conversionoptions.h convert.h cuesheeteditor.h \
+ dcopinterface.h dirdialog.h filelist.h logger.h logviewer.h options.h optionsdetailed.h \
+ optionseditor.h optionsrequester.h optionssimple.h outputdirectory.h \
+ progressindicator.h replaygain.h replaygainfilelist.h replaygainscanner.h soundkonverter.h \
+ soundkonverterapp.h
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(EXTRACTRC) `find . -name \*.ui -o -name \*.rc` > rc.cpp
+ $(XGETTEXT) *.cpp -o $(podir)/soundkonverter.pot
+
+KDE_ICON = AUTO
+
+#########################################################################
+# APPLICATION SECTION
+#########################################################################
+# this is the program that gets installed. it's name is used for all
+# of the other Makefile.am variables
+bin_PROGRAMS = soundkonverter
+
+# the application source, library search path, and link libraries
+soundkonverter_SOURCES = cddb.cpp cdmanager.cpp cdopener.cpp combobutton.cpp \
+ config.cpp configbackendspage.cpp configdialog.cpp configenvironmentpage.cpp \
+ configgeneralpage.cpp configpagebase.cpp configpluginspage.cpp conversionoptions.cpp \
+ convert.cpp cuesheeteditor.cpp dcopinterface.skel dirdialog.cpp filelist.cpp \
+ logger.cpp logviewer.cpp main.cpp options.cpp optionsdetailed.cpp \
+ optionseditor.cpp optionsrequester.cpp optionssimple.cpp outputdirectory.cpp paranoia.cpp \
+ progressindicator.cpp replaygain.cpp replaygainfilelist.cpp replaygainscanner.cpp \
+ soundkonverter.cpp soundkonverterapp.cpp
+soundkonverter_LDFLAGS = $(KDE_RPATH) $(all_libraries)
+soundkonverter_LDADD = $(LIB_KDEUI) $(LIB_KFILE) metadata/libmetadata.la \
+ pluginloader/libpluginloader.la -lcdda_interface -lcdda_paranoia
+
+xdg_apps_DATA = soundkonverter.desktop
+
+# this is where the shell's XML-GUI resource file goes
+shellrcdir = $(kde_datadir)/soundkonverter
+shellrc_DATA = soundkonverterui.rc
+
+SUBDIRS = amarokscript metadata pics pluginloader plugins
+soundkonverter_DEPENDENCIES = metadata/libmetadata.la \
+ pluginloader/libpluginloader.la
+amarokscriptsdir = $(kde_datadir)/amarok/scripts
+servicemenusdir = $(kde_datadir)/konqueror/servicemenus
+servicemenus_DATA = audiocd_extract_with_soundkonverter.desktop
+amarokscriptdir = $(kde_datadir)/soundkonverter/amarokscript
+bin_SCRIPTS = userscript.sh
+userscriptdir = $(kde_datadir)/soundkonverter
+userscript_SCRIPTS = userscript.sh
diff --git a/src/amarokscript/Makefile.am b/src/amarokscript/Makefile.am
new file mode 100755
index 0000000..b8049a7
--- /dev/null
+++ b/src/amarokscript/Makefile.am
@@ -0,0 +1,3 @@
+amarokscriptdir = $(kde_datadir)/soundkonverter/amarokscript
+amarokscript_DATA = README \
+ soundKonverter.rb
diff --git a/src/amarokscript/README b/src/amarokscript/README
new file mode 100644
index 0000000..1186231
--- /dev/null
+++ b/src/amarokscript/README
@@ -0,0 +1,43 @@
+<div align="center"><b>soundKonverter amaroK-Script (v0.3.8)</b></div>
+
+<p>
+<b>About:</b><br>
+This script only adds a context menu for easily accesssing the soundKonverter tool.<br>This version was designed to work with soundKonverter 0.2.80+<br>With soundKonverter you can convert audio files into another audio format. And you can add/remove the ReplayGain tag to/from audio files.
+</p>
+
+<p>
+<b>Usage:</b><br>
+Just run this script and click on one or multiple files in your playlist with the right mouse button. Select soundKonverter and your desired action. After that soundKonverter should start (if you have installed it). Look into the handbook of soundKonverter for further information.
+</p>
+
+<p>
+<b>Dependencies:</b><br>
+<ul>
+<li>amaroK 1.4.0</li>
+<li>Ruby 1.6</li>
+<li>soundKonverter 0.2.80+</li>
+</ul>
+</p>
+
+<p>
+<b>ChangeLog:</b><br>
+Version 0.1:<br>
+<ul>
+<li>initial release</li>
+</ul>
+Version 0.2.80:<br>
+<ul>
+<li>add: cd ripping menu</li>
+</ul>
+</p>
+
+<p>
+<b>License:</b><br>
+GNU General Public License V2
+</p>
+
+<p>
+<b>Author:</b><br>
+Daniel Faust ([email protected])
+</p>
+
diff --git a/src/amarokscript/soundKonverter.rb b/src/amarokscript/soundKonverter.rb
new file mode 100755
index 0000000..144c0c8
--- /dev/null
+++ b/src/amarokscript/soundKonverter.rb
@@ -0,0 +1,296 @@
+#!/usr/bin/env ruby
+#
+# amaroK-Script for integrating soundKonverter into amaroK
+#
+# (c) 2005 Daniel Faust <[email protected]>
+# License: GNU General Public License V2
+
+
+# FIXME after adding some files to soundkonverter, it is impossible to repeat that, until soundkonverter gets closed.
+
+# FIXME don't open files on every request. load options on startup and save on exit!
+
+#`dcop amarok playlist addMedia KURL`
+#`dcop amarok playlist addMediaList KURL::List`
+#`dcop amarok collection scanCollectionChanges`
+
+
+require "uri"
+
+begin
+ require "Qt"
+rescue LoadError
+ error = 'Qt Ruby bindings are required for this script.'
+ `dcop amarok playlist popupMessage "soundKonverter: #{error}"`
+ exit
+end
+
+class MainWidget < Qt::Dialog
+
+slots 'accept()'
+
+def initialize(parent = nil, name = nil)
+ super
+
+ box = Qt::VBoxLayout.new( self, 11, 6 );
+
+ lTitle = Qt::Label.new('soundKonverter plugin settings', self)
+ box.addWidget( lTitle )
+ lTitle.setAlignment(Qt::AlignCenter)
+ font = Qt::Font.new()
+ font.setPixelSize(18)
+ font.setBold(true)
+ lTitle.setFont(font)
+
+ box.addSpacing( 5 )
+
+ mediaDeviceBox = Qt::GroupBox.new( 1, Qt::Vertical, "Options for transfering to media devices", self )
+ box.addWidget( mediaDeviceBox )
+ mediaDeviceBox.layout().setSpacing( 6 )
+ mediaDeviceBox.layout().setMargin( 6 )
+ Qt::Label.new('Profile for lossy conversion:', mediaDeviceBox)
+ @cTranscodeProfile = Qt::ComboBox.new(mediaDeviceBox)
+ @cTranscodeProfile.insertItem("Very low")
+ @cTranscodeProfile.insertItem("Low")
+ @cTranscodeProfile.insertItem("Medium")
+ @cTranscodeProfile.insertItem("High")
+ @cTranscodeProfile.insertItem("Very high")
+ @cTranscodeProfile.setCurrentItem( 1 )
+
+# box.addSpacing( 5 )
+#
+# rippingBox = Qt::GroupBox.new( 2, Qt::Horizontal, "Use pre-defined options for CD ripping", self )
+# box.addWidget( rippingBox )
+# rippingBox.layout().setSpacing( 6 )
+# rippingBox.layout().setMargin( 6 )
+# rippingBox.setCheckable( true )
+# rippingBox.setEnabled( false )
+#
+# profileBox = Qt::HBoxLayout.new( rippingBox, 6 );
+# lRippingProfile = Qt::Label.new('Profile:', rippingBox)
+# profileBox.addWidget( lRippingProfile )
+# @cRippingProfile = Qt::ComboBox.new(rippingBox)
+# profileBox.addWidget( @cRippingProfile )
+# @cRippingProfile.insertItem("Very low")
+# @cRippingProfile.insertItem("Low")
+# @cRippingProfile.insertItem("Medium")
+# @cRippingProfile.insertItem("High")
+# @cRippingProfile.insertItem("Very high")
+# @cRippingProfile.insertItem("Lossless")
+# @cRippingProfile.insertItem("Last used")
+# @cRippingProfile.setCurrentItem(3)
+# lRippingFormat = Qt::Label.new('Format:', rippingBox)
+# profileBox.addWidget( lRippingFormat )
+# @cRippingFormat = Qt::ComboBox.new(rippingBox)
+# profileBox.addWidget( @cRippingFormat )
+# @cRippingFormat.insertItem("ogg")
+# @cRippingFormat.insertItem("mp3")
+# @cRippingFormat.insertItem("flac")
+# directoryBox = Qt::HBoxLayout.new( rippingBox, 6 );
+# lRippingDirectory = Qt::Label.new('Directory:', rippingBox)
+# directoryBox.addWidget( lRippingDirectory )
+# @cRippingDirectory = Qt::ComboBox.new(rippingBox)
+# directoryBox.addWidget( @cRippingDirectory )
+# @cRippingDirectory.setEditable(true)
+# @cRippingDirectory.insertItem("/home/daniel/soundKonverter/%b/%d - %n - %t")
+# @cRippingDirectory.insertItem("Last used")
+
+ box.addSpacing( 5 )
+
+ buttonsBox = Qt::HBoxLayout.new(box)
+ buttonsBox.setSpacing(6)
+ buttonsBox.addStretch()
+ okPushButton = Qt::PushButton.new( self, "ok" )
+ buttonsBox.addWidget( okPushButton )
+ okPushButton.setText( "OK" )
+ okPushButton.setDefault( true )
+
+ connect( okPushButton, SIGNAL( 'clicked()' ),
+ self, SLOT( 'accept()' )
+ )
+
+ cancelPushButton = Qt::PushButton.new( self, "cancel" )
+ buttonsBox.addWidget( cancelPushButton )
+ cancelPushButton.setText( "Cancel" )
+ cancelPushButton.setAccel( Qt::KeySequence.new(Key_Escape) )
+
+ connect( cancelPushButton, SIGNAL( 'clicked()' ),
+ self, SLOT( 'reject()' )
+ )
+
+ file = Qt::File.new( File.dirname( File.expand_path( __FILE__ ) ) + "/config" )
+ if file.open( Qt::IO_ReadOnly )
+ ts = Qt::TextStream.new( file )
+ content = ''
+ ts >> content
+ if content == 'Very_low'
+ @cTranscodeProfile.setCurrentItem( 0 )
+ elsif content == 'Low'
+ @cTranscodeProfile.setCurrentItem( 1 )
+ elsif content == 'Medium'
+ @cTranscodeProfile.setCurrentItem( 2 )
+ elsif content == 'High'
+ @cTranscodeProfile.setCurrentItem( 3 )
+ elsif content == 'Very_high'
+ @cTranscodeProfile.setCurrentItem( 4 )
+ end
+ file.close()
+ end
+
+# file = Qt::File.new( File.dirname( File.expand_path( __FILE__ ) ) + "/profiles" )
+# if file.open( Qt::IO_ReadOnly )
+# ts = Qt::TextStream.new( file )
+# while !ts.eof()
+# content = ''
+# ts >> content
+# @cProfile.insertItem( content )
+# ts >> content
+# end
+#
+# file.close()
+# end
+end
+
+
+def accept()
+ file = Qt::File.new( File.dirname( File.expand_path( __FILE__ ) ) + "/config" )
+ if file.open( Qt::IO_WriteOnly )
+ ts = Qt::TextStream.new( file )
+ if @cTranscodeProfile.currentText() == 'Very low'
+ content = 'Very_low'
+ elsif @cTranscodeProfile.currentText() == 'Low'
+ content = 'Low'
+ elsif @cTranscodeProfile.currentText() == 'Medium'
+ content = 'Medium'
+ elsif @cTranscodeProfile.currentText() == 'High'
+ content = 'High'
+ elsif @cTranscodeProfile.currentText() == 'Very high'
+ content = 'Very_high'
+ end
+ ts << content + "\n"
+ file.close()
+ end
+
+ super
+end
+
+end
+
+
+MenuItemName1 = "soundKonverter \"Convert selected files\""
+MenuItemName2 = "soundKonverter \"Add Replay Gain to selected files\""
+MenuItemName3 = "soundKonverter \"Rip and play audio CD\""
+
+
+def cleanup()
+ `dcop amarok script removeCustomMenuItem #{MenuItemName1}`
+ `dcop amarok script removeCustomMenuItem #{MenuItemName2}`
+ `dcop amarok script removeCustomMenuItem #{MenuItemName3}`
+end
+
+
+trap( "SIGTERM" ) { cleanup() }
+
+`dcop amarok script addCustomMenuItem #{MenuItemName1}`
+`dcop amarok script addCustomMenuItem #{MenuItemName2}`
+`dcop amarok script addCustomMenuItem #{MenuItemName3}`
+
+loop do
+ message = gets().chomp()
+ command = /[A-Za-z]*/.match( message ).to_s()
+
+ case command
+ when "configure"
+ app = Qt::Application.new(ARGV)
+ widget = MainWidget.new
+ app.setMainWidget(widget)
+ widget.show()
+ app.exec()
+
+ when "transcode"
+ args = message.split()
+ filename = args[1]
+ #uri = URI.parse( args[1] )
+ #filename = URI.unescape( uri.path() )
+ filetype = args[2]
+ profile = ''
+
+ file = Qt::File.new( File.dirname( File.expand_path( __FILE__ ) ) + "/formats" )
+ if file.open( Qt::IO_ReadOnly )
+ ts = Qt::TextStream.new( file )
+ while !ts.eof()
+ mode = ''
+ formats = ''
+ ts >> mode
+ ts >> formats
+ if formats.split(',').include?( filetype )
+ profile = mode
+ end
+ end
+ file.close()
+ end
+
+ if profile == ''
+ file = Qt::File.new( File.dirname( File.expand_path( __FILE__ ) ) + "/config" )
+ if file.open( Qt::IO_ReadOnly )
+ ts = Qt::TextStream.new( file )
+ content = ''
+ ts >> content
+ if content == 'Very_low'
+ profile = 'Very low'
+ elsif content == 'Low'
+ profile = 'Low'
+ elsif content == 'Medium'
+ profile = 'Medium'
+ elsif content == 'High'
+ profile = 'High'
+ elsif content == 'Very_high'
+ profile = 'Very high'
+ end
+ file.close()
+ else
+ profile = 'Low'
+ end
+ end
+
+ `dcop amarok playlist shortStatusMessage "starting soundKonverter in background"`
+ `soundkonverter --invisible --profile '#{profile}' --format '#{filetype}' --output '/tmp' --command "dcop amarok mediabrowser transcodingFinished %u file://%o" '#{filename}'`
+
+ when "customMenuClicked"
+ if message.include?( "Convert selected files" )
+ args = message.split()
+ # Remove the command args
+ 5.times() { args.delete_at( 0 ) }
+
+ # Iterate over all selected files
+ files = ''
+ args.each() do |arg|
+ uri = URI.parse( arg )
+ file = URI.unescape( uri.path() )
+ files += ' "'+file+'"'
+ end
+ `dcop amarok playlist shortStatusMessage "starting soundKonverter"`
+ `soundkonverter #{files}`
+ end
+ if message.include?( "Add Replay Gain to selected files" )
+ args = message.split()
+ # Remove the command args
+ 8.times() { args.delete_at( 0 ) }
+
+ files = ''
+ args.each() do |arg|
+ uri = URI.parse( arg )
+ file = URI.unescape( uri.path() )
+ files += ' "'+file+'"'
+ end
+ `dcop amarok playlist shortStatusMessage "starting soundKonverter"`
+ `soundkonverter --replaygain #{files}`
+ end
+ if message.include?( "Rip and play audio CD" )
+ `dcop amarok playlist popupMessage "Select all tracks to rip and press 'Start' in order to start ripping.\nThe tracks will be added to the playlist, when they are ready."`
+ #`dcop amarok playlist shortStatusMessage "starting soundKonverter"`
+ `soundkonverter --invisible --rip auto --command "dcop amarok playlist addMedia %o"`
+ end
+ end
+end
+
diff --git a/src/audiocd_extract_with_soundkonverter.desktop b/src/audiocd_extract_with_soundkonverter.desktop
new file mode 100755
index 0000000..a6d4120
--- /dev/null
+++ b/src/audiocd_extract_with_soundkonverter.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Actions=ExtractSoundKonverter
+ServiceTypes=media/audiocd
+Icon=soundkonverter
+X-KDE-Priority=TopLevel
+
+[Desktop Action ExtractSoundKonverter]
+Exec=soundkonverter --rip %u
+Icon=soundkonverter
+Name=Extract with soundKonverter
+Name[de]=Mit soundKonverter auslesen
diff --git a/src/cddb.cpp b/src/cddb.cpp
new file mode 100755
index 0000000..58ebde7
--- /dev/null
+++ b/src/cddb.cpp
@@ -0,0 +1,669 @@
+/*
+ Copyright (C) 2000 Michael Matz <[email protected]>
+
+ 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.
+*/
+
+#include <config.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <errno.h>
+#include <unistd.h>
+#include <qdir.h>
+#include <qtextstream.h>
+#include <qregexp.h>
+//#include <qapp.h>
+#include <qstring.h>
+// #include <qcursor.h>
+//#include <kdebug.h>
+#include <ksock.h>
+#include <kextsock.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+
+#include "cddb.h"
+#include "cddb.moc"
+
+// FIXME //kdDebug
+
+CDDB::CDDB()
+ : ks(0), port(80), remote(false), save_local(false)
+{
+ QString s = QDir::homeDirPath()+"/.cddb";
+ cddb_dirs +=s;
+}
+
+
+
+CDDB::~CDDB()
+{
+ deinit();
+}
+
+
+
+bool
+CDDB::set_server(const char *hostname, unsigned short int _port)
+{
+ if (ks)
+ {
+ if (h_name == hostname && port == _port)
+ return true;
+ deinit();
+ }
+ remote = (hostname != 0) && (*hostname != 0);
+ //kdDebug(7101) << "CDDB: set_server, host=" << hostname << "port=" << _port << endl;
+ if (remote)
+ {
+ ks = new KExtendedSocket(hostname, _port);
+ if (ks->connect() < 0)
+ {
+ //kdDebug(7101) << "CDDB: Can't connect!" << endl;
+ delete ks;
+ ks = 0;
+ return false;
+ }
+
+ h_name = hostname;
+ port = _port;
+ QCString r;
+ readLine(r); // the server greeting
+ writeLine("cddb hello kde-user blubb kio_audiocd 0.4");
+ readLine(r);
+ }
+ return true;
+}
+
+
+
+bool
+CDDB::deinit()
+{
+ if (ks)
+ {
+ writeLine("quit");
+ QCString r;
+ readLine(r);
+ ks->close();
+ }
+ h_name.resize(0);
+ port = 0;
+ remote = false;
+ ks = 0;
+ return true;
+}
+
+
+
+bool
+CDDB::readLine(QCString& ret)
+{
+ int read_length = 0;
+ char small_b[128];
+ //fd_set set;
+
+ ret.resize(0);
+ while (read_length < 40000)
+ {
+ // Look for a \n in buf
+ int ni = buf.find('\n');
+ if (ni >= 0)
+ {
+ // Nice, so return this substring (without the \n),
+ // and truncate buf accordingly
+ ret = buf.left(ni);
+ if (ret.length() && ret[ret.length()-1] == '\r')
+ ret.resize(ret.length());
+ buf.remove(0, ni+1);
+ //kdDebug(7101) << "CDDB: got `" << ret << "'" << endl;
+ return true;
+ }
+
+ // Try to refill the buffer
+ ks->waitForMore(60 * 1000);
+ ssize_t l = ks->readBlock(small_b, sizeof(small_b)-1);
+ if (l <= 0)
+ {
+ // l==0 normally means fd got closed, but we really need a lineend
+ return false;
+ }
+ small_b[l] = 0;
+ read_length += l;
+ buf += small_b;
+ }
+ return false;
+}
+
+
+
+bool
+CDDB::writeLine(const QCString& line)
+{
+ const char *b = line.data();
+ int l = line.length();
+ //kdDebug(7101) << "CDDB: send `" << line << "'" << endl;
+ while (l)
+ {
+ ssize_t wl = ks->writeBlock(b, l);
+ if (wl < 0 && errno != EINTR)
+ return false;
+ if (wl < 0)
+ wl = 0;
+ l -= wl;
+ b += wl;
+ }
+ l = line.length();
+ if (l && line.data()[l-1] != '\n')
+ {
+ char c = '\n';
+ ssize_t wl;
+ do {
+ wl = ks->writeBlock(&c, 1);
+ } while (wl <= 0 && errno == EINTR);
+ if (wl<=0 && errno != EINTR)
+ return false;
+ }
+ return true;
+}
+
+
+
+unsigned int
+CDDB::get_discid(QValueList<int>& track_ofs)
+{
+ unsigned int id = 0;
+ int num_tracks = track_ofs.count() - 2;
+
+ // the last two track_ofs[] are disc begin and disc end
+
+ for (int i = num_tracks - 1; i >= 0; i--)
+ {
+ int n = track_ofs[i];
+ n /= 75;
+ while (n > 0)
+ {
+ id += n % 10;
+ n /= 10;
+ }
+ }
+ unsigned int l = track_ofs[num_tracks + 1];
+ l -= track_ofs[num_tracks];
+ l /= 75;
+ id = ((id % 255) << 24) | (l << 8) | num_tracks;
+ return id;
+}
+
+
+
+static int
+get_code (const QCString &s)
+{
+ bool ok;
+ int code = s.left(3).toInt(&ok);
+ if (!ok)
+ code = -1;
+ return code;
+}
+
+
+
+static void
+parse_query_resp (const QCString& _r, QCString& catg, QCString& d_id, QCString& title)
+{
+ QCString r = _r.stripWhiteSpace();
+ int i = r.find(' ');
+ if (i)
+ {
+ catg = r.left(i).stripWhiteSpace();
+ r.remove(0, i+1);
+ r = r.stripWhiteSpace();
+ }
+ i = r.find(' ');
+ if (i)
+ {
+ d_id = r.left(i).stripWhiteSpace();
+ r.remove(0, i+1);
+ r = r.stripWhiteSpace();
+ }
+ title = r;
+}
+
+
+
+QString
+CDDB::track(int i) const
+{
+ if (i < 0 || i >= int(m_names.count()))
+ return QString();
+ return m_names[i].utf8();
+}
+
+
+
+
+QString
+CDDB::artist(int i) const
+{
+ if (i < 0 || i >= int(m_artists.count()))
+ return QString();
+ return m_artists[i].utf8();
+}
+
+
+
+bool
+CDDB::parse_read_resp(QTextStream *stream, QTextStream *write_stream)
+{
+ /* Note, that m_names and m_title should be empty */
+ QCString end = ".";
+
+ m_disc = 0;
+ m_year = 0;
+ m_genre = "";
+
+ /* Fill table, so we can index it below. */
+ for (int i = 0; i < m_tracks; i++)
+ {
+ m_names.append("");
+ m_artists.append("");
+ }
+ while (1)
+ {
+ QCString r;
+ if (stream)
+ {
+ if (stream->atEnd())
+ break;
+ r = stream->readLine().latin1();
+ }
+ else
+ {
+ if (!readLine(r))
+ return false;
+ }
+ /* Normally the "." is not saved into the local files, but be
+ tolerant about this. */
+ if (r == end)
+ break;
+ if (write_stream)
+ *write_stream << r << endl;
+ r = r.stripWhiteSpace();
+ if (r.isEmpty() || r[0] == '#')
+ continue;
+ if (r.left(7) == "DTITLE=")
+ {
+ r.remove(0, 7);
+ m_title += QString::fromLocal8Bit(r.stripWhiteSpace());
+ }
+ else if (r.left(6) == "TTITLE")
+ {
+ r.remove(0, 6);
+ int e = r.find('=');
+ if (e)
+ {
+ bool ok;
+ int i = r.left(e).toInt(&ok);
+ if (ok && i >= 0 && i < m_tracks)
+ {
+ r.remove(0, e+1);
+ m_names[i] += QString::fromLocal8Bit(r);
+ }
+ }
+ }
+ else if (r.left(6) == "DYEAR=")
+ {
+ r.remove(0, 6);
+ QString year = QString::fromLocal8Bit(r.stripWhiteSpace());
+ m_year = year.toInt();
+ //kdDebug(7101) << "CDDB: found Year: " << QString().sprintf("%04i",m_year) << endl;
+ }
+ else if (r.left(7) == "DGENRE=")
+ {
+ r.remove(0, 7);
+ m_genre = QString::fromLocal8Bit(r.stripWhiteSpace());
+ //kdDebug(7101) << "CDDB: found Genre: " << m_genre << endl;
+ }
+ }
+
+ /* XXX We should canonicalize the strings ("\n" --> '\n' e.g.) */
+
+ int si = m_title.find(" / ");
+ if (si > 0)
+ {
+ m_artist = m_title.left(si).stripWhiteSpace();
+ m_title.remove(0, si+3);
+ m_title = m_title.stripWhiteSpace();
+ }
+
+ si = m_title.find(" - CD");
+ if (si > 0)
+ {
+ QString disc = m_title.right(m_title.length()-(si+5)).stripWhiteSpace();
+ m_disc = disc.toInt();
+ //kdDebug(7101) << "CDDB: found Disc: " << disc << endl;
+ m_title = m_title.left(si).stripWhiteSpace();
+ }
+
+ if (m_title.isEmpty())
+ m_title = i18n("No Title");
+ /*else
+ m_title.replace(QRegExp("/"), "%2f");*/
+ if (m_artist.isEmpty())
+ m_artist = i18n("Unknown");
+ /*else
+ m_artist.replace(QRegExp("/"), "%2f");*/
+
+ //kdDebug(7101) << "CDDB: found Title: `" << m_title << "'" << endl;
+ for (int i = 0; i < m_tracks; i++)
+ {
+ if (m_names[i].isEmpty())
+ m_names[i] += i18n("Track %1").arg(i);
+ //m_names[i].replace(QRegExp("/"), "%2f");
+ si = m_names[i].find(" - ");
+ if (si < 0)
+ {
+ si = m_names[i].find(" / ");
+ }
+ if (si > 0)
+ {
+ m_artists[i] = m_names[i].left(si).stripWhiteSpace();
+ m_names[i].remove(0, si+3);
+ m_names[i] = m_names[i].stripWhiteSpace();
+ }
+ else
+ {
+ m_artists[i] = m_artist;
+ }
+ //kdDebug(7101) << "CDDB: found Track " << i+1 << ": `" << m_names[i] << "'" << endl;
+ }
+ return true;
+}
+
+
+
+void
+CDDB::add_cddb_dirs(const QStringList& list)
+{
+ QString s = QDir::homeDirPath()+"/.cddb";
+
+ cddb_dirs = list;
+ if (cddb_dirs.isEmpty())
+ cddb_dirs += s;
+}
+
+
+
+/* Locates and opens the local file corresponding to that discid.
+ Returns TRUE, if file is found and ready for reading.
+ Returns FALSE, if file isn't found. In this case ret_file is initialized
+ with a QFile which resides in the first cddb_dir, and has a temp name
+ (the ID + getpid()). You can open it for writing. */
+bool
+CDDB::searchLocal(unsigned int id, QFile *ret_file)
+{
+ QDir dir;
+ QString filename;
+ filename = QString("%1").arg(id, 0, 16).rightJustify(8, '0');
+ QStringList::ConstIterator it;
+ for (it = cddb_dirs.begin(); it != cddb_dirs.end(); ++it)
+ {
+ dir.setPath(*it);
+ if (!dir.exists())
+ continue;
+ /* First look in dir directly. */
+ ret_file->setName (*it + "/" + filename);
+ if (ret_file->exists() && ret_file->open(IO_ReadOnly))
+ return true;
+ /* And then in the subdirs of dir (representing the categories normally).
+ */
+ const QFileInfoList *subdirs = dir.entryInfoList (QDir::Dirs);
+ QFileInfoListIterator fiit(*subdirs);
+ QFileInfo *fi;
+ while ((fi = fiit.current()) != 0)
+ {
+ ret_file->setName (*it + "/" + fi->fileName() + "/" + filename);
+ if (ret_file->exists() && ret_file->open(IO_ReadOnly))
+ return true;
+ ++fiit;
+ }
+ }
+ QString pid;
+ pid.setNum(::getpid());
+ ret_file->setName (cddb_dirs[0] + "/" + filename + "." + pid);
+ /* Try to create the save location. */
+ dir.setPath(cddb_dirs[0]);
+ if (save_local && !dir.exists())
+ {
+ //dir = QDir::current();
+ dir.mkdir(cddb_dirs[0]);
+ }
+ return false;
+}
+
+
+
+bool
+CDDB::queryCD(QValueList<int>& track_ofs)
+{
+ int num_tracks = track_ofs.count() - 2;
+ if (num_tracks < 1)
+ return false;
+ unsigned int id = get_discid(track_ofs);
+ QFile file;
+ bool local;
+
+ /* Already read this ID. */
+ if (id == m_discid)
+ return true;
+
+ emit cddbMessage(i18n("Searching local cddb entry ..."));
+ qApp->processEvents();
+
+ /* First look for a local file. */
+ local = searchLocal (id, &file);
+ /* If we have no local file, and no remote connection, barf. */
+ if (!local && (!remote || ks == 0))
+ return false;
+
+ m_tracks = num_tracks;
+ m_title = "";
+ m_artist = "";
+ m_names.clear();
+ m_discid = id;
+ if (local)
+ {
+ QTextStream stream(&file);
+ /* XXX Hmm, what encoding is used by CDDB files? local? Unicode?
+ Nothing? */
+ //stream.setEncoding(QTextStream::Locale);
+ parse_read_resp(&stream, 0);
+ file.close();
+ return true;
+ }
+
+ emit cddbMessage(i18n("Searching remote cddb entry ..."));
+ qApp->processEvents();
+
+ /* Remote CDDB query. */
+ unsigned int length = track_ofs[num_tracks+1] - track_ofs[num_tracks];
+ QCString q;
+ q.sprintf("cddb query %08x %d", id, num_tracks);
+ QCString num;
+ for (int i = 0; i < num_tracks; i++)
+ q += " " + num.setNum(track_ofs[i]);
+ q += " " + num.setNum(length / 75);
+ if (!writeLine(q))
+ return false;
+ QCString r;
+ if (!readLine(r))
+ return false;
+ r = r.stripWhiteSpace();
+ int code = get_code(r);
+ if (code == 200)
+ {
+ QCString catg, d_id, title;
+ QDir dir;
+ QCString s, pid;
+
+ emit cddbMessage(i18n("Found exact match cddb entry ..."));
+ qApp->processEvents();
+
+ /* an exact match */
+ r.remove(0, 3);
+ parse_query_resp(r, catg, d_id, title);
+ //kdDebug(7101) << "CDDB: found exact CD: category=" << catg << " DiscId=" << d_id << " Title=`" << title << "'" << endl;
+ q = "cddb read " + catg + " " + d_id;
+ if (!writeLine(q))
+ return false;
+ if (!readLine(r))
+ return false;
+ r = r.stripWhiteSpace();
+ code = get_code(r);
+ if (code != 210)
+ return false;
+
+ pid.setNum(::getpid());
+ s = cddb_dirs[0].latin1();
+ //s = s + "/" +catg; // xine seems to not search in local subdirs
+ dir.setPath( s );
+ if ( !dir.exists() ) dir.mkdir( s );
+ s = s+"/"+ d_id;
+ file.setName( s );
+
+ if (save_local && file.open(IO_WriteOnly))
+ {
+ //kdDebug(7101) << "CDDB: file name to save =" << file.name() << endl;
+ QTextStream stream(&file);
+ if (!parse_read_resp(0, &stream))
+ {
+ file.remove();
+ return false;
+ }
+ file.close();
+ /*QString newname (file.name());
+ newname.truncate(newname.findRev('.'));
+ if (QDir::current().rename(file.name(), newname)) {
+ //kdDebug(7101) << "CDDB: rename failed" << endl;
+ file.remove();
+ } */
+ }
+ else if (!parse_read_resp(0, 0))
+ return false;
+ }
+ else if (code == 211)
+ {
+ // Found some close matches. We'll read the query response and ask the user
+ // which one should be fetched from the server.
+ QCString end = ".";
+ QCString catg, d_id, title;
+ QDir dir;
+ QCString s, pid, first_match;
+ QStringList disc_ids;
+
+ /* some close matches */
+ //XXX may be try to find marker based on r
+ emit cddbMessage(i18n("Found close cddb entry ..."));
+ qApp->processEvents();
+
+ int i=0;
+ while (1)
+ {
+ if (!readLine(r))
+ return false;
+ r = r.stripWhiteSpace();
+ if (r == end)
+ break;
+ disc_ids.append(r);
+ if (i == 0)
+ first_match = r;
+ i++;
+ }
+
+ bool ok = false;
+
+ // We don't want to be thinking too much, do we?
+// QApplication::restoreOverrideCursor();
+
+ // Oh, mylord, which match should I serve you?
+ QString _answer = KInputDialog::getItem(i18n("CDDB Matches"), i18n("Several close CDDB entries found. Choose one:"),
+ disc_ids, 0, false, &ok );
+ QCString answer = _answer.utf8();
+
+ if (ok){ // Get user selected match
+ parse_query_resp(answer, catg, d_id, title);
+ }
+ else{ // Get first match
+ parse_query_resp(first_match, catg, d_id, title);
+ }
+
+ // Now we can continue thinking...
+// QApplication::setOverrideCursor( QCursor(Qt::WaitCursor) );
+
+ /*kdDebug(7101) << "CDDB: found close CD: category=" << catg << " DiscId="
+ << d_id << " Title=`" << title << "'" << endl;*/
+
+ // ... and forth we go as usual
+
+ q = "cddb read " + catg + " " + d_id;
+ if (!writeLine(q))
+ return false;
+ if (!readLine(r))
+ return false;
+ r = r.stripWhiteSpace();
+ code = get_code(r);
+ if (code != 210)
+ return false;
+
+ pid.setNum(::getpid());
+ s = cddb_dirs[0].latin1();
+ dir.setPath( s );
+ if ( !dir.exists() ) dir.mkdir( s );
+ s = s+"/"+ d_id;
+ file.setName( s );
+
+ if (save_local && file.open(IO_WriteOnly))
+ {
+ //kdDebug(7101) << "CDDB: file name to save =" << file.name() << endl;
+ QTextStream stream(&file);
+ if (!parse_read_resp(0, &stream))
+ {
+ file.remove();
+ return false;
+ }
+ file.close();
+ }
+ else if (!parse_read_resp(0, 0))
+ return false;
+
+ }
+ else
+ {
+ /* 202 - no match found
+ 403 - Database entry corrupt
+ 409 - no handshake */
+ //kdDebug(7101) << "CDDB: query returned code " << code << endl;
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/cddb.h b/src/cddb.h
new file mode 100755
index 0000000..62c38a7
--- /dev/null
+++ b/src/cddb.h
@@ -0,0 +1,79 @@
+/*
+ Copyright (C) 2000 Michael Matz <[email protected]>
+ Modified 2006 Daniel Faust <[email protected]>, thx Michael
+
+ 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.
+*/
+
+#ifndef CDDB_H
+#define CDDB_H
+
+#include <qcstring.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <qobject.h>
+
+class QFile;
+class QTextStream;
+class KExtendedSocket;
+
+class CDDB : public QObject
+{
+ Q_OBJECT
+public:
+ CDDB();
+ ~CDDB();
+ bool set_server(const char *hostname = 0, unsigned short int port = 0);
+ void add_cddb_dirs(const QStringList& list);
+ void save_cddb (bool save) { save_local = save; }
+ unsigned int get_discid(QValueList<int>& track_ofs);
+ bool queryCD(QValueList<int>& track_ofs);
+ QString title() const { return m_title.utf8(); }
+ QString artist(int i) const;
+ int trackCount() const { return m_tracks; }
+ QString track(int i) const;
+ int disc() const { return m_disc; }
+ QString genre() const { return m_genre.utf8(); }
+ int year() const { return m_year; }
+
+private:
+ bool readLine(QCString& s);
+ bool writeLine(const QCString& s);
+ bool deinit();
+ bool parse_read_resp(QTextStream*, QTextStream*);
+ bool searchLocal(unsigned int id, QFile *ret_file);
+ KExtendedSocket *ks;
+ QCString h_name;
+ unsigned short int port;
+ bool remote;
+ bool save_local;
+ QStringList cddb_dirs;
+ QCString buf;
+ unsigned int m_discid;
+
+ int m_tracks;
+ int m_disc;
+ int m_year;
+ QString m_genre;
+ QString m_title;
+ QString m_artist;
+ QStringList m_artists;
+ QStringList m_names;
+
+signals:
+ void cddbMessage( QString );
+};
+
+#endif // CDDB_H
diff --git a/src/cdmanager.cpp b/src/cdmanager.cpp
new file mode 100755
index 0000000..cd9dd27
--- /dev/null
+++ b/src/cdmanager.cpp
@@ -0,0 +1,190 @@
+
+#include "cdmanager.h"
+#include "paranoia.h"
+#include "cddb.h"
+#include "conversionoptions.h"
+
+#include <qstringlist.h>
+#include <qvaluelist.h>
+
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kinputdialog.h>
+#include <dcopref.h>
+
+
+// ### soundkonverter 0.4 implement reading of milliseconds/frames
+
+// TODO implement reading of cd data
+
+CDDevice::CDDevice( const QString& _device )
+{
+ QStringList s;
+ bool init = false;
+ QValueList<int> qvl;
+ int i;
+ QStringList dcopList, devList;
+ bool ok = false;
+
+ tags.clear();
+
+ if( !_device.isEmpty() )
+ s.append( _device );
+ else {
+ DCOPRef mediamanager( "kded", "mediamanager" );
+ DCOPReply reply = mediamanager.call( "fullList()" );
+ if( reply.isValid() ) {
+ dcopList = reply;
+ i = 0;
+ while( i < (int)dcopList.count() ) {
+ if( dcopList[i+10] == "media/audiocd" ) {
+ devList.append( dcopList[i+5] );
+ }
+ i += 13;
+ }
+ if( devList.count() > 1 ) {
+ QString choice = KInputDialog::getItem( i18n("Audio CD"), i18n("Several audio CDs found. Choose one:"), devList, 0, false, &ok );
+ if( ok ) s.append( choice );
+ else s.append( 0 ); // TODO if canceled, the cd opener should close, not use the first item
+ }
+ else if( devList.count()==1 ) {
+ s.append( devList[0] );
+ }
+ else {
+ s.append( "/dev/cdrom" );
+ s.append( "/dev/dvd" );
+ }
+ }
+ else {
+ s.append( "/dev/cdrom" );
+ s.append( "/dev/dvd" );
+ }
+ }
+
+ para = new Paranoia();
+ for( i = 0; i < (int)s.count(); i++ ) {
+ if( init = para->init(s[i]) ) {
+ device = s[i];
+ break;
+ }
+ }
+ if( init ) {
+ trackCount = para->getTracks();
+ timeCount = para->trackTime( -1 );
+ for( i = 0; i < para->getTracks(); i++) {
+ qvl.append( para->trackFirstSector(i+1) + 150 );
+ }
+ qvl.append( para->discFirstSector() );
+ qvl.append( para->discLastSector() );
+ CDDB* cddb = new CDDB();
+ cddb->save_cddb( true );
+ if( cddb->queryCD(qvl) ) {
+ for( i = 0; i < para->getTracks(); i++ ) {
+ tags += new TagData( cddb->artist(i), "", cddb->title(), cddb->track(i), cddb->genre(), "",
+ i+1, cddb->disc(), cddb->year(), para->trackTime(i) );
+ }
+ }
+ else {
+ cddb->set_server( "freedb.freedb.org", 8880 );
+ if( cddb->queryCD(qvl) ) {
+ for( i = 0; i < para->getTracks(); i++ ) {
+ tags += new TagData( cddb->artist(i), "", cddb->title(), cddb->track(i), cddb->genre(), "",
+ i+1, cddb->disc(), cddb->year(), para->trackTime(i) );
+ }
+ }
+ else {
+ for( i = 0; i < para->getTracks(); i++ ) {
+ tags += new TagData( i18n("Unknown"), "", i18n("Unknown"), i18n("Unknown"), "", "", i+1, 1, 0, para->trackTime(i) );
+ }
+ }
+ }
+ delete cddb;
+ }
+ else {
+ KMessageBox::information( 0, i18n("No audio CD found."), i18n("Warning") );
+ device = "";
+ delete para;
+ para = 0;
+ }
+}
+
+CDDevice::~CDDevice()
+{}
+
+
+CDManager::CDManager()
+{}
+
+CDManager::~CDManager()
+{
+ while( cdDevices.count() > 0 ) delete cdDevices.first();
+}
+
+QString CDManager::newCDDevice( const QString& device )
+{
+ CDDevice* cdDevice = new CDDevice( device );
+
+ for( QValueList<CDDevice*>::Iterator it = cdDevices.begin(); it != cdDevices.end(); ++it ) {
+ if( (*it)->device = cdDevice->device ) {
+ cdDevices.remove( *it );
+ delete (*it);
+ break;
+ }
+ }
+
+ cdDevices += cdDevice;
+ return cdDevice->device;
+}
+
+QValueList<TagData*> CDManager::getTrackList( const QString& device )
+{
+ for( QValueList<CDDevice*>::Iterator it = cdDevices.begin(); it != cdDevices.end(); ++it ) {
+ if( (*it)->device = device ) return (*it)->tags;
+ }
+
+ QValueList<TagData*> list;
+ return list;
+}
+
+TagData* CDManager::getTags( const QString& device, int track )
+{
+ for( QValueList<CDDevice*>::Iterator it = cdDevices.begin(); it != cdDevices.end(); ++it ) {
+ if( (*it)->device = device ) {
+ if( track > 0 ) {
+ QValueList<TagData*>::Iterator tag = (*it)->tags.at( track - 1 );
+ return (*tag);
+ }
+ else {
+ return (*it)->discTags;
+ }
+ }
+ }
+
+ return 0;
+}
+
+int CDManager::getTrackCount( const QString& device )
+{
+ for( QValueList<CDDevice*>::Iterator it = cdDevices.begin(); it != cdDevices.end(); ++it ) {
+ if( (*it)->device = device ) return (*it)->trackCount;
+ }
+
+ return 0;
+}
+
+int CDManager::getTimeCount( const QString& device )
+{
+ for( QValueList<CDDevice*>::Iterator it = cdDevices.begin(); it != cdDevices.end(); ++it ) {
+ if( (*it)->device = device ) return (*it)->timeCount;
+ }
+
+ return 0;
+}
+
+void CDManager::setDiscTags( const QString& device, TagData* tags )
+{
+ for( QValueList<CDDevice*>::Iterator it = cdDevices.begin(); it != cdDevices.end(); ++it ) {
+ if( (*it)->device = device ) (*it)->discTags = tags;
+ }
+}
+
diff --git a/src/cdmanager.h b/src/cdmanager.h
new file mode 100755
index 0000000..9af80c8
--- /dev/null
+++ b/src/cdmanager.h
@@ -0,0 +1,95 @@
+
+
+#ifndef CDMANAGER_H
+#define CDMANAGER_H
+
+#include "tagengine.h"
+
+#include <qobject.h>
+
+class ConversionOptions;
+class Paranoia;
+
+/**
+ * @short All data needed for a cd device
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class CDDevice
+{
+public:
+ /**
+ * Constructor
+ */
+ CDDevice( const QString& _device="" );
+
+ /**
+ * Destructor
+ */
+ virtual ~CDDevice();
+
+ QString device;
+ Paranoia* para;
+ QValueList<TagData*> tags;
+ TagData* discTags;
+ int trackCount;
+ int timeCount;
+};
+
+/**
+ * @short The CD manager
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class CDManager : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ CDManager();
+
+ /**
+ * Destructor
+ */
+ virtual ~CDManager();
+
+ /**
+ * Create a new CDDevice entry in cdDevices. Use @param device or auto search for an audio cd
+ * Return the used device (usefull, if auto searching was used)
+ */
+ QString newCDDevice( const QString& device="" );
+
+ /**
+ * Return a list of all tracks on the cd in drive @param device
+ */
+ QValueList<TagData*> getTrackList( const QString& device );
+
+ /**
+ * Return the tags of the track @param track on the cd in drive @param device
+ */
+ TagData* getTags( const QString& device, int track );
+
+ /**
+ * Set the tags of the cd in drive @param device
+ */
+ void setDiscTags( const QString& device, TagData* tags );
+
+ /**
+ * Return the sum of all tracks of the cd in drive @param device
+ */
+ int getTrackCount( const QString& device );
+
+ /**
+ * Return the complete length of the cd in drive @param device
+ */
+ int getTimeCount( const QString& device );
+
+private:
+ /** a list of all devices */
+ QValueList<CDDevice*> cdDevices;
+
+};
+
+#endif // CDMANAGER_H
diff --git a/src/cdopener.cpp b/src/cdopener.cpp
new file mode 100755
index 0000000..acabbc8
--- /dev/null
+++ b/src/cdopener.cpp
@@ -0,0 +1,781 @@
+
+#include "cdopener.h"
+#include "cdmanager.h"
+#include "tagengine.h"
+#include "config.h"
+#include "ripperpluginloader.h"
+#include "combobutton.h"
+
+#include <klistview.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <klineedit.h>
+#include <kcombobox.h>
+#include <knuminput.h>
+#include <ktextedit.h>
+#include <kfiledialog.h>
+#include <kmessagebox.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qgroupbox.h>
+#include <qdatetime.h>
+#include <qcolor.h>
+#include <qdir.h>
+#include <qfile.h>
+
+// ### soundkonverter 0.4: implement cd info text
+
+CDOpener::CDOpener( Config* _config, CDManager* _cdManager, TagEngine* _tagEngine, const QString& _device, QWidget* parent, const char* name, /*Mode default_mode, const QString& default_text,*/ bool modal, WFlags f )
+ : KDialog( parent, name, modal, f )
+{
+ cdManager = _cdManager;
+ tagEngine = _tagEngine;
+ config = _config;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ // let the dialog look nice
+ setCaption( i18n("Add CD tracks") );
+ setIcon( iconLoader->loadIcon("cdaudio_unmount",KIcon::Small) );
+
+ // the grid for all widgets in the dialog
+ QGridLayout* gridLayout = new QGridLayout( this, 1, 1, 11, 6, "gridLayout" );
+
+ // the grid for the artist and album input
+ QGridLayout* topGridLayout = new QGridLayout( this, 1, 1, 0, 6, "topGridLayout" );
+ gridLayout->addLayout( topGridLayout, 0, 0 );
+
+ // set up the first row at the top
+ QLabel* lArtistLabel = new QLabel( i18n("Artist:"), this, "lArtistLabel" );
+ topGridLayout->addWidget( lArtistLabel, 0, 0 );
+ cArtist = new KComboBox( true, this, "cArtist" );
+ topGridLayout->addWidget( cArtist, 0, 1 );
+ cArtist->setMinimumWidth( 200 );
+ cArtist->insertItem( i18n("Various Artists") );
+ cArtist->setCurrentText( "" );
+ connect( cArtist, SIGNAL(textChanged(const QString&)),
+ this, SLOT(artistChanged(const QString&))
+ );
+ // add a horizontal box layout for the composer
+ QHBoxLayout* artistBox = new QHBoxLayout( -1, "artistBox" );
+ topGridLayout->addLayout( artistBox, 0, 3 );
+ // and fill it up
+ QLabel* lComposerLabel = new QLabel( i18n("Composer:"), this, "lComposerLabel" );
+ lComposerLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
+ topGridLayout->addWidget( lComposerLabel, 0, 2 );
+ cComposer = new KComboBox( true, this, "cComposer" );
+ artistBox->addWidget( cComposer );
+ cComposer->insertItem( i18n("Various Composer") );
+ cComposer->setCurrentText( "" );
+ //cComposer->setSizePolicy( QSizePolicy::Maximum );
+ connect( cComposer, SIGNAL(textChanged(const QString&)),
+ this, SLOT(composerChanged(const QString&))
+ );
+ //artistBox->addStretch();
+ artistBox->addSpacing( 130 );
+// pCDDB = new KPushButton( iconLoader->loadIcon("cdaudio_unmount",KIcon::Small), i18n("Request CDDB"), this, "pCDDB" );
+// topGridLayout->addWidget( pCDDB, 0, 8 );
+
+ // set up the second row at the top
+ QLabel* lAlbumLabel = new QLabel( i18n("Album:"), this, "lAlbumLabel" );
+ topGridLayout->addWidget( lAlbumLabel, 1, 0 );
+ lAlbum = new KLineEdit( this, "lAlbum" );
+ topGridLayout->addWidget( lAlbum, 1, 1 );
+ // add a horizontal box layout for the disc number, year and genre
+ QHBoxLayout* albumBox = new QHBoxLayout( -1, "albumBox" );
+ topGridLayout->addLayout( albumBox, 1, 3 );
+ // and fill it up
+ QLabel* lDiscLabel = new QLabel( i18n("Disc No.:"), this, "lDiscLabel" );
+ lDiscLabel->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
+ topGridLayout->addWidget( lDiscLabel, 1, 2 );
+ iDisc = new KIntSpinBox( 1, 99, 1, 1, 10, this, "iDisc" );
+ albumBox->addWidget( iDisc );
+ QLabel* lYearLabel = new QLabel( i18n("Year:"), this, "lYearLabel" );
+ albumBox->addWidget( lYearLabel );
+ iYear = new KIntSpinBox( 0, 99999, 1, QDate::currentDate().year(), 10, this, "iYear" );
+ albumBox->addWidget( iYear );
+ QLabel* lGenreLabel = new QLabel( i18n("Genre:"), this, "lGenreLabel" );
+ albumBox->addWidget( lGenreLabel );
+ cGenre = new KComboBox( true, this, "cGenre" );
+ cGenre->insertStringList( tagEngine->genreList );
+ cGenre->setCurrentText( "" );
+ KCompletion* cGenreCompletion = cGenre->completionObject();
+ cGenreCompletion->insertItems( tagEngine->genreList );
+ cGenreCompletion->setIgnoreCase( true );
+ albumBox->addWidget( cGenre );
+
+ // generate the list view for the tracks
+ trackList = new KListView( this, "trackList" );
+ gridLayout->addWidget( trackList, 1, 0 );
+ // and fill in the headers
+ trackList->addColumn( i18n("Track") );
+ trackList->addColumn( i18n("Artist"), 0 );
+ trackList->addColumn( i18n("Composer"), 0 );
+ trackList->addColumn( i18n("Title") );
+ trackList->addColumn( i18n("Time") );
+ trackList->setSelectionMode( QListView::Extended );
+ trackList->setAllColumnsShowFocus( true );
+ trackList->setResizeMode( QListView::LastColumn );
+ connect( trackList, SIGNAL(selectionChanged()),
+ this, SLOT(trackChanged())
+ );
+ connect( trackList, SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)),
+ this, SLOT(addClicked())
+ );
+ gridLayout->setRowStretch( 1, 1 );
+
+ // create the box at the bottom for editing the tags
+ tagGroupBox = new QGroupBox( i18n("No track selected"), this, "tagGroupBox" );
+ gridLayout->addWidget( tagGroupBox, 2, 0 );
+ tagGroupBox->setColumnLayout( 0, Qt::Vertical );
+ tagGroupBox->layout()->setSpacing( 6 );
+ tagGroupBox->layout()->setMargin( 6 );
+ QGridLayout* tagGridLayout = new QGridLayout( tagGroupBox->layout(), 1, 1, -1, "tagGridLayout" );
+
+ // add the up and down buttons
+ pTrackUp = new KPushButton( " ", tagGroupBox, "pTrackUp" );
+ pTrackUp->setPixmap( iconLoader->loadIcon("up",KIcon::Toolbar) );
+ pTrackUp->setAutoRepeat( true );
+ connect( pTrackUp, SIGNAL(clicked()),
+ this, SLOT(trackUpPressed())
+ );
+ tagGridLayout->addWidget( pTrackUp, 0, 0 );
+ pTrackDown = new KPushButton( " ", tagGroupBox, "pTrackDown" );
+ pTrackDown->setPixmap( iconLoader->loadIcon("down",KIcon::Toolbar) );
+ pTrackDown->setAutoRepeat( true );
+ connect( pTrackDown, SIGNAL(clicked()),
+ this, SLOT(trackDownPressed())
+ );
+ tagGridLayout->addWidget( pTrackDown, 1, 0 );
+
+ // add the inputs
+ // add a horizontal box layout for the title
+ QHBoxLayout* trackTitleBox = new QHBoxLayout( -1, "trackTitleBox" );
+ tagGridLayout->addLayout( trackTitleBox, 0, 2 );
+ // and fill it up
+ QLabel *lTrackTitleLabel = new QLabel( i18n("Title:"), tagGroupBox, "lTrackTitleLabel" );
+ tagGridLayout->addWidget( lTrackTitleLabel, 0, 1 );
+ lTrackTitle = new KLineEdit( tagGroupBox, "lTrackTitle" );
+ trackTitleBox->addWidget( lTrackTitle );
+ connect( lTrackTitle, SIGNAL(textChanged(const QString&)),
+ this, SLOT(trackTitleChanged(const QString&))
+ );
+ pTrackTitleEdit = new KPushButton( " ", tagGroupBox, "pTrackTitleEdit" );
+ pTrackTitleEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pTrackTitleEdit->setFixedSize( lTrackTitle->sizeHint().height(), lTrackTitle->sizeHint().height() );
+ pTrackTitleEdit->hide();
+ trackTitleBox->addWidget( pTrackTitleEdit );
+ connect( pTrackTitleEdit, SIGNAL(clicked()),
+ this, SLOT(editTrackTitleClicked())
+ );
+ // add a horizontal box layout for the composer
+ QHBoxLayout* trackArtistBox = new QHBoxLayout( -1, "trackArtistBox" );
+ tagGridLayout->addLayout( trackArtistBox, 1, 2 );
+ // and fill it up
+ QLabel* lTrackArtistLabel = new QLabel( i18n("Artist:"), tagGroupBox, "lTrackArtistLabel" );
+ tagGridLayout->addWidget( lTrackArtistLabel, 1, 1 );
+ lTrackArtist = new KLineEdit( tagGroupBox, "lTrackArtist" );
+ trackArtistBox->addWidget( lTrackArtist );
+ connect( lTrackArtist, SIGNAL(textChanged(const QString&)),
+ this, SLOT(trackArtistChanged(const QString&))
+ );
+ pTrackArtistEdit = new KPushButton( " ", tagGroupBox, "pTrackArtistEdit" );
+ pTrackArtistEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pTrackArtistEdit->setFixedSize( lTrackArtist->sizeHint().height(), lTrackArtist->sizeHint().height() );
+ pTrackArtistEdit->hide();
+ trackArtistBox->addWidget( pTrackArtistEdit );
+ connect( pTrackArtistEdit, SIGNAL(clicked()),
+ this, SLOT(editTrackArtistClicked())
+ );
+ QLabel* lTrackComposerLabel = new QLabel( i18n("Composer:"), tagGroupBox, "lTrackComposerLabel" );
+ trackArtistBox->addWidget( lTrackComposerLabel );
+ lTrackComposer = new KLineEdit( tagGroupBox, "lTrackComposer" );
+ trackArtistBox->addWidget( lTrackComposer );
+ connect( lTrackComposer, SIGNAL(textChanged(const QString&)),
+ this, SLOT(trackComposerChanged(const QString&))
+ );
+ pTrackComposerEdit = new KPushButton( " ", tagGroupBox, "pTrackComposerEdit" );
+ pTrackComposerEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pTrackComposerEdit->setFixedSize( lTrackComposer->sizeHint().height(), lTrackComposer->sizeHint().height() );
+ pTrackComposerEdit->hide();
+ trackArtistBox->addWidget( pTrackComposerEdit );
+ connect( pTrackComposerEdit, SIGNAL(clicked()),
+ this, SLOT(editTrackComposerClicked())
+ );
+ // add a horizontal box layout for the comment
+ QHBoxLayout* trackCommentBox = new QHBoxLayout( -1, "trackCommentBox" );
+ tagGridLayout->addLayout( trackCommentBox, 2, 2 );
+ // and fill it up
+ QLabel* lTrackCommentLabel = new QLabel( i18n("Comment:"), tagGroupBox, "lTrackCommentLabel" );
+ tagGridLayout->addWidget( lTrackCommentLabel, 2, 1 );
+ tTrackComment = new KTextEdit( tagGroupBox, "tTrackComment" );
+ trackCommentBox->addWidget( tTrackComment );
+ tTrackComment->setFixedHeight( 65 );
+ connect( tTrackComment, SIGNAL(textChanged()),
+ this, SLOT(trackCommentChanged())
+ );
+ pTrackCommentEdit = new KPushButton( " ", tagGroupBox, "pTrackCommentEdit" );
+ pTrackCommentEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pTrackCommentEdit->setFixedSize( lTrackTitle->sizeHint().height(), lTrackTitle->sizeHint().height() );
+ pTrackCommentEdit->hide();
+ trackCommentBox->addWidget( pTrackCommentEdit );
+ connect( pTrackCommentEdit, SIGNAL(clicked()),
+ this, SLOT(editTrackCommentClicked())
+ );
+
+ // draw a horizontal line
+ QFrame* lineFrame = new QFrame( this, "lineFrame" );
+ lineFrame->setFrameShape( QFrame::HLine );
+ lineFrame->setFrameShadow( QFrame::Sunken );
+ lineFrame->setFrameShape( QFrame::HLine );
+ gridLayout->addWidget( lineFrame, 3, 0 );
+ gridLayout->setRowSpacing( 3, 16 );
+
+ // add a horizontal box layout for the control elements
+ QHBoxLayout* controlBox = new QHBoxLayout( -1, "controlBox" );
+ gridLayout->addLayout( controlBox, 4, 0 );
+
+ // add the control elements
+ pSaveCue = new KPushButton( iconLoader->loadIcon("filesave",KIcon::Small), i18n("Save cuesheet ..."), this, "pSaveCue" );
+ controlBox->addWidget( pSaveCue );
+ connect( pSaveCue, SIGNAL(clicked()),
+ this, SLOT(saveCuesheetClicked())
+ );
+ controlBox->addStretch();
+
+ cAdd = new ComboButton( this, "cAdd" );
+ cAdd->insertItem( iconLoader->loadIcon("fileopen",KIcon::Small),i18n("Add all tracks") );
+ cAdd->insertItem( iconLoader->loadIcon("fileopen",KIcon::Small),i18n("Add selected tracks") );
+ RipperPlugin* plugin = config->getCurrentRipper();
+ if( plugin != 0 && plugin->rip.full_disc.enabled ) cAdd->insertItem( iconLoader->loadIcon("cdaudio_unmount",KIcon::Small),i18n("Add full CD as one file") );
+ //cAdd->setSizeMode( ComboButton::Min );
+ controlBox->addWidget( cAdd );
+ connect( cAdd, SIGNAL(clicked(int)),
+ this, SLOT(addClicked(int))
+ );
+ pCancel = new KPushButton( iconLoader->loadIcon("cancel",KIcon::Small), i18n("Cancel"), this, "pCancel" );
+ controlBox->addWidget( pCancel );
+ connect( pCancel, SIGNAL(clicked()),
+ this, SLOT(reject())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+
+ bool various_artists = false;
+ bool various_composer = false;
+ QString artist = "";
+ QString composer = "";
+ QString album = "";
+ int disc = 0;
+ int year = 0;
+ QString genre = "";
+
+ device = cdManager->newCDDevice( _device );
+ // don't execute the dialog, if no audio cd was found
+ noCD = device.isEmpty();
+
+ QValueList<TagData*> tags = cdManager->getTrackList( device );
+ for( QValueList<TagData*>::Iterator it = tags.begin(); it != tags.end(); ++it ) {
+ if( artist == "" ) artist = (*it)->artist;
+ else if( artist != (*it)->artist ) various_artists = true;
+
+ if( composer == "" ) composer = (*it)->composer;
+ else if( composer != (*it)->composer ) various_composer = true;
+
+ if( album == "" ) album = (*it)->album;
+ if( disc == 0 ) disc = (*it)->disc;
+ if( year == 0 ) year = (*it)->year;
+ if( genre == "" ) genre = (*it)->genre;
+
+ new KListViewItem( trackList, QString().sprintf("%02i",(*it)->track), (*it)->artist, (*it)->composer, (*it)->title, QString().sprintf("%i:%02i",(*it)->length/60,(*it)->length%60) );
+ }
+ trackList->setSorting( -1 );
+
+ // fill in the cd information
+ if( various_artists ) cArtist->setCurrentItem( 0 );
+ else cArtist->setCurrentText( artist );
+ artistChanged( cArtist->currentText() );
+
+ if( various_composer ) cComposer->setCurrentItem( 0 );
+ else cComposer->setCurrentText( composer );
+ composerChanged( cComposer->currentText() );
+
+ lAlbum->setText( album );
+ if( disc != 0 ) iDisc->setValue( disc );
+ if( year != 0 ) iYear->setValue( year );
+ cGenre->setCurrentText( genre );
+
+ //trackList->setCurrentItem( trackList->firstChild() );
+ trackList->setSelected( trackList->firstChild(), true );
+
+ pTrackUp->setEnabled( false );
+ if( trackList->firstChild() != 0 ) {
+ if( trackList->firstChild()->itemBelow() != 0 ) {
+ pTrackDown->setEnabled( true );
+ }
+ else {
+ pTrackDown->setEnabled( false );
+ }
+ }
+ else {
+ pTrackDown->setEnabled( false );
+ }
+}
+
+CDOpener::~CDOpener()
+{}
+
+int CDOpener::columnByName( const QString& name )
+{
+ for( int i = 0; i < trackList->columns(); ++i ) {
+ if( trackList->columnText( i ) == name ) return i;
+ }
+ return -1;
+}
+
+void CDOpener::trackUpPressed()
+{
+ QListViewItem* item = selectedItems.first()->itemAbove();
+
+ if( item != 0 ) {
+ item->setSelected( true );
+ trackList->repaintItem( item );
+ trackList->ensureItemVisible( item );
+ }
+ else {
+ return; // NULL pointer (cannot be)
+ }
+
+ for( QValueList<QListViewItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->setSelected( false );
+ trackList->repaintItem( *it );
+ }
+
+ if( item->itemAbove() == 0 ) {
+ pTrackUp->setEnabled( false );
+ }
+
+ pTrackDown->setEnabled( true );
+
+ trackChanged();
+}
+
+void CDOpener::trackDownPressed()
+{
+ QListViewItem* item = selectedItems.last()->itemBelow();
+
+ if( item != 0 ) {
+ item->setSelected( true );
+ trackList->repaintItem( item );
+ trackList->ensureItemVisible( item );
+ }
+ else {
+ return; // NULL pointer (cannot be)
+ }
+
+ for( QValueList<QListViewItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->setSelected( false );
+ trackList->repaintItem( *it );
+ }
+
+ pTrackUp->setEnabled( true );
+
+ if( item->itemBelow() == 0 ) {
+ pTrackDown->setEnabled( false );
+ }
+
+ trackChanged();
+}
+
+void CDOpener::trackChanged()
+{
+ // NOTE if no track is selected soundkonverter could use the current item as default item (like qlistview does)
+ int i = 0;
+
+ // rebuild the list of the selected tracks
+ selectedTracks.clear();
+ selectedItems.clear();
+ for( QListViewItem *item = trackList->firstChild(); item != NULL; item = item->nextSibling() ) {
+ i++;
+ if( item->isSelected() ) {
+ selectedTracks.append( i );
+ selectedItems.append( item );
+ }
+ }
+
+ // insert the new values
+ if( selectedTracks.count() < 1 ) {
+ pTrackUp->setEnabled( false );
+ pTrackDown->setEnabled( false );
+
+ lTrackTitle->setEnabled( false );
+ lTrackTitle->setText( "" );
+ pTrackTitleEdit->hide();
+ lTrackArtist->setEnabled( false );
+ lTrackArtist->setText( "" );
+ pTrackArtistEdit->hide();
+ lTrackComposer->setEnabled( false );
+ lTrackComposer->setText( "" );
+ pTrackComposerEdit->hide();
+ tTrackComment->setEnabled( false );
+ tTrackComment->setReadOnly( true );
+ tTrackComment->setText( "" );
+ pTrackCommentEdit->hide();
+
+ return;
+ }
+ else if( selectedTracks.count() > 1 ) {
+ if( selectedItems.first()->itemAbove() != 0 ) pTrackUp->setEnabled( true );
+ else pTrackUp->setEnabled( false );
+
+ if( selectedItems.last()->itemBelow() != 0 ) pTrackDown->setEnabled( true );
+ else pTrackDown->setEnabled( false );
+
+ QString trackListString = "";
+ if( selectedTracks.count() == trackList->childCount() ) {
+ trackListString = i18n("All tracks");
+ }
+ else {
+ trackListString = i18n("Tracks") + QString().sprintf(" %02i",selectedTracks.first());
+ QValueList<int>::Iterator track = selectedTracks.begin();
+ track++;
+ for( ; track != selectedTracks.end(); ++track ) {
+ trackListString += QString().sprintf(", %02i",*track);
+ }
+ }
+ tagGroupBox->setTitle( trackListString );
+
+ QString title = cdManager->getTags( device, selectedTracks.first() )->title;
+ bool equalTitles = true;
+ QString artist = cdManager->getTags( device, selectedTracks.first() )->artist;
+ bool equalArtists = true;
+ QString composer = cdManager->getTags( device, selectedTracks.first() )->composer;
+ bool equalComposers = true;
+ QString comment = cdManager->getTags( device, selectedTracks.first() )->comment;
+ bool equalComments = true;
+ for( QValueList<int>::Iterator track = selectedTracks.begin(); track != selectedTracks.end(); ++track ) {
+ TagData* tags = cdManager->getTags( device, *track );
+
+ if( title != tags->title ) equalTitles = false;
+ if( artist != tags->artist ) equalArtists = false;
+ if( composer != tags->composer ) equalComposers = false;
+ if( comment != tags->comment ) equalComments = false;
+ }
+
+ if( equalTitles ) {
+ lTrackTitle->setEnabled( true );
+ lTrackTitle->setText( title );
+ pTrackTitleEdit->hide();
+ }
+ else {
+ lTrackTitle->setEnabled( false );
+ lTrackTitle->setText( "" );
+ pTrackTitleEdit->show();
+ }
+
+ if( cArtist->currentText() == i18n("Various Artists") && equalArtists ) {
+ lTrackArtist->setEnabled( true );
+ lTrackArtist->setText( artist );
+ pTrackArtistEdit->hide();
+ }
+ else if( cArtist->currentText() == i18n("Various Artists") ) {
+ lTrackArtist->setEnabled( false );
+ lTrackArtist->setText( "" );
+ pTrackArtistEdit->show();
+ }
+ else {
+ lTrackArtist->setEnabled( false );
+ lTrackArtist->setText( cArtist->currentText() );
+ pTrackArtistEdit->hide();
+ }
+
+ if( cComposer->currentText() == i18n("Various Composer") && equalComposers ) {
+ lTrackComposer->setEnabled( true );
+ lTrackComposer->setText( composer );
+ pTrackComposerEdit->hide();
+ }
+ else if( cComposer->currentText() == i18n("Various Composer") ) {
+ lTrackComposer->setEnabled( false );
+ lTrackComposer->setText( "" );
+ pTrackComposerEdit->show();
+ }
+ else {
+ lTrackComposer->setEnabled( false );
+ lTrackComposer->setText( cComposer->currentText() );
+ pTrackComposerEdit->hide();
+ }
+
+ if( equalComments ) {
+ tTrackComment->setEnabled( true );
+ tTrackComment->setReadOnly( false );
+ tTrackComment->setText( comment );
+ pTrackCommentEdit->hide();
+ }
+ else {
+ tTrackComment->setEnabled( false );
+ tTrackComment->setReadOnly( true );
+ tTrackComment->setText( "" );
+ pTrackCommentEdit->show();
+ }
+ }
+ else {
+ if( selectedItems.first()->itemAbove() != 0 ) pTrackUp->setEnabled( true );
+ else pTrackUp->setEnabled( false );
+
+ if( selectedItems.first()->itemBelow() != 0 ) pTrackDown->setEnabled( true );
+ else pTrackDown->setEnabled( false );
+
+ tagGroupBox->setTitle( i18n("Track") + QString().sprintf(" %02i",selectedTracks.first()) );
+
+ TagData* tags = cdManager->getTags( device, selectedTracks.first() );
+ lTrackTitle->setEnabled( true );
+ lTrackTitle->setText( tags->title );
+ pTrackTitleEdit->hide();
+ if( cArtist->currentText() == i18n("Various Artists") ) {
+ lTrackArtist->setEnabled( true );
+ lTrackArtist->setText( tags->artist );
+ pTrackArtistEdit->hide();
+ }
+ else {
+ lTrackArtist->setEnabled( false );
+ lTrackArtist->setText( cArtist->currentText() );
+ pTrackArtistEdit->hide();
+ }
+ if( cComposer->currentText() == i18n("Various Composer") ) {
+ lTrackComposer->setEnabled( true );
+ lTrackComposer->setText( tags->composer );
+ pTrackComposerEdit->hide();
+ }
+ else {
+ lTrackComposer->setEnabled( false );
+ lTrackComposer->setText( cComposer->currentText() );
+ pTrackComposerEdit->hide();
+ }
+ tTrackComment->setEnabled( true );
+ tTrackComment->setReadOnly( false );
+ tTrackComment->setText( tags->comment );
+ pTrackCommentEdit->hide();
+ }
+}
+
+void CDOpener::artistChanged( const QString& text )
+{
+ if( text == i18n("Various Artists") ) {
+ trackList->adjustColumn( columnByName( i18n("Artist") ) );
+ }
+ else {
+ trackList->setColumnWidth( columnByName( i18n("Artist") ), 0 );
+ }
+
+ trackChanged();
+}
+
+void CDOpener::composerChanged( const QString& text )
+{
+ if( text == i18n("Various Composer") ) {
+ trackList->adjustColumn( columnByName( i18n("Composer") ) );
+ }
+ else {
+ trackList->setColumnWidth( columnByName( i18n("Composer") ), 0 );
+ }
+
+ trackChanged();
+}
+
+void CDOpener::trackTitleChanged( const QString& text )
+{
+ if( !lTrackTitle->isEnabled() ) return;
+
+ for( QValueList<QListViewItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->setText( columnByName( i18n("Title") ), text );
+ }
+ for( QValueList<int>::Iterator it = selectedTracks.begin(); it != selectedTracks.end(); ++it ) {
+ TagData* tags = cdManager->getTags( device, *it );
+ tags->title = text;
+ }
+}
+
+void CDOpener::trackArtistChanged( const QString& text )
+{
+ if( !lTrackArtist->isEnabled() ) return;
+
+ for( QValueList<QListViewItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->setText( columnByName( i18n("Artist") ), text );
+ }
+ for( QValueList<int>::Iterator it = selectedTracks.begin(); it != selectedTracks.end(); ++it ) {
+ TagData* tags = cdManager->getTags( device, *it );
+ tags->artist = text;
+ }
+}
+
+void CDOpener::trackComposerChanged( const QString& text )
+{
+ if( !lTrackComposer->isEnabled() ) return;
+
+ for( QValueList<QListViewItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->setText( columnByName( i18n("Composer") ), text );
+ }
+ for( QValueList<int>::Iterator it = selectedTracks.begin(); it != selectedTracks.end(); ++it ) {
+ TagData* tags = cdManager->getTags( device, *it );
+ tags->composer = text;
+ }
+}
+
+void CDOpener::trackCommentChanged()
+{
+ QString text = tTrackComment->text();
+
+ if( !tTrackComment->isEnabled() ) return;
+
+ for( QValueList<int>::Iterator it = selectedTracks.begin(); it != selectedTracks.end(); ++it ) {
+ TagData* tags = cdManager->getTags( device, *it );
+ tags->comment = text;
+ }
+}
+
+void CDOpener::editTrackTitleClicked()
+{
+ lTrackTitle->setEnabled( true );
+ lTrackTitle->setFocus();
+ pTrackTitleEdit->hide();
+ trackTitleChanged( lTrackTitle->text() );
+}
+
+void CDOpener::editTrackArtistClicked()
+{
+ lTrackArtist->setEnabled( true );
+ lTrackArtist->setFocus();
+ pTrackArtistEdit->hide();
+ trackArtistChanged( lTrackArtist->text() );
+}
+
+void CDOpener::editTrackComposerClicked()
+{
+ lTrackComposer->setEnabled( true );
+ lTrackComposer->setFocus();
+ pTrackComposerEdit->hide();
+ trackComposerChanged( lTrackComposer->text() );
+}
+
+void CDOpener::editTrackCommentClicked()
+{
+ tTrackComment->setEnabled( true );
+ tTrackComment->setReadOnly( false );
+ tTrackComment->setFocus();
+ pTrackCommentEdit->hide();
+ trackCommentChanged();
+}
+
+void CDOpener::addClicked( int index )
+{
+ if( index == 0 )
+ {
+ QValueList<int> allTracks;
+
+ // TODO save all options (album artist, disc, genre, etc.)
+ for( int it = 1; it <= cdManager->getTrackCount(device); ++it ) {
+ TagData* tags = cdManager->getTags( device, it );
+ if( cArtist->currentText() != i18n("Various Artists") ) tags->artist = cArtist->currentText();
+ if( cComposer->currentText() != i18n("Various Composer") ) tags->composer = cComposer->currentText();
+ tags->album = lAlbum->text();
+ tags->disc = iDisc->value();
+ tags->year = iYear->value();
+ tags->genre = cGenre->currentText();
+ allTracks += it;
+ }
+ emit addTracks( device, allTracks );
+ }
+ else if( index == 1 )
+ {
+ // TODO save all options (album artist, disc, genre, etc.)
+ for( QValueList<int>::Iterator it = selectedTracks.begin(); it != selectedTracks.end(); ++it ) {
+ TagData* tags = cdManager->getTags( device, *it );
+ if( cArtist->currentText() != i18n("Various Artists") ) tags->artist = cArtist->currentText();
+ if( cComposer->currentText() != i18n("Various Composer") ) tags->composer = cComposer->currentText();
+ tags->album = lAlbum->text();
+ tags->disc = iDisc->value();
+ tags->year = iYear->value();
+ tags->genre = cGenre->currentText();
+ }
+ emit addTracks( device, selectedTracks );
+ }
+ else
+ {
+ // TODO save all options (album artist, disc, genre, etc.)
+ cdManager->setDiscTags( device,
+ new TagData( cArtist->currentText(), cComposer->currentText(),
+ lAlbum->text(), /*cArtist->currentText() + " - " + */lAlbum->text(),
+ cGenre->currentText(), "",
+ 1, iDisc->value(), iYear->value(),
+ cdManager->getTimeCount(device) ) );
+ emit addDisc( device );
+ }
+ accept();
+}
+
+// void CDOpener::addAsOneTrackClicked()
+// {
+// // TODO save all options (album artist, disc, genre, etc.)
+// cdManager->setDiscTags( device,
+// new TagData( cArtist->currentText(), cComposer->currentText(),
+// lAlbum->text(), /*cArtist->currentText() + " - " + */lAlbum->text(),
+// cGenre->currentText(), "",
+// 1, iDisc->value(), iYear->value(),
+// cdManager->getTimeCount(device) ) );
+//
+// emit addDisc( device );
+// accept();
+// }
+
+void CDOpener::saveCuesheetClicked()
+{
+ QString filename = KFileDialog::getSaveFileName( QDir::homeDirPath(), "*.cue" );
+ if( filename.isEmpty() ) return;
+
+ QFile cueFile( filename );
+ if( cueFile.exists() ) {
+ int ret = KMessageBox::questionYesNo( this,
+ i18n("A file with this name already exists.\n\nDo you want to overwrite the existing one?"),
+ i18n("File already exists") );
+ if( ret == KMessageBox::No ) return;
+ }
+ if( !cueFile.open( IO_WriteOnly ) ) return;
+
+ QString content;
+
+ content.append( "TITLE \"" + lAlbum->text() + "\"\n" );
+ content.append( "PERFORMER \"" + cArtist->currentText() + "\"\n" );
+ content.append( "FILE \"\" MP3\n" );
+
+ int INDEX = 0;
+ bool addFrames = false;
+ QValueList<TagData*> tags = cdManager->getTrackList( device );
+ for( QValueList<TagData*>::Iterator it = tags.begin(); it != tags.end(); ++it ) {
+ content.append( QString().sprintf(" TRACK %02i AUDIO\n",(*it)->track ) );
+ content.append( " TITLE \"" + (*it)->title + "\"\n" );
+ content.append( " PERFORMER \"" + (*it)->artist + "\"\n" );
+ if( addFrames ) {
+ content.append( QString().sprintf(" INDEX 01 %02i:%02i:37\n",INDEX/60,INDEX%60) );
+ INDEX++;
+ addFrames = false;
+ }
+ else {
+ content.append( QString().sprintf(" INDEX 01 %02i:%02i:00\n",INDEX/60,INDEX%60) );
+ addFrames = true;
+ }
+
+ INDEX += (*it)->length;
+ }
+
+ QTextStream ts( &cueFile );
+ ts << content;
+
+ cueFile.close();
+}
+
+
+
diff --git a/src/cdopener.h b/src/cdopener.h
new file mode 100755
index 0000000..09e941a
--- /dev/null
+++ b/src/cdopener.h
@@ -0,0 +1,139 @@
+
+
+#ifndef CDOPENER_H
+#define CDOPENER_H
+
+#include <kdialog.h>
+
+class CDManager;
+class TagEngine;
+class Config;
+class ComboButton;
+class KListView;
+class KPushButton;
+class KLineEdit;
+class KComboBox;
+class KIntSpinBox;
+class KTextEdit;
+class QGroupBox;
+class QListViewItem;
+
+/**
+ * @short Shows a dialog for selecting files from a CD
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class CDOpener : public KDialog
+{
+ Q_OBJECT
+public:
+// enum Mode {
+// all_tracks,
+// selected_tracks,
+// full_cd
+// };
+
+ /**
+ * Constructor
+ * @param parent The parent widget
+ * @param name The name of the file list
+ * @p modal Sets whether the dialog is modal or not
+ * @p f Some flags
+ */
+ CDOpener( Config*, CDManager*, TagEngine*, const QString &device, QWidget *parent = 0, const char *name = 0, /*Mode default_mode = all_tracks, const QString& default_text = "",*/ bool modal = true, WFlags f = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~CDOpener();
+
+ /** true if no CD was found (don't execute the dialog) */
+ bool noCD;
+
+private:
+ /** A list of all tracks on the CD */
+ KListView *trackList;
+
+ /** A combobox for entering the artist or selecting VA of the whole CD */
+ KComboBox *cArtist;
+ /** A combobox for entering the composer or selecting VC of the whole CD */
+ KComboBox *cComposer;
+ /** A lineedit for entering the album name */
+ KLineEdit *lAlbum;
+ /** A spinbox for entering or selecting the disc number */
+ KIntSpinBox *iDisc;
+ /** A spinbox for entering or selecting the year of the album */
+ KIntSpinBox *iYear;
+ /** A combobox for entering or selecting the genre of the album */
+ KComboBox *cGenre;
+
+ /** Request CDDB information */
+// KPushButton *pCDDB;
+
+ /** The groupbox shows the selected track numbers */
+ QGroupBox *tagGroupBox;
+
+ /** Set the focus of the tag editor to the track over it */
+ KPushButton *pTrackUp;
+ /** Set the focus of the tag editor to the track under it */
+ KPushButton *pTrackDown;
+
+ /** A lineedit for entering the title of track */
+ KLineEdit *lTrackTitle;
+ KPushButton *pTrackTitleEdit;
+ /** A lineedit for entering the artist of a track */
+ KLineEdit *lTrackArtist;
+ KPushButton *pTrackArtistEdit;
+ /** A lineedit for entering the composer of a track */
+ KLineEdit *lTrackComposer;
+ KPushButton *pTrackComposerEdit;
+ /** A textedit for entering a comment for a track */
+ KTextEdit *tTrackComment;
+ KPushButton *pTrackCommentEdit;
+
+ /** Save the tag information to a cue file */
+ KPushButton *pSaveCue;
+ //** Add whole CD as one track and quit the dialog */
+ //KPushButton *pAddAsOneTrack;
+ //** Add selected tracks to the file list and quit the dialog */
+ //KPushButton *pAdd;
+ ComboButton* cAdd;
+ /** Quit the dialog */
+ KPushButton *pCancel;
+
+ CDManager* cdManager;
+ TagEngine* tagEngine;
+ Config* config;
+
+ QString device;
+
+ QValueList<int> selectedTracks;
+ QValueList<QListViewItem*> selectedItems;
+
+ int columnByName( const QString& name );
+
+private slots:
+ void trackChanged();
+ void trackUpPressed();
+ void trackDownPressed();
+ void artistChanged( const QString& text );
+ void composerChanged( const QString& text );
+ void trackTitleChanged( const QString& text );
+ void trackArtistChanged( const QString& text );
+ void trackComposerChanged( const QString& text );
+ void trackCommentChanged();
+ void editTrackTitleClicked();
+ void editTrackArtistClicked();
+ void editTrackComposerClicked();
+ void editTrackCommentClicked();
+ void addClicked( int index = 1 );
+// void addAsOneTrackClicked();
+ void saveCuesheetClicked();
+
+signals:
+ void addTracks( const QString& device, QValueList<int> );
+ void addDisc( const QString& device );
+ //void openCuesheetEditor( const QString& content );
+};
+
+#endif // CDOPENER_H
diff --git a/src/combobutton.cpp b/src/combobutton.cpp
new file mode 100755
index 0000000..ff5d758
--- /dev/null
+++ b/src/combobutton.cpp
@@ -0,0 +1,114 @@
+
+#include "combobutton.h"
+
+#include <qlayout.h>
+#include <qstring.h>
+#include <qpixmap.h>
+#include <qstyle.h>
+#include <kpushbutton.h>
+#include <kcombobox.h>
+
+ComboButton::ComboButton( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ m_increaseHeight = 0;
+
+ QGridLayout *grid = new QGridLayout( this, 1, 1 );
+
+ m_box = new KComboBox(this);
+ grid->addWidget(m_box,0,0);
+
+ connect( m_box, SIGNAL(activated(int)),
+ this, SLOT(boxActivated(int))
+ );
+
+ m_button = new KPushButton( QString::null, this, "pushbutton" );
+ grid->addWidget( m_button, 0, 0 );
+
+ connect( m_button, SIGNAL(clicked()),
+ this, SLOT(buttonClicked())
+ );
+
+ m_sizeMode = Max;
+
+ balanceSize();
+}
+
+ComboButton::~ComboButton()
+{
+}
+
+void ComboButton::balanceSize()
+{
+ int width;
+
+ if( m_sizeMode == Max )
+ width = m_box->sizeHint().width()-17;
+ else
+ width = m_button->sizeHint().width();
+
+ int height = ( m_box->sizeHint().height() > m_button->sizeHint().height() ) ? m_box->sizeHint().height() : m_button->sizeHint().height();
+
+ m_box->setFixedSize( width+17, height+m_increaseHeight );
+ m_button->setFixedSize( width, height+m_increaseHeight );
+}
+
+void ComboButton::repaintButton()
+{
+ m_button->setText( m_box->currentText() );
+ if(m_box->pixmap( m_box->currentItem()) )
+ m_button->setIconSet( *m_box->pixmap(m_box->currentItem()) );
+ balanceSize();
+}
+
+void ComboButton::insertItem( const QString &text, int index )
+{
+ m_box->insertItem( text, index );
+ repaintButton();
+}
+
+void ComboButton::insertItem( const QPixmap &pixmap, const QString &text, int index )
+{
+ m_box->insertItem( pixmap, text, index );
+ repaintButton();
+}
+
+void ComboButton::increaseHeight( int height )
+{
+ m_increaseHeight = height;
+ balanceSize();
+}
+
+void ComboButton::boxActivated( int index )
+{
+ repaintButton();
+ emit clicked( index );
+}
+
+void ComboButton::buttonClicked()
+{
+ emit clicked( m_box->currentItem() );
+}
+
+void ComboButton::setSizeMode( int mode )
+{
+ m_sizeMode = mode;
+ balanceSize();
+}
+
+int ComboButton::sizeMode()
+{
+ return m_sizeMode;
+}
+
+void ComboButton::setFont( const QFont& font )
+{
+ m_button->setFont( font );
+ m_box->setFont( font );
+}
+
+QFont ComboButton::font()
+{
+ return m_button->font();
+}
+
diff --git a/src/combobutton.h b/src/combobutton.h
new file mode 100755
index 0000000..3f07199
--- /dev/null
+++ b/src/combobutton.h
@@ -0,0 +1,110 @@
+
+
+#ifndef COMBOBUTTON_H
+#define COMBOBUTTON_H
+
+#include <qwidget.h>
+
+class QString;
+class QPixmap;
+class KPushButton;
+class KComboBox;
+
+/**
+ * @short ComboButton
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ComboButton : public QWidget
+{
+ Q_OBJECT
+public:
+ enum SizeMode {
+ Min, Max
+ };
+
+ /**
+ * Constructor
+ * @param parent The parent widget
+ * @param name The name of the file list
+ */
+ ComboButton( QWidget *parent, const char *name = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~ComboButton();
+
+ /**
+ * Insert a new item with @p text at position @p index
+ */
+ void insertItem( const QString &text, int index = -1 );
+ /**
+ * Insert a new item with an icon @p pixmap and @p text at position @p index
+ */
+ void insertItem( const QPixmap &pixmap, const QString &text, int index = -1 );
+
+ /**
+ * Increase the combobutton's height by @p height
+ */
+ void increaseHeight( int height );
+
+ /**
+ * Sets m_sizeMode to @p mode
+ */
+ void setSizeMode( int mode );
+
+ /**
+ * Returns the m_sizeMode
+ */
+ int sizeMode();
+
+ /**
+ * Sets the font of the combobutton
+ */
+ void setFont( const QFont& font );
+
+ /**
+ * Returns the font of the button
+ */
+ QFont font();
+
+private:
+ /** A pointer to the button */
+ KPushButton *m_button;
+ /** A pointer to the combobox */
+ KComboBox *m_box;
+
+ int m_increaseHeight;
+
+ /** The actual size mode */
+ int m_sizeMode;
+
+ /** Recalculate the size of the combobutton */
+ void balanceSize();
+ /** The button gets a new label, etc. */
+ void repaintButton();
+
+//public slots:
+ //void setCurrentItem(const QString &item, bool insert=false, int index=-1);
+ //void setCurrentItem(int index);
+
+private slots:
+ /**
+ * Is called when the user selects an item from the popdown menu of the combobox
+ */
+ void boxActivated( int index );
+ /**
+ * Is called when the user clicks the button
+ */
+ void buttonClicked();
+
+signals:
+ /**
+ * The signal clicked is emitted, when the user selects an item
+ */
+ void clicked( int index );
+
+};
+
+#endif // COMBOBUTTON_H
diff --git a/src/config.cpp b/src/config.cpp
new file mode 100755
index 0000000..b8b1904
--- /dev/null
+++ b/src/config.cpp
@@ -0,0 +1,1516 @@
+
+#include "config.h"
+#include "logger.h"
+#include "convertpluginloader.h"
+#include "replaygainpluginloader.h"
+#include "ripperpluginloader.h"
+#include "options.h"
+
+#include <qdir.h>
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kmimetype.h>
+#include <kapplication.h>
+#include <kconfig.h>
+#include <kio/netaccess.h>
+
+// NOTE currently only the possibilities of a file format are respected,
+// the possibilities of the current encoder are ignored
+// - seems to be fixed
+
+FormatItem::FormatItem()
+{
+ // reset everything
+ mime_types.clear();
+ size = 0;
+ compressionType = FormatInfo::lossy;
+ compressionLevel = 0;
+ internalReplayGain = false;
+ encoders.clear();
+ decoders.clear();
+ replaygains.clear();
+ //repairers.clear();
+ encoder = 0;
+ decoder = 0;
+ replaygain = 0;
+ //repairer = 0;
+}
+
+FormatItem::~FormatItem()
+{}
+
+
+Config::Config( Logger* _logger )
+{
+ logger = _logger;
+
+ currentRipper = 0; // this seems to be unnecessary
+}
+
+Config::~Config()
+{}
+
+void Config::read()
+{
+ loadPlugins();
+ readProfiles();
+
+ KConfig *conf = kapp->config();
+ QStringList listDefaults;
+ int intDefault;
+ bool boolDefault;
+ QString ripper;
+ QString encoder;
+ QString decoder;
+ QString replaygain;
+ int rank;
+
+ conf->setGroup( "General" ) ;
+ data.app.configVersion = conf->readNumEntry( "configVersion", 0 );
+
+/* if( configVersion == 0 ) {
+ conf->setGroup( "Interface" );
+ data.general.updateDelay = conf->readNumEntry( "pauseMS", 500 );
+ data.general.startTab = conf->readNumEntry( "startTab", 0 );
+ data.general.showToolBar = conf->readBoolEntry( "showToolBar", false );
+
+ conf->setGroup( "Backends" );
+ listDefaults.clear();
+ QString datadir = locateLocal( "data", "soundkonverter/bin/" );
+ datadir.remove( datadir.length() - 1, 1 );
+ listDefaults.append( datadir );
+ listDefaults.append( QDir::homeDirPath() + "/bin" );
+ listDefaults.append( "/usr/local/bin" );
+ listDefaults.append( "/usr/bin" );
+ data.environment.directories = conf->readListEntry( "directories", listDefaults );
+ listDefaults.clear();
+ data.environment.foundPrograms = conf->readListEntry( "programs", listDefaults );
+
+ conf->setGroup( "Misc" );
+ //configVersion = conf->readNumEntry( "configVersion", 0 );
+ data.general.askForNewOptions = conf->readBoolEntry( "askForNewOptions", true );
+
+ return;
+ }*/
+
+ conf->setGroup( "General" ) ;
+ //configVersion = conf->readNumEntry( "configVersion", 0 );
+ data.general.startTab = conf->readNumEntry( "startTab", 0 );
+ data.general.lastTab = conf->readNumEntry( "lastTab", 0 );
+ data.general.defaultProfile = conf->readEntry( "defaultProfile", i18n("Last used") );
+ data.general.defaultFormat = conf->readEntry( "defaultFormat", i18n("Last used") );
+// data.general.defaultOutputDirectory = conf->readEntry( "defaultOutputDirectory", QDir::homeDirPath() + "/soundKonverter/%b/%d - %n - %a - %t" );
+ data.general.specifyOutputDirectory = conf->readEntry( "specifyOutputDirectory", QDir::homeDirPath() + "/soundKonverter" );
+ data.general.metaDataOutputDirectory = conf->readEntry( "metaDataOutputDirectory", QDir::homeDirPath() + "/soundKonverter/%b/%d - %n - %a - %t" );
+ data.general.copyStructureOutputDirectory = conf->readEntry( "copyStructureOutputDirectory", QDir::homeDirPath() + "/soundKonverter" );
+ data.general.useVFATNames = conf->readBoolEntry( "useVFATNames", true );
+ data.general.conflictHandling = conf->readNumEntry( "conflictHandling", 0 );
+ data.general.priority = conf->readNumEntry( "priority", 10 );
+ data.general.numFiles = conf->readNumEntry( "numFiles", 3 );
+ data.general.updateDelay = conf->readNumEntry( "updateDelay", 500 );
+ data.general.askForNewOptions = conf->readBoolEntry( "askForNewOptions", true );
+ data.general.executeUserScript = conf->readBoolEntry( "executeUserScript", false );
+ data.general.showToolBar = conf->readBoolEntry( "showToolBar", false );
+
+ conf->setGroup( "Plugins" );
+ data.plugins.checkForUpdates = conf->readBoolEntry( "checkForUpdates", false );
+
+ conf->setGroup( "Environment" );
+ listDefaults.clear();
+ QString datadir = locateLocal( "data", "soundkonverter/bin/" );
+ datadir.remove( datadir.length() - 1, 1 );
+ listDefaults.append( datadir );
+ listDefaults.append( QDir::homeDirPath() + "/bin" );
+ listDefaults.append( "/usr/local/bin" );
+ listDefaults.append( "/usr/bin" );
+ data.environment.directories = conf->readListEntry( "directories", listDefaults );
+ listDefaults.clear();
+ data.environment.foundPrograms = conf->readListEntry( "foundPrograms", listDefaults );
+
+ conf->setGroup( "Ripper" );
+ ripper = conf->readEntry( "ripper", "kio_audiocd" );
+
+ if( ripper == "kio_audiocd" ) rank = 10000;
+ else rank = 60; // kio_audiocd ranking
+ currentRipper = 0; // this is a valid ripper (kio_audiocd)
+ for( QValueList<RipperPlugin*>::Iterator b = rippers.begin(); b != rippers.end(); ++b ) {
+ binaries[ (*b)->rip.bin ] = "";
+ if( (*b)->rip.rank > rank ) {
+ rank = (*b)->rip.rank;
+ currentRipper = (*b);
+ }
+ if( (*b)->rip.bin == ripper ) {
+ rank = 10000; // should be high enough to overbid the other plugins
+ currentRipper = (*b);
+ }
+ }
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it )
+ {
+ for( QValueList<ConvertPlugin*>::Iterator b = (*it).encoders.begin(); b != (*it).encoders.end(); ++b ) {
+ binaries[ (*b)->enc.bin ] = "";
+ }
+
+ for( QValueList<ConvertPlugin*>::Iterator b = (*it).decoders.begin(); b != (*it).decoders.end(); ++b ) {
+ binaries[ (*b)->dec.bin ] = "";
+ }
+
+ for( QValueList<ReplayGainPlugin*>::Iterator b = (*it).replaygains.begin(); b != (*it).replaygains.end(); ++b ) {
+ binaries[ (*b)->replaygain.bin ] = "";
+ }
+
+ for( QMap<QString, QString>::Iterator b = binaries.begin(); b != binaries.end(); ++b ) {
+ for( QStringList::Iterator c = data.environment.directories.begin(); c != data.environment.directories.end(); ++c )
+ {
+ if( b.data() == "" && QFile::exists((*c) + "/" + b.key()) ) {
+ b.data() = (*c) + "/" + b.key();
+ }
+ }
+ }
+ QStringList foundPrograms;
+ for( QMap<QString, QString>::Iterator b = binaries.begin(); b != binaries.end(); ++b ) {
+ if( b.data() != "" ) {
+ foundPrograms.append( b.data() );
+ }
+ }
+ if( foundPrograms != data.environment.foundPrograms ) {
+ backendsChanged = true;
+ }
+ else {
+ backendsChanged = false;
+ }
+ }
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it )
+ {
+ conf->setGroup( (*it).mime_types.first() );
+ encoder = conf->readEntry( "encoder" );
+ decoder = conf->readEntry( "decoder" );
+ replaygain = conf->readEntry( "replaygain" );
+
+ (*it).encoder = 0;
+ (*it).decoder = 0;
+ (*it).replaygain = 0;
+
+ rank = 0;
+ for( QValueList<ConvertPlugin*>::Iterator b = (*it).encoders.begin(); b != (*it).encoders.end(); ++b ) {
+ if( (*b)->enc.rank > rank && binaries[(*b)->enc.bin] != "" ) {
+ rank = (*b)->enc.rank;
+ (*it).encoder = (*b);
+ }
+ if( (*b)->enc.bin == encoder && binaries[(*b)->enc.bin] != "" ) {
+ rank = 10000; // should be high enougth to overbid the other plugins
+ (*it).encoder = (*b);
+ }
+ }
+
+ rank = 0;
+ for( QValueList<ConvertPlugin*>::Iterator b = (*it).decoders.begin(); b != (*it).decoders.end(); ++b ) {
+ if( (*b)->dec.rank > rank && binaries[(*b)->dec.bin] != "" ) {
+ rank = (*b)->dec.rank;
+ (*it).decoder = (*b);
+ }
+ if( (*b)->dec.bin == decoder && binaries[(*b)->dec.bin] != "" ) {
+ rank = 10000; // should be high enougth to overbid the other plugins
+ (*it).decoder = (*b);
+ }
+ }
+
+ rank = 0;
+ for( QValueList<ReplayGainPlugin*>::Iterator b = (*it).replaygains.begin(); b != (*it).replaygains.end(); ++b ) {
+ if( (*b)->replaygain.rank > rank && binaries[(*b)->replaygain.bin] != "" ) {
+ rank = (*b)->replaygain.rank;
+ (*it).replaygain = (*b);
+ }
+ if( (*b)->replaygain.bin == replaygain && binaries[(*b)->replaygain.bin] != "" ) {
+ rank = 10000; // should be high enougth to overbid the other plugins
+ (*it).replaygain = (*b);
+ }
+ }
+
+ if( (*it).encoder != 0 && (*it).encoder->enc.strength.enabled ) {
+ if( (*it).encoder->enc.strength.range_max >= (*it).encoder->enc.strength.range_min )
+ intDefault = (*it).encoder->enc.strength.default_value / (*it).encoder->enc.strength.step;
+ else
+ intDefault = ( (*it).encoder->enc.strength.range_min - (*it).encoder->enc.strength.default_value ) / (*it).encoder->enc.strength.step;
+ }
+ else {
+ intDefault = 0;
+ }
+ (*it).compressionLevel = conf->readNumEntry( "compressionLevel", intDefault );
+
+ if( (*it).encoder != 0 && (*it).encoder->enc.replaygain.enabled ) {
+ if( (*it).replaygain != 0 && (*it).replaygain->replaygain.rank > (*it).encoder->enc.replaygain.rank ) {
+ boolDefault = false;
+ }
+ else {
+ boolDefault = true;
+ }
+ }
+ else {
+ boolDefault = false;
+ }
+ (*it).internalReplayGain = conf->readBoolEntry( "internalReplayGain", boolDefault );
+ }
+}
+
+void Config::write( bool sync )
+{
+ QTime time;
+ time.start();
+
+ writeProfiles();
+ writeServiceMenu();
+ writeAmarokScript();
+
+ KConfig *conf = kapp->config();
+
+ conf->setGroup( "Ripper" );
+ conf->writeEntry( "ripper", (currentRipper)?currentRipper->rip.bin:"kio_audiocd" );
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it )
+ {
+ conf->setGroup( (*it).mime_types.first() );
+ if( (*it).encoder ) conf->writeEntry( "encoder", (*it).encoder->enc.bin );
+ if( (*it).decoder ) conf->writeEntry( "decoder", (*it).decoder->dec.bin );
+ if( (*it).replaygain ) conf->writeEntry( "replaygain", (*it).replaygain->replaygain.bin );
+ conf->writeEntry( "compressionLevel", (*it).compressionLevel );
+ conf->writeEntry( "internalReplayGain", (*it).internalReplayGain );
+ }
+
+ conf->setGroup( "General" ) ;
+ conf->writeEntry( "configVersion", 300 );
+ conf->writeEntry( "startTab", data.general.startTab );
+ conf->writeEntry( "lastTab", data.general.lastTab );
+ conf->writeEntry( "defaultProfile", data.general.defaultProfile );
+ conf->writeEntry( "defaultFormat", data.general.defaultFormat );
+// conf->writeEntry( "defaultOutputDirectory", data.general.defaultOutputDirectory );
+ conf->writeEntry( "specifyOutputDirectory", data.general.specifyOutputDirectory );
+ conf->writeEntry( "metaDataOutputDirectory", data.general.metaDataOutputDirectory );
+ conf->writeEntry( "copyStructureOutputDirectory", data.general.copyStructureOutputDirectory );
+ conf->writeEntry( "useVFATNames", data.general.useVFATNames );
+ conf->writeEntry( "conflictHandling", data.general.conflictHandling );
+ conf->writeEntry( "priority", data.general.priority );
+ conf->writeEntry( "numFiles", data.general.numFiles );
+ conf->writeEntry( "updateDelay", data.general.updateDelay );
+ conf->writeEntry( "askForNewOptions", data.general.askForNewOptions );
+ conf->writeEntry( "executeUserScript", data.general.executeUserScript );
+ conf->writeEntry( "showToolBar", data.general.showToolBar );
+
+ conf->setGroup( "Plugins" );
+ conf->writeEntry( "checkForUpdates", data.plugins.checkForUpdates );
+
+ conf->setGroup( "Environment" );
+ conf->writeEntry( "directories", data.environment.directories );
+ data.environment.foundPrograms.clear();
+ for( QMap<QString, QString>::Iterator b = binaries.begin(); b != binaries.end(); ++b ) {
+ if( b.data() != "" ) {
+ data.environment.foundPrograms.append( b.data() );
+ }
+ }
+ conf->writeEntry( "foundPrograms", data.environment.foundPrograms );
+
+ if( sync ) conf->sync();
+
+ emit configChanged();
+
+ logger->log( 1000, "wrote preferences: " + QString::number(time.elapsed()) );
+}
+
+void Config::readProfiles()
+{
+ int version;
+ QString name;
+ ConversionOptions options;
+
+ QDomDocument domTree;
+ QFile opmlFile( locateLocal("data","soundkonverter/profiles.xml") );
+ if( !opmlFile.open( IO_ReadOnly ) ) return;
+ if( !domTree.setContent( &opmlFile ) ) return;
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "profiles" ) return;
+ QDomNode node, sub1Node, sub2Node;
+ node = root.firstChild();
+
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+
+ version = node.toElement().attribute("version").toInt();
+
+ }
+ else if( node.isElement() && node.nodeName() == "profile" ) {
+
+ name = node.toElement().attribute("name");
+
+ sub1Node = node.toElement().firstChild();
+ while( !sub1Node.isNull() ) {
+ if( sub1Node.isElement() && sub1Node.nodeName() == "encodingOptions" ) {
+// TODO clean up
+ options.encodingOptions.sFormat = sub1Node.toElement().attribute("sFormat");
+ options.encodingOptions.sQualityMode = sub1Node.toElement().attribute("sQualityMode");
+ options.encodingOptions.iQuality = sub1Node.toElement().attribute("iQuality").toInt();
+ options.encodingOptions.sBitrateMode = sub1Node.toElement().attribute("sBitrateMode");
+ options.encodingOptions.bBitrateRange = sub1Node.toElement().attribute("bBitrateRange").toInt();
+ options.encodingOptions.iMinBitrate = sub1Node.toElement().attribute("iMinBitrate").toInt();
+ options.encodingOptions.iMaxBitrate = sub1Node.toElement().attribute("iMaxBitrate").toInt();
+
+ sub2Node = sub1Node.toElement().firstChild();
+ while( !sub2Node.isNull() ) {
+ if( sub2Node.isElement() && sub2Node.nodeName() == "samplingRate" ) {
+
+ options.encodingOptions.samplingRate.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
+ options.encodingOptions.samplingRate.iSamplingRate = sub2Node.toElement().attribute("iSamplingRate").toInt();
+
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "channels" ) {
+
+ options.encodingOptions.channels.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
+ options.encodingOptions.channels.sChannels = sub2Node.toElement().attribute("sChannels");
+
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "replaygain" ) {
+
+ options.encodingOptions.replaygain.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
+
+ }
+ sub2Node = sub2Node.nextSibling();
+ }
+
+ options.encodingOptions.sInOutFiles = sub1Node.toElement().attribute("sInOutFiles");
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "outputOptions" ) {
+
+ options.outputOptions.mode = (OutputDirectory::Mode)sub1Node.toElement().attribute("mode").toInt();
+ options.outputOptions.directory = sub1Node.toElement().attribute("directory");
+
+ }
+ sub1Node = sub1Node.nextSibling();
+ }
+ ProfileData profileData;
+ profileData.name = name;
+ profileData.options = options;
+ profiles += profileData;
+ }
+ node = node.nextSibling();
+ }
+}
+
+void Config::writeProfiles()
+{
+ QDomDocument domTree;
+ QDomElement root = domTree.createElement( "soundkonverter" );
+ root.setAttribute( "type", "profiles" );
+ domTree.appendChild( root );
+
+ QDomElement info = domTree.createElement( "info" );
+ info.setAttribute( "version", "300" );
+ root.appendChild( info );
+
+ for( QValueList<ProfileData>::Iterator it = profiles.begin(); it != profiles.end(); ++it ) {
+ QDomElement profile = domTree.createElement( "profile" );
+ profile.setAttribute( "name", (*it).name );
+
+ QDomElement encodingOptions = domTree.createElement( "encodingOptions" );
+
+ encodingOptions.setAttribute( "sFormat", (*it).options.encodingOptions.sFormat );
+ encodingOptions.setAttribute( "sQualityMode", (*it).options.encodingOptions.sQualityMode );
+ encodingOptions.setAttribute( "iQuality", (*it).options.encodingOptions.iQuality );
+ encodingOptions.setAttribute( "sBitrateMode", (*it).options.encodingOptions.sBitrateMode );
+ encodingOptions.setAttribute( "bBitrateRange", (*it).options.encodingOptions.bBitrateRange );
+ encodingOptions.setAttribute( "iMinBitrate", (*it).options.encodingOptions.iMinBitrate );
+ encodingOptions.setAttribute( "iMaxBitrate", (*it).options.encodingOptions.iMaxBitrate );
+
+ QDomElement samplingRate = domTree.createElement( "samplingRate" );
+
+ samplingRate.setAttribute( "bEnabled", (*it).options.encodingOptions.samplingRate.bEnabled );
+ samplingRate.setAttribute( "iSamplingRate", (*it).options.encodingOptions.samplingRate.iSamplingRate );
+
+ encodingOptions.appendChild( samplingRate );
+
+ QDomElement channels = domTree.createElement( "channels" );
+
+ channels.setAttribute( "bEnabled", (*it).options.encodingOptions.channels.bEnabled );
+ channels.setAttribute( "sChannels", (*it).options.encodingOptions.channels.sChannels );
+
+ encodingOptions.appendChild( channels );
+
+ QDomElement replaygain = domTree.createElement( "replaygain" );
+
+ replaygain.setAttribute( "bEnabled", (*it).options.encodingOptions.replaygain.bEnabled );
+
+ encodingOptions.appendChild( replaygain );
+
+ encodingOptions.setAttribute( "sInOutFiles", (*it).options.encodingOptions.sInOutFiles );
+
+ profile.appendChild( encodingOptions );
+
+ QDomElement outputOptions = domTree.createElement( "outputOptions" );
+
+ outputOptions.setAttribute( "mode", int((*it).options.outputOptions.mode) );
+ outputOptions.setAttribute( "directory", (*it).options.outputOptions.directory );
+
+ profile.appendChild( outputOptions );
+
+ root.appendChild( profile );
+ }
+
+ QFile opmlFile( locateLocal("data","soundkonverter/profiles.xml") );
+ if( !opmlFile.open( IO_WriteOnly ) ) return;
+
+ QTextStream ts( &opmlFile );
+ ts << domTree.toString();
+
+ opmlFile.close();
+}
+
+void Config::writeServiceMenu()
+{
+ int num;
+ QString content;
+ QFile file;
+
+ num = 0;
+ content = "";
+ content += "[Desktop Entry]\n";
+ content += "ServiceTypes=";
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+// if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).mime_types.first() != "application/octet-stream" ) {
+// if( !(*it).encoder->enc.lossy.enabled && !(*it).encoder->enc.lossless.enabled && !(*it).encoder->enc.hybrid.enabled ) continue;
+// for( QStringList::Iterator b = (*it).mime_types.begin(); b != (*it).mime_types.end(); ++b ) {
+// content += (*b) + ",";
+// num++;
+// }
+// }
+ if( (*it).decoder != 0 && binaries[(*it).decoder->dec.bin] != "" && (*it).mime_types.first() != "application/octet-stream" ) {
+ for( QStringList::Iterator b = (*it).mime_types.begin(); b != (*it).mime_types.end(); ++b ) {
+ content += (*b) + ",";
+ num++;
+ }
+ }
+ }
+
+ if( num != 0 )
+ {
+ content += "audio/x-wav";
+
+ content += "\n";
+ content += "Icon=soundkonverter\n";
+ content += "Actions=ConvertWithSoundkonverter\n\n";
+
+ content += "[Desktop Action ConvertWithSoundkonverter]\n";
+ content += "Name="+i18n("Convert with soundKonverter ...")+"\n";
+ content += "Icon=soundkonverter\n";
+ content += "Exec=soundkonverter %F\n";
+
+ file.setName( locateLocal("data","konqueror/servicemenus/")+"convert_with_soundkonverter.desktop" );
+ if ( file.open( IO_WriteOnly ) ) {
+ QTextStream stream( &file );
+ stream << content;
+ file.close();
+ }
+ }
+
+ num = 0;
+ content = "";
+ content += "[Desktop Entry]\n";
+ content += "ServiceTypes=";
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).replaygain != 0 && binaries[(*it).replaygain->replaygain.bin] != "" && (*it).mime_types.first() != "application/octet-stream" ) {
+ for( QStringList::Iterator b = (*it).mime_types.begin(); b != (*it).mime_types.end(); ++b ) {
+ content += (*b) + ",";
+ num++;
+ }
+ }
+ }
+
+ if( num != 0 )
+ {
+ content = content.left(content.length()-1);
+
+ content += "\n";
+ content += "Icon=soundkonverter_replaygain\n";
+ content += "Actions=AddReplayGainWithSoundkonverter\n\n";
+
+ content += "[Desktop Action AddReplayGainWithSoundkonverter]\n";
+ content += "Name="+i18n("Add Replay Gain with soundKonverter ...")+"\n";
+ content += "Icon=soundkonverter_replaygain\n";
+ content += "Exec=soundkonverter --replaygain %F\n";
+
+ file.setName( locateLocal("data","konqueror/servicemenus/")+"add_replaygain_with_soundkonverter.desktop" );
+ if( file.open(IO_WriteOnly) ) {
+ QTextStream st( &file );
+ st << content;
+ file.close();
+ }
+ }
+}
+
+void Config::writeAmarokScript()
+{
+ int num, num1, num2;
+ QString content, content1, content2;
+ QFile file;
+
+ num = 0;
+ content = "";
+ content += "name = soundKonverter\n";
+ content += "type = transcode\n\n";
+ content += "[Transcode]\n";
+ content += "target_formats = ";
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).mime_types.first() != "application/octet-stream" ) {
+ if( !(*it).encoder->enc.lossy.enabled && !(*it).encoder->enc.lossless.enabled && !(*it).encoder->enc.hybrid.enabled ) continue;
+ for( QStringList::Iterator b = (*it).extensions.begin(); b != (*it).extensions.end(); ++b ) {
+ if( content.find(" "+(*b).lower()+" ") == -1 ) content += (*b).lower() + " ";
+ num++;
+ }
+ }
+ }
+
+ if( num != 0 ) // TODO what is, if there are only decoders?
+ {
+ content += "wav";
+ content += "\n";
+
+ file.setName( locateLocal("data","amarok/scripts/soundKonverter/")+"soundKonverter.spec" );
+ if( file.open(IO_WriteOnly) ) {
+ QTextStream st( &file );
+ st << content;
+ file.close();
+ }
+ }
+
+ num1 = num2 = 0;
+ content = content1 = content2 = "";
+
+ // NOTE duplicate entries won't be shown to the user at any time, so they aren't filtered
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).mime_types.first() != "application/octet-stream" ) {
+ if( (*it).encoder->enc.lossless.enabled ) {
+ for( QStringList::Iterator b = (*it).extensions.begin(); b != (*it).extensions.end(); ++b ) {
+ if( content1.find(","+(*b).lower()+",") == -1 ) content1 += (*b).lower() + ","; // NOTE the first entry will be shown twice
+ num1++;
+ }
+ }
+ if( (*it).encoder->enc.hybrid.enabled ) {
+ for( QStringList::Iterator b = (*it).extensions.begin(); b != (*it).extensions.end(); ++b ) {
+ if( content2.find(","+(*b).lower()+",") == -1 ) content2 += (*b).lower() + ","; // NOTE the first entry will be shown twice
+ num2++;
+ }
+ }
+ }
+ }
+
+ if( num1 != 0 )
+ {
+ content1 = content1.left(content1.length()-1);
+ content += "Lossless";
+ content += "\n";
+ content += content1;
+ content += "\n";
+ }
+
+ if( num2 != 0 )
+ {
+ content2 = content2.left(content2.length()-1);
+ content += "Hybrid";
+ content += "\n";
+ content += content2;
+ content += "\n";
+ }
+
+ if( num1 != 0 || num2 != 0 )
+ {
+ file.setName( locateLocal("data","amarok/scripts/soundKonverter/")+"formats" );
+ if( file.open(IO_WriteOnly) ) {
+ QTextStream st( &file );
+ st << content;
+ file.close();
+ }
+ }
+
+ KStandardDirs* stdDirs = new KStandardDirs();
+ if( !QFile::exists(locateLocal("data","amarok/scripts/soundKonverter/soundKonverter.rb")) ) {
+ KIO::NetAccess::file_copy( stdDirs->findResource("data","soundkonverter/amarokscript/soundKonverter.rb"), locateLocal("data","amarok/scripts/soundKonverter/soundKonverter.rb"), 0755, true );
+ }
+ if( !QFile::exists(locateLocal("data","amarok/scripts/soundKonverter/README")) ) {
+ KIO::NetAccess::file_copy( stdDirs->findResource("data","soundkonverter/amarokscript/README"), locateLocal("data","amarok/scripts/soundKonverter/README"), -1, true );
+ }
+ delete stdDirs;
+}
+
+void Config::loadFileInfos()
+{}
+
+void Config::loadPlugins()
+{
+ // NOTE can be speeded up
+
+ //kdDebug() << "entering: `" << "Config::loadPlugins()" << "'" << endl;
+ logger->log( 1000, "entering: `Config::loadPlugins()'" );
+ QStringList list;
+ QDir dir;
+ QString correction_file_mime_type;
+
+ /** a map that holds the _identifier_ and the _version_ number of all plugins */
+ QMap<QString, PluginMapData> pluginMap;
+
+ int version;
+ QString identifier;
+ int i;
+
+ ConvertPluginLoader* convertPluginLoader = new ConvertPluginLoader();
+ ReplayGainPluginLoader* replaygainPluginLoader = new ReplayGainPluginLoader();
+ RipperPluginLoader* ripperPluginLoader = new RipperPluginLoader();
+
+ KStandardDirs stddir;
+ QStringList directories = stddir.findDirs( "data", "soundkonverter/plugins/" );
+ for( QStringList::Iterator a = directories.begin(); a != directories.end(); ++a )
+ {
+ //kdDebug() << " searching directory: `" << *a << "'" << endl;
+ logger->log( 1000, " searching directory: `" + *a + "'" );
+ dir.setPath( *a );
+ list = dir.entryList( "*", QDir::All, QDir::Name );
+ for( QStringList::Iterator b = list.begin(); b != list.end(); ++b )
+ {
+ if( *b != "." && *b != ".." && (*b).right(19) == ".soundkonverter.xml" )
+ {
+ //kdDebug() << " found file: `" << *b << "'" << endl;
+ logger->log( 1000, " found file: `" + *b + "'" );
+ if( convertPluginLoader->verifyFile( QString(*a).append(*b) ) != -1 )
+ {
+ version = convertPluginLoader->verifyFile( QString(*a).append(*b) );
+ identifier = *b;
+ identifier.remove( identifier.length() - 19, 19 );
+ i = identifier.findRev( "-" );
+ identifier.remove( i, identifier.length() - i );
+// i = identifier.find( "." );
+// identifier.remove( 0, i + 1 );
+ if( !pluginMap.contains(identifier) ) {
+ PluginMapData data;
+ data.version = 0;
+ pluginMap.insert( identifier, data );
+ }
+ if( pluginMap[identifier].version < version )
+ {
+ pluginMap[identifier].version = version;
+ pluginMap[identifier].filename = QString(*a).append(*b);
+ pluginMap[identifier].type = "converter";
+ logger->log( 1000, " updating version for: `" + identifier + "'" );
+ }
+ }
+ else if( replaygainPluginLoader->verifyFile( QString(*a).append(*b) ) != -1 )
+ {
+ version = replaygainPluginLoader->verifyFile( QString(*a).append(*b) );
+ identifier = *b;
+ identifier.remove( identifier.length() - 19, 19 );
+ i = identifier.findRev( "-" );
+ identifier.remove( i, identifier.length() - i );
+// i = identifier.find( "." );
+// identifier.remove( 0, i + 1 );
+ if( !pluginMap.contains(identifier) ) {
+ PluginMapData data;
+ data.version = 0;
+ pluginMap.insert( identifier, data );
+ }
+ if( pluginMap[identifier].version < version )
+ {
+ pluginMap[identifier].version = version;
+ pluginMap[identifier].filename = QString(*a).append(*b);
+ pluginMap[identifier].type = "replaygain";
+ logger->log( 1000, " updating version for: `" + identifier + "'" );
+ }
+ }
+ else if( ripperPluginLoader->verifyFile( QString(*a).append(*b) ) != -1 )
+ {
+ version = ripperPluginLoader->verifyFile( QString(*a).append(*b) );
+ identifier = *b;
+ identifier.remove( identifier.length() - 19, 19 );
+ i = identifier.findRev( "-" );
+ identifier.remove( i, identifier.length() - i );
+// i = identifier.find( "." );
+// identifier.remove( 0, i + 1 );
+ if( !pluginMap.contains(identifier) ) {
+ PluginMapData data;
+ data.version = 0;
+ pluginMap.insert( identifier, data );
+ }
+ if( pluginMap[identifier].version < version )
+ {
+ pluginMap[identifier].version = version;
+ pluginMap[identifier].filename = QString(*a).append(*b);
+ pluginMap[identifier].type = "ripper";
+ logger->log( 1000, " updating version for: `" + identifier + "'" );
+ }
+ }
+ else
+ {
+ //kdDebug() << " file is invalid: `" << *b << "'" << endl;
+ logger->log( 1000, " file is invalid: `" + *b + "'" );
+ }
+ }
+ }
+ }
+
+ for( QMap<QString, PluginMapData>::Iterator a = pluginMap.begin(); a != pluginMap.end(); ++a ) {
+ if( a.data().type == "converter" )
+ {
+ ConvertPlugin* plugin = convertPluginLoader->loadFile( a.data().filename );
+ if( plugin->info.version != -1 )
+ {
+ if( plugin->enc.hybrid.enabled )
+ {
+ correction_file_mime_type = plugin->enc.hybrid.correction_file_mime_type;
+ }
+ if( plugin->enc.enabled )
+ {
+ for( QStringList::Iterator b = plugin->enc.mime_types.begin(); b != plugin->enc.mime_types.end(); ++b )
+ {
+ //kdDebug() << " registering encoder for: `" << *b << "'" << endl;
+ logger->log( 1000, " registering encoder for: `" + *b + "'" );
+ registerFormatFeatures( *b, plugin, 0, 0, correction_file_mime_type );
+ }
+ }
+ if( plugin->dec.enabled )
+ {
+ for( QStringList::Iterator b = plugin->dec.mime_types.begin(); b != plugin->dec.mime_types.end(); ++b )
+ {
+ //kdDebug() << " registering decoder for: `" << *b << "'" << endl;
+ logger->log( 1000, " registering decoder for: `" + *b + "'" );
+ registerFormatFeatures( *b, 0, plugin, 0, correction_file_mime_type );
+ }
+ }
+ converters.append( plugin );
+ //kdDebug() << " plugin sucessfully loaded: `" << a.key() << "'" << endl;
+ logger->log( 1000, " plugin sucessfully loaded: `" + a.key() + "'" );
+ }
+ else
+ {
+ //kdError() << " failed to load Plugin: `" << a.key() << "'" << endl;
+ logger->log( 1000, " failed to load Plugin: `" + a.key() + "'" );
+ }
+ }
+ else if( a.data().type == "replaygain" )
+ {
+ ReplayGainPlugin* plugin = replaygainPluginLoader->loadFile( a.data().filename );
+ if( plugin->info.version != -1 )
+ {
+ for( QStringList::Iterator b = plugin->replaygain.mime_types.begin(); b != plugin->replaygain.mime_types.end(); ++b )
+ {
+ //kdDebug() << " registering replaygain for: `" << *b << "'" << endl;
+ logger->log( 1000, " registering replaygain for: `" + *b + "'" );
+ registerFormatFeatures( *b, 0, 0, plugin );
+ }
+ replaygains.append( plugin );
+ //kdDebug() << " plugin sucessfully loaded: `" << a.key() << "'" << endl;
+ logger->log( 1000, " plugin sucessfully loaded: `" + a.key() + "'" );
+ }
+ else
+ {
+ //kdError() << " failed to load Plugin: `" << a.key() << "'" << endl;
+ logger->log( 1000, " failed to load Plugin: `" + a.key() + "'" );
+ }
+ }
+ else if( a.data().type == "ripper" )
+ {
+ RipperPlugin* plugin = ripperPluginLoader->loadFile( a.data().filename );
+ if( plugin->info.version != -1 )
+ {
+ rippers.append( plugin );
+ //kdDebug() << " plugin sucessfully loaded: `" << a.key() << "'" << endl;
+ logger->log( 1000, " plugin sucessfully loaded: `" + a.key() + "'" );
+ }
+ else
+ {
+ //kdError() << " failed to load Plugin: `" << a.key() << "'" << endl;
+ logger->log( 1000, " failed to load Plugin: `" + a.key() + "'" );
+ }
+ }
+ }
+
+ delete convertPluginLoader;
+ delete replaygainPluginLoader;
+ delete ripperPluginLoader;
+
+ // NOTE in order to make a plugin working, there must be a format_infos file.
+
+ // TODO remove duplicate extensions
+
+ FormatInfoLoader* formatInfoLoader = new FormatInfoLoader();
+
+ //kdDebug() << " trying to open: `" << "soundkonverter/format_infos/"+language+"/" << "'" << endl;
+// logger->log( 1000, " trying to open: `soundkonverter/format_infos/" + language + "/'" );
+// directories = stddir.findDirs( "data", "soundkonverter/format_infos/"+language+"/" );
+// if( directories.isEmpty() ) directories = stddir.findDirs( "data", "soundkonverter/format_infos/en/" );
+ directories = stddir.findDirs( "data", "soundkonverter/format_infos/" );
+ for( QStringList::Iterator a = directories.begin(); a != directories.end(); ++a )
+ {
+ //kdDebug() << " searching directory: `" << *a << "'" << endl;
+ logger->log( 1000, " searching directory: `" + *a + "'" );
+ dir.setPath( *a );
+ list = dir.entryList( "*", QDir::All, QDir::Name );
+ for( QStringList::Iterator b = list.begin(); b != list.end(); ++b )
+ {
+ if( *b != "." && *b != ".." && (*b).right(4) == ".xml" )
+ {
+ //kdDebug() << " found file: `" << *b << "'" << endl;
+ logger->log( 1000, " found file: `" + *b + "'" );
+ if( formatInfoLoader->verifyFile( QString(*a).append(*b) ) )
+ {
+ FormatInfo* formatInfo = formatInfoLoader->loadFile( QString(*a).append(*b) );
+
+ for( QValueList<FormatItem>::Iterator c = formats.begin(); c != formats.end(); ++c ) {
+ for( QStringList::Iterator d = formatInfo->mime_types.begin(); d != formatInfo->mime_types.end(); ++d ) {
+ if( (*c).mime_types.findIndex(*d) != -1 ) {
+ (*c).description = "<p>" + formatInfo->description + "</p>";
+ (*c).description.replace("\\n","</p>\n<p>");
+ for( QStringList::Iterator d = formatInfo->urls.begin(); d != formatInfo->urls.end(); ++d ) {
+ (*c).description += "\n<p><a href=\"" + (*d) + "\">" + (*d) + "</a></p>";
+ }
+ (*c).compressionType = formatInfo->compressionType;
+ (*c).size = formatInfo->size;
+ // add extensions for mime types
+ logger->log( 1000, " found mime type: `" + *d + "'" );
+ QStringList extensions = KMimeType::mimeType( *d )->patterns();
+ for( QStringList::Iterator c = extensions.begin(); c != extensions.end(); ++c ) {
+ (*c).remove( 0, 2 );
+ logger->log( 1000, " adding extension: `" + *c + "'" );
+ }
+ (*c).extensions += extensions;
+ // add extensions (from format file) HACK
+ extensions = formatInfo->extensions;
+ if( !extensions.isEmpty() ) {
+ logger->log( 1000, " found extensions..." );
+ for( QStringList::Iterator c = extensions.begin(); c != extensions.end(); ++c ) {
+ logger->log( 1000, " adding extension: `" + *c + "'" );
+ }
+ (*c).extensions += extensions;
+ }
+ // add extensions for correction file mime types
+ for( QStringList::Iterator d = (*c).correction_file_mime_types.begin(); d != (*c).correction_file_mime_types.end(); ++d ) {
+ logger->log( 1000, " found correction mime type: `" + *d + "'" );
+ QStringList extensions = KMimeType::mimeType( *d )->patterns();
+ for( QStringList::Iterator e = extensions.begin(); e != extensions.end(); ++e ) {
+ (*e).remove( 0, 2 );
+ logger->log( 1000, " adding correction extension: `" + *e + "'" );
+ (*c).correction_file_extensions += (*e);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ delete formatInfoLoader;
+}
+
+void Config::registerFormatFeatures( const QString &mime_type,
+ ConvertPlugin* encoder, ConvertPlugin* decoder,
+ ReplayGainPlugin* replaygain/*, RepairPlugin* repairer*/,
+ const QString &correction_file_mime_type )
+{
+ //kdDebug() << " entering: `" << "Config::registerFormatFeatures( ... )" << "'" << endl;
+ logger->log( 1000, " entering: `Config::registerFormatFeatures( ... )'" );
+
+ // iterate through all file formats and search for an existing one
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(mime_type) != -1 ) { // we found an existing entry for our file format
+ //kdDebug() << " found an existing entry: `" << mime_type << "'" << endl;
+ logger->log( 1000, " found an existing entry: `" + mime_type + "'" );
+
+ if( encoder != 0 ) (*it).encoders.append( encoder );
+// if( (*it).encoder == 0 && encoder != 0 ) (*it).encoder = encoder;
+// if( (*it).encoder != 0 && binaries[(*it).encoder.enc.bin] == "" )
+ if( decoder != 0 ) (*it).decoders.append( decoder );
+// if( (*it).decoder == 0 && decoder != 0 ) (*it).decoder = decoder;
+ if( replaygain != 0 ) (*it).replaygains.append( replaygain );
+// if( (*it).replaygain == 0 && replaygain != 0 ) (*it).replaygain = replaygain;
+ //if( repairer ) (*it).repairer.append( repairer );*/
+ if( !correction_file_mime_type.isEmpty() ) (*it).correction_file_mime_types.append( correction_file_mime_type );
+
+ // everything done, we can return!
+ return;
+ }
+ }
+ //kdDebug() << " creating a new entry: `" << mime_type << "'" << endl;
+ logger->log( 1000, " creating a new entry: `" + mime_type + "'" );
+
+ // well it seems, we haven't found an entry. create a new!
+ QValueList<FormatItem>::Iterator newItem = formats.append( FormatItem() );
+ (*newItem).mime_types = mime_type;
+ if( encoder != 0 ) (*newItem).encoders.append( encoder );
+// if( encoder != 0 ) (*newItem).encoder = encoder;
+ if( decoder != 0 ) (*newItem).decoders.append( decoder );
+// if( decoder != 0 ) (*newItem).decoder = decoder;
+ if( replaygain != 0 ) (*newItem).replaygains.append( replaygain );
+// if( replaygain != 0 ) (*newItem).replaygain = replaygain;
+ //if( repairer ) (*newItem).repairer.append( repairer );
+ if( !correction_file_mime_type.isEmpty() ) (*newItem).correction_file_mime_types.append( correction_file_mime_type );
+
+}
+
+/*void Config::registerFileFormat( const QString &mime_type, const QString &option, const QString &value )
+{
+ // iterate through all file formats and search for an existing one
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(mime_type) != -1 ) { // we found an existing entry for our file format
+ if( option == "mime_type" ) (*it).mime_types.append( value );
+ //else if( option == "description" ) // TODO implement a qmap ?!
+ else if( option == "size" ) (*it).size = value.toInt();
+ else if( option == "compression_type" ) {
+ if( value == "lossy" ) (*it).compressionType = FormatInfo::lossy;
+ else if( value == "lossless" ) (*it).compressionType = FormatInfo::lossless;
+ else (*it).compressionType = FormatInfo::hybrid;
+ }
+
+ // everything done, we can return!
+ return;
+ }
+ }
+
+ // well it seems, we haven't found an entry. create a new!
+ QValueList<FormatItem>::Iterator newItem = formats.append( FormatItem() );
+ if( option == "mime_type" ) (*newItem).mime_types.append( value );
+ //else if( option == "description" ) // TODO implement a qmap ?!
+ else if( option == "size" ) (*newItem).size = value.toInt();
+ else if( option == "compression_type" ) {
+ if( value == "lossy" ) (*newItem).compressionType = FormatInfo::lossy;
+ else if( value == "lossless" ) (*newItem).compressionType = FormatInfo::lossless;
+ else (*newItem).compressionType = FormatInfo::hybrid;
+ }
+}*/
+
+ConvertPlugin* Config::encoderForFormat( const QString &format )
+{
+ // iterate through all file formats and search for our format in mime_types and extensions
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ return (*it).encoder;
+ }
+ }
+
+ return 0;
+}
+
+ConvertPlugin* Config::decoderForFormat( const QString &format )
+{
+ // iterate through all file formats and search for our format in mime_types and extensions
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ return (*it).decoder;
+ }
+ }
+
+ return 0;
+}
+
+ReplayGainPlugin* Config::replaygainForFormat( const QString &format )
+{
+ // iterate through all file formats and search for our format in mime_types and extensions
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ return (*it).replaygain;
+ }
+ }
+
+ return 0;
+}
+
+FormatItem* Config::getFormatItem( const QString &format )
+{
+ // iterate through all file formats and search for our format in mime_types and extensions
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ return &(*it);
+ }
+ }
+
+ return 0;
+}
+
+// NOTE speed up the following 3 functions (cache the data) (use the extensions variable in FormatItem)
+// NOTE seems to be called too often ???
+QStringList Config::allFormats()
+{
+ QStringList list;
+
+ //kdDebug() << "entering: `" << "Config::allFormats()" << "'" << endl;
+ logger->log( 1000, "entering: `Config::allFormats()'" );
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ //kdDebug() << " mime type: `" << (*it).mime_types.first() << "'" << endl;
+ logger->log( 1000, " mime type: `" + (*it).mime_types.first() + "'" );
+ if( (*it).mime_types.first() != "application/octet-stream" ) {
+// QString extension = KMimeType::mimeType( (*it).mime_types.first() )->patterns().first().lower();
+// extension.remove( 0, 2 );
+ QString extension = (*it).extensions.first().lower();
+ //kdDebug() << " extension: `" << extension << "'" << endl;
+ logger->log( 1000, " extension: `" + extension + "'" );
+ if( !extension.isEmpty() && !list.contains(extension) ) {
+ list.append( extension );
+ //kdDebug() << " (added)" << endl;
+ logger->log( 1000, " (added)" );
+ }
+ }
+ }
+
+ return list;
+}
+
+QStringList Config::allEncodableFormats()
+{
+ QStringList list;
+
+ //kdDebug() << "entering: `" << "Config::allEncodableFormats()" << "'" << endl;
+ logger->log( 1000, "entering: `Config::allEncodableFormats()'" );
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ //kdDebug() << " mime type: `" << (*it).mime_types.first() << "'" << endl;
+ logger->log( 1000, " mime type: `" + (*it).mime_types.first() + "'" );
+ if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).mime_types.first() != "application/octet-stream" ) {
+ if( !(*it).encoder->enc.lossy.enabled && !(*it).encoder->enc.lossless.enabled && !(*it).encoder->enc.hybrid.enabled ) continue;
+// QString extension = KMimeType::mimeType( (*it).mime_types.first() )->patterns().first().lower();
+// extension.remove( 0, 2 );
+ QString extension = (*it).extensions.first().lower();
+ //kdDebug() << " extension: `" << extension << "'" << endl;
+ logger->log( 1000, " extension: `" + extension + "'" );
+ if( !extension.isEmpty() && !list.contains(extension) ) {
+ list.append( extension );
+ //kdDebug() << " (added)" << endl;
+ logger->log( 1000, " (added)" );
+ }
+ }
+ }
+
+ return list;
+}
+
+QStringList Config::allLossyEncodableFormats()
+{
+ QStringList list;
+
+ //kdDebug() << "entering: `" << "Config::allLossyEncodableFormats()" << "'" << endl;
+// logger->log( 1000, "entering: `Config::allLossyEncodableFormats()'" );
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ //kdDebug() << " mime type: `" << (*it).mime_types.first() << "'" << endl;
+// logger->log( 1000, " mime type: `" + (*it).mime_types.first() + "'" );
+ if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).encoder->enc.lossy.enabled && (*it).compressionType & FormatInfo::lossy && (*it).mime_types.first() != "application/octet-stream" ) {
+// QString extension = KMimeType::mimeType( (*it).mime_types.first() )->patterns().first().lower();
+// extension.remove( 0, 2 );
+ QString extension = (*it).extensions.first().lower();
+ //kdDebug() << " extension: `" << extension << "'" << endl;
+// logger->log( 1000, " extension: `" + extension + "'" );
+ if( !extension.isEmpty() && !list.contains(extension) ) {
+ list.append( extension );
+ //kdDebug() << " (added)" << endl;
+// logger->log( 1000, " (added)" );
+ }
+ }
+ }
+
+ return list;
+}
+
+QStringList Config::allLosslessEncodableFormats()
+{
+ QStringList list;
+
+ //kdDebug() << "entering: `" << "Config::allLosslessEncodableFormats()" << "'" << endl;
+ logger->log( 1000, "entering: `Config::allLosslessEncodableFormats()'" );
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ //kdDebug() << " mime type: `" << (*it).mime_types.first() << "'" << endl;
+ logger->log( 1000, " mime type: `" + (*it).mime_types.first() + "'" );
+ if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).encoder->enc.lossless.enabled && (*it).compressionType & FormatInfo::lossless && (*it).mime_types.first() != "application/octet-stream" ) {
+// QString extension = KMimeType::mimeType( (*it).mime_types.first() )->patterns().first().lower();
+// extension.remove( 0, 2 );
+ QString extension = (*it).extensions.first().lower();
+ //kdDebug() << " extension: `" << extension << "'" << endl;
+ logger->log( 1000, " extension: `" + extension + "'" );
+ if( !extension.isEmpty() && !list.contains(extension) ) {
+ list.append( extension );
+ //kdDebug() << " (added)" << endl;
+ logger->log( 1000, " (added)" );
+ }
+ }
+ }
+
+ return list;
+}
+
+QStringList Config::allHybridEncodableFormats()
+{
+ QStringList list;
+
+ //kdDebug() << "entering: `" << "Config::allHybridEncodableFormats()" << "'" << endl;
+ logger->log( 1000, "entering: `Config::allHybridEncodableFormats()'" );
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ //kdDebug() << " mime type: `" << (*it).mime_types.first() << "'" << endl;
+ logger->log( 1000, " mime type: `" + (*it).mime_types.first() + "'" );
+ if( (*it).encoder != 0 && binaries[(*it).encoder->enc.bin] != "" && (*it).encoder->enc.hybrid.enabled && (*it).compressionType & FormatInfo::hybrid && (*it).mime_types.first() != "application/octet-stream" ) {
+// QString extension = KMimeType::mimeType( (*it).mime_types.first() )->patterns().first().lower();
+// extension.remove( 0, 2 );
+ QString extension = (*it).extensions.first().lower();
+ //kdDebug() << " extension: `" << extension << "'" << endl;
+ logger->log( 1000, " extension: `" + extension + "'" );
+ if( !extension.isEmpty() && !list.contains(extension) ) {
+ list.append( extension );
+ //kdDebug() << " (added)" << endl;
+ logger->log( 1000, " (added)" );
+ }
+ }
+ }
+
+ return list;
+}
+
+QString Config::getCorrectionExtension( const QString &format )
+{
+ // iterate through all file formats and search for our format
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ return (*it).correction_file_extensions.first();
+ }
+ }
+
+ return "";
+}
+
+QString Config::getFormatDescription( const QString &format ) // NOTE could be removed
+{
+ // iterate through all file formats and search for our format
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ return (*it).description;
+ }
+ }
+
+ return "";
+}
+
+void Config::addProfile( const QString &name, const ConversionOptions& profile )
+{
+ ProfileData profileData;
+ profileData.name = name;
+ profileData.options = profile;
+ profiles += profileData;
+ if( name != i18n("Last used") && name != "Last used" ) writeProfiles(); // will only be saved at app exit, when saving everything anyway
+ emit configChanged();
+}
+
+void Config::removeProfile( const QString &name )
+{
+ for( QValueList<ProfileData>::Iterator it = profiles.begin(); it != profiles.end(); ++it ) {
+ if( (*it).name == name ) {
+ profiles.remove( it );
+ if( name != i18n("Last used") && name != "Last used" ) writeProfiles(); // will only be saved at app exit, when saving everything anyway
+ return;
+ }
+ }
+ emit configChanged();
+}
+
+ConversionOptions Config::getProfile( const QString &name )
+{
+ for( QValueList<ProfileData>::Iterator it = profiles.begin(); it != profiles.end(); ++it ) {
+ if( /*(*it).name != i18n("Last used") &&*/ (*it).name == name ) {
+ return (*it).options;
+ }
+ }
+
+ ConversionOptions options;
+ options.encodingOptions.sFormat = "";
+ return options;
+}
+
+QString Config::getProfileName( const ConversionOptions& options )
+{
+ if( options.encodingOptions.sQualityMode == i18n("Lossless") ) {
+ return i18n("Lossless");
+ }
+
+ if( options.encodingOptions.sQualityMode == i18n("Hybrid") ) {
+ return i18n("Hybrid");
+ }
+
+ for( QValueList<ProfileData>::Iterator it = profiles.begin(); it != profiles.end(); ++it ) {
+ if( (*it).name != i18n("Last used") && (*it).options.nearlyEqual( options ) ) {
+ return (*it).name;
+ }
+ }
+
+ ConvertPlugin* plugin = encoderForFormat( options.encodingOptions.sFormat );
+ if( plugin != 0 ) {
+ if( plugin->enc.lossy.quality.enabled ) {
+ if( options.encodingOptions.sQualityMode == i18n("Quality") && options.encodingOptions.iQuality == 20 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == true && options.encodingOptions.samplingRate.iSamplingRate == 22050 &&
+ options.encodingOptions.channels.bEnabled == true && options.encodingOptions.channels.sChannels == i18n("Mono") &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Very low");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Quality") && options.encodingOptions.iQuality == 30 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == true && options.encodingOptions.samplingRate.iSamplingRate == 22050 &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Low");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Quality") && options.encodingOptions.iQuality == 40 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Medium");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Quality") && options.encodingOptions.iQuality == 50 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("High");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Quality") && options.encodingOptions.iQuality == 60 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Very high");
+ }
+ }
+ if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled ) {
+ if( options.encodingOptions.sQualityMode == i18n("Bitrate") && options.encodingOptions.iQuality == 64 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == true && options.encodingOptions.samplingRate.iSamplingRate == 22050 &&
+ options.encodingOptions.channels.bEnabled == true && options.encodingOptions.channels.sChannels == i18n("Mono") &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Very low");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Bitrate") && options.encodingOptions.iQuality == 96 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == true && options.encodingOptions.samplingRate.iSamplingRate == 22050 &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Low");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Bitrate") && options.encodingOptions.iQuality == 192 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Medium");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Bitrate") && options.encodingOptions.iQuality == 240 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("High");
+ }
+ if( options.encodingOptions.sQualityMode == i18n("Bitrate") && options.encodingOptions.iQuality == 320 &&
+ options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Very high");
+ }
+ }
+ /*{
+ if( options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == true && options.encodingOptions.samplingRate.iSamplingRate == 22050 &&
+ options.encodingOptions.channels.bEnabled == true && options.encodingOptions.channels.sChannels == i18n("Mono") &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Very low");
+ }
+ if( options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == true && options.encodingOptions.samplingRate.iSamplingRate == 22050 &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Low");
+ }
+ if( options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Medium");
+ }
+ if( options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("High");
+ }
+ if( options.encodingOptions.bBitrateRange == false &&
+ options.encodingOptions.samplingRate.bEnabled == false &&
+ options.encodingOptions.channels.bEnabled == false &&
+ options.encodingOptions.sInOutFiles == binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files ) {
+ return i18n("Very high");
+ }
+ }*/
+ }
+ return i18n("User defined");
+}
+
+QStringList Config::getAllProfiles()
+{
+ QStringList list;
+
+ for( QValueList<ProfileData>::Iterator it = profiles.begin(); it != profiles.end(); ++it ) {
+ /*if( (*it).name != i18n("Last used") )*/ list += (*it).name;
+ }
+
+ return list;
+}
+
+bool Config::acceptFile( const QString& format )
+{
+ if( format == "audio/x-wav" || format == "wav" ) return true;
+
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ if( (*it).decoder != 0 ) return true;
+ }
+ }
+
+ return false;
+}
+
+bool Config::acceptReplayGainFile( const QString& format )
+{
+ for( QValueList<FormatItem>::Iterator it = formats.begin(); it != formats.end(); ++it ) {
+ if( (*it).mime_types.findIndex(format) != -1 || (*it).extensions.findIndex(format) != -1 ) { // we found it
+ if( (*it).replaygain != 0 ) return true;
+ }
+ }
+
+ return false;
+}
+
+QString Config::fileFilter( bool wav )
+{
+ QString filter1;
+ if( wav ) filter1 += "*.wav *.WAV *.Wav";
+ QString filter2;
+ if( wav ) filter2 += "\n*.wav *.WAV *.Wav|wav " + i18n("files") + " (*.wav)";
+ QString temp;
+
+ for( QValueList<FormatItem>::Iterator a = formats.begin(); a != formats.end(); ++a ) {
+ if( (*a).decoder != 0 ) {
+ temp = "";
+ for( QStringList::Iterator b = (*a).extensions.begin(); b != (*a).extensions.end(); ++b ) {
+ filter1 += " *." + (*b);
+ if( !temp.contains(*b,false) ) temp += " *." + (*b).lower();
+ }
+ //temp.stripWhiteSpace(); // NOTE doesn't work
+ if( temp != "" && temp.length() < 80 ) {
+ temp.remove( 0, 1 );
+ temp = "\n" + temp + "|" + (*a).extensions.first() + " " + i18n("files") + " (" + temp + ")";
+ if( !filter2.contains(temp) ) { // HACK when unsing multiple mime types, there were too much entries
+ filter2 += temp;
+ }
+ }
+ else if( temp != "" ) {
+ temp.remove( 0, 1 );
+ temp = "\n" + temp + "|" + (*a).extensions.first() + " " + i18n("files");
+ if( !filter2.contains(temp) ) { // HACK when unsing multiple mime types, there were too much entries
+ filter2 += temp;
+ }
+ }
+ }
+ }
+
+ filter1.stripWhiteSpace();
+ filter2.stripWhiteSpace();
+
+ return filter1 + "|" + i18n("all supported formats") + "\n*.*|" + i18n("all formats") + filter2;
+}
+
+QStringList Config::fileTypes( bool wav )
+{
+ QStringList types;
+ if( wav ) types.append( "wav" );
+ QString temp;
+
+ for( QValueList<FormatItem>::Iterator a = formats.begin(); a != formats.end(); ++a ) {
+ if( (*a).decoder != 0 ) {
+ temp = "";
+ for( QStringList::Iterator b = (*a).extensions.begin(); b != (*a).extensions.end(); ++b ) {
+ if( !temp.contains(*b,false) ) temp.append( (*b).lower() + ", " );
+ }
+ if( temp != "" ) {
+ temp = temp.left( temp.length() - 2 );
+ if( types.findIndex(temp) == -1 ) types.append( temp );
+ }
+ }
+ }
+
+ return types;
+}
+
+QString Config::replayGainFilter()
+{
+ QString filter1;
+ QString filter2;
+ QString temp;
+
+ for( QValueList<FormatItem>::Iterator a = formats.begin(); a != formats.end(); ++a ) {
+ if( (*a).replaygain != 0 ) {
+ temp = "";
+ for( QStringList::Iterator b = (*a).extensions.begin(); b != (*a).extensions.end(); ++b ) {
+ filter1 += " *." + (*b);
+ if( !temp.contains(*b,false) ) temp += " *." + (*b).lower();
+ }
+ //temp.stripWhiteSpace(); // NOTE doesn't work
+ if( temp != "" && temp.length() < 80 ) {
+ temp.remove( 0, 1 );
+ filter2 += "\n" + temp + "|" + (*a).extensions.first() + " " + i18n("files") + " (" + temp + ")";
+ }
+ else if( temp != "" ) {
+ temp.remove( 0, 1 );
+ filter2 += "\n" + temp + "|" + (*a).extensions.first() + " " + i18n("files");
+ }
+ }
+ }
+
+ filter1.stripWhiteSpace();
+ filter2.stripWhiteSpace();
+
+ return filter1 + "|" + i18n("all supported formats") + "\n*.*|" + i18n("all formats") + filter2;
+}
+
+QStringList Config::replayGainFileTypes()
+{
+ QStringList types;
+ QString temp;
+
+ for( QValueList<FormatItem>::Iterator a = formats.begin(); a != formats.end(); ++a ) {
+ if( (*a).replaygain != 0 ) {
+ temp = "";
+ for( QStringList::Iterator b = (*a).extensions.begin(); b != (*a).extensions.end(); ++b ) {
+ if( !temp.contains(*b,false) ) temp.append( (*b).lower() + ", " );
+ }
+ if( temp != "" ) {
+ temp = temp.left( temp.length() - 2 );
+ if( types.findIndex(temp) == -1 ) types.append( temp );
+ }
+ }
+ }
+
+ return types;
+}
+
+
diff --git a/src/config.h b/src/config.h
new file mode 100755
index 0000000..130ddf9
--- /dev/null
+++ b/src/config.h
@@ -0,0 +1,354 @@
+
+
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#include "formatinfoloader.h"
+#include "conversionoptions.h"
+
+#include <qobject.h>
+#include <qvaluelist.h>
+#include <qstringlist.h>
+#include <qmap.h>
+
+class Logger;
+class ConvertPlugin;
+//class ConvertPluginLoader;
+class ReplayGainPlugin;
+//class ReplayGainPluginLoader;
+class RipperPlugin;
+struct ProfileData;
+
+
+/**
+ * @short Format items for the format list
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class FormatItem
+{
+public:
+ /**
+ * Constructor
+ */
+ FormatItem();
+
+ /**
+ * Destructor
+ */
+ virtual ~FormatItem();
+
+ QStringList mime_types;
+ QStringList extensions; // for easy use
+ QStringList correction_file_mime_types;
+ QStringList correction_file_extensions; // for easy use
+ QString description;
+ FormatInfo::CompressionType compressionType;
+ int compressionLevel; // the value from the config dialog
+ bool internalReplayGain;
+ int size;
+ QValueList<ConvertPlugin*> encoders;
+ QValueList<ConvertPlugin*> decoders;
+ QValueList<ReplayGainPlugin*> replaygains;
+ //QValueList<RepairerPlugin*> repairers;
+ ConvertPlugin* encoder;
+ ConvertPlugin* decoder;
+ ReplayGainPlugin* replaygain;
+ //RepairerPlugin* repairer;
+};
+
+
+/**
+ * @short Configuration class
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class Config : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ Config( Logger* );
+
+ /**
+ * Destructor
+ */
+ virtual ~Config();
+
+ /**
+ * Read the preferences from the configuration file
+ */
+ void read();
+
+ /**
+ * Write the preferences to the configuration file
+ */
+ void write( bool sync = true );
+
+ /**
+ * Read the profiles from the profiles file
+ */
+ void readProfiles();
+
+ /**
+ * Write the profiles to the profiles file
+ */
+ void writeProfiles();
+
+ /**
+ * Get the encoder for a given file format (mime type or extension)
+ */
+ ConvertPlugin* encoderForFormat( const QString &format );
+
+ /**
+ * Get the decoder for a given file format (mime type or extension)
+ */
+ ConvertPlugin* decoderForFormat( const QString &format );
+
+ /**
+ * Get the decoder for a given file format (mime type or extension)
+ */
+ ReplayGainPlugin* replaygainForFormat( const QString &format );
+
+ /**
+ * Get the format information for a given file format (mime type or extension)
+ */
+ FormatItem* getFormatItem( const QString &format );
+
+ /**
+ * Get the current ripper
+ */
+ RipperPlugin* getCurrentRipper() { return currentRipper; }
+
+ /**
+ * Set the current ripper
+ */
+ void setCurrentRipper( RipperPlugin* ripper ) { currentRipper = ripper; }
+
+ /**
+ * Returns a list of all loaded rippers
+ */
+ QValueList<RipperPlugin*> allRippers() { return rippers; }
+
+ /**
+ * Returns a list of all loaded converters
+ */
+ QValueList<ConvertPlugin*> allConverters() { return converters; }
+
+ /**
+ * Returns a list of all loaded replaygains
+ */
+ QValueList<ReplayGainPlugin*> allReplayGains() { return replaygains; }
+
+ /**
+ * Returns a list of all known file formats
+ */
+ QStringList allFormats();
+
+ /**
+ * Returns a list of all known encodeable file formats
+ */
+ QStringList allEncodableFormats();
+
+ /**
+ * Returns a list of all known lossy encodeable file formats
+ */
+ QStringList allLossyEncodableFormats();
+
+ /**
+ * Returns a list of all known lossless encodeable file formats
+ */
+ QStringList allLosslessEncodableFormats();
+
+ /**
+ * Returns a list of all known hybrid encodeable file formats
+ */
+ QStringList allHybridEncodableFormats();
+
+ /**
+ * Returns the extension of the correction file format for the given format
+ * If there is nor correction file format, the returned string is empty
+ */
+ QString getCorrectionExtension( const QString &format );
+
+ /**
+ * Returns a localized description for the given format
+ */
+ QString getFormatDescription( const QString &format );
+
+ /**
+ * Add a new profile
+ */
+ void addProfile( const QString &name, const ConversionOptions& profile );
+
+ /**
+ * Remove a new profile
+ */
+ void removeProfile( const QString &name );
+
+ /**
+ * Returns the conversion options for a profile
+ */
+ ConversionOptions getProfile( const QString &name );
+
+ /**
+ * Returns the name of the profile with conversion options @p options
+ */
+ QString getProfileName( const ConversionOptions& options );
+
+ /**
+ * Returns a list of all user defined profiles
+ */
+ QStringList getAllProfiles();
+
+ /**
+ * Returns true if the @p file can be added to the conversion list (can be decoded)
+ */
+ bool acceptFile( const QString& format );
+
+ /**
+ * Returns true if the @p file can be added to the replay gain tool
+ */
+ bool acceptReplayGainFile( const QString& format );
+
+ /**
+ * Returns a file filter suitable for the file open dialog
+ */
+ QString fileFilter( bool wav = true );
+
+ /**
+ * Returns a string list of supported file formats
+ */
+ QStringList fileTypes( bool wav = true );
+
+ /**
+ * Returns a file filter suitable for the file open dialog for the replay gain scanner
+ */
+ QString replayGainFilter();
+
+ /**
+ * Returns a string list of Replay Gain supported file formats
+ */
+ QStringList replayGainFileTypes();
+
+ struct Data {
+ struct General {
+ int startTab;
+ int lastTab;
+ QString defaultProfile;
+ QString defaultFormat;
+// QString defaultOutputDirectory;
+ QString specifyOutputDirectory;
+ QString metaDataOutputDirectory;
+ QString copyStructureOutputDirectory;
+ int priority;
+ bool useVFATNames;
+ int conflictHandling;
+ int numFiles;
+ int updateDelay;
+ bool askForNewOptions;
+ bool executeUserScript;
+ bool showToolBar;
+ } general;
+ struct Plugins {
+ bool checkForUpdates;
+ } plugins;
+ struct Environment {
+ QStringList directories;
+ QStringList foundPrograms;
+ } environment;
+ struct App {
+ int configVersion;
+ } app;
+ } data;
+
+ bool onlinePluginsChanged;
+ bool backendsChanged;
+
+ QMap<QString, QString> binaries;
+
+private:
+ struct PluginMapData {
+ int version;
+ QString filename;
+ QString type;
+ };
+
+ Logger* logger;
+
+ /** holds all known formats */
+ QValueList<FormatItem> formats;
+
+ /** holds all known rippers */
+ QValueList<RipperPlugin*> rippers;
+ RipperPlugin* currentRipper;
+
+ /** holds all known converters */
+ QValueList<ConvertPlugin*> converters;
+ /** holds all known replaygain apps */
+ QValueList<ReplayGainPlugin*> replaygains;
+ /* holds all known file repairing apps */
+// QValueList<RepairerItem> repairer;
+
+ //ConvertPluginLoader* convertPluginLoader;
+ //ReplayGainPluginLoader* replaygainPluginLoader;
+
+ /** holds all user defined profiles */
+ QValueList<ProfileData> profiles;
+
+ /**
+ * Load the files with the file infos (format descriptions, file size, etc.)
+ */
+ void loadFileInfos();
+
+ /**
+ * Load all plugins that can be found
+ */
+ void loadPlugins();
+
+ /**
+ * A plugin wants to register it's features for a file format
+ * @p binary The binary name of the app that wants to register it's features
+ * @p extension The file name extension for that is wants to register
+ * @p features The features of the app for that extension
+ */
+ void registerFormatFeatures( const QString &mime_type,
+ ConvertPlugin* encoder = 0, ConvertPlugin* decoder = 0,
+ ReplayGainPlugin* replaygain = 0/*, RepairPlugin* repairer = 0*/,
+ const QString &correction_file_mime_type = QString::null );
+
+ /**
+ * Generate the service menu
+ */
+ void writeServiceMenu();
+
+ /**
+ * Copy the amarok script
+ */
+ void writeAmarokScript();
+
+ /*
+ * We have new inforamtion for a file format
+ * @p extension The file name extension for that is wants to register
+ * @p newSynonym Register a new synonym for this file format?
+ * @p newMimeType Register a new mime type for this file format?
+ * @p newDescription Register a new description for this file format?
+ * @p newSize Register a new size for this file format for calculating the progress?
+ */
+ /*void registerFileFormat( const QString &extension, const QString &newSynonym = QString::null,
+ const QString &newMimeType = QString::null,
+ const QString &newDescription = QString::null,
+ int newSize = 0, FormatInfo::CompressionType newCompressionType = FormatInfo::lossy );*/
+ //void registerFileFormat( const QString &mime_type, const QString &option, const QString &value );
+ // NOTE this function is obsolete.
+ // after loading all plugins and creating all neccessary file format items,
+ // sk will search for format info files and update the items.
+
+signals:
+ void configChanged();
+
+};
+
+#endif // CONFIG_H
diff --git a/src/configbackendspage.cpp b/src/configbackendspage.cpp
new file mode 100755
index 0000000..d7b1589
--- /dev/null
+++ b/src/configbackendspage.cpp
@@ -0,0 +1,493 @@
+
+#include "configbackendspage.h"
+#include "convertpluginloader.h"
+#include "replaygainpluginloader.h"
+#include "ripperpluginloader.h"
+#include "config.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qgroupbox.h>
+#include <qtooltip.h>
+#include <qvbox.h>
+#include <qslider.h>
+#include <qcheckbox.h>
+
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kscrollview.h>
+#include <kstandarddirs.h>
+
+
+ConfigBackendsPage::ConfigBackendsPage( Config* _config, QMap<QString, QString>* _binaries, QWidget* parent, const char *name )
+ : ConfigPageBase( parent, name )
+{
+ config = _config;
+ binaries = _binaries;
+
+ grid = new QGridLayout( parent );
+ scrollView = new KScrollView( parent, "scrollView" );
+ scrollView->setResizePolicy( QScrollView::AutoOneFit );
+ grid->addWidget( scrollView, 0, 0 );
+ box = new QVBox( scrollView->viewport() );
+ box->setMargin( 11 );
+ box->setSpacing( 6 );
+ scrollView->addChild( box );
+
+ QHBox* legendBox = new QHBox( box );
+ legendBox->setMargin( 0 );
+ legendBox->setSpacing( 6 );
+ KStandardDirs* stdDirs = new KStandardDirs();
+ QLabel* lLegendLabel = new QLabel( i18n("Legend")+":", legendBox, "lLegendLabel" );
+ QLabel* lLegendGreen = new QLabel( "", legendBox, "lLegendGreen" );
+ lLegendGreen->setPixmap( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledgreen_legend.png")) );
+ QLabel* lLegendFull = new QLabel( i18n("Full support"), legendBox, "lLegendFull" );
+ QLabel* lLegendYellow = new QLabel("",legendBox,"lLegendYellow");
+ lLegendYellow->setPixmap( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledyellow_legend.png")) );
+ QLabel* lLegendMost = new QLabel( i18n("Most supported"), legendBox, "lLegendMost" );
+ QLabel* lLegendRed = new QLabel( "", legendBox, "lLegendRed" );
+ lLegendRed->setPixmap( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledred_legend.png")) );
+ QLabel* lLegendBasic = new QLabel( i18n("Basic support"), legendBox, "lLegendBasic" );
+ legendBox->setStretchFactor( lLegendLabel, 1 );
+
+ QGroupBox* ripperGroup = new QGroupBox( 1, Qt::Vertical, box, "ripperGroup" );
+ ripperGroup->layout()->setSpacing( 6 );
+ ripperGroup->layout()->setMargin( 6 );
+ QLabel* lRipper = new QLabel( i18n("CD Ripper")+":", ripperGroup, "lRipper" );
+ cRipper = new KComboBox( ripperGroup, "cRipper" );
+ connect( cRipper, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+
+ delete stdDirs;
+
+ rebuild();
+}
+
+ConfigBackendsPage::~ConfigBackendsPage()
+{}
+
+void ConfigBackendsPage::resetDefaults()
+{
+ int i;
+ int item;
+ int rank;
+
+ i = 1;
+ rank = 60;
+ item = 0;
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it )
+ {
+ if( (*it)->rip.rank > rank ) {
+ rank = (*it)->rip.rank;
+ item = i;
+ }
+ i++;
+ }
+ cRipper->setCurrentItem( item );
+
+ for( QValueList<FormatOptions>::Iterator a = formatOptions.begin(); a != formatOptions.end(); ++a )
+ {
+ FormatItem* formatItem = config->getFormatItem( (*a).format );
+ if( formatItem == 0 ) continue;
+
+ i = item = rank = 0;
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ if( (*b)->enc.rank > rank ) {
+ rank = (*b)->enc.rank;
+ item = i;
+ }
+ i++;
+ }
+ (*a).cEncoder->setCurrentItem( item );
+
+ i = item = rank = 0;
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->decoders.begin(); b != formatItem->decoders.end(); ++b ) {
+ if( (*b)->dec.rank > rank ) {
+ rank = (*b)->dec.rank;
+ item = i;
+ }
+ i++;
+ }
+ (*a).cDecoder->setCurrentItem( item );
+
+ i = item = rank = 0;
+ for( QValueList<ReplayGainPlugin*>::Iterator b = formatItem->replaygains.begin(); b != formatItem->replaygains.end(); ++b ) {
+ if( (*b)->replaygain.rank > rank ) {
+ rank = (*b)->replaygain.rank;
+ item = i;
+ }
+ i++;
+ }
+ (*a).cReplayGain->setCurrentItem( item );
+ }
+
+ encoderChanged();
+
+ for( QValueList<FormatOptions>::Iterator a = formatOptions.begin(); a != formatOptions.end(); ++a )
+ {
+ FormatItem* formatItem = config->getFormatItem( (*a).format );
+ if( formatItem == 0 ) continue;
+
+ QString encoder = (*a).cEncoder->currentText();
+
+ (*a).cInternalReplayGain->setChecked( false );
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ if( (*b)->enc.bin == encoder && (*b)->enc.strength.enabled ) {
+ if( (*b)->enc.strength.range_max >= (*b)->enc.strength.range_min )
+ (*a).sStrength->setValue( (*b)->enc.strength.default_value / (*b)->enc.strength.step );
+ else
+ (*a).sStrength->setValue( ( (*b)->enc.strength.range_min - (*b)->enc.strength.default_value ) / (*b)->enc.strength.step );
+ }
+ if( (*b)->enc.bin == encoder && (*b)->enc.replaygain.enabled ) {
+ if( formatItem->replaygain != 0 && formatItem->replaygain->replaygain.rank > (*b)->enc.replaygain.rank ) {
+ (*a).cInternalReplayGain->setChecked( false );
+ }
+ else {
+ (*a).cInternalReplayGain->setChecked( true );
+ }
+ }
+ }
+ }
+
+ strengthChanged();
+ cfgChanged();
+}
+
+void ConfigBackendsPage::saveSettings()
+{
+ config->setCurrentRipper( 0 );
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it )
+ {
+ if( (*it)->rip.bin == cRipper->currentText() ) {
+ config->setCurrentRipper( *it );
+ }
+ }
+
+ for( QValueList<FormatOptions>::Iterator a = formatOptions.begin(); a != formatOptions.end(); ++a )
+ {
+ FormatItem* formatItem = config->getFormatItem( (*a).format );
+ if( formatItem == 0 ) continue;
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ if( (*b)->enc.bin == (*a).cEncoder->currentText() ) {
+ formatItem->encoder = (*b);
+ if( (*b)->enc.strength.enabled ) {
+ formatItem->compressionLevel = (*a).sStrength->value();
+ }
+ if( (*b)->enc.replaygain.enabled ) {
+ formatItem->internalReplayGain = (*a).cInternalReplayGain->isChecked();
+ }
+ }
+ }
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->decoders.begin(); b != formatItem->decoders.end(); ++b ) {
+ if( (*b)->dec.bin == (*a).cDecoder->currentText() ) {
+ formatItem->decoder = (*b);
+ }
+ }
+
+ for( QValueList<ReplayGainPlugin*>::Iterator b = formatItem->replaygains.begin(); b != formatItem->replaygains.end(); ++b ) {
+ if( (*b)->replaygain.bin == (*a).cReplayGain->currentText() ) {
+ formatItem->replaygain = (*b);
+ }
+ }
+ }
+}
+
+void ConfigBackendsPage::rebuild()
+{
+ for( QValueList<FormatOptions>::Iterator it = formatOptions.begin(); it != formatOptions.end(); ++it )
+ {
+ delete (*it).lEncoder;
+ disconnect( (*it).cEncoder, SIGNAL(activated(int)), 0, 0 );
+ delete (*it).cEncoder;
+ delete (*it).lStrength;
+ disconnect( (*it).sStrength,SIGNAL(valueChanged(int)), 0, 0 );
+ delete (*it).sStrength;
+ delete (*it).lStrengthDisplay;
+ delete (*it).lDecoder;
+ disconnect( (*it).cDecoder, SIGNAL(activated(int)), 0, 0 );
+ delete (*it).cDecoder;
+ delete (*it).lReplayGain;
+ disconnect( (*it).cReplayGain, SIGNAL(activated(int)), 0, 0 );
+ delete (*it).cReplayGain;
+ disconnect( (*it).cInternalReplayGain, SIGNAL(toggled(bool)), 0, 0 );
+ delete (*it).cInternalReplayGain;
+ delete (*it).grid;
+ delete (*it).group;
+ }
+
+ formatOptions.clear();
+
+ // TODO show all extensions
+
+ QStringList formats = config->allFormats();
+
+ for( QStringList::Iterator it = formats.begin(); it != formats.end(); ++it )
+ {
+ FormatOptions options;
+ options.format = *it;
+
+ FormatItem *formatItem = config->getFormatItem( options.format );
+ QString title;
+ if( formatItem ) {
+ for( QStringList::Iterator at = formatItem->extensions.begin(); at != formatItem->extensions.end(); ++at ) {
+ if( !title.contains((*at).lower()) ) title += (*at).lower() + ", ";
+ }
+ title = title.left( title.length() - 2 );
+/* title += " [";
+ for( QStringList::Iterator bt = formatItem->mime_types.begin(); bt != formatItem->mime_types.end(); ++bt ) {
+ if( !title.contains((*bt).lower()) ) title += (*bt).lower() + ", ";
+ }
+ title = title.left( title.length() - 2 ) + "]";*/
+ }
+ else {
+ title = options.format;
+ }
+
+ options.group = new QGroupBox( title, box, options.format );
+ options.group->setColumnLayout( 0, Qt::Vertical );
+ options.group->layout()->setSpacing( 6 );
+ options.group->layout()->setMargin( 6 );
+ options.group->show();
+ options.grid = new QGridLayout( options.group->layout() );
+
+ options.lEncoder = new QLabel( i18n("Encoder")+":", options.group, options.format );
+ options.lEncoder->show();
+ options.grid->addWidget( options.lEncoder, 0, 0 );
+ options.cEncoder = new KComboBox( options.group, options.format );
+ options.cEncoder->show();
+ connect( options.cEncoder, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+ connect( options.cEncoder, SIGNAL(activated(int)),
+ this, SLOT(encoderChanged())
+ );
+ options.grid->addWidget( options.cEncoder, 0, 1 );
+
+ options.lStrength = new QLabel( i18n("Strength")+":", options.group, options.format );
+ options.lStrength->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
+ options.lStrength->show();
+ options.grid->addWidget( options.lStrength, 0, 2 );
+ options.sStrength = new QSlider( Qt::Horizontal, options.group, options.format );
+ options.sStrength->setTickmarks( QSlider::Below );
+ options.sStrength->show();
+ QToolTip::add( options.sStrength, i18n("Set the compression strength:\n\nLeft = fast conversion\nRight = good resultant file") );
+ options.grid->addWidget( options.sStrength, 0, 3 );
+ connect( options.sStrength, SIGNAL(valueChanged(int)),
+ this, SLOT(cfgChanged())
+ );
+ connect( options.sStrength, SIGNAL(valueChanged(int)),
+ this, SLOT(strengthChanged())
+ );
+
+ options.lDecoder = new QLabel( i18n("Decoder")+":", options.group, options.format );
+ options.lDecoder->show();
+ options.grid->addWidget( options.lDecoder, 1, 0 );
+ options.cDecoder = new KComboBox( options.group, options.format );
+ options.cDecoder->show();
+ options.grid->addWidget( options.cDecoder, 1, 1 );
+ connect( options.cDecoder, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+ options.lStrengthDisplay = new QLabel( "", options.group, options.format );
+ options.lStrengthDisplay->setAlignment( Qt::AlignRight | Qt::AlignVCenter );
+ options.lStrengthDisplay->setEnabled( false );
+ options.grid->addWidget( options.lStrengthDisplay, 1, 3 );
+
+ options.lReplayGain = new QLabel( i18n("Replay Gain")+":", options.group, options.format );
+ options.lReplayGain->show();
+ options.grid->addWidget( options.lReplayGain, 2, 0 );
+ options.cReplayGain = new KComboBox( options.group, options.format );
+ options.cReplayGain->show();
+ options.grid->addWidget( options.cReplayGain, 2, 1 );
+ connect( options.cReplayGain, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+ options.cInternalReplayGain = new QCheckBox( i18n("Use internal Replay Gain"), options.group, options.format );
+ QToolTip::add( options.cInternalReplayGain, i18n("Use the internal Replay Gain calculator of the encoder") );
+ options.grid->addWidget( options.cInternalReplayGain, 2, 3 );
+ connect( options.cInternalReplayGain, SIGNAL(toggled(bool)),
+ this, SLOT(cfgChanged())
+ );
+
+ options.grid->setColStretch( 0, 0 );
+ options.grid->setColStretch( 1, 1 );
+ options.grid->setColStretch( 2, 1 );
+ options.grid->setColStretch( 3, 1 );
+
+ formatOptions.append( options );
+ }
+
+ refill();
+}
+
+void ConfigBackendsPage::refill()
+{
+ KStandardDirs* stdDirs = new KStandardDirs();
+ int i, item;
+
+ cRipper->clear();
+ i = item = 0;
+ cRipper->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledyellow.png")), i18n("KDE audio CD protocol") );
+ i++;
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it )
+ {
+ if( (*binaries)[(*it)->rip.bin] == "" ) continue;
+
+ if( (*it)->rip.rank >= 70 ) cRipper->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledgreen.png")), (*it)->rip.bin );
+ else if( (*it)->rip.rank >= 40 ) cRipper->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledyellow.png")), (*it)->rip.bin );
+ else cRipper->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledred.png")), (*it)->rip.bin );
+ if( (*it) == config->getCurrentRipper() ) item = i;
+ i++;
+ }
+ cRipper->setCurrentItem( item );
+
+ for( QValueList<FormatOptions>::Iterator a = formatOptions.begin(); a != formatOptions.end(); ++a )
+ {
+ FormatItem* formatItem = config->getFormatItem( (*a).format );
+ (*a).cEncoder->clear();
+ (*a).cDecoder->clear();
+ (*a).cReplayGain->clear();
+ if( formatItem == 0 ) continue;
+
+ i = item = 0;
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ if( (*binaries)[(*b)->enc.bin] == "" ) continue;
+
+ if( (*b)->enc.rank >= 70 ) (*a).cEncoder->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledgreen.png")), (*b)->enc.bin );
+ else if( (*b)->enc.rank >= 40 ) (*a).cEncoder->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledyellow.png")), (*b)->enc.bin );
+ else (*a).cEncoder->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledred.png")), (*b)->enc.bin );
+ if( (*b) == formatItem->encoder ) item = i;
+ i++;
+ }
+ (*a).cEncoder->setCurrentItem( item );
+
+ i = item = 0;
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->decoders.begin(); b != formatItem->decoders.end(); ++b ) {
+ if( (*binaries)[(*b)->dec.bin] == "" ) continue;
+
+ if( (*b)->dec.rank >= 70 ) (*a).cDecoder->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledgreen.png")), (*b)->dec.bin );
+ else if( (*b)->dec.rank >= 40 ) (*a).cDecoder->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledyellow.png")), (*b)->dec.bin );
+ else (*a).cDecoder->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledred.png")), (*b)->dec.bin );
+ if( (*b) == formatItem->decoder ) item = i;
+ i++;
+ }
+ (*a).cDecoder->setCurrentItem( item );
+
+ i = item = 0;
+ for( QValueList<ReplayGainPlugin*>::Iterator b = formatItem->replaygains.begin(); b != formatItem->replaygains.end(); ++b ) {
+ if( (*binaries)[(*b)->replaygain.bin] == "" ) continue;
+
+ if( (*b)->replaygain.rank >= 70 ) (*a).cReplayGain->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledgreen.png")), (*b)->replaygain.bin );
+ else if( (*b)->replaygain.rank >= 40 ) (*a).cReplayGain->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledyellow.png")), (*b)->replaygain.bin );
+ else (*a).cReplayGain->insertItem( QPixmap(stdDirs->findResource("data","soundkonverter/pics/ledred.png")), (*b)->replaygain.bin );
+ if( (*b) == formatItem->replaygain ) item = i;
+ i++;
+ }
+ (*a).cReplayGain->setCurrentItem( item );
+ }
+
+ delete stdDirs;
+
+ encoderChanged();
+}
+
+void ConfigBackendsPage::encoderChanged()
+{
+ bool recalc;
+
+ for( QValueList<FormatOptions>::Iterator a = formatOptions.begin(); a != formatOptions.end(); ++a )
+ {
+ FormatItem* formatItem = config->getFormatItem( (*a).format );
+ (*a).lStrength->hide();
+ (*a).sStrength->hide();
+ (*a).lStrengthDisplay->hide();
+ (*a).cInternalReplayGain->hide();
+ if( formatItem == 0 ) continue;
+
+ QString encoder = (*a).cEncoder->currentText();
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ if( QObject::sender() && (*a).format == QObject::sender()->name() ) recalc = true;
+ else if( !QObject::sender() ) recalc = true;
+ else recalc = false;
+ if( (*b)->enc.bin == encoder && (*b)->enc.strength.enabled ) {
+ (*a).lStrength->show();
+ if( (*b)->enc.strength.range_max >= (*b)->enc.strength.range_min ) {
+ (*a).sStrength->setMinValue( (int)((*b)->enc.strength.range_min/(*b)->enc.strength.step) );
+ (*a).sStrength->setMaxValue( (int)((*b)->enc.strength.range_max/(*b)->enc.strength.step) );
+ }
+ else {
+ (*a).sStrength->setMinValue( (int)((*b)->enc.strength.range_max/(*b)->enc.strength.step) );
+ (*a).sStrength->setMaxValue( (int)((*b)->enc.strength.range_min/(*b)->enc.strength.step) );
+ }
+ (*a).sStrength->setPageStep( 1 );
+ if( QObject::sender() && (*a).format == QObject::sender()->name() ) {
+ if( (*b)->enc.strength.range_max >= (*b)->enc.strength.range_min )
+ (*a).sStrength->setValue( (*b)->enc.strength.default_value / (*b)->enc.strength.step );
+ else
+ (*a).sStrength->setValue( ( (*b)->enc.strength.range_min - (*b)->enc.strength.default_value ) / (*b)->enc.strength.step );
+ }
+ else if( !QObject::sender() ) {
+ (*a).sStrength->setValue( formatItem->compressionLevel );
+ }
+ (*a).sStrength->show();
+ (*a).lStrengthDisplay->show();
+ }
+ if( (*b)->enc.bin == encoder && (*b)->enc.replaygain.enabled ) {
+ (*a).cInternalReplayGain->show();
+ if( recalc ) (*a).cInternalReplayGain->setChecked( formatItem->internalReplayGain );
+ }
+ }
+ }
+
+ strengthChanged();
+}
+
+void ConfigBackendsPage::strengthChanged()
+{
+ for( QValueList<FormatOptions>::Iterator a = formatOptions.begin(); a != formatOptions.end(); ++a )
+ {
+ FormatItem* formatItem = config->getFormatItem( (*a).format );
+ if( formatItem == 0 ) continue;
+
+ QString encoder = (*a).cEncoder->currentText();
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ if( (*b)->enc.bin == encoder && (*b)->enc.strength.enabled ) {
+ QString strength = (*b)->enc.strength.param;
+ int compressionLevel = (*a).sStrength->value();
+ if( (*b)->enc.strength.profiles.empty() ) {
+ if( (*b)->enc.strength.step < 1 ) {
+ if( (*b)->enc.strength.range_max >= (*b)->enc.strength.range_min )
+ strength.replace( "%c", QString::number( compressionLevel * (*b)->enc.strength.step ) );
+ else
+ strength.replace( "%c", QString::number( (*b)->enc.strength.range_min - compressionLevel * (*b)->enc.strength.step ) );
+ }
+ else {
+ if( (*b)->enc.strength.range_max >= (*b)->enc.strength.range_min )
+ strength.replace( "%c", QString::number( (int)(compressionLevel * (*b)->enc.strength.step) ) );
+ else
+ strength.replace( "%c", QString::number( (int)((*b)->enc.strength.range_min - compressionLevel * (*b)->enc.strength.step) ) );
+ }
+ if( (*b)->enc.strength.separator != '.' ) strength.replace( QChar('.'), (*b)->enc.strength.separator );
+ }
+ else {
+ QStringList::Iterator it = (*b)->enc.strength.profiles.at( (int)compressionLevel );
+ strength.replace( "%c", *it );
+ }
+ (*a).lStrengthDisplay->setText( "( \"" + strength + "\" )" );
+ }
+ }
+ }
+}
+
+
+
diff --git a/src/configbackendspage.h b/src/configbackendspage.h
new file mode 100755
index 0000000..27071f2
--- /dev/null
+++ b/src/configbackendspage.h
@@ -0,0 +1,81 @@
+
+
+#ifndef CONFIGBACKENDSPAGE_H
+#define CONFIGBACKENDSPAGE_H
+
+#include <configpagebase.h>
+
+#include <qstringlist.h>
+
+class Config;
+class KComboBox;
+class KScrollView;
+class QGroupBox;
+class QGridLayout;
+class QLabel;
+class QSlider;
+class QCheckBox;
+class QVBox;
+
+/**
+* @short The page for configuring the environment
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConfigBackendsPage : public ConfigPageBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ ConfigBackendsPage( Config*, QMap<QString, QString>*, QWidget *parent=0, const char *name=0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~ConfigBackendsPage();
+
+private:
+ struct FormatOptions
+ {
+ // TODO remove the string lists
+ QString format;
+ QGroupBox* group;
+ QGridLayout* grid;
+ QLabel* lEncoder;
+ KComboBox* cEncoder;
+ QLabel* lStrength;
+ QSlider* sStrength;
+ QLabel* lStrengthDisplay;
+ QLabel* lDecoder;
+ KComboBox* cDecoder;
+ QLabel* lReplayGain;
+ KComboBox* cReplayGain;
+ QCheckBox* cInternalReplayGain;
+ };
+
+ QValueList<FormatOptions> formatOptions;
+
+ QGridLayout* grid;
+ KScrollView* scrollView;
+ QVBox* box;
+
+ KComboBox* cRipper;
+
+ Config* config;
+
+ QMap<QString, QString>* binaries;
+
+public slots:
+ void resetDefaults();
+ void saveSettings();
+ void rebuild();
+ void refill();
+ void encoderChanged();
+ void strengthChanged();
+
+};
+
+#endif // CONFIGBACKENDSPAGE_H
+
diff --git a/src/configdialog.cpp b/src/configdialog.cpp
new file mode 100755
index 0000000..3e58007
--- /dev/null
+++ b/src/configdialog.cpp
@@ -0,0 +1,174 @@
+
+
+#include "configdialog.h"
+
+#include "config.h"
+#include "configgeneralpage.h"
+#include "configpluginspage.h"
+#include "configenvironmentpage.h"
+#include "configbackendspage.h"
+
+/*#include "config_dialogue_interface.h"
+#include "config_dialogue_backend_plugins.h"
+#include "config_dialogue_backends.h"
+#include "config_dialogue_en_decoder.h"
+#include "config_dialogue_en_decoder_options.h"
+
+#include "tools.h"
+#include "backend_plugins.h"
+#include "replaygain_plugins.h"
+
+#include <qlayout.h>
+*/
+//#include <kconfig.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <kpushbutton.h>
+
+ConfigDialog::ConfigDialog( Config* _config, QWidget *parent, const char *name, Page startPage )
+ : KDialogBase(
+ IconList,
+ i18n("Settings"),
+ Apply | Cancel | Default | Help | Ok,
+ Ok, // default button
+ parent,
+ name,
+ true, // modal
+ true // separator
+ )
+{
+ config = _config;
+
+ //resize( 600, 400 );
+
+ binaries = config->binaries;
+
+ connect( this, SIGNAL(applyClicked()),
+ this,SLOT(applyClickedSlot())
+ );
+ connect( this, SIGNAL(okClicked()),
+ this,SLOT(okClickedSlot())
+ );
+ connect( this, SIGNAL(defaultClicked()),
+ this,SLOT(defaultClickedSlot())
+ );
+
+ generalPage = addPage( i18n("General"), "misc" );
+ configGeneralPage = new ConfigGeneralPage( config, generalPage, "configGeneralPage" );
+ connect( configGeneralPage, SIGNAL(configChanged()),
+ this, SLOT(configChanged())
+ );
+ connect( this, SIGNAL(saveGeneral()),
+ configGeneralPage, SLOT(saveSettings())
+ );
+ connect( this, SIGNAL(resetGeneral()),
+ configGeneralPage, SLOT(resetDefaults())
+ );
+
+ pluginsPage = addPage( i18n("Plugins"), "connect_creating" );
+ configPluginsPage = new ConfigPluginsPage( config, pluginsPage, "configPluginsPage" );
+ connect( configPluginsPage, SIGNAL(configChanged()),
+ this, SLOT(configChanged())
+ );
+ connect( this, SIGNAL(savePlugins()),
+ configPluginsPage, SLOT(saveSettings())
+ );
+ connect( this, SIGNAL(resetPlugins()),
+ configPluginsPage, SLOT(resetDefaults())
+ );
+
+ environmentPage = addPage( i18n("Environment"), "filefind" );
+ configEnvironmentPage = new ConfigEnvironmentPage( config, &binaries, environmentPage, "configEnvironmentPage" );
+ connect( configEnvironmentPage, SIGNAL(configChanged()),
+ this, SLOT(configChanged())
+ );
+ connect( this, SIGNAL(saveEnvironment()),
+ configEnvironmentPage, SLOT(saveSettings())
+ );
+ connect( this, SIGNAL(resetEnvironment()),
+ configEnvironmentPage, SLOT(resetDefaults())
+ );
+
+ backendsPage = addPage( i18n("Backends"), "kcmsystem" );
+ configBackendsPage = new ConfigBackendsPage( config, &binaries, backendsPage, "configBackendsPage" );
+ connect( configBackendsPage, SIGNAL(configChanged()),
+ this, SLOT(configChanged())
+ );
+ connect( this, SIGNAL(saveBackends()),
+ configBackendsPage, SLOT(saveSettings())
+ );
+ connect( this, SIGNAL(resetBackends()),
+ configBackendsPage, SLOT(resetDefaults())
+ );
+ connect( configEnvironmentPage, SIGNAL(rebuildBackendsPage()),
+ configBackendsPage, SLOT(rebuild())
+ );
+
+ setConfigChanged( false );
+
+ showPage( startPage );
+}
+
+ConfigDialog::~ConfigDialog()
+{
+}
+
+QFrame *ConfigDialog::addPage(const QString &itemName, const QString &iconName)
+{
+ return KDialogBase::addPage( itemName, QString::null, MainBarIcon(iconName,32) );
+}
+
+void ConfigDialog::setConfigChanged( const bool value )
+{
+ actionButton( Apply )->setEnabled(value);
+}
+
+void ConfigDialog::configChanged()
+{
+ setConfigChanged( true );
+}
+
+void ConfigDialog::applyClickedSlot()
+{
+ okClickedSlot();
+ setConfigChanged( false );
+}
+
+void ConfigDialog::okClickedSlot()
+{
+ emit saveGeneral();
+ emit savePlugins();
+ emit saveEnvironment();
+ emit saveBackends();
+ config->write();
+}
+
+void ConfigDialog::defaultClickedSlot()
+{
+ int index = activePageIndex();
+ QStringList listDefaults;
+
+ if( index == -1 )
+ return;
+
+ if( index == pageIndex(generalPage) )
+ {
+ emit resetGeneral();
+ setConfigChanged( true );
+ }
+ else if( index == pageIndex(pluginsPage) )
+ {
+ emit resetPlugins();
+ setConfigChanged( true );
+ }
+ else if( index == pageIndex(environmentPage) )
+ {
+ emit resetEnvironment();
+ setConfigChanged( true );
+ }
+ else if( index == pageIndex(backendsPage) )
+ {
+ emit resetBackends();
+ setConfigChanged( true );
+ }
+}
diff --git a/src/configdialog.h b/src/configdialog.h
new file mode 100755
index 0000000..19d3d12
--- /dev/null
+++ b/src/configdialog.h
@@ -0,0 +1,79 @@
+
+
+#ifndef CONFIGDIALOG_H
+#define CONFIGDIALOG_H
+
+#include <kdialogbase.h>
+
+#include <qmap.h>
+
+
+class Config;
+class ConfigGeneralPage;
+class ConfigPluginsPage;
+class ConfigEnvironmentPage;
+class ConfigBackendsPage;
+
+/**
+ * @short Config dialog
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConfigDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ enum Page {
+ GeneralPage,
+ PluginsPage,
+ EnvironmentPage,
+ BackendsPage
+ };
+
+ /**
+ * Constructor
+ */
+ ConfigDialog( Config*, QWidget *parent = 0, const char *name = 0, Page startPage = GeneralPage );
+
+ /**
+ * Destructor
+ */
+ virtual ~ConfigDialog();
+
+private:
+ QFrame* addPage( const QString &itemName, const QString &iconName );
+
+ QFrame* generalPage;
+ ConfigGeneralPage* configGeneralPage;
+ QFrame* pluginsPage;
+ ConfigPluginsPage* configPluginsPage;
+ QFrame* environmentPage;
+ ConfigEnvironmentPage* configEnvironmentPage;
+ QFrame* backendsPage;
+ ConfigBackendsPage* configBackendsPage;
+
+ void setConfigChanged( const bool );
+
+ Config* config;
+
+ QMap<QString, QString> binaries;
+
+private slots:
+ void configChanged();
+ void okClickedSlot();
+ void applyClickedSlot();
+ void defaultClickedSlot();
+
+signals:
+ void saveGeneral();
+ void savePlugins();
+ void saveEnvironment();
+ void saveBackends();
+
+ void resetGeneral();
+ void resetPlugins();
+ void resetEnvironment();
+ void resetBackends();
+};
+
+#endif // CONFIGDIALOG_H
diff --git a/src/configenvironmentpage.cpp b/src/configenvironmentpage.cpp
new file mode 100755
index 0000000..26dbd5b
--- /dev/null
+++ b/src/configenvironmentpage.cpp
@@ -0,0 +1,223 @@
+
+#include "configenvironmentpage.h"
+
+#include "config.h"
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <klistbox.h>
+//#include <keditlistbox.h>
+//#include <kurlrequester.h>
+#include <kfiledialog.h>
+#include <kstandarddirs.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qtooltip.h>
+#include <qmap.h>
+
+
+ConfigEnvironmentPage::ConfigEnvironmentPage( Config* _config, QMap<QString, QString>* _binaries, QWidget *parent, const char *name )
+ : ConfigPageBase( parent, name )
+{
+ config = _config;
+ binaries = _binaries;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ QVBoxLayout* box = new QVBoxLayout( parent, 0, 6 );
+
+ QLabel* lDirectoriesLabel = new QLabel( i18n("Directories to be scanned")+":", parent, "lDirectoriesLabel" );
+ box->addWidget( lDirectoriesLabel );
+
+// KEditListBox* eDirectories = new KEditListBox( parent, "eDirectories" );
+// box->addWidget( eDirectories );
+
+ QHBoxLayout* directoriesBox = new QHBoxLayout( box );
+ lDirectories = new KListBox( parent, "lDirectories" );
+ lDirectories->insertStringList( config->data.environment.directories );
+ directoriesBox->addWidget( lDirectories );
+ connect( lDirectories, SIGNAL(highlighted(int)),
+ this, SLOT(directoriesSelectionChanged(int))
+ );
+
+ QVBoxLayout* directoriesMiddleBox = new QVBoxLayout( directoriesBox );
+ pDirUp = new KPushButton( "", parent, "pDirUp" );
+ pDirUp->setPixmap( iconLoader->loadIcon("up",KIcon::Toolbar) );
+ pDirUp->setEnabled( false );
+ QToolTip::add( pDirUp, i18n("Move selected directory one position up.\nThis effects which backend will be chosen, if there are several versions.") );
+ directoriesMiddleBox->addWidget( pDirUp );
+ connect( pDirUp, SIGNAL(clicked()),
+ this, SLOT(dirUp())
+ );
+
+ directoriesMiddleBox->addStretch();
+ pDirDown = new KPushButton( "", parent, "pDirDown" );
+ pDirDown->setPixmap( iconLoader->loadIcon("down",KIcon::Toolbar) );
+ pDirDown->setEnabled( false );
+ QToolTip::add( pDirDown, i18n("Move selected directory one position down.\nThis effects which backend will be chosen, if there are several versions.") );
+ directoriesMiddleBox->addWidget( pDirDown );
+ connect( pDirDown, SIGNAL(clicked()),
+ this, SLOT(dirDown())
+ );
+
+ QVBoxLayout* directoriesRightBox = new QVBoxLayout( directoriesBox );
+ pAddDirectory = new KPushButton( iconLoader->loadIcon("add",KIcon::Small), i18n("Add ..."), parent, "pAddDirectory" );
+ directoriesRightBox->addWidget( pAddDirectory );
+ connect( pAddDirectory, SIGNAL(clicked()),
+ this, SLOT(addDirectory())
+ );
+
+ pRemoveDirectory = new KPushButton( iconLoader->loadIcon("remove",KIcon::Small), i18n("Remove"), parent, "pRemoveDirectory" );
+ directoriesRightBox->addWidget( pRemoveDirectory );
+ pRemoveDirectory->setEnabled( false );
+ connect( pRemoveDirectory, SIGNAL(clicked()),
+ this, SLOT(removeDirectory())
+ );
+
+ directoriesRightBox->addStretch();
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* programsBox = new QHBoxLayout( box );
+
+ QVBoxLayout* foundProgramsBox = new QVBoxLayout( programsBox );
+ QLabel* lFoundProgramsLabel = new QLabel( i18n("Programs found")+":", parent, "lFoundProgramsLabel" );
+ foundProgramsBox->addWidget( lFoundProgramsLabel );
+ lFoundPrograms = new KListBox( parent, "lFoundPrograms" );
+ lFoundPrograms->setSelectionMode( QListBox::NoSelection );
+ foundProgramsBox->addWidget( lFoundPrograms );
+ //connect(lPrograms,SIGNAL(highlighted(int)),this,SLOT(programsSelectionChanged(int)));
+ programsBox->setStretchFactor( foundProgramsBox, 3 );
+
+ QVBoxLayout* notFoundProgramsBox = new QVBoxLayout( programsBox );
+ QLabel* lNotFoundProgramsLabel = new QLabel( i18n("Programs not found")+":", parent, "lNotFoundProgramsLabel" );
+ notFoundProgramsBox->addWidget( lNotFoundProgramsLabel );
+ lNotFoundPrograms = new KListBox( parent, "lNotFoundPrograms" );
+ lNotFoundPrograms->setSelectionMode( QListBox::NoSelection );
+ notFoundProgramsBox->addWidget( lNotFoundPrograms );
+ //connect(lPrograms,SIGNAL(highlighted(int)),this,SLOT(programsSelectionChanged(int)));
+ programsBox->setStretchFactor( notFoundProgramsBox, 2 );
+
+ for( QMap<QString, QString>::Iterator it = config->binaries.begin(); it != config->binaries.end(); ++it ) {
+ if( it.data() != "" ) {
+ lFoundPrograms->insertItem( it.data() );
+ }
+ else {
+ lNotFoundPrograms->insertItem( it.key() );
+ }
+ }
+
+// box->addStretch();
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+ConfigEnvironmentPage::~ConfigEnvironmentPage()
+{}
+
+void ConfigEnvironmentPage::resetDefaults()
+{
+ lDirectories->clear();
+ QString datadir = locateLocal( "data", "soundkonverter/bin/" );
+ datadir.remove( datadir.length() - 1, 1 );
+ lDirectories->insertItem( datadir );
+ lDirectories->insertItem( QDir::homeDirPath() + "/bin" );
+ lDirectories->insertItem( "/usr/local/bin" );
+ lDirectories->insertItem( "/usr/bin" );
+
+ refill();
+ cfgChanged();
+}
+
+void ConfigEnvironmentPage::saveSettings()
+{
+ config->data.environment.directories.clear();
+ for( uint i = 0; i < lDirectories->count(); i++ ) {
+ config->data.environment.directories.append( lDirectories->text(i) );
+ }
+
+ config->binaries = *binaries;
+}
+
+void ConfigEnvironmentPage::directoriesSelectionChanged( int index )
+{
+ pRemoveDirectory->setEnabled( true );
+ if( index != 0 ) pDirUp->setEnabled( true );
+ else pDirUp->setEnabled( false );
+ if( index != lDirectories->count() - 1 ) pDirDown->setEnabled( true );
+ else pDirDown->setEnabled( false );
+}
+
+void ConfigEnvironmentPage::dirUp()
+{
+ int index = lDirectories->currentItem();
+ if( index > 0 ) {
+ QString text = lDirectories->currentText();
+ lDirectories->removeItem( index );
+ lDirectories->insertItem( text, index - 1 );
+ lDirectories->setSelected( index - 1, true );
+ refill();
+ cfgChanged();
+ }
+}
+
+void ConfigEnvironmentPage::dirDown()
+{
+ int index = lDirectories->currentItem();
+ if( (uint)index < lDirectories->count() - 1 ) {
+ QString text = lDirectories->currentText();
+ lDirectories->removeItem( index );
+ lDirectories->insertItem( text, index + 1 );
+ lDirectories->setSelected( index + 1, true );
+ refill();
+ cfgChanged();
+ }
+}
+
+void ConfigEnvironmentPage::addDirectory()
+{
+ QString dirname = KFileDialog::getExistingDirectory( "/", 0 );
+ if( dirname != NULL ) {
+ lDirectories->insertItem( dirname );
+ refill();
+ cfgChanged();
+ }
+}
+
+void ConfigEnvironmentPage::removeDirectory()
+{
+ lDirectories->removeItem( lDirectories->currentItem() );
+ refill();
+ cfgChanged();
+}
+
+void ConfigEnvironmentPage::refill()
+{
+ for( QMap<QString, QString>::Iterator it = binaries->begin(); it != binaries->end(); ++it ) {
+ it.data() = "";
+ for( uint i = 0; i < lDirectories->count(); i++ ) {
+ if( it.data() == "" && QFile::exists(lDirectories->text(i) + "/" + it.key()) ) {
+ it.data() = lDirectories->text(i) + "/" + it.key();
+ }
+ }
+ }
+
+ lFoundPrograms->clear();
+ lNotFoundPrograms->clear();
+ for( QMap<QString, QString>::Iterator it = binaries->begin(); it != binaries->end(); ++it ) {
+ if( it.data() != "" ) {
+ lFoundPrograms->insertItem( it.data() );
+ }
+ else {
+ lNotFoundPrograms->insertItem( it.key() );
+ }
+ }
+
+ emit rebuildBackendsPage();
+}
+
+
diff --git a/src/configenvironmentpage.h b/src/configenvironmentpage.h
new file mode 100755
index 0000000..37b0c2e
--- /dev/null
+++ b/src/configenvironmentpage.h
@@ -0,0 +1,62 @@
+
+
+#ifndef CONFIGENVIRONMENTPAGE_H
+#define CONFIGENVIRONMENTPAGE_H
+
+#include <configpagebase.h>
+
+class Config;
+class KPushButton;
+class KListBox;
+
+/**
+* @short The page for configuring the environment
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConfigEnvironmentPage : public ConfigPageBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ ConfigEnvironmentPage( Config*, QMap<QString, QString>*, QWidget *parent=0, const char *name=0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~ConfigEnvironmentPage();
+
+private:
+ KListBox* lDirectories;
+ KPushButton* pDirUp;
+ KPushButton* pDirDown;
+ KPushButton* pAddDirectory;
+ KPushButton* pRemoveDirectory;
+ KListBox* lFoundPrograms;
+ KListBox* lNotFoundPrograms;
+
+ Config* config;
+
+ QMap<QString, QString>* binaries;
+
+public slots:
+ void resetDefaults();
+ void saveSettings();
+
+private slots:
+ void directoriesSelectionChanged( int );
+ void dirUp();
+ void dirDown();
+ void addDirectory();
+ void removeDirectory();
+ void refill();
+
+signals:
+ void rebuildBackendsPage();
+
+};
+
+#endif // CONFIGENVIRONMENTPAGE_H
+
diff --git a/src/configgeneralpage.cpp b/src/configgeneralpage.cpp
new file mode 100755
index 0000000..9408ffd
--- /dev/null
+++ b/src/configgeneralpage.cpp
@@ -0,0 +1,323 @@
+
+#include "configgeneralpage.h"
+
+#include "config.h"
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <knuminput.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+#include <kfiledialog.h>
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qtooltip.h>
+#include <qdir.h>
+#include <qregexp.h>
+
+// ### soundkonverter 0.4: add an option to use vfat save names when the output device is vfat
+
+ConfigGeneralPage::ConfigGeneralPage( Config* _config, QWidget *parent, const char *name )
+ : ConfigPageBase( parent, name )
+{
+ config = _config;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ QVBoxLayout* box = new QVBoxLayout( parent, 0, 6 );
+
+ QHBoxLayout* startTabBox = new QHBoxLayout( box, 6 );
+ QLabel* lStartTab = new QLabel( i18n("Start in Mode")+":", parent, "lStartTab" );
+ startTabBox->addWidget( lStartTab );
+ cStartTab = new KComboBox( parent, "cStartTab" );
+ cStartTab->insertItem( i18n("Last used") );
+ cStartTab->insertItem( i18n("Simple") );
+ cStartTab->insertItem( i18n("Detailed") );
+ cStartTab->setCurrentItem( config->data.general.startTab );
+ startTabBox->addWidget( cStartTab );
+ connect( cStartTab, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* defaultProfileBox = new QHBoxLayout( box, 6 );
+ QLabel* lDefaultProfile = new QLabel( i18n("Default profile")+":", parent, "lDefaultProfile" );
+ defaultProfileBox->addWidget( lDefaultProfile );
+ cDefaultProfile = new KComboBox( parent, "cDefaultProfile" );
+ sDefaultProfile += i18n("Very low");
+ sDefaultProfile += i18n("Low");
+ sDefaultProfile += i18n("Medium");
+ sDefaultProfile += i18n("High");
+ sDefaultProfile += i18n("Very high");
+ sDefaultProfile += i18n("Lossless");
+ sDefaultProfile += i18n("Hybrid");
+ sDefaultProfile += config->getAllProfiles();
+ sDefaultProfile.remove( i18n("Last used") );
+ sDefaultProfile.remove( "Last used" );
+ sDefaultProfile.prepend( i18n("Last used") );
+ cDefaultProfile->insertStringList( sDefaultProfile );
+ cDefaultProfile->setCurrentItem( profileIndex(config->data.general.defaultProfile) );
+ defaultProfileBox->addWidget( cDefaultProfile );
+ connect( cDefaultProfile, SIGNAL(activated(int)),
+ this, SLOT(profileChanged())
+ );
+ connect( cDefaultProfile, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+ QLabel* lDefaultFormat = new QLabel( i18n("Default format")+":", parent, "lDefaultFormat" );
+ defaultProfileBox->addWidget( lDefaultFormat );
+ cDefaultFormat = new KComboBox( parent, "cDefaultFormat" );
+ defaultProfileBox->addWidget( cDefaultFormat );
+ connect( cDefaultFormat, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+ profileChanged();
+
+ box->addSpacing( 5 );
+
+/* QHBoxLayout* defaultDirBox = new QHBoxLayout( box, 6 );
+ QLabel* lDefaultDir = new QLabel( i18n("Default output directory")+":", parent, "lDefaultDir" );
+ defaultDirBox->addWidget( lDefaultDir );
+ lDir = new KLineEdit( parent, "lDir" );
+ lDir->setText( config->data.general.defaultOutputDirectory );
+ QToolTip::add( lDir, i18n("<p>The following strings are space holders, that will be replaced by the information in the meta data:</p><p>%a - Artist<br>%b - Album<br>%c - Comment<br>%d - Disc number<br>%g - Genre<br>%n - Track number<br>%p - Composer<br>%t - Title<br>%y - Year<br>%f - Original file name<p>") );
+ defaultDirBox->addWidget( lDir );
+ connect( lDir, SIGNAL(textChanged(const QString&)),
+ this, SLOT(cfgChanged())
+ );*/
+ /*pDirInfo = new KPushButton( iconLoader->loadIcon("messagebox_info",KIcon::Small), "", parent, "pDirInfo" );
+ QToolTip::add( pDirInfo, i18n("Information about the wildcards.") );
+ defaultDirBox->addWidget( pDirInfo );
+ connect( pDirInfo, SIGNAL(clicked()),
+ this, SLOT(dirInfo())
+ );*/
+// pDirSelect = new KPushButton( iconLoader->loadIcon("folder",KIcon::Small), "", parent, "pDirSelect" );
+// QToolTip::add( pDirSelect, i18n("Choose an output directory") );
+// defaultDirBox->addWidget( pDirSelect );
+// connect( pDirSelect, SIGNAL(clicked()),
+// this, SLOT(selectDir())
+// );
+
+ QHBoxLayout* priorityBox = new QHBoxLayout( box, 6 );
+ QLabel* lPriority = new QLabel( i18n("Process priority of the backends")+":", parent, "lPriority" );
+ priorityBox->addWidget( lPriority );
+ cPriority = new KComboBox( parent, "cPriority" );
+ sPriority += i18n("Normal");
+ sPriority += i18n("Low");
+ cPriority->insertStringList( sPriority );
+ cPriority->setCurrentItem( config->data.general.priority / 10 ); // NOTE that just works for 'normal' and 'low'
+ priorityBox->addWidget( cPriority );
+ connect( cPriority, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* useVFATNamesBox = new QHBoxLayout( box, 6 );
+ cUseVFATNames = new QCheckBox( i18n("Use FAT compatible output file names"), parent, "cUseVFATNames" );
+ QToolTip::add( cUseVFATNames, i18n("Replaces some special characters like \'?\' by \'_\'.") );
+ cUseVFATNames->setChecked( config->data.general.useVFATNames );
+ useVFATNamesBox->addWidget( cUseVFATNames );
+ connect( cUseVFATNames, SIGNAL(toggled(bool)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* conflictHandlingBox = new QHBoxLayout( box, 6 );
+ QLabel* lConflictHandling = new QLabel( i18n("Conflict handling")+":", parent, "lConflictHandling" );
+ conflictHandlingBox->addWidget( lConflictHandling );
+ cConflictHandling = new KComboBox( parent, "cConflictHandling" );
+ QToolTip::add( cConflictHandling, i18n("Do that if the output file already exists") );
+ sConflictHandling += i18n("Generate new file name");
+ sConflictHandling += i18n("Skip file");
+ cConflictHandling->insertStringList( sConflictHandling );
+ cConflictHandling->setCurrentItem( config->data.general.conflictHandling );
+ conflictHandlingBox->addWidget( cConflictHandling );
+ connect( cConflictHandling, SIGNAL(activated(int)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* numFilesBox = new QHBoxLayout( box, 6 );
+ QLabel* lNumFiles = new QLabel( i18n("Number of files to convert at once")+":", parent, "lNumFiles" );
+ numFilesBox->addWidget( lNumFiles );
+ iNumFiles = new KIntSpinBox( 1, 100, 1, config->data.general.numFiles, 10, parent, "iNumFiles" );
+ numFilesBox->addWidget( iNumFiles );
+ connect( iNumFiles, SIGNAL(valueChanged(int)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* updateDelayBox = new QHBoxLayout( box, 6 );
+ QLabel* lUpdateDelay = new QLabel( i18n("Status update delay (time in msec.)")+":", parent, "lUpdateDelay" );
+ updateDelayBox->addWidget( lUpdateDelay );
+ iUpdateDelay = new KIntSpinBox( 100, 5000, 100, config->data.general.updateDelay, 10, parent, "iUpdateDelay" );
+ QToolTip::add( iUpdateDelay, i18n("Update the progress bar in this interval (time in milliseconds)") );
+ updateDelayBox->addWidget( iUpdateDelay );
+ connect( iUpdateDelay, SIGNAL(valueChanged(int)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* askForNewOptionsBox = new QHBoxLayout( box, 6 );
+ cAskForNewOptions = new QCheckBox( i18n("Ask for new options, when adding files from external program"), parent, "cAskForNewOptions" );
+ QToolTip::add( cAskForNewOptions, i18n("If you open a file with soundKonverter and soundKonverter is already running,\nyou can either be asked to define new converting options\nor the current settings from the soundKonverter main window are used.") );
+ cAskForNewOptions->setChecked( config->data.general.askForNewOptions );
+ askForNewOptionsBox->addWidget( cAskForNewOptions );
+ connect( cAskForNewOptions, SIGNAL(toggled(bool)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addSpacing( 5 );
+
+ QHBoxLayout* executeUserScriptBox = new QHBoxLayout( box, 6 );
+ cExecuteUserScript = new QCheckBox( i18n("Execute user script (for advanced users)"), parent, "cAskForNewOptions" );
+ QToolTip::add( cExecuteUserScript, i18n("Executes a script after every finished conversion. Have a look at $KDEDIR/soundkonverter/userscript.sh") );
+ cExecuteUserScript->setChecked( config->data.general.executeUserScript );
+ executeUserScriptBox->addWidget( cExecuteUserScript );
+ connect( cExecuteUserScript, SIGNAL(toggled(bool)),
+ this, SLOT(cfgChanged())
+ );
+
+ box->addStretch();
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+ConfigGeneralPage::~ConfigGeneralPage()
+{}
+
+void ConfigGeneralPage::resetDefaults()
+{
+ cStartTab->setCurrentItem( 0 );
+ cDefaultProfile->setCurrentItem( 0 );
+ cDefaultFormat->setCurrentItem( 0 );
+// lDir->setText( QDir::homeDirPath() + "/soundKonverter/%b/%d - %n - %a - %t" );
+ cPriority->setCurrentItem( 1 );
+ cUseVFATNames->setChecked( true );
+ cConflictHandling->setCurrentItem( 0 );
+ iNumFiles->setValue( 3 );
+ iUpdateDelay->setValue( 500 );
+ cAskForNewOptions->setChecked( true );
+ cExecuteUserScript->setChecked( false );
+
+ cfgChanged();
+}
+
+void ConfigGeneralPage::saveSettings()
+{
+ config->data.general.startTab = cStartTab->currentItem();
+ config->data.general.defaultProfile = cDefaultProfile->currentText();
+ config->data.general.defaultFormat = cDefaultFormat->currentText();
+// config->data.general.defaultOutputDirectory = lDir->text();
+ config->data.general.priority = cPriority->currentItem() * 10; // NOTE that just works for 'normal' and 'low'
+ config->data.general.useVFATNames = cUseVFATNames->isChecked();
+ config->data.general.conflictHandling = cConflictHandling->currentItem();
+ config->data.general.numFiles = iNumFiles->value();
+ config->data.general.updateDelay = iUpdateDelay->value();
+ config->data.general.askForNewOptions = cAskForNewOptions->isChecked();
+ config->data.general.executeUserScript = cExecuteUserScript->isChecked();
+}
+
+int ConfigGeneralPage::profileIndex( const QString &string )
+{
+ return sDefaultProfile.findIndex( string );
+}
+
+int ConfigGeneralPage::formatIndex( const QString &string )
+{
+ return sDefaultFormat.findIndex( string );
+}
+
+// void ConfigGeneralPage::selectDir()
+// {
+// QString startDir = lDir->text();
+// int i = startDir.find( QRegExp("%[aAbBcCdDgGnNpPtTyY]{1,1}") );
+// if( i != -1 ) {
+// i = startDir.findRev( "/", i );
+// startDir = startDir.left( i );
+// }
+//
+// QString directory = KFileDialog::getExistingDirectory( startDir, 0, i18n("Choose an output directory") );
+// if( !directory.isEmpty() ) {
+// QString dir = lDir->text();
+// i = dir.find( QRegExp("%[aAbBcCdDgGnNpPtTyY]{1,1}") );
+// if( i != -1 ) {
+// i = dir.findRev( "/", i );
+// lDir->setText( directory + dir.mid(i) );
+// }
+// else {
+// lDir->setText( directory );
+// }
+// }
+// }
+
+void ConfigGeneralPage::profileChanged()
+{
+ QString last;
+
+ if( cDefaultProfile->currentText() == i18n("Last used") ) {
+ last = cDefaultFormat->currentText();
+ if( last.isEmpty() ) last = config->data.general.defaultFormat;
+ cDefaultFormat->clear();
+ sDefaultFormat = i18n("Last used");
+ cDefaultFormat->insertStringList( sDefaultFormat );
+ cDefaultFormat->setCurrentItem( formatIndex(last) );
+ return;
+ }
+
+ ConversionOptions options = config->getProfile( cDefaultProfile->currentText() );
+ if( !options.encodingOptions.sFormat.isEmpty() ) {
+ last = cDefaultFormat->currentText();
+ cDefaultFormat->clear();
+ sDefaultFormat = options.encodingOptions.sFormat;
+ cDefaultFormat->insertStringList( sDefaultFormat );
+ cDefaultFormat->setCurrentItem( formatIndex(last) );
+ return;
+ }
+
+ if( cDefaultProfile->currentText() == i18n("Very low") ||
+ cDefaultProfile->currentText() == i18n("Low") ||
+ cDefaultProfile->currentText() == i18n("Medium") ||
+ cDefaultProfile->currentText() == i18n("High") ||
+ cDefaultProfile->currentText() == i18n("Very high") ) {
+ last = cDefaultFormat->currentText();
+ if( last.isEmpty() ) last = config->data.general.defaultFormat;
+ cDefaultFormat->clear();
+ sDefaultFormat = i18n("Last used");
+ sDefaultFormat += config->allLossyEncodableFormats();
+ cDefaultFormat->insertStringList( sDefaultFormat );
+ cDefaultFormat->setCurrentItem( formatIndex(last) );
+ }
+ else if( cDefaultProfile->currentText() == i18n("Lossless") ) {
+ last = cDefaultFormat->currentText();
+ if( last.isEmpty() ) last = config->data.general.defaultFormat;
+ cDefaultFormat->clear();
+ sDefaultFormat = i18n("Last used");
+ sDefaultFormat += config->allLosslessEncodableFormats();
+ sDefaultFormat += "wav";
+ cDefaultFormat->insertStringList( sDefaultFormat );
+ cDefaultFormat->setCurrentItem( formatIndex(last) );
+ }
+ else if( cDefaultProfile->currentText() == i18n("Hybrid") ) {
+ last = cDefaultFormat->currentText();
+ if( last.isEmpty() ) last = config->data.general.defaultFormat;
+ cDefaultFormat->clear();
+ sDefaultFormat = i18n("Last used");
+ sDefaultFormat += config->allHybridEncodableFormats();
+ cDefaultFormat->insertStringList( sDefaultFormat );
+ cDefaultFormat->setCurrentItem( formatIndex(last) );
+ }
+}
+
+
diff --git a/src/configgeneralpage.h b/src/configgeneralpage.h
new file mode 100755
index 0000000..270cb47
--- /dev/null
+++ b/src/configgeneralpage.h
@@ -0,0 +1,69 @@
+
+
+#ifndef CONFIGGENERALPAGE_H
+#define CONFIGGENERALPAGE_H
+
+#include <configpagebase.h>
+
+class Config;
+class QCheckBox;
+class KComboBox;
+class KIntSpinBox;
+class KLineEdit;
+class KPushButton;
+
+/**
+ * @short The page for the general configuration
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConfigGeneralPage : public ConfigPageBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ ConfigGeneralPage( Config*, QWidget *parent=0, const char *name=0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~ConfigGeneralPage();
+
+private:
+ KComboBox* cStartTab;
+ KComboBox* cDefaultProfile;
+ QStringList sDefaultProfile;
+ KComboBox* cDefaultFormat;
+ QStringList sDefaultFormat;
+// KLineEdit* lDir;
+ //KPushButton* pDirInfo;
+// KPushButton* pDirSelect;
+ KComboBox* cPriority;
+ QStringList sPriority;
+ QCheckBox* cUseVFATNames;
+ QStringList sConflictHandling;
+ KComboBox* cConflictHandling;
+ KIntSpinBox* iNumFiles;
+ KIntSpinBox* iUpdateDelay;
+ QCheckBox* cAskForNewOptions;
+ QCheckBox* cExecuteUserScript;
+
+ Config* config;
+
+ int profileIndex( const QString& string );
+ int formatIndex( const QString& string );
+
+public slots:
+ void resetDefaults();
+ void saveSettings();
+
+private slots:
+// void selectDir();
+ void profileChanged();
+
+};
+
+#endif // CONFIGGENERALPAGE_H
+
diff --git a/src/configpagebase.cpp b/src/configpagebase.cpp
new file mode 100755
index 0000000..1aedea5
--- /dev/null
+++ b/src/configpagebase.cpp
@@ -0,0 +1,20 @@
+
+#include "configpagebase.h"
+
+ConfigPageBase::ConfigPageBase( QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{}
+
+ConfigPageBase::~ConfigPageBase()
+{}
+
+void ConfigPageBase::resetDefaults()
+{}
+
+void ConfigPageBase::saveSettings()
+{}
+
+void ConfigPageBase::cfgChanged()
+{
+ emit configChanged();
+}
diff --git a/src/configpagebase.h b/src/configpagebase.h
new file mode 100755
index 0000000..802bb2d
--- /dev/null
+++ b/src/configpagebase.h
@@ -0,0 +1,38 @@
+
+
+#ifndef CONFIGPAGEBASE_H
+#define CONFIGPAGEBASE_H
+
+#include <qwidget.h>
+
+/**
+ * @short The base for all pages of the config dialog
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConfigPageBase : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ ConfigPageBase( QWidget *parent=0, const char *name=0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~ConfigPageBase();
+
+public slots:
+ virtual void resetDefaults();
+ virtual void saveSettings();
+ void cfgChanged();
+
+signals:
+ void configChanged();
+
+};
+
+#endif // CONFIGPAGEBASE_H
+
diff --git a/src/configpluginspage.cpp b/src/configpluginspage.cpp
new file mode 100755
index 0000000..a6bc560
--- /dev/null
+++ b/src/configpluginspage.cpp
@@ -0,0 +1,639 @@
+
+#include "configpluginspage.h"
+#include "config.h"
+#include "convertpluginloader.h"
+#include "replaygainpluginloader.h"
+#include "ripperpluginloader.h"
+
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qcheckbox.h>
+// #include <qevent.h>
+// #include <qdragobject.h>
+#include <qtooltip.h>
+#include <qlocale.h>
+// #include <qurl.h>
+
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kio/job.h>
+#include <kstandarddirs.h>
+#include <kmessagebox.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <klistbox.h>
+//#include <kurl.h>
+
+ConfigPluginsPage::ConfigPluginsPage( Config* _config, QWidget* parent, const char* name )
+ : ConfigPageBase( parent, name )
+{
+ config = _config;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ QVBoxLayout* box = new QVBoxLayout( parent, 0, 6 );
+
+ QLabel* lPluginsLabel = new QLabel( i18n("Installed plugins")+":", parent, "lPluginsLabel" );
+ box->addWidget( lPluginsLabel );
+ QHBoxLayout* pluginsBox = new QHBoxLayout( box );
+ lPlugins = new KListBox( parent, "lPlugins" );
+ pluginsBox->addWidget(lPlugins);
+ connect( lPlugins, SIGNAL(highlighted(int)),
+ this, SLOT(pluginsSelectionChanged(int))
+ );
+ refreshPlugins();
+
+ QVBoxLayout* pluginsRightBox = new QVBoxLayout( pluginsBox );
+ pAddPlugin = new KPushButton( iconLoader->loadIcon("add",KIcon::Small), i18n("Add ..."), parent, "pAddPlugin" );
+ pluginsRightBox->addWidget( pAddPlugin );
+ connect( pAddPlugin, SIGNAL(clicked()),
+ this, SLOT(getPlugin())
+ );
+ pRemovePlugin = new KPushButton( iconLoader->loadIcon("remove",KIcon::Small), i18n("Remove"), parent, "pRemovePlugin" );
+ pRemovePlugin->setEnabled( false );
+ pluginsRightBox->addWidget( pRemovePlugin );
+ connect( pRemovePlugin, SIGNAL(clicked()),
+ this, SLOT(removePlugin())
+ );
+ pluginsRightBox->addStretch();
+ pAboutPlugin = new KPushButton( iconLoader->loadIcon("messagebox_info",KIcon::Small), i18n("About"), parent, "pAboutPlugin" );
+ pAboutPlugin->setEnabled( false );
+ pluginsRightBox->addWidget( pAboutPlugin );
+ connect( pAboutPlugin, SIGNAL(clicked()),
+ this, SLOT(aboutPlugin())
+ );
+/* NOTE kaligames.de is down
+ box->addSpacing( 5 );
+
+ QLabel* lOnlinePluginsLabel = new QLabel( i18n("Available plugins")+":", parent, "lOnlinePluginsLabel" );
+ box->addWidget( lOnlinePluginsLabel );
+ QHBoxLayout* onlinePluginsBox = new QHBoxLayout( box );
+ lOnlinePlugins = new KListBox( parent, "lOnlinePlugins" );
+ onlinePluginsBox->addWidget( lOnlinePlugins );
+ connect( lOnlinePlugins, SIGNAL(highlighted(int)),
+ this, SLOT(onlinePluginsSelectionChanged(int))
+ );
+ QVBoxLayout* onlinePluginsRightBox = new QVBoxLayout( onlinePluginsBox );
+ pRefreshOnlinePlugins = new KPushButton( iconLoader->loadIcon("reload",KIcon::Small), i18n("Refresh"), parent, "pRefreshOnlinePlugins" );
+ QToolTip::add( pRefreshOnlinePlugins, i18n("Download the latest list of available plugins.") );
+ onlinePluginsRightBox->addWidget( pRefreshOnlinePlugins );
+ connect( pRefreshOnlinePlugins, SIGNAL(clicked()),
+ this, SLOT(refreshOnlinePlugins())
+ );
+ // TODO upgrade button
+// pUpgradeOnlinePlugins = new KPushButton( iconLoader->loadIcon("filesave",KIcon::Small), i18n("Upgrade"), parent, "pUpgradeOnlinePlugins" );
+// pUpgradeOnlinePlugins->setEnabled( false );
+// QToolTip::add( pUpgradeOnlinePlugins, i18n("Download all plugins and install them into the soundKonverter directory.") );
+// onlinePluginsRightBox->addWidget( pUpgradeOnlinePlugins );
+// connect(pInstallAllOnlinePlugins,SIGNAL(clicked()),this,SLOT(upgradeOnlinePlugins()));
+ onlinePluginsRightBox->addStretch();
+ pInstallOnlinePlugin = new KPushButton( iconLoader->loadIcon("filesave",KIcon::Small), i18n("Install"), parent, "pInstallOnlinePlugin" );
+ pInstallOnlinePlugin->setEnabled( false );
+ QToolTip::add( pInstallOnlinePlugin, i18n("Download the selected plugin and install it into the soundKonverter directory.") );
+ onlinePluginsRightBox->addWidget( pInstallOnlinePlugin );
+ connect( pInstallOnlinePlugin, SIGNAL(clicked()),
+ this, SLOT(getOnlinePlugin())
+ );
+ pAboutOnlinePlugin = new KPushButton( iconLoader->loadIcon("messagebox_info",KIcon::Small), i18n("About"), parent, "pAboutOnlinePlugin" );
+ pAboutOnlinePlugin->setEnabled( false );
+ onlinePluginsRightBox->addWidget( pAboutOnlinePlugin );
+ connect( pAboutOnlinePlugin, SIGNAL(clicked()),
+ this, SLOT(aboutOnlinePlugin())
+ );
+ cCheckOnlinePlugins = new QCheckBox( i18n("Check for new plugins on every startup"), parent, "cCheckOnlinePlugins" );
+ cCheckOnlinePlugins->setChecked( config->data.plugins.checkForUpdates );
+ box->addWidget( cCheckOnlinePlugins );
+ connect( cCheckOnlinePlugins, SIGNAL(toggled(bool)),
+ this, SLOT(cfgChanged())
+ );
+
+// box->addStretch();
+
+ // delete the icon loader object
+ delete iconLoader;
+
+ if( config->data.plugins.checkForUpdates && config->onlinePluginsChanged ) {
+ // NOTE copied from below
+ QString line;
+ bool add;
+ QFile file( locateLocal("data","soundkonverter/pluginlist.txt") );
+ if( file.open(IO_ReadOnly) ) {
+ QTextStream stream( &file );
+ while( !stream.atEnd() ) {
+ line = stream.readLine(); // line of text excluding '\n'
+ line.replace( "&amp;", "&" );
+ line.replace( "&auml;", "ä" );
+ line.replace( "&Auml;", "Ä" );
+ line.replace( "&ouml;", "ö" );
+ line.replace( "&Ouml;", "Ö" );
+ line.replace( "&uuml;", "ü" );
+ line.replace( "&Uuml;", "Ü" );
+ line.replace( "&szlig;", "ß" );
+
+ add = true;
+ for( uint i=0; i<lPlugins->count(); i++ ) {
+ if( lPlugins->text(i) == line ) {
+ add = false;
+ break;
+ }
+ }
+ if( add ) lOnlinePlugins->insertItem( line );
+ }
+ file.close();
+ }
+ }
+*/
+}
+
+ConfigPluginsPage::~ConfigPluginsPage()
+{}
+
+void ConfigPluginsPage::resetDefaults()
+{
+// cCheckOnlinePlugins->setChecked( false );
+
+// cfgChanged();
+}
+
+void ConfigPluginsPage::saveSettings()
+{
+// config->data.plugins.checkForUpdates = cCheckOnlinePlugins->isChecked();
+}
+
+void ConfigPluginsPage::pluginsSelectionChanged( int index )
+{
+ QString name = lPlugins->text( index );
+
+ QValueList<ConvertPlugin*> converters = config->allConverters();
+ for( QValueList<ConvertPlugin*>::Iterator it = converters.begin(); it != converters.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ QFileInfo file( (*it)->filePathName );
+ if( file.isWritable() ) pRemovePlugin->setEnabled( true );
+ else pRemovePlugin->setEnabled( false );
+ break;
+ }
+ }
+
+ QValueList<ReplayGainPlugin*> replaygains = config->allReplayGains();
+ for( QValueList<ReplayGainPlugin*>::Iterator it = replaygains.begin(); it != replaygains.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ QFileInfo file( (*it)->filePathName );
+ if( file.isWritable() ) pRemovePlugin->setEnabled( true );
+ else pRemovePlugin->setEnabled( false );
+ break;
+ }
+ }
+
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ QFileInfo file( (*it)->filePathName );
+ if( file.isWritable() ) pRemovePlugin->setEnabled( true );
+ else pRemovePlugin->setEnabled( false );
+ break;
+ }
+ }
+
+ pAboutPlugin->setEnabled( true );
+}
+
+void ConfigPluginsPage::refreshPlugins()
+{
+ lPlugins->clear();
+
+ QValueList<ConvertPlugin*> converters = config->allConverters();
+ for( QValueList<ConvertPlugin*>::Iterator it = converters.begin(); it != converters.end(); ++it ) {
+ lPlugins->insertItem( (*it)->info.name + " v. " + QString::number((*it)->info.version) );
+ //lPlugins->insertItem( i18n("%1, Version: %2").arg((*it)->info.name).arg((*it)->info.version) );
+ }
+
+ QValueList<ReplayGainPlugin*> replaygains = config->allReplayGains();
+ for( QValueList<ReplayGainPlugin*>::Iterator it = replaygains.begin(); it != replaygains.end(); ++it ) {
+ lPlugins->insertItem( (*it)->info.name + " v. " + QString::number((*it)->info.version) );
+ //lPlugins->insertItem( i18n("%1, Version: %2").arg((*it)->info.name).arg((*it)->info.version) );
+ }
+
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it ) {
+ lPlugins->insertItem( (*it)->info.name + " v. " + QString::number((*it)->info.version) );
+ //lPlugins->insertItem( i18n("%1, Version: %2").arg((*it)->info.name).arg((*it)->info.version) );
+ }
+}
+
+void ConfigPluginsPage::getPlugin()
+{
+ QString url = KFileDialog::getOpenFileName( QDir::homeDirPath(), i18n("*.soundkonverter.xml|Plugins (*.soundkonverter.xml)"), this, i18n("Choose a plugin to add!") );
+ if( !url.isEmpty() ) {
+ QString filePathName = KURL::decode_string( url );
+ QString fileName = filePathName.right( filePathName.length() - filePathName.findRev("/") );
+ getPluginFilePathName = locateLocal("data","soundkonverter/plugins/") + fileName;
+ getPluginJob = KIO::file_copy( url, getPluginFilePathName, -1, true, false, false );
+ connect( getPluginJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(getPluginFinished(KIO::Job*))
+ );
+ }
+}
+
+void ConfigPluginsPage::getPluginFinished( KIO::Job* job )
+{
+ if( job->error() == 0 ) {
+ ConvertPluginLoader* convertPluginLoader = new ConvertPluginLoader();
+ ReplayGainPluginLoader* replaygainPluginLoader = new ReplayGainPluginLoader();
+ RipperPluginLoader* ripperPluginLoader = new RipperPluginLoader();
+
+ if( convertPluginLoader->verifyFile(getPluginFilePathName) == -1 &&
+ replaygainPluginLoader->verifyFile(getPluginFilePathName) == -1 &&
+ ripperPluginLoader->verifyFile(getPluginFilePathName) == -1 )
+ {
+ KIO::del( getPluginFilePathName, false, false );
+ KMessageBox::error( this,
+ i18n("The plugin could not be installed. Please ensure that you have selected a valid soundKonverter plugin file."),
+ i18n("Error while installing plugin") );
+ }
+ else
+ {
+ // TODO reload plugins without restart
+// ConvertPlugin* plugin = convertPluginLoader->loadFile( getPluginFilePathName );
+// if( plugin->info.version != -1 ) {
+// lPlugins->insertItem( plugin->info.name + " v. " + QString::number(plugin->info.version) + " (" + i18n("restart necessary") + ")" );
+// }
+// else {
+// delete plugin;
+// ConvertPlugin* plugin = convertPluginLoader->loadFile( getPluginFilePathName );
+// if( plugin->info.version != -1 ) {
+// lPlugins->insertItem( plugin->info.name + " v. " + QString::number(plugin->info.version) + " (" + i18n("restart necessary") + ")" );
+// }
+// else {
+// delete plugin;
+// ConvertPlugin* plugin = convertPluginLoader->loadFile( getPluginFilePathName );
+// if( plugin->info.version != -1 ) {
+// lPlugins->insertItem( plugin->info.name + " v. " + QString::number(plugin->info.version) + " (" + i18n("restart necessary") + ")" );
+// }
+// }
+// }
+// delete plugin;
+ KMessageBox::information( this,
+ i18n("The plugin was installed successfully. Please restart soundKonverter in order to activate it."),
+ i18n("Plugin successfully installed") );
+ //config->reloadPlugins();
+ //refreshPlugins();
+ //emit rescanForBackends();
+ //emit reloadEnDecoderPage();
+ }
+
+ delete convertPluginLoader;
+ delete replaygainPluginLoader;
+ delete ripperPluginLoader;
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin could not be installed. Please ensure that you have write permission on your whole user directory."),
+ i18n("Error while installing plugin") );
+ }
+}
+
+void ConfigPluginsPage::removePlugin()
+{
+ // TODO reload plugins without restart
+
+ QString name = lPlugins->currentText();
+
+ QValueList<ConvertPlugin*> converters = config->allConverters();
+ for( QValueList<ConvertPlugin*>::Iterator it = converters.begin(); it != converters.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ QFile file( (*it)->filePathName );
+ if( file.remove() ) {
+ lPlugins->removeItem( lPlugins->currentItem() );
+ KMessageBox::information( this,
+ i18n("The plugin was removed successfully. Please restart soundKonverter in order to deactivate it."),
+ i18n("Plugin successfully removed") );
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin could not be removed. Please ensure that you have write permission on your whole user directory."),
+ i18n("Error while removing plugin") );
+ }
+ break;
+ }
+ }
+
+ QValueList<ReplayGainPlugin*> replaygains = config->allReplayGains();
+ for( QValueList<ReplayGainPlugin*>::Iterator it = replaygains.begin(); it != replaygains.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ QFile file( (*it)->filePathName );
+ if( file.remove() ) {
+ lPlugins->removeItem( lPlugins->currentItem() );
+ KMessageBox::information( this,
+ i18n("The plugin was removed successfully. Please restart soundKonverter in order to deactivate it."),
+ i18n("Plugin successfully removed") );
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin could not be removed. Please ensure that you have write permission on your whole user directory."),
+ i18n("Error while removing plugin") );
+ }
+ break;
+ }
+ }
+
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ QFile file( (*it)->filePathName );
+ if( file.remove() ) {
+ lPlugins->removeItem( lPlugins->currentItem() );
+ KMessageBox::information( this,
+ i18n("The plugin was removed successfully. Please restart soundKonverter in order to deactivate it."),
+ i18n("Plugin successfully removed") );
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin could not be removed. Please ensure that you have write permission on your whole user directory."),
+ i18n("Error while removing plugin") );
+ }
+ break;
+ }
+ }
+
+/* backendPlugins.remove(lPlugins->currentText());
+ replayGainPlugins.remove(lPlugins->currentText());
+ backendPlugins.reload();
+ replayGainPlugins.reload();
+ lPlugins->clear();
+ lPlugins->insertStringList(backendPlugins.loadedPlugins());
+ lPlugins->insertStringList(replayGainPlugins.loadedPlugins());
+ emit rescanForBackends();
+ emit reloadEnDecoderPage();*/
+}
+
+void ConfigPluginsPage::aboutPlugin()
+{
+ // TODO add link support
+
+ QString name = lPlugins->currentText();
+
+ QValueList<ConvertPlugin*> converters = config->allConverters();
+ for( QValueList<ConvertPlugin*>::Iterator it = converters.begin(); it != converters.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ KMessageBox::information( this,
+ i18n((*it)->info.about) + "\n" +
+ i18n("Version") + ": " + QString::number((*it)->info.version) + "\n" +
+ i18n("Author") + ": " + (*it)->info.author,
+ i18n("About") + ": " + (*it)->info.name );
+ break;
+ }
+ }
+
+ QValueList<ReplayGainPlugin*> replaygains = config->allReplayGains();
+ for( QValueList<ReplayGainPlugin*>::Iterator it = replaygains.begin(); it != replaygains.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ KMessageBox::information( this,
+ i18n((*it)->info.about) + "\n" +
+ i18n("Version") + ": " + QString::number((*it)->info.version) + "\n" +
+ i18n("Author") + ": " + (*it)->info.author,
+ i18n("About") + ": " + (*it)->info.name );
+ break;
+ }
+ }
+
+ QValueList<RipperPlugin*> rippers = config->allRippers();
+ for( QValueList<RipperPlugin*>::Iterator it = rippers.begin(); it != rippers.end(); ++it ) {
+ if( name == (*it)->info.name + " v. " + QString::number((*it)->info.version) ) {
+ KMessageBox::information( this,
+ i18n((*it)->info.about) + "\n" +
+ i18n("Version") + ": " + QString::number((*it)->info.version) + "\n" +
+ i18n("Author") + ": " + (*it)->info.author,
+ i18n("About") + ": " + (*it)->info.name );
+ break;
+ }
+ }
+}
+
+void ConfigPluginsPage::onlinePluginsSelectionChanged( int index )
+{
+ if( lOnlinePlugins->currentText() != i18n("No new plugins available!") ) {
+ pInstallOnlinePlugin->setEnabled( true );
+ pAboutOnlinePlugin->setEnabled( true );
+ }
+ else {
+ pInstallOnlinePlugin->setEnabled( false );
+ pAboutOnlinePlugin->setEnabled( false );
+ }
+}
+
+void ConfigPluginsPage::refreshOnlinePlugins()
+{
+ pRefreshOnlinePlugins->setEnabled( false );
+
+ refreshOnlinePluginsJob = KIO::file_copy( "http://kaligames.de/downloads/soundkonverter/plugins/download.php?version=" + QString::number(config->data.app.configVersion),
+ locateLocal("data","soundkonverter/pluginlist.txt"), -1, true, false, false );
+ connect( refreshOnlinePluginsJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(refreshOnlinePluginsFinished(KIO::Job*))
+ );
+}
+
+void ConfigPluginsPage::refreshOnlinePluginsFinished( KIO::Job* job )
+{
+ if( job->error() == 0 ) {
+ lOnlinePlugins->clear();
+
+ QString line;
+ bool add;
+ QFile file( locateLocal("data","soundkonverter/pluginlist.txt") );
+ if( file.open(IO_ReadOnly) ) {
+ QTextStream stream( &file );
+ while( !stream.atEnd() ) {
+ line = stream.readLine(); // line of text excluding '\n'
+ line.replace( "&amp;", "&" );
+ line.replace( "&auml;", "ä" );
+ line.replace( "&Auml;", "Ä" );
+ line.replace( "&ouml;", "ö" );
+ line.replace( "&Ouml;", "Ö" );
+ line.replace( "&uuml;", "ü" );
+ line.replace( "&Uuml;", "Ü" );
+ line.replace( "&szlig;", "ß" );
+
+ add = true;
+ for( uint i=0; i<lPlugins->count(); i++ ) {
+ if( lPlugins->text(i) == line ) {
+ add = false;
+ break;
+ }
+ }
+ if( add ) lOnlinePlugins->insertItem( line );
+ }
+ file.close();
+ }
+
+ if( lOnlinePlugins->count() == 0 ) {
+ lOnlinePlugins->insertItem( i18n("No new plugins available!") );
+ }
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin list could not be downloaded. Please ensure, that your internet connection works correct.\nMaybe our server is busy at the moment, please try it again later."),
+ i18n("Error while loading plugin list") );
+ }
+
+ pRefreshOnlinePlugins->setEnabled( true );
+}
+
+void ConfigPluginsPage::getOnlinePlugin()
+{
+ pInstallOnlinePlugin->setEnabled( false );
+ QString name;
+
+ for( uint i=0; i<lOnlinePlugins->count(); i++ ) {
+ if( lOnlinePlugins->isSelected(i) ) {
+ name = lOnlinePlugins->text( i );
+ lOnlinePlugins->removeItem( i );
+ break;
+ }
+ }
+
+ name.replace( "&", "&amp;" );
+ name.replace( "ä", "&auml;" );
+ name.replace( "Ä", "&Auml;" );
+ name.replace( "ö", "&ouml;" );
+ name.replace( "Ö", "&Ouml;" );
+ name.replace( "ü", "&uuml;" );
+ name.replace( "Ü", "&Uuml;" );
+ name.replace( "ß", "&szlig;" );
+ KURL::encode_string( name );
+ getOnlinePluginJob = KIO::file_copy( "http://kaligames.de/downloads/soundkonverter/plugins/getfile.php?version=" + QString::number(config->data.app.configVersion) + "&file=" + name,
+ locateLocal("data","soundkonverter/plugins/newplugin.xml"), -1, true, false, false );
+ connect( getOnlinePluginJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(getOnlinePluginFinished(KIO::Job*))
+ );
+}
+
+void ConfigPluginsPage::getOnlinePluginFinished( KIO::Job* job )
+{
+ if( job->error() == 0 ) {
+ QString name;
+ QString line;
+ QFile file( locateLocal("data","soundkonverter/plugins/newplugin.xml") );
+ if( file.open(IO_ReadOnly) ) {
+ QTextStream stream( &file );
+ name = stream.readLine(); // read the file name from the top of the file
+ getPluginFilePathName = locateLocal("data","soundkonverter/plugins/") + name;
+ QFile newFile( getPluginFilePathName );
+ if( newFile.open(IO_WriteOnly) ) {
+ QTextStream newStream( &newFile );
+ while( !stream.atEnd() ) {
+ line = stream.readLine(); // line of text excluding '\n'
+ newStream << line << "\n";
+ }
+ newFile.close();
+ }
+ file.close();
+ }
+ file.remove();
+
+ ConvertPluginLoader* convertPluginLoader = new ConvertPluginLoader();
+ ReplayGainPluginLoader* replaygainPluginLoader = new ReplayGainPluginLoader();
+ RipperPluginLoader* ripperPluginLoader = new RipperPluginLoader();
+
+ if( convertPluginLoader->verifyFile(getPluginFilePathName) == -1 &&
+ replaygainPluginLoader->verifyFile(getPluginFilePathName) == -1 &&
+ ripperPluginLoader->verifyFile(getPluginFilePathName) == -1 )
+ {
+ KIO::del( getPluginFilePathName, false, false );
+ KMessageBox::error( this,
+ i18n("The plugin could not be installed. Please ensure that you have selected a valid soundKonverter plugin file."),
+ i18n("Error while installing plugin") );
+ }
+ else
+ {
+ // TODO reload plugins without restart
+// ConvertPlugin* plugin = convertPluginLoader->loadFile( getPluginFilePathName );
+// if( plugin->info.version != -1 ) {
+// lPlugins->insertItem( plugin->info.name + " v. " + QString::number(plugin->info.version) + " (" + i18n("restart necessary") + ")" );
+// }
+// else {
+// delete plugin;
+// ConvertPlugin* plugin = convertPluginLoader->loadFile( getPluginFilePathName );
+// if( plugin->info.version != -1 ) {
+// lPlugins->insertItem( plugin->info.name + " v. " + QString::number(plugin->info.version) + " (" + i18n("restart necessary") + ")" );
+// }
+// else {
+// delete plugin;
+// ConvertPlugin* plugin = convertPluginLoader->loadFile( getPluginFilePathName );
+// if( plugin->info.version != -1 ) {
+// lPlugins->insertItem( plugin->info.name + " v. " + QString::number(plugin->info.version) + " (" + i18n("restart necessary") + ")" );
+// }
+// }
+// }
+// delete plugin;
+ KMessageBox::information( this,
+ i18n("The plugin was installed successfully. Please restart soundKonverter in order to activate it."),
+ i18n("Plugin successfully installed") );
+ //config->reloadPlugins();
+ //refreshPlugins();
+ //emit rescanForBackends();
+ //emit reloadEnDecoderPage();
+ }
+
+ delete convertPluginLoader;
+ delete replaygainPluginLoader;
+ delete ripperPluginLoader;
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin could not be installed. Please ensure that you have write permission on your whole user directory."),
+ i18n("Error while installing plugin") );
+ }
+}
+
+void ConfigPluginsPage::aboutOnlinePlugin()
+{
+ pAboutOnlinePlugin->setEnabled( false );
+
+ QString name = lOnlinePlugins->currentText();
+ name.replace( "&", "&amp;" );
+ name.replace( "ä", "&auml;" );
+ name.replace( "Ä", "&Auml;" );
+ name.replace( "ö", "&ouml;" );
+ name.replace( "Ö", "&Ouml;" );
+ name.replace( "ü", "&uuml;" );
+ name.replace( "Ü", "&Uuml;" );
+ name.replace( "ß", "&szlig;" );
+ KURL::encode_string( name );
+ aboutOnlinePluginJob = KIO::file_copy( "http://kaligames.de/downloads/soundkonverter/plugins/info.php?file=" + name + "&lang=" + QLocale::languageToString(QLocale::system().language()),
+ locateLocal("data","soundkonverter/plugin_info.txt"), -1, true, false, false );
+ connect( aboutOnlinePluginJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(aboutOnlinePluginFinished(KIO::Job*))
+ );
+}
+
+void ConfigPluginsPage::aboutOnlinePluginFinished( KIO::Job* job )
+{
+ if( job->error() == 0 ) {
+ QString name = lOnlinePlugins->currentText();
+
+ QFile file( locateLocal("data","soundkonverter/plugin_info.txt") );
+ if( file.open(IO_ReadOnly) ) {
+ QTextStream stream( &file );
+ QString data = stream.readLine();
+ KMessageBox::information( this, i18n(data), i18n("About") + ": " + name,
+ QString::null, KMessageBox::Notify | KMessageBox::AllowLink );
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin info could not be downloaded. Please ensure, that your internet connection works correctly."),
+ i18n("Error while loading plugin info") );
+ }
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("The plugin info could not be downloaded. Please ensure, that your internet connection works correctly."),
+ i18n("Error while loading plugin info") );
+ }
+
+ pAboutOnlinePlugin->setEnabled( true );
+}
+
+
diff --git a/src/configpluginspage.h b/src/configpluginspage.h
new file mode 100755
index 0000000..2cca5ed
--- /dev/null
+++ b/src/configpluginspage.h
@@ -0,0 +1,82 @@
+
+#ifndef CONFIGPLUGINSPAGE_H
+#define CONFIGPLUGINSPAGE_H
+
+#include <configpagebase.h>
+
+#include <kio/jobclasses.h>
+
+class Config;
+class KPushButton;
+class KListBox;
+class QCheckBox;
+
+/**
+ * @short The page for configuring the plugins
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConfigPluginsPage : public ConfigPageBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ ConfigPluginsPage( Config*, QWidget *parent=0, const char *name=0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~ConfigPluginsPage();
+
+private:
+ KListBox* lPlugins;
+ KPushButton* pAddPlugin;
+ KIO::FileCopyJob* getPluginJob;
+ KPushButton* pRemovePlugin;
+ KPushButton* pAboutPlugin;
+ KListBox* lOnlinePlugins;
+ KPushButton* pRefreshOnlinePlugins;
+ KIO::FileCopyJob* refreshOnlinePluginsJob;
+ KPushButton* pInstallOnlinePlugin;
+ KIO::FileCopyJob* getOnlinePluginJob;
+ KPushButton* pUpgradeOnlinePlugins;
+ KPushButton* pAboutOnlinePlugin;
+ KIO::FileCopyJob* aboutOnlinePluginJob;
+ QCheckBox* cCheckOnlinePlugins;
+
+ QString getPluginFilePathName;
+
+ Config* config;
+
+public slots:
+ void resetDefaults();
+ void saveSettings();
+
+// signals:
+// void configChanged();
+// void recalcEnDecoderPage();
+// void reloadEnDecoderPage();
+// void rescanForBackends();
+
+private slots:
+ void pluginsSelectionChanged( int );
+ void refreshPlugins();
+ void getPlugin();
+ void getPluginFinished( KIO::Job* );
+ void removePlugin();
+ void aboutPlugin();
+
+ void onlinePluginsSelectionChanged( int );
+ void refreshOnlinePlugins();
+ void refreshOnlinePluginsFinished( KIO::Job* );
+ void getOnlinePlugin();
+ void getOnlinePluginFinished( KIO::Job* );
+ void aboutOnlinePlugin();
+ void aboutOnlinePluginFinished( KIO::Job* );
+
+};
+
+#endif // CONFIGPLUGINSPAGE_H
+
diff --git a/src/conversionoptions.cpp b/src/conversionoptions.cpp
new file mode 100755
index 0000000..2c5ad22
--- /dev/null
+++ b/src/conversionoptions.cpp
@@ -0,0 +1,86 @@
+
+#include "conversionoptions.h"
+
+ConversionOptions::ConversionOptions() // TODO reset all values
+{}
+
+ConversionOptions::~ConversionOptions()
+{}
+
+bool ConversionOptions::nearlyEqual( const ConversionOptions& other )
+{
+ if( encodingOptions.sFormat != other.encodingOptions.sFormat ||
+ encodingOptions.sQualityMode != other.encodingOptions.sQualityMode ||
+ encodingOptions.iQuality != other.encodingOptions.iQuality ||
+ encodingOptions.sBitrateMode != other.encodingOptions.sBitrateMode ||
+ encodingOptions.bBitrateRange != other.encodingOptions.bBitrateRange ||
+ encodingOptions.samplingRate.bEnabled != other.encodingOptions.samplingRate.bEnabled ||
+ encodingOptions.channels.bEnabled != other.encodingOptions.channels.bEnabled ||
+ encodingOptions.replaygain.bEnabled != other.encodingOptions.replaygain.bEnabled ||
+ encodingOptions.sInOutFiles != other.encodingOptions.sInOutFiles ) {
+ return false;
+ }
+ if( encodingOptions.bBitrateRange ) {
+ if( encodingOptions.iMinBitrate != other.encodingOptions.iMinBitrate ||
+ encodingOptions.iMaxBitrate != other.encodingOptions.iMaxBitrate ) {
+ return false;
+ }
+ }
+ if( encodingOptions.samplingRate.bEnabled ) {
+ if( encodingOptions.samplingRate.iSamplingRate != other.encodingOptions.samplingRate.iSamplingRate ) {
+ return false;
+ }
+ }
+ if( encodingOptions.channels.bEnabled ) {
+ if( encodingOptions.channels.sChannels != other.encodingOptions.channels.sChannels ) {
+ return false;
+ }
+ }
+
+ if( outputOptions.mode != other.outputOptions.mode ||
+ outputOptions.directory != other.outputOptions.directory ) {
+ return false;
+ }
+
+ return true;
+}
+
+/*bool ConversionOptions::nearlyEqual( ConversionOptions* other )
+{
+ if( encodingOptions.sFormat != other->encodingOptions.sFormat ||
+ encodingOptions.sQualityMode != other->encodingOptions.sQualityMode ||
+ encodingOptions.iQuality != other->encodingOptions.iQuality ||
+ encodingOptions.sBitrateMode != other->encodingOptions.sBitrateMode ||
+ encodingOptions.bBitrateRange != other->encodingOptions.bBitrateRange ||
+ encodingOptions.samplingRate.bEnabled != other->encodingOptions.samplingRate.bEnabled ||
+ encodingOptions.channels.bEnabled != other->encodingOptions.channels.bEnabled ||
+ encodingOptions.replaygain.bEnabled != other->encodingOptions.replaygain.bEnabled ||
+ encodingOptions.sInOutFiles != other->encodingOptions.sInOutFiles ) {
+ return false;
+ }
+ if( encodingOptions.bBitrateRange ) {
+ if( encodingOptions.iMinBitrate != other->encodingOptions.iMinBitrate ||
+ encodingOptions.iMaxBitrate != other->encodingOptions.iMaxBitrate ) {
+ return false;
+ }
+ }
+ if( encodingOptions.samplingRate.bEnabled ) {
+ if( encodingOptions.samplingRate.iSamplingRate != other->encodingOptions.samplingRate.iSamplingRate ) {
+ return false;
+ }
+ }
+ if( encodingOptions.channels.bEnabled ) {
+ if( encodingOptions.channels.sChannels != other->encodingOptions.channels.sChannels ) {
+ return false;
+ }
+ }
+
+ if( outputOptions.mode != other->outputOptions.mode ||
+ outputOptions.directory != other->outputOptions.directory ) {
+ return false;
+ }
+
+ return true;
+}*/
+
+
diff --git a/src/conversionoptions.h b/src/conversionoptions.h
new file mode 100755
index 0000000..b1e9d9a
--- /dev/null
+++ b/src/conversionoptions.h
@@ -0,0 +1,65 @@
+
+
+#ifndef CONVERSIONOPTIONS_H
+#define CONVERSIONOPTIONS_H
+
+#include "outputdirectory.h"
+
+#include <qstring.h>
+
+/**
+ * @short Here the options for the conversion process can be stored
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConversionOptions
+{
+public:
+ struct EncodingOptions {
+ QString sFormat; // output format
+ QString sQualityMode; // which mode are we using? quality, bitrate, lossless? i18n()!!!
+ int iQuality; // the encoding quality / bitrate
+ QString sBitrateMode; // when using bitrate mode, which? abr, cbr?
+ bool bBitrateRange; // enable bitrate range?
+ int iMinBitrate, iMaxBitrate; // when using bitrate range
+ struct SamplingRateOptions { // special options, when sampling rate is enabled
+ bool bEnabled;
+ int iSamplingRate;
+ } samplingRate;
+ struct ChannelsOptions { // special options, when channels is enabled
+ bool bEnabled;
+ QString sChannels;
+ } channels;
+ struct ReplaygainOptions { // special options, when replaygain is enabled
+ bool bEnabled;
+ } replaygain;
+ QString sInOutFiles; // could be called 'user defined parameter'
+ // but it is analog to the in_out_files option in the plugins
+ };
+ struct OutputOptions {
+ OutputDirectory::Mode mode;
+ QString directory;
+ };
+
+ /**
+ * Constructor
+ */
+ ConversionOptions();
+
+ /**
+ * Destructor
+ */
+ virtual ~ConversionOptions();
+
+ /**
+ * Checks whether this ConversionOptions and @p other are equal, except the 'filePathName' and 'outputFilePathName' members
+ */
+ bool nearlyEqual( const ConversionOptions& other );
+
+ QString filePathName; // the path and name of the file
+ QString outputFilePathName; // if the user wants to change the output directory/file name per file!
+ EncodingOptions encodingOptions; // what shall we do with the file?
+ OutputOptions outputOptions; // where to save the file?
+};
+
+#endif // CONVERSIONOPTIONS_H
diff --git a/src/convert.cpp b/src/convert.cpp
new file mode 100755
index 0000000..ce435ab
--- /dev/null
+++ b/src/convert.cpp
@@ -0,0 +1,1615 @@
+
+#include "convert.h"
+//#include "conversionoptions.h"
+#include "convertpluginloader.h"
+#include "replaygainpluginloader.h"
+#include "replaygain.h"
+#include "ripperpluginloader.h"
+#include "config.h"
+#include "tagengine.h"
+#include "cdmanager.h"
+#include "logger.h"
+#include "filelist.h"
+#include "replaygainscanner.h"
+
+#include <math.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+//#include <kdebug.h>
+#include <ktempfile.h>
+#include <kio/job.h>
+//#include <kprocess.h>
+#include <kstandarddirs.h>
+
+#include <qfile.h>
+#include <qtimer.h>
+
+ConvertItem::ConvertItem()
+{
+ // create a new item with the file list item pointer set to zero
+ ConvertItem( (FileListItem*)0 );
+}
+
+ConvertItem::ConvertItem( FileListItem* item )
+{
+ fileListItem = item;
+ getTime = getCorrectionTime = ripTime = decodeTime = encodeTime = replaygainTime = 0;
+}
+
+ConvertItem::~ConvertItem()
+{}
+
+
+Convert::Convert( Config* _config, TagEngine* _tagEngine, CDManager* _cdManager, FileList* _fileList, Logger* _logger )
+{
+ config = _config;
+ tagEngine = _tagEngine;
+ cdManager = _cdManager;
+ fileList = _fileList;
+ connect( fileList, SIGNAL(convertItem(FileListItem*)),
+ this, SLOT(add(FileListItem*))
+ );
+ connect( fileList, SIGNAL(stopItem(FileListItem*)),
+ this, SLOT(stop(FileListItem*))
+ );
+ connect( this, SIGNAL(finished(FileListItem*,int)),
+ fileList, SLOT(itemFinished(FileListItem*,int))
+ );
+ connect( this, SIGNAL(rippingFinished(const QString&)),
+ fileList, SLOT(rippingFinished(const QString&))
+ );
+ logger = _logger;
+ connect( this, SIGNAL(finishedProcess(int,int)),
+ logger, SLOT(processCompleted(int,int))
+ );
+
+ tUpdateProgressIndicator = new QTimer( this, "tUpdateProgressIndicator" );
+ connect( tUpdateProgressIndicator, SIGNAL(timeout()),
+ this, SLOT(updateProgressIndicator())
+ );
+}
+
+Convert::~Convert()
+{}
+
+void Convert::cleanUp()
+{
+ // TODO clean up
+}
+
+void Convert::get( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Getting file") );
+ item->state = ConvertItem::get;
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting file")+"... 00 %" );
+
+ KURL source( item->fileListItem->options.filePathName.replace("?","%3f") );
+ KURL destination( item->tempInFile->name() );
+
+ if( source.isLocalFile() && destination.isLocalFile() ) {
+ item->convertProcess->clearArguments();
+
+ *(item->convertProcess) << "cp";
+ *(item->convertProcess) << source.path();
+ *(item->convertProcess) << destination.path();
+
+ logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+ }
+ else {
+ item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, true, false, false );
+ connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)),
+ this, SLOT(moveProgress(KIO::Job*,unsigned long))
+ );
+ connect( item->moveJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(moveFinished(KIO::Job*))
+ );
+ }
+}
+
+void Convert::getCorrection( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Getting correction file") );
+ item->state = ConvertItem::get_correction;
+
+ // calculate the name of the correction input file
+ QFile file( OutputDirectory::changeExtension(item->fileListItem->options.filePathName,item->correctionInputExtension) );
+ if( !file.exists() ) {
+ logger->log( item->logID, " " + i18n("Aborting, file does not exist") + " (" + file.name() + ")" );
+ executeNextStep( item );
+ return;
+ }
+
+ KURL source( file.name() );
+ KURL destination( item->correctionInFile );
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting correction file")+"... 00 %" );
+
+ if( source.isLocalFile() && destination.isLocalFile() ) {
+ item->convertProcess->clearArguments();
+
+ *(item->convertProcess) << "cp";
+ *(item->convertProcess) << source.path();
+ *(item->convertProcess) << destination.path();
+
+ logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+ }
+ else {
+ item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, true, false, false );
+ connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)),
+ this, SLOT(moveProgress(KIO::Job*,unsigned long))
+ );
+ connect( item->moveJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(moveFinished(KIO::Job*))
+ );
+ }
+}
+
+void Convert::rip( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Ripping") );
+ item->state = ConvertItem::rip;
+
+/** kaudiocreator
+ QString wavFile;
+ QString args = job->device;
+ if(!args.isEmpty())
+ args = QString("?device=%1").arg(args);
+ args = args+"&fileNameTemplate=Track %{number}";
+ if(job->track < 10)
+ wavFile = QString("audiocd:/Wav/Track 0%1.wav%2").arg(job->track).arg(args);
+ else
+ wavFile = QString("audiocd:/Wav/Track %1.wav%2").arg(job->track).arg(args);
+*/
+
+ RipperPlugin* plugin = config->getCurrentRipper();
+
+ if( plugin == 0 ) {
+ // NOTE process devices like '/dev/cdrom' - seems to be done
+ // TODO implement process priority (nice level)
+ QString src;
+ if( item->fileListItem->track != 0 ) {
+ // TODO does it work with cds with less than 10 tracks?
+ src.sprintf( "audiocd:/Wav/Track %02i.wav?device=" + item->fileListItem->device + "&fileNameTemplate=Track %%{number}", item->fileListItem->track );
+ }
+ else {
+ // FIXME implement ripping of full cds
+ src = "audiocd:/Full CD/Full CD.wav?device=" + item->fileListItem->device + "&albumTemplate=Full CD";
+ item->tracks = 1;
+ }
+ KURL source( src );
+ KURL dest( item->tempWavFile->name() );
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... 00 %" );
+ item->fileListItem->ripping = true;
+
+ item->moveJob = new KIO::FileCopyJob( source, dest, -1, false, true, false, false );
+ connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)),
+ this, SLOT(moveProgress(KIO::Job*,unsigned long))
+ );
+ connect( item->moveJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(moveFinished(KIO::Job*))
+ );
+ }
+ else {
+ QStringList params;
+ QString param, paramSplinter;
+
+
+ item->convertProcess->clearArguments();
+
+ param = QString::null;
+ if( plugin->rip.param ) param.append( " " + plugin->rip.param );
+ if( plugin->rip.device ) param.append( " " + plugin->rip.device );
+ if( plugin->rip.overwrite ) param.append( " " + plugin->rip.overwrite );
+
+ if( item->fileListItem->track != 0 ) {
+ if( plugin->rip.track ) param.append( " " + plugin->rip.track );
+ }
+ else {
+ if( plugin->rip.full_disc.param ) param.append( " " + plugin->rip.full_disc.param );
+ item->tracks = cdManager->getTrackCount( item->fileListItem->device );
+ item->track = 0;
+ }
+
+// if( plugin->rip.out_file.find("%p") != -1 ) {
+// QString t_str = plugin->rip.out_file;
+// t_str.replace( "%p", param );
+// param = plugin->rip.bin + " " + t_str;
+// }
+// else {
+// param = plugin->rip.bin + param + " " + plugin->rip.out_file;
+// }
+
+ QString t_str = plugin->rip.out_file;
+ t_str.replace( "%p", param );
+ param = config->binaries[plugin->rip.bin] + " " + t_str;
+
+ param.simplifyWhiteSpace();
+
+ params = QStringList::split( ' ', param );
+
+ for( QStringList::Iterator it = params.begin(); it != params.end(); ++it )
+ {
+ paramSplinter = *it;
+ paramSplinter.replace( "%d", item->fileListItem->device );
+ paramSplinter.replace( "%t", QString().sprintf("%i",item->fileListItem->track) );
+ paramSplinter.replace( "%n", QString().sprintf("%i",cdManager->getTrackCount(item->fileListItem->device)) );
+ paramSplinter.replace( "%o", item->tempWavFile->name() );
+ *(item->convertProcess) << paramSplinter;
+ }
+
+ param.replace( "%d", item->fileListItem->device );
+ param.replace( "%t", QString().sprintf("%i",item->fileListItem->track) );
+ param.replace( "%n", QString().sprintf("%i",cdManager->getTrackCount(item->fileListItem->device)) );
+ param.replace( "%o", "\""+item->tempWavFile->name()+"\"" );
+ logger->log( item->logID, param );
+
+ //kdDebug() << " Executing: `" << param << "'" << endl;
+
+ //item->readOutputTimer.start();
+ item->lastOutputTimer.start();
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... 00 %" );
+ item->fileListItem->ripping = true;
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+ }
+}
+
+void Convert::decode( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Decoding") );
+ item->state = ConvertItem::decode;
+
+ QStringList params;
+ QString param, paramSplinter;
+
+ item->convertProcess->clearArguments();
+
+ ConvertPlugin* plugin = config->decoderForFormat( item->fileListItem->mimeType );
+ if( plugin == 0 ) {
+ logger->log( item->logID, " NULL POINTER: Convert::decode( ... ) / plugin" );
+ return;
+ }
+
+ param = "";
+ if( !plugin->dec.param.isEmpty() ) param.append( " " + plugin->dec.param );
+ if( !plugin->dec.overwrite.isEmpty() ) param.append( " " + plugin->dec.overwrite );
+
+ QString t_str = plugin->dec.in_out_files;
+ t_str.replace( "%p", param );
+ param = config->binaries[plugin->dec.bin] + " " + t_str;
+
+ param = param.simplifyWhiteSpace();
+
+ params = QStringList::split( ' ', param );
+
+ for( QStringList::Iterator it = params.begin(); it != params.end(); ++it )
+ {
+ paramSplinter = *it;
+ paramSplinter.replace( "%i", item->tempInFile->name() );
+ paramSplinter.replace( "%o", item->tempWavFile->name() );
+ *(item->convertProcess) << paramSplinter;
+ }
+
+ param.replace( "%i", "\""+item->tempInFile->name()+"\"" );
+ param.replace( "%o", "\""+item->tempWavFile->name()+"\"" );
+ //item->log = param;
+ logger->log( item->logID, param );
+
+ //kdDebug() << " Executing: `" << param << "'" << endl;
+
+ //item->readOutputTimer.start();
+ item->lastOutputTimer.start();
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Decoding")+"... 00 %" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+}
+
+void Convert::encode( ConvertItem* item )
+{
+ // TODO test quality profiles (never done)
+
+ QString sStrength;
+ QString sBitrate;
+ QString sQuality;
+ QString sMinBitrate;
+ QString sMaxBitrate;
+ QString sSamplingRate;
+
+ int t_int;
+ float t_float;
+
+ logger->log( item->logID, i18n("Encoding") );
+ item->state = ConvertItem::encode;
+
+ QStringList params;
+ QString param, paramSplinter;
+
+ item->convertProcess->clearArguments();
+
+ // NOTE use mimetype
+ FormatItem* formatItem = config->getFormatItem( item->fileListItem->options.encodingOptions.sFormat );
+ if( formatItem == 0 ) {
+ //kdDebug() << "NULL POINTER: `" << "Convert::encode( ... ) / formatItem" << "'" << endl;
+ logger->log( 1000, "NULL POINTER: `Convert::encode( ... ) / formatItem'" );
+ return;
+ }
+ ConvertPlugin* plugin = formatItem->encoder;
+ if( plugin == 0 ) {
+ //kdDebug() << "NULL POINTER: `" << "Convert::encode( ... ) / plugin" << "'" << endl;
+ logger->log( 1000, "NULL POINTER: `Convert::encode( ... ) / plugin'" );
+ return;
+ }
+
+// item->binary = plugin->enc.bin;
+
+ param = "";
+ if( !plugin->enc.param.isEmpty() ) param.append( " " + plugin->enc.param );
+ if( !plugin->enc.overwrite.isEmpty() ) param.append( " " + plugin->enc.overwrite );
+
+ if( plugin->enc.strength.enabled ) {
+ param.append( " " + plugin->enc.strength.param );
+ int compressionLevel = formatItem->compressionLevel;
+
+ if( plugin->enc.strength.profiles.empty() ) {
+ if( plugin->enc.strength.step < 1 ) {
+ if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min )
+ sStrength = QString::number( compressionLevel * plugin->enc.strength.step );
+ else
+ sStrength = QString::number( plugin->enc.strength.range_min - compressionLevel * plugin->enc.strength.step );
+ }
+ else {
+ if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min )
+ sStrength = QString::number( int(compressionLevel * plugin->enc.strength.step) );
+ else
+ sStrength = QString::number( int(plugin->enc.strength.range_min - compressionLevel * plugin->enc.strength.step) );
+ }
+ if( plugin->enc.strength.separator != '.' ) sStrength.replace( QChar('.'), plugin->enc.strength.separator );
+ }
+ else {
+ QStringList::Iterator it = plugin->enc.strength.profiles.at( compressionLevel );
+ sStrength = *it;
+ }
+ }
+
+ if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") ) {
+ if( item->fileListItem->options.encodingOptions.sBitrateMode == "cbr" && plugin->enc.lossy.bitrate.cbr.enabled ) {
+ param.append( " " + plugin->enc.lossy.bitrate.cbr.param );
+ sBitrate = QString::number( item->fileListItem->options.encodingOptions.iQuality );
+ }
+ else if( item->fileListItem->options.encodingOptions.sBitrateMode == "abr" && plugin->enc.lossy.bitrate.abr.enabled ) {
+ param.append( " " + plugin->enc.lossy.bitrate.abr.param );
+ sBitrate = QString::number( item->fileListItem->options.encodingOptions.iQuality );
+ if( item->fileListItem->options.encodingOptions.bBitrateRange && plugin->enc.lossy.bitrate.abr.bitrate_range.enabled ) {
+ param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_min );
+ sMinBitrate = QString::number( item->fileListItem->options.encodingOptions.iMinBitrate );
+ param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_max );
+ sMaxBitrate = QString::number( item->fileListItem->options.encodingOptions.iMaxBitrate );
+ }
+ }
+ }
+ else if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Quality") && plugin->enc.lossy.quality.enabled ) {
+ param.append( " " + plugin->enc.lossy.quality.param );
+ if( plugin->enc.lossy.quality.profiles.empty() ) {
+ if( plugin->enc.lossy.quality.step < 1 ) {
+ if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
+ t_float = ( (float)item->fileListItem->options.encodingOptions.iQuality * ( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100 ) + plugin->enc.lossy.quality.range_min;
+ else
+ t_float = ( (100.0f - (float)item->fileListItem->options.encodingOptions.iQuality) * ( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100 ) + plugin->enc.lossy.quality.range_max;
+ //t_float -= t_float%plugin->enc.quality.step;
+ //sQuality = QString().sprintf( "%.2f", t_float );
+ sQuality = QString::number( t_float );
+ }
+ else {
+ if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
+ t_int = ( item->fileListItem->options.encodingOptions.iQuality * (int)( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100) + (int)plugin->enc.lossy.quality.range_min;
+ else
+ t_int = ( (100 - item->fileListItem->options.encodingOptions.iQuality) * (int)( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100) + (int)plugin->enc.lossy.quality.range_max;
+ //t_int -= t_int%plugin->enc.quality.step;
+ sQuality = QString::number( t_int );
+ }
+ if( plugin->enc.bin == "oggenc" ) sQuality.replace(QChar('.'),KGlobal::locale()->decimalSymbol()); // HACK make oggenc usable with all langauges
+ else if( plugin->enc.lossy.quality.separator != '.' ) sQuality.replace(QChar('.'),plugin->enc.lossy.quality.separator);
+ }
+ else {
+ QStringList::Iterator it = plugin->enc.lossy.quality.profiles.at( rint(item->fileListItem->options.encodingOptions.iQuality*plugin->enc.lossy.quality.range_max/100) );
+ sQuality = *it;
+ }
+ }
+ else if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Lossless") && plugin->enc.lossless.enabled ) {
+ param.append( " " + plugin->enc.lossless.param );
+ }
+ else if( item->fileListItem->options.encodingOptions.sQualityMode == i18n("Hybrid") && plugin->enc.hybrid.enabled ) {
+ param.append( " " + plugin->enc.hybrid.param );
+ sBitrate = QString::number( item->fileListItem->options.encodingOptions.iQuality );
+ }
+
+ if( item->fileListItem->options.encodingOptions.samplingRate.bEnabled && plugin->enc.lossy.samplingrate.enabled ) {
+ param.append( " " + plugin->enc.lossy.samplingrate.param );
+ if( plugin->enc.lossy.samplingrate.unit == PluginLoaderBase::Hz ) {
+ sSamplingRate = QString::number( item->fileListItem->options.encodingOptions.samplingRate.iSamplingRate );
+ }
+ else {
+ sSamplingRate = QString::number( (float)item->fileListItem->options.encodingOptions.samplingRate.iSamplingRate/1000 );
+ }
+ }
+
+ if( item->fileListItem->options.encodingOptions.channels.bEnabled ) {
+ if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Mono") && plugin->enc.lossy.channels.mono_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.mono_param );
+ }
+ else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Stereo") && plugin->enc.lossy.channels.stereo_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.stereo_param );
+ }
+ else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Joint-Stereo") && plugin->enc.lossy.channels.joint_stereo_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.joint_stereo_param );
+ }
+ else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Forced Joint-Stereo") && plugin->enc.lossy.channels.forced_joint_stereo_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.forced_joint_stereo_param );
+ }
+ else if( item->fileListItem->options.encodingOptions.channels.sChannels == i18n("Dual Channels") && plugin->enc.lossy.channels.dual_channels_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.dual_channels_param );
+ }
+ }
+
+ if( item->fileListItem->options.encodingOptions.replaygain.bEnabled && plugin->enc.replaygain.enabled && plugin->enc.replaygain.use && formatItem->internalReplayGain ) {
+ param.append( " " + plugin->enc.replaygain.use );
+ }
+ else if( plugin->enc.replaygain.enabled && plugin->enc.replaygain.avoid ) {
+ param.append( " " + plugin->enc.replaygain.avoid );
+ }
+
+// if( !tagEngine->canWrite(item->fileListItem->options.encodingOptions.sFormat) && item->fileListItem->tags && plugin->enc.tag.enabled ) {
+ if( item->fileListItem->tags && plugin->enc.tag.enabled && item->fileListItem->options.encodingOptions.sFormat != "aac" ) { // HACK don't write metadata to aac
+ if( !plugin->enc.tag.param.isEmpty() ) param.append( " " + plugin->enc.tag.param );
+ if( !plugin->enc.tag.artist.isEmpty() && !item->fileListItem->tags->artist.isEmpty() ) param.append( " " + plugin->enc.tag.artist );
+ if( !plugin->enc.tag.album.isEmpty() && !item->fileListItem->tags->album.isEmpty() ) param.append( " " + plugin->enc.tag.album );
+ if( !plugin->enc.tag.comment.isEmpty() && !item->fileListItem->tags->comment.isEmpty() ) param.append( " " + plugin->enc.tag.comment );
+ if( !plugin->enc.tag.disc.isEmpty() && item->fileListItem->tags->disc != 0 ) param.append( " " + plugin->enc.tag.disc );
+ if( !plugin->enc.tag.genre.isEmpty() && !item->fileListItem->tags->genre.isEmpty() ) param.append( " " + plugin->enc.tag.genre );
+ if( !plugin->enc.tag.track.isEmpty() && item->fileListItem->tags->track != 0 ) param.append( " " + plugin->enc.tag.track );
+ if( !plugin->enc.tag.composer.isEmpty() && !item->fileListItem->tags->composer.isEmpty() ) param.append( " " + plugin->enc.tag.composer );
+ if( !plugin->enc.tag.title.isEmpty() && !item->fileListItem->tags->title.isEmpty() ) param.append( " " + plugin->enc.tag.title );
+ if( !plugin->enc.tag.year.isEmpty() && item->fileListItem->tags->year != 0 ) param.append( " " + plugin->enc.tag.year );
+ }
+
+ QString sInOutFiles = item->fileListItem->options.encodingOptions.sInOutFiles;
+ param = sInOutFiles.replace( "%p", param );
+
+ // cosmetic surgery
+ param = param.simplifyWhiteSpace();
+
+ params = QStringList::split( ' ', param );
+
+ QString inputFile;
+ if( item->mode & ConvertItem::decode || item->mode & ConvertItem::rip ) inputFile = item->tempWavFile->name();
+ else inputFile = item->tempInFile->name();
+
+ for( QStringList::Iterator it = params.begin(); it != params.end(); ++it )
+ {
+ paramSplinter = *it;
+ paramSplinter.replace( "%i", inputFile );
+ paramSplinter.replace( "%o", item->tempOutFile->name() );
+ paramSplinter.replace( "%c", sStrength );
+ paramSplinter.replace( "%b", sBitrate );
+ paramSplinter.replace( "%q", sQuality );
+ paramSplinter.replace( "%m", sMinBitrate );
+ paramSplinter.replace( "%M", sMaxBitrate );
+ paramSplinter.replace( "%s", sSamplingRate );
+
+ if( item->fileListItem->tags ) {
+ paramSplinter.replace( "%ta", ( item->fileListItem->tags->artist != "" ) ? item->fileListItem->tags->artist : i18n("Unknown") );
+ paramSplinter.replace( "%tb", ( item->fileListItem->tags->album != "" ) ? item->fileListItem->tags->album : i18n("Unknown") );
+ paramSplinter.replace( "%tc", ( item->fileListItem->tags->comment != "" ) ? item->fileListItem->tags->comment : i18n("Unknown") );
+ paramSplinter.replace( "%td", ( QString::number(item->fileListItem->tags->disc) != "" ) ? QString::number(item->fileListItem->tags->disc) : "0" );
+ paramSplinter.replace( "%tg", ( item->fileListItem->tags->genre != "" ) ? item->fileListItem->tags->genre : i18n("Unknown") );
+ paramSplinter.replace( "%tn", ( QString::number(item->fileListItem->tags->track) != "" ) ? QString::number(item->fileListItem->tags->track) : "0" );
+ paramSplinter.replace( "%tp", ( item->fileListItem->tags->composer != "" ) ? item->fileListItem->tags->composer : i18n("Unknown") );
+ paramSplinter.replace( "%tt", ( item->fileListItem->tags->title != "" ) ? item->fileListItem->tags->title : i18n("Unknown") );
+ paramSplinter.replace( "%ty", ( QString::number(item->fileListItem->tags->year) != "" ) ? QString::number(item->fileListItem->tags->year) : "0" );
+ }
+
+ if( paramSplinter != "" && paramSplinter != " " ) *(item->convertProcess) << paramSplinter; // NOTE fixes wavpack encoding
+ }
+
+ param.replace( "%i", "\""+inputFile+"\"" );
+ param.replace( "%o", "\""+item->tempOutFile->name()+"\"" );
+ param.replace( "%c", sStrength );
+ param.replace( "%b", sBitrate );
+ param.replace( "%q", sQuality );
+ param.replace( "%m", sMinBitrate );
+ param.replace( "%M", sMaxBitrate );
+ param.replace( "%s", sSamplingRate );
+
+ if( item->fileListItem->tags ) {
+ param.replace( "%ta", "\""+item->fileListItem->tags->artist+"\"" );
+ param.replace( "%tb", "\""+item->fileListItem->tags->album+"\"" );
+ param.replace( "%tc", "\""+item->fileListItem->tags->comment+"\"" );
+ param.replace( "%td", QString::number(item->fileListItem->tags->disc) );
+ param.replace( "%tg", "\""+item->fileListItem->tags->genre+"\"" );
+ param.replace( "%tn", QString::number(item->fileListItem->tags->track) );
+ param.replace( "%tp", "\""+item->fileListItem->tags->composer+"\"" );
+ param.replace( "%tt", "\""+item->fileListItem->tags->title+"\"" );
+ param.replace( "%ty", QString::number(item->fileListItem->tags->year) );
+ }
+
+ logger->log( item->logID, param );
+
+ //kdDebug() << " Executing: `" << param << "'" << endl;
+
+ //item->readOutputTimer.start();
+ item->lastOutputTimer.start();
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Encoding")+"... 00 %" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+}
+
+void Convert::replaygain( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Applying Replay Gain") );
+ item->state = ConvertItem::replaygain;
+
+ FormatItem* formatItem = config->getFormatItem( item->fileListItem->options.encodingOptions.sFormat );
+ if( formatItem == 0 ) {
+ logger->log( item->logID, " NULL POINTER: Convert::replaygain( ... ) / formatItem" );
+ return;
+ }
+ ConvertPlugin* plugin = formatItem->encoder;
+ if( plugin == 0 ) {
+ logger->log( item->logID, " NULL POINTER: Convert::replaygain( ... ) / plugin" );
+ return;
+ }
+
+ if( plugin->enc.replaygain.enabled && formatItem->internalReplayGain ) {
+ executeNextStep( item );
+ return;
+ }
+
+ item->replayGain = new ReplayGain( config, logger );
+ bool ret = item->replayGain->apply( item->tempOutFile->name(), item->fileListItem->options.encodingOptions.sFormat, item->convertProcess, item->logID );
+
+ if( !ret ) {
+ executeNextStep( item );
+ return;
+ }
+
+ //item->readOutputTimer.start();
+ item->lastOutputTimer.start();
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Replay Gain")+"... 00 %" );
+}
+
+void Convert::writeTags( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Writing tags") );
+ item->state = ConvertItem::write_tags;
+
+ if( item->mode & ConvertItem::encode ) {
+ tagEngine->writeTags( item->tempOutFile->name(), item->fileListItem->tags );
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Writing tags")+"... 00 %" );
+ }
+
+ executeNextStep( item );
+}
+
+void Convert::put( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Moving file") );
+ item->state = ConvertItem::put;
+
+ QString src;
+ if( item->mode & ConvertItem::encode ) src = item->tempOutFile->name();
+ else src = item->tempWavFile->name();
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving file")+"... 00 %" );
+
+ item->outputFilePathName = OutputDirectory::makePath( OutputDirectory::uniqueFileName(OutputDirectory::calcPath(item->fileListItem,config)) ).replace("%2f","%252f");
+
+ KURL source( src );
+ KURL destination( item->outputFilePathName );
+
+ if( source.isLocalFile() && destination.isLocalFile() ) {
+ item->convertProcess->clearArguments();
+
+ *(item->convertProcess) << "cp";
+ *(item->convertProcess) << source.path();
+ *(item->convertProcess) << destination.path();
+
+ logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+ }
+ else {
+ item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, false, false, false );
+ connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)),
+ this, SLOT(moveProgress(KIO::Job*,unsigned long))
+ );
+ connect( item->moveJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(moveFinished(KIO::Job*))
+ );
+ }
+}
+
+void Convert::putCorrection( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Moving correction file") );
+ item->state = ConvertItem::put_correction;
+
+ QString src = item->correctionOutFile;
+
+ QString dest = OutputDirectory::makePath( OutputDirectory::calcPath(item->fileListItem,config,item->correctionOutputExtension) ).replace("%2f","%252f");
+
+ KURL source( src );
+// KURL destination( dest );
+ KURL destination;
+ destination.setPath( dest );
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving correction file")+"... 00 %" );
+
+ if( source.isLocalFile() && destination.isLocalFile() ) {
+ item->convertProcess->clearArguments();
+
+ *(item->convertProcess) << "cp";
+ *(item->convertProcess) << source.path();
+ *(item->convertProcess) << destination.path();
+
+ logger->log( item->logID, "cp \"" + source.path() + "\" \"" + destination.path() + "\"" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+ }
+ else {
+ item->moveJob = new KIO::FileCopyJob( source, destination, -1, false, false, false, false );
+ connect( item->moveJob, SIGNAL(percent(KIO::Job*,unsigned long)),
+ this, SLOT(moveProgress(KIO::Job*,unsigned long))
+ );
+ connect( item->moveJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(moveFinished(KIO::Job*))
+ );
+ }
+}
+
+void Convert::executeUserScript( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Running user script") );
+ item->state = ConvertItem::execute_userscript;
+
+ KURL source( item->fileListItem->options.filePathName );
+ KURL destination( item->outputFilePathName );
+
+ item->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Running user script")+"... 00 %" );
+
+ item->convertProcess->clearArguments();
+
+ QString userscript = locate( "data", "soundkonverter/userscript.sh" );
+ if( userscript == "" ) executeNextStep( item );
+
+ *(item->convertProcess) << userscript;
+ *(item->convertProcess) << source.path();
+ *(item->convertProcess) << destination.path();
+
+ logger->log( item->logID, userscript + " \"" + source.path() + "\" \"" + destination.path() + "\"" );
+
+ item->convertProcess->setPriority( config->data.general.priority );
+ item->convertProcess->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+}
+
+void Convert::executeNextStep( ConvertItem* item )
+{
+ logger->log( item->logID, i18n("Executing next step") );
+
+ item->percent = 0;
+ item->lastPercent = 0; // used for ripping a whole cd to one file
+
+ switch( item->state )
+ {
+ case ConvertItem::get:
+ {
+ if( item->mode & ConvertItem::get_correction ) getCorrection( item );
+ else if( item->mode & ConvertItem::rip ) rip( item );
+ else if( item->mode & ConvertItem::decode ) decode( item );
+ else if( item->mode & ConvertItem::encode ) encode( item );
+ else if( item->mode & ConvertItem::replaygain ) replaygain( item );
+ else if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::get_correction:
+ {
+ if( item->mode & ConvertItem::rip ) rip( item );
+ else if( item->mode & ConvertItem::decode ) decode( item );
+ else if( item->mode & ConvertItem::encode ) encode( item );
+ else if( item->mode & ConvertItem::replaygain ) replaygain( item );
+ else if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::rip:
+ {
+ if( item->mode & ConvertItem::decode ) decode( item );
+ else if( item->mode & ConvertItem::encode ) encode( item );
+ else if( item->mode & ConvertItem::replaygain ) replaygain( item );
+ else if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::decode:
+ {
+ if( item->mode & ConvertItem::encode ) encode( item );
+ else if( item->mode & ConvertItem::replaygain ) replaygain( item );
+ else if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::encode:
+ {
+ if( item->mode & ConvertItem::replaygain ) replaygain( item );
+ else if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::replaygain:
+ {
+ if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::write_tags:
+ {
+ if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::put:
+ {
+ if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::put_correction:
+ {
+ if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ case ConvertItem::execute_userscript:
+ {
+ remove( item );
+ break;
+ }
+ default: // case (ConvertItem::Mode)0x0000:
+ {
+ if( item->mode & ConvertItem::get ) get( item );
+ else if( item->mode & ConvertItem::get_correction ) getCorrection( item );
+ else if( item->mode & ConvertItem::rip ) rip( item );
+ else if( item->mode & ConvertItem::decode ) decode( item );
+ else if( item->mode & ConvertItem::encode ) encode( item );
+ else if( item->mode & ConvertItem::replaygain ) replaygain( item );
+ else if( item->mode & ConvertItem::write_tags ) writeTags( item );
+ else if( item->mode & ConvertItem::put ) put( item );
+ else if( item->mode & ConvertItem::put_correction ) putCorrection( item );
+ else if( config->data.general.executeUserScript ) executeUserScript( item );
+ else remove( item );
+ break;
+ }
+ }
+}
+
+void Convert::moveProgress( KIO::Job* job, unsigned long percent )
+{
+ // search the item list for our item
+ for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) {
+ if( (*item)->moveJob == job ) {
+ (*item)->percent = percent;
+ }
+ }
+}
+
+void Convert::moveFinished( KIO::Job* job )
+{
+ // search the item list for our item
+ for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) {
+ if( (*item)->moveJob == job ) {
+
+ (*item)->percent = 0;
+ if( (*item)->state == ConvertItem::get ) {
+ if( job->error() != 0 ) {
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+
+ logger->log( (*item)->logID, " " + i18n("Got file") );
+
+ // if file is remote or the tag reding failed previously, read the tags now
+ if( (*item)->fileListItem->tags == 0 ) {
+ (*item)->fileListItem->tags = tagEngine->readTags( (*item)->tempInFile->name() );
+ }
+
+ emit countTime( (*item)->getTime );
+ }
+ else if( (*item)->state == ConvertItem::get_correction ) {
+ if( job->error() != 0 ) {
+ emit uncountTime( (*item)->getTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+
+ logger->log( (*item)->logID, " " + i18n("Got file") );
+ emit countTime( (*item)->getCorrectionTime );
+ }
+ else if( (*item)->state == ConvertItem::rip ) {
+ if( job->error() != 0 ) {
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+
+ //logger->log( (*item)->logID, " " + i18n("Ripped track") );
+ (*item)->fileListItem->ripping = false;
+ emit countTime( (*item)->ripTime );
+ emit rippingFinished( (*item)->fileListItem->device );
+ }
+ else if( (*item)->state == ConvertItem::put ) {
+ if( job->error() != 0 ) {
+ logger->log( (*item)->logID, i18n("Could not write to file: `%1'").arg(OutputDirectory::calcPath((*item)->fileListItem,config)) );
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ emit uncountTime( (*item)->replaygainTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+
+ logger->log( (*item)->logID, " " + i18n("File moved") );
+ }
+ else if( (*item)->state == ConvertItem::put_correction ) {
+ if( job->error() != 0 ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ emit uncountTime( (*item)->replaygainTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+
+ logger->log( (*item)->logID, " " + i18n("File moved") );
+ }
+
+ executeNextStep( *item );
+ return;
+ }
+ }
+}
+
+void Convert::processOutput( KProcess* proc, char* data, int )
+{
+ int iPercent = 0, iTime = 0, iPos = 0, iNum = 0;
+
+ // search the item list for our item
+ for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ )
+ {
+ if( (*item)->convertProcess == proc )
+ {
+ QString log_data = data;
+/* log_data.replace("\n","\\n");
+ log_data.replace("\t","\\t");
+ log_data.replace("\r","\\r");
+ log_data.replace("\b","\\b");*/
+ logger->log( (*item)->logID, " " + i18n("Output") + ": " + log_data );
+
+ //if( (*item)->readOutputTimer.elapsed() < /*config->pauseTime*/ 100 ) return; // TODO use config value
+ //(*item)->readOutputTimer.start();
+
+ if( (*item)->state == ConvertItem::decode )
+ {
+ if( (*item)->fileListItem == 0 ) return;
+
+ ConvertPlugin* plugin = config->decoderForFormat( (*item)->fileListItem->mimeType );
+ // TODO null pointer check
+
+ QString outputPattern = plugin->dec.output;
+ //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins
+
+ if( outputPattern.find("%p") != -1 ) {
+ outputPattern.replace( "%p", "%i" );
+ sscanf( data, outputPattern, &iPercent );
+ }
+ else if( outputPattern.find("%t") != -1 ) {
+ outputPattern.replace( "%t", "%i" );
+ sscanf( data, outputPattern, &iTime );
+ iPercent = iTime * 100 / (*item)->fileListItem->time;
+ }
+ else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) {
+ if( outputPattern.find("%0") < outputPattern.find("%1") ) {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iPos, &iNum );
+ }
+ else {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iNum, &iPos );
+ }
+ if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum;
+ }
+
+ if( iPercent > 0 && iPercent <= 100 )
+ {
+ // TODO guess progress, when no signal is received
+ (*item)->lastOutputTimer.start();
+ (*item)->percent = iPercent;
+ }
+ }
+ else if( (*item)->state == ConvertItem::encode )
+ {
+ if( (*item)->fileListItem == 0 ) return;
+
+ // NOTE use mimetype
+ ConvertPlugin* plugin = config->encoderForFormat( (*item)->fileListItem->options.encodingOptions.sFormat );
+ // TODO null pointer check
+
+ QString outputPattern;
+ if( (*item)->fileListItem->options.encodingOptions.sQualityMode == i18n("Quality") ) outputPattern = plugin->enc.lossy.quality.output;
+ else if( (*item)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*item)->fileListItem->options.encodingOptions.sBitrateMode == "cbr" ) outputPattern = plugin->enc.lossy.bitrate.cbr.output;
+ else if( (*item)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*item)->fileListItem->options.encodingOptions.sBitrateMode == "abr" ) outputPattern = plugin->enc.lossy.bitrate.abr.output;
+
+ //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins
+
+ if( outputPattern.find("%p") != -1 ) {
+ outputPattern.replace( "%p", "%i" );
+ sscanf( data, outputPattern, &iPercent );
+ }
+ else if( outputPattern.find("%t") != -1 ) {
+ outputPattern.replace( "%t", "%i" );
+ sscanf( data, outputPattern, &iTime );
+ iPercent = iTime * 100 / (*item)->fileListItem->time;
+ }
+ else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) {
+ if( outputPattern.find("%0") < outputPattern.find("%1") ) {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iPos, &iNum );
+ }
+ else {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iNum, &iPos );
+ }
+ if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum;
+ }
+
+ if( iPercent > 0 && iPercent <= 100 )
+ {
+ // TODO guess progress, when no signal is received
+ (*item)->lastOutputTimer.start();
+ (*item)->percent = iPercent;
+ }
+ }
+ else if( (*item)->state == ConvertItem::rip ) // ### soundkonverter 0.4 make the progress dependent on the track length
+ {
+ if( (*item)->fileListItem == 0 ) return;
+
+ RipperPlugin* plugin = config->getCurrentRipper();
+ // TODO null pointer check
+
+ QString outputPattern;
+ if( (*item)->fileListItem->track != 0 ) outputPattern = plugin->rip.output;
+ else outputPattern = plugin->rip.full_disc.output;
+ //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins
+
+ if( outputPattern.find("%p") != -1 || outputPattern.find("%a") != -1 ) {
+ outputPattern.replace( "%p", "%i" );
+ outputPattern.replace( "%a", "%i" );
+ sscanf( data, outputPattern, &iPercent );
+ }
+ else if( outputPattern.find("%t") != -1 ) {
+ outputPattern.replace( "%t", "%i" );
+ sscanf( data, outputPattern, &iTime );
+ iPercent = iTime * 100 / (*item)->fileListItem->time;
+ }
+ else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) {
+ if( outputPattern.find("%0") < outputPattern.find("%1") ) {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iPos, &iNum );
+ }
+ else {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iNum, &iPos );
+ }
+ if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum;
+ }
+
+ if( iPercent > 0 && iPercent <= 100 )
+ {
+ // TODO guess progress, when no signal is received
+ (*item)->lastOutputTimer.start();
+ if( (*item)->fileListItem->track == 0 && plugin->rip.full_disc.output.find("%a") != -1 ) {
+ if( iPercent < (*item)->lastPercent ) (*item)->track++;
+ (*item)->lastPercent = iPercent;
+ (*item)->percent = (*item)->track * 100 / (*item)->tracks + iPercent / (*item)->tracks;
+ }
+ else {
+ (*item)->percent = iPercent;
+ }
+ }
+ }
+ return;
+ }
+ }
+}
+
+void Convert::processExit( KProcess* proc )
+{
+ // search the item list for our item
+ for( QValueList<ConvertItem*>::Iterator item = items.begin(); item != items.end(); item++ ) {
+// if( (*item)->convertProcess == proc && (*item)->fileListItem != 0 ) {
+ if( (*item)->convertProcess == proc ) {
+
+ (*item)->percent = 0;
+ if( (*item)->state == ConvertItem::rip ) {
+ if( proc->signalled() ) { // FIXME does only check, whether the process has been killed
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ else if( !proc->normalExit() ) {
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ (*item)->fileListItem->ripping = false;
+ emit countTime( (*item)->ripTime );
+ emit rippingFinished( (*item)->fileListItem->device );
+ }
+ }
+ if( (*item)->state == ConvertItem::get ) {
+ if( proc->signalled() ) {
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ else if( !proc->normalExit() ) {
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ logger->log( (*item)->logID, " " + i18n("Got file") );
+
+ // if file is remote or the tag reding failed previously, read the tags now
+ if( (*item)->fileListItem->tags == 0 ) {
+ (*item)->fileListItem->tags = tagEngine->readTags( (*item)->tempInFile->name() );
+ }
+
+ emit countTime( (*item)->getTime );
+ }
+ }
+ if( (*item)->state == ConvertItem::get_correction ) {
+ if( proc->signalled() ) {
+ emit uncountTime( (*item)->getTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ else if( !proc->normalExit() ) {
+ emit uncountTime( (*item)->getTime );
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ logger->log( (*item)->logID, " " + i18n("Got file") );
+ emit countTime( (*item)->getCorrectionTime );
+ }
+ }
+ if( (*item)->state == ConvertItem::decode ) {
+ if( proc->signalled() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ else if( !proc->normalExit() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ emit countTime( (*item)->decodeTime );
+ }
+ }
+ if( (*item)->state == ConvertItem::encode ) {
+ if( proc->signalled() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ else if( !proc->normalExit() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+/* if( (*item)->binary == "faac" ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }*/
+ emit countTime( (*item)->encodeTime );
+ }
+ }
+ if( (*item)->state == ConvertItem::put ) {
+ if( proc->signalled() ) {
+ logger->log( (*item)->logID, i18n("Could not write to file: `%1'").arg(OutputDirectory::calcPath((*item)->fileListItem,config)) );
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ emit uncountTime( (*item)->replaygainTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ if( !proc->normalExit() ) {
+ logger->log( (*item)->logID, i18n("Could not write to file: `%1'").arg(OutputDirectory::calcPath((*item)->fileListItem,config)) );
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ emit uncountTime( (*item)->replaygainTime );
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ logger->log( (*item)->logID, " " + i18n("File moved") );
+ }
+ }
+ if( (*item)->state == ConvertItem::put_correction ) {
+ if( proc->signalled() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ emit uncountTime( (*item)->replaygainTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ if( !proc->normalExit() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ emit uncountTime( (*item)->replaygainTime );
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ logger->log( (*item)->logID, " " + i18n("File moved") );
+ }
+ }
+ if( (*item)->state == ConvertItem::replaygain ) {
+ if( proc->signalled() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ remove( *item, 1 );
+ updateProgressIndicator();
+ return;
+ }
+ else if( !proc->normalExit() ) {
+ emit uncountTime( (*item)->getTime );
+ emit uncountTime( (*item)->getCorrectionTime );
+ emit uncountTime( (*item)->ripTime );
+ emit uncountTime( (*item)->decodeTime );
+ emit uncountTime( (*item)->encodeTime );
+ remove( *item, -1 );
+ updateProgressIndicator();
+ return;
+ }
+ else {
+ emit countTime( (*item)->replaygainTime );
+ }
+ }
+ // TODO did we get errors? (complete)
+ executeNextStep( *item );
+ return;
+ }
+ }
+}
+
+void Convert::add( FileListItem* item )
+{
+ logger->log( 1000, i18n("Adding new item to conversion list: `%1'").arg(item->options.filePathName) );
+
+ // append the item to the item list and store the iterator
+ QValueList<ConvertItem*>::Iterator newItem = items.append( new ConvertItem( item ) );
+
+ // register at the logger
+ (*newItem)->logID = logger->registerProcess( item->options.filePathName );
+ logger->log( 1000, " " + i18n("Got log ID: %1").arg((*newItem)->logID) );
+
+ logger->log( (*newItem)->logID, "Mime Type: " + (*newItem)->fileListItem->mimeType );
+ if( (*newItem)->fileListItem->tags ) logger->log( (*newItem)->logID, i18n("Tags successfully read") );
+ else logger->log( (*newItem)->logID, i18n("Reading tags failed") );
+
+ // set some variables to default values
+ (*newItem)->mode = (ConvertItem::Mode)0x0000;
+ (*newItem)->state = (ConvertItem::Mode)0x0000;
+ (*newItem)->convertProcess = 0;
+ (*newItem)->moveJob = 0;
+ (*newItem)->replayGain = 0;
+
+ /* seems to be unnecessary
+ (*newItem)->correctionInFile = QString::null();
+ (*newItem)->correctionOutFile = QString::null();
+ (*newItem)->correctionInputExtension = QString::null();
+ (*newItem)->correctionOutputExtension = QString::null();*/
+
+ // connect convertProcess of our new item with the slots of Convert
+ (*newItem)->convertProcess = new KProcess();
+ connect( (*newItem)->convertProcess, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ this, SLOT(processOutput(KProcess*,char*,int))
+ );
+ connect( (*newItem)->convertProcess, SIGNAL(receivedStderr(KProcess*,char*,int)),
+ this, SLOT(processOutput(KProcess*,char*,int))
+ );
+ connect( (*newItem)->convertProcess, SIGNAL(processExited(KProcess*)),
+ this, SLOT(processExit(KProcess*))
+ );
+
+ // NOTE the tempInFile is also created if the file is a audio cd track
+
+ // set up the names of our temp files
+ (*newItem)->tempInFile = new KTempFile( QString::null, "." + item->fileFormat );
+ (*newItem)->tempInFile->setAutoDelete( true );
+ (*newItem)->tempInFile->close();
+
+ (*newItem)->tempWavFile = new KTempFile( QString::null, ".wav" );
+ (*newItem)->tempWavFile->setAutoDelete( true );
+ (*newItem)->tempWavFile->close();
+
+ (*newItem)->tempOutFile = new KTempFile( QString::null, "." + item->options.encodingOptions.sFormat );
+ (*newItem)->tempOutFile->setAutoDelete( true );
+ (*newItem)->tempOutFile->close();
+
+ if( item->track >= 0 ) // it's an audio cd track
+ {
+ (*newItem)->mode = ConvertItem::Mode( ConvertItem::rip );
+ }
+ else // it's a file
+ {
+ (*newItem)->mode = ConvertItem::Mode( ConvertItem::get );
+ if( item->fileFormat != "wav" )
+ {
+ (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::decode );
+ }
+ }
+
+ if( item->options.encodingOptions.sFormat != "wav" )
+ {
+ (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::encode );
+ }
+ if( item->options.encodingOptions.replaygain.bEnabled )
+ {
+ (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::replaygain );
+ }
+
+ QString extension;
+
+ extension = config->getCorrectionExtension( item->mimeType );
+ if( !extension.isEmpty() ) {
+ (*newItem)->correctionInputExtension = extension;
+ (*newItem)->correctionInFile = OutputDirectory::changeExtension( (*newItem)->tempInFile->name(), extension );
+ (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::get_correction );
+ logger->log( (*newItem)->logID, " correctionInFile: `" + (*newItem)->correctionInFile + "'" );
+ }
+
+ extension = config->getCorrectionExtension( item->options.encodingOptions.sFormat );
+ if( !extension.isEmpty() && item->options.encodingOptions.sQualityMode == i18n("Hybrid") ) {
+ (*newItem)->correctionOutputExtension = extension;
+ (*newItem)->correctionOutFile = OutputDirectory::changeExtension( (*newItem)->tempOutFile->name(), extension );
+ (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::put_correction );
+ logger->log( (*newItem)->logID, " correctionOutFile: `" + (*newItem)->correctionOutFile + "'" );
+ }
+
+ (*newItem)->mode = ConvertItem::Mode( (*newItem)->mode | ConvertItem::write_tags | ConvertItem::put );
+
+ // TODO use the values from the format info files !!!
+
+ if( (*newItem)->mode & ConvertItem::get ) {
+ if( !item->local ) {
+ (*newItem)->getTime = 0.8; // TODO use the file size from the format info files
+ }
+ }
+ if( (*newItem)->mode & ConvertItem::get_correction ) {
+ if( !item->local ) {
+ (*newItem)->getCorrectionTime = 2.0; // TODO use the file size from the format info files
+ }
+ }
+ if( (*newItem)->mode & ConvertItem::rip ) {
+ (*newItem)->ripTime = 1.0;
+ }
+ if( (*newItem)->mode & ConvertItem::decode ) {
+ (*newItem)->decodeTime = 0.4;
+ }
+ if( (*newItem)->mode & ConvertItem::encode ) {
+ (*newItem)->encodeTime = 1.0;
+ }
+ if( (*newItem)->mode & ConvertItem::replaygain ) {
+ (*newItem)->replaygainTime = 0.2;
+ }
+
+ float sum = ( (*newItem)->getTime + (*newItem)->getCorrectionTime + (*newItem)->ripTime + (*newItem)->decodeTime + (*newItem)->encodeTime + (*newItem)->replaygainTime ) / item->time;
+
+ (*newItem)->getTime /= sum;
+ (*newItem)->getCorrectionTime /= sum;
+ (*newItem)->ripTime /= sum;
+ (*newItem)->decodeTime /= sum;
+ (*newItem)->encodeTime /= sum;
+ (*newItem)->replaygainTime /= sum;
+
+ // visual feedback
+ item->converting = true;
+
+ if( !tUpdateProgressIndicator->isActive() ) {
+ tUpdateProgressIndicator->start( config->data.general.updateDelay );
+ }
+
+ // and start
+ executeNextStep( *newItem );
+}
+
+void Convert::stop( FileListItem* item )
+{
+ // search the item list for our item to stop
+ for( QValueList<ConvertItem*>::Iterator stopItem = items.begin(); stopItem != items.end(); stopItem++ ) {
+ // is fileListItem pointing at the same address, as item
+ if( (*stopItem)->fileListItem == item ) {
+
+ if( (*stopItem)->convertProcess->isRunning() ) {
+ bool ret = (*stopItem)->convertProcess->kill( SIGKILL );
+ //kdDebug() << "Killing process... (" << ret << ")" << endl;
+ if( ret ) {
+ logger->log( (*stopItem)->logID, i18n("Killing process ...") );
+ }
+ else {
+ logger->log( (*stopItem)->logID, i18n("Killing process failed. Stopping after files are completed ...") );
+ }
+ }
+ else if( (*stopItem)->moveJob != 0 ) {
+ //kdDebug() << "Killing file copy..." << endl;
+ // FIXME crashes sometimes - should be fixed by SIGKILL
+ // FIXME crash if file_copy was stealthed
+ logger->log( (*stopItem)->logID, i18n("Killing process ...") );
+ (*stopItem)->moveJob->kill( false );
+ }
+
+ return;
+ }
+ }
+}
+
+void Convert::remove( ConvertItem* item, int state )
+{
+ // TODO "remove" (re-add) the times to the progress indicator
+ //emit uncountTime( item->getTime + item->getCorrectionTime + item->ripTime +
+ // item->decodeTime + item->encodeTime + item->replaygainTime );
+
+ logger->log( item->logID, i18n("Removing file from conversion list. Exit code %1").arg(state) );
+
+ if( item->fileListItem->notify != "" ) {
+ QString command = item->fileListItem->notify;
+ command.replace( "%u", item->fileListItem->url );
+ command.replace( "%i", item->fileListItem->options.filePathName.replace(" ","%20") );
+ command.replace( "%o", item->outputFilePathName.replace(" ","%20") );
+ logger->log( item->logID, " "+i18n("Executing command: \"%1\"").arg(command) );
+ notify.clearArguments();
+ QString paramSplinter;
+ // FIXME split correct (strings with spaces are splited by mistake)
+ // FIXME only one command can be executed at once!?
+ QStringList params = QStringList::split( ' ', item->fileListItem->notify );
+ for( QStringList::Iterator it = params.begin(); it != params.end(); ++it )
+ {
+ paramSplinter = *it;
+ paramSplinter.replace( "%u", item->fileListItem->url );
+ paramSplinter.replace( "%i", item->fileListItem->options.filePathName );
+ paramSplinter.replace( "%o", item->outputFilePathName );
+ notify << paramSplinter;
+ }
+ notify.start( KProcess::DontCare );
+ }
+
+ item->fileListItem->converting = false;
+ emit finished( item->fileListItem, state ); // send signal to FileList
+ emit finishedProcess( item->logID, state ); // send signal to Logger
+
+ item->fileListItem = 0;
+ if( item->convertProcess != 0 ) delete item->convertProcess;
+ item->convertProcess = 0;
+ //if( item->moveJob != 0 ) delete item->moveJob; // NOTE makes soundkonverter crash
+ //item->moveJob = 0;
+ if( item->replayGain != 0 ) delete item->replayGain;
+ item->replayGain = 0;
+
+ if( item->tempInFile != 0 ) delete item->tempInFile;
+ item->tempInFile = 0;
+ if( item->tempWavFile != 0 ) delete item->tempWavFile;
+ item->tempWavFile = 0;
+ if( item->tempOutFile != 0 ) delete item->tempOutFile;
+ item->tempOutFile = 0;
+ QFile file;
+ file.setName( item->correctionInFile );
+ if( file.exists() ) file.remove();
+ file.setName( item->correctionOutFile );
+ if( file.exists() ) file.remove();
+
+ for( QValueList<ConvertItem*>::Iterator it = items.begin(); it != items.end(); it++ ) {
+ if( (*it) == item ) {
+ items.remove( it );
+ break;
+ }
+ }
+
+ delete item;
+ item = 0;
+
+ if( items.count() == 0 ) {
+ tUpdateProgressIndicator->stop();
+ }
+}
+
+void Convert::updateProgressIndicator()
+{
+ float time = 0;
+
+ for( QValueList<ConvertItem*>::Iterator it = items.begin(); it != items.end(); it++ ) {
+ if( (*it)->state == ConvertItem::get ) {
+ time += (*it)->getTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting file")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ else if( (*it)->state == ConvertItem::get_correction ) {
+ time += (*it)->getCorrectionTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Getting correction file")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ else if( (*it)->state == ConvertItem::rip ) {
+ RipperPlugin* plugin = config->getCurrentRipper();
+ if( plugin != 0 && plugin->rip.output.isEmpty() ) {
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... "+i18n("State")+": "+i18n("Unknown") );
+ }
+ else {
+ time += (*it)->ripTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Ripping")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ }
+ else if( (*it)->state == ConvertItem::decode ) {
+ ConvertPlugin* plugin = config->decoderForFormat( (*it)->fileListItem->mimeType );
+ if( plugin == 0 || plugin->dec.output.isEmpty() ) {
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Decoding")+"... "+i18n("State")+": "+i18n("Unknown") );
+ }
+ else {
+ time += (*it)->decodeTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Decoding")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ }
+ else if( (*it)->state == ConvertItem::encode ) {
+ ConvertPlugin* plugin = config->encoderForFormat( (*it)->fileListItem->options.encodingOptions.sFormat );
+ QString outputPattern;
+ if( plugin != 0 && (*it)->fileListItem->options.encodingOptions.sQualityMode == i18n("Quality") ) outputPattern = plugin->enc.lossy.quality.output;
+ else if( plugin != 0 && (*it)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*it)->fileListItem->options.encodingOptions.sBitrateMode == "cbr" ) outputPattern = plugin->enc.lossy.bitrate.cbr.output;
+ else if( plugin != 0 && (*it)->fileListItem->options.encodingOptions.sQualityMode == i18n("Bitrate") && (*it)->fileListItem->options.encodingOptions.sBitrateMode == "abr" ) outputPattern = plugin->enc.lossy.bitrate.abr.output;
+ if( outputPattern.isEmpty() ) {
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Encoding")+"... "+i18n("State")+": "+i18n("Unknown") );
+ }
+ else {
+ time += (*it)->encodeTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Encoding")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ }
+ else if( (*it)->state == ConvertItem::replaygain ) {
+ time += (*it)->replaygainTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Replay Gain")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ else if( (*it)->state == ConvertItem::put ) {
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving file")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ else if( (*it)->state == ConvertItem::put_correction ) {
+ time += (*it)->getCorrectionTime * (*it)->percent / 100;
+ (*it)->fileListItem->setText( fileList->columnByName(i18n("State")), i18n("Moving correction file")+"... "+QString().sprintf("%02i %%",(*it)->percent) );
+ }
+ }
+ emit update( time );
+}
+
+// void Convert::priorityChanged( int priority )
+// { // FIXME setting a higher priority does not work
+// KProcess pChangePriority;
+//
+// for( QValueList<ConvertItem*>::Iterator it = items.begin(); it != items.end(); it++ ) {
+// if( (*it)->convertProcess->isRunning() ) {
+// //(*it)->convertProcess->setPriority( priority );
+// pChangePriority.clearArguments();
+// pChangePriority << "renice";
+// QString prio;
+// prio.sprintf( "%i", priority );
+// pChangePriority << prio;
+// QString pid;
+// pid.sprintf( "%i", (*it)->convertProcess->pid() );
+// pChangePriority << pid;
+// //QString cmd;
+// //cmd.sprintf( "renice %i %i", );
+// pChangePriority.start( KProcess::Block );
+// }
+// }
+// }
+
+
diff --git a/src/convert.h b/src/convert.h
new file mode 100755
index 0000000..d69fec4
--- /dev/null
+++ b/src/convert.h
@@ -0,0 +1,281 @@
+
+
+#ifndef CONVERT_H
+#define CONVERT_H
+
+#include <kio/jobclasses.h>
+
+#include <qobject.h>
+#include <qvaluelist.h>
+
+#include <kprocess.h>
+
+class Config;
+class TagEngine;
+class CDManager;
+class FileList;
+class FileListItem;
+class ReplayGain;
+class Logger;
+class KTempFile;
+//class KProcess;
+
+/**
+ * @short The items for the conversion (for every active file)
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConvertItem
+{
+public:
+ /**
+ * A list of flags for knowing what to do
+ */
+ enum Mode {
+ get = 0x0001, // Copy the file to tmp
+ get_correction = 0x0002, // Copy the correction file to tmp
+ rip = 0x0004, // Rip the file
+ decode = 0x0008, // Decode the file
+ encode = 0x0010, // Encode the file
+ replaygain = 0x0020, // Apply replaygain
+ write_tags = 0x0040, // Write the tags to the file
+ put = 0x0080, // Move the file to the output directory
+ put_correction = 0x0100, // Move the correction file to the output directory
+ execute_userscript= 0x0200 // Run the user script
+ };
+
+ /**
+ * Default Constructor
+ */
+ ConvertItem();
+
+ /**
+ * Constructor
+ * @p item A pointer to the file list item
+ */
+ ConvertItem( FileListItem* item );
+
+ /**
+ * Destructor
+ */
+ virtual ~ConvertItem();
+
+ /** a reference to the file list item */
+ FileListItem* fileListItem;
+
+ /** for adding replay gain */
+ ReplayGain* replayGain;
+
+ /** if we need to encode, decode, etc. here we have our processes */
+ KProcess* convertProcess;
+ /** for moving the file to the temporary or output directory */
+ KIO::Job* moveJob;
+
+ KTempFile* tempInFile;
+ KTempFile* tempWavFile;
+ KTempFile* tempOutFile;
+
+ QString correctionInFile;
+ QString correctionOutFile;
+ QString correctionInputExtension;
+ QString correctionOutputExtension;
+
+ //QTime readOutputTimer;
+ QTime lastOutputTimer;
+
+ /** what shall we do with the file? */
+ Mode mode;
+ /** and what are we doing with the file? */
+ Mode state;
+
+ /** the id with that the item is registered at the logger */
+ int logID;
+ /** the binary for special treatment */
+// QString binary;
+
+ /** the path and the name of the output file (needed for executing a command after conversion) */
+ QString outputFilePathName;
+
+ /** if it is an audio cd and it should be ripped to one file: the number of tracks on the cd */
+ int tracks;
+ /** the current track */
+ int track;
+ int lastPercent;
+
+ /** the time from the file list item splitted up */
+ float getTime;
+ float getCorrectionTime;
+ float ripTime;
+ float decodeTime;
+ float encodeTime;
+ float replaygainTime;
+ /** the current conversion progress */
+ int percent;
+};
+
+
+/**
+ * @short The conversion engine
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class Convert : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ Convert( Config*, TagEngine*, CDManager*, FileList*, Logger* );
+
+ /**
+ * Destructor
+ */
+ virtual ~Convert();
+
+ void cleanUp();
+
+private:
+ /**
+ * Copy the file with the file list item @p item to a temporary directory and download or rip, when necessary
+ */
+ void get( ConvertItem* item );
+
+ /**
+ * Copy the correction file with the file list item @p item to a temporary directory and download or rip, when necessary
+ */
+ void getCorrection( ConvertItem* item );
+
+ /**
+ * Rip the file with the convert item @p item from the CD
+ */
+ void rip( ConvertItem* item );
+
+ /**
+ * Decode the file with the convert item @p item
+ */
+ void decode( ConvertItem* item );
+
+ /**
+ * Encode the file with the convert item @p item
+ */
+ void encode( ConvertItem* item );
+
+ /**
+ * Calculate replaygain tags of the file with the convert item @p item
+ */
+ void replaygain( ConvertItem* item );
+
+ /**
+ * Write the tags of the file with the convert item @p item
+ */
+ void writeTags( ConvertItem* item );
+
+ /**
+ * Copy the file with the convert item @p item to the output directory
+ */
+ void put( ConvertItem* item );
+
+ /**
+ * Copy the correction file with the convert item @p item to the output directory
+ */
+ void putCorrection( ConvertItem* item );
+
+ /**
+ * Run the userscript for the convert item @p item
+ */
+ void executeUserScript( ConvertItem* item );
+
+ /**
+ * Decide, what to do next with out item @p item
+ */
+ void executeNextStep( ConvertItem* item );
+
+ /**
+ * Remove item @p item and emit the state @p state
+ */
+ void remove( ConvertItem* item, int state = 0 );
+
+ /** holds all active files */
+ QValueList<ConvertItem*> items;
+
+ Config* config;
+ TagEngine* tagEngine;
+ CDManager* cdManager;
+ FileList* fileList;
+ Logger* logger;
+ QTimer* tUpdateProgressIndicator;
+ KProcess notify;
+
+private slots:
+ /**
+ * The file is being moved
+ * @p job The pinter to the job
+ */
+ void moveProgress( KIO::Job* job, unsigned long percent );
+
+ /**
+ * The file has been moved
+ * @p job The pinter to the job
+ */
+ void moveFinished( KIO::Job* job );
+
+ /**
+ * Get the process' output
+ * @p proc The pinter to the progess
+ * @p data The output data
+ * @p length The length of the data
+ */
+ void processOutput( KProcess *proc, char *data, int length );
+
+ /**
+ * The process has exited
+ * @p proc The pinter to the progess
+ */
+ void processExit( KProcess *proc );
+
+ /**
+ * Updates the progress indicator
+ */
+ void updateProgressIndicator();
+
+public slots:
+ /**
+ * Add a new @p item to the item list and start
+ */
+ void add( FileListItem* item );
+
+ /**
+ * Stop the item with the file list item @p item in the item list and remove it
+ */
+ void stop( FileListItem* item );
+
+ /**
+ * Change the process priorities
+ */
+// void priorityChanged( int );
+
+signals:
+ /**
+ * A job was completed
+ * The job with the file list item @p item was completed
+ * And report the finish @p state ( 0 = ok, -1 = error, 1 = aborted )
+ */
+ void finished( FileListItem* item, int state );
+
+ /**
+ * Send the logger a signal
+ */
+ void finishedProcess( int id, int state );
+
+ /**
+ * The next track from @p device can be ripped while the track is being encoded
+ */
+ void rippingFinished( const QString& device );
+
+ void countTime( float );
+ void uncountTime( float );
+ void update( float );
+};
+
+#endif // CONVERT_H
diff --git a/src/cuesheeteditor.cpp b/src/cuesheeteditor.cpp
new file mode 100755
index 0000000..206611b
--- /dev/null
+++ b/src/cuesheeteditor.cpp
@@ -0,0 +1,325 @@
+
+#include "cuesheeteditor.h"
+
+#include <qlayout.h>
+#include <qstring.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+#include <kmessagebox.h>
+//#include <ktextedit.h>
+
+/*#include <kparts/factory.h> // KPart Factory
+#include <klibloader.h> // LibLoader, contains factories
+#include <kate/document.h> // Katepart document
+#include <kate/view.h> // Katepart view
+*/
+
+// ### soundkonverter 0.4: import/export flac cuesheet
+
+CuesheetEditor::CuesheetEditor( QWidget *parent, const char *name, bool modal, WFlags f )
+ : KDialog( parent, name, modal, f )
+{
+ // TODO can the cuesheet editor be extendet by more tags (composer), etc.
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ setCaption( i18n("Cuesheet Editor") );
+ resize( 600, 400 );
+ setIcon( iconLoader->loadIcon("kwrite",KIcon::Small) );
+
+ QGridLayout *grid = new QGridLayout( this, 4, 1, 11, 6 );
+
+ tTextEdit = new KTextEdit( this, "tTextEdit" );
+ tTextEdit->setFocus();
+ grid->addWidget( tTextEdit, 0, 0 );
+
+/*
+ // Get KPart factory for the katepart library.
+ // This returns 0, if the library can not be found
+ KParts::Factory* factory = (KParts::Factory *)
+ KLibLoader::self()->factory ("libkatepart");
+
+ if (factory)
+ {
+ // The library was found, so create the Kate::Document
+ KTextEditor::Document *doc = (KTextEditor::Document *)
+ factory->createPart( 0, "", this, "", "KTextEditor::Document" );
+
+ // The document only represents the document, to view
+ // the document's content
+ // we have to create a view for the document.
+ Kate::View *view = (Kate::View *) doc->createView( this, 0L );
+
+ // all went well, so return the view
+ //return view;
+ grid->addWidget( view, 0, 0 );
+ }
+ else
+ {
+ // The library was not found
+ //return 0L;
+ }
+*/
+
+ QHBoxLayout *buttonBox = new QHBoxLayout();
+ grid->addLayout( buttonBox, 3, 0 );
+
+ pHelp = new KPushButton( iconLoader->loadIcon("help",KIcon::Small), "", this, "pHelp" );
+ buttonBox->addWidget( pHelp );
+ connect( pHelp, SIGNAL(clicked()),
+ this, SLOT(help())
+ );
+
+ pGenerate = new KPushButton( iconLoader->loadIcon("filenew",KIcon::Small), i18n("Generate"), this, "pGenerate" );
+ buttonBox->addWidget( pGenerate );
+ connect( pGenerate, SIGNAL(clicked()),
+ this, SLOT(generate())
+ );
+
+ pConvert = new KPushButton( iconLoader->loadIcon("run",KIcon::Small), i18n("Format"), this, "pConvert" );
+ buttonBox->addWidget( pConvert );
+ connect( pConvert, SIGNAL(clicked()),
+ this, SLOT(convert())
+ );
+
+ pShift = new KPushButton( iconLoader->loadIcon("reload",KIcon::Small), i18n("Shift Title/Performer"), this, "pShift" );
+ buttonBox->addWidget( pShift );
+ connect( pShift, SIGNAL(clicked()),
+ this, SLOT(shift())
+ );
+
+ buttonBox->addStretch();
+
+ pOk = new KPushButton(iconLoader->loadIcon("exit",KIcon::Small), i18n("Close"), this, "pOk" );
+ pOk->setFocus();
+ buttonBox->addWidget( pOk );
+ connect( pOk, SIGNAL(clicked()),
+ this, SLOT(accept())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+CuesheetEditor::~CuesheetEditor()
+{}
+
+void CuesheetEditor::help()
+{
+ KMessageBox::information( this,
+ i18n("<p>With this small tool you can process cue files as they are used for burning music mixes and for organizing them in media players like amaroK.<br><br>You can generate a new file by pasting text into the input area. It must be formated the following way:<br><br>Title - Artist [time]<br>Title - Artist [3:25]<br>Title - Artist [2:37]<br>...<br>A tip: Use kwrite and regular expressions to format it this way.</p>"),
+ i18n("Cuesheet Editor - Help") );
+}
+
+void CuesheetEditor::generate()
+{
+ QString text = tTextEdit->text();
+ QString newText;
+ QStringList titleList, performerList;
+ QValueList<int> timeList;
+ QString time;
+ int min, sec;
+ int index;
+
+ while( index != -1 )
+ {
+ index = text.find( " - " );
+ if( index == -1 )
+ break;
+ titleList.append( text.left(index) );
+ text.remove( 0, index + 3 );
+ index=text.find( " [" );
+ if( index == -1 )
+ break;
+ performerList.append( text.left(index) );
+ text.remove( 0, index + 2 );
+ index = text.find( "]" );
+ if( index == -1 )
+ break;
+ time = text.left( index );
+ sscanf( time, "%i:%i", &min, &sec );
+ timeList.append( min * 60 + sec );
+ text.remove( 0, index + 2 );
+ }
+
+ newText.append( "TITLE \"\"\n" );
+ newText.append( "PERFORMER \"\"\n" );
+ newText.append( "FILE \"\" MP3\n" );
+
+ int TRACK = 1;
+ int INDEX = 0;
+ bool addFrames = false;
+ QStringList::Iterator performerIt = performerList.begin();
+ QValueList<int>::Iterator timeIt = timeList.begin();
+ for( QStringList::Iterator titleIt = titleList.begin(); titleIt != titleList.end(); ++titleIt )
+ {
+ newText.append( QString().sprintf(" TRACK %02i AUDIO\n",TRACK ) );
+ newText.append( " TITLE \"" + (*titleIt) + "\"\n" );
+ newText.append( " PERFORMER \"" + (*performerIt) + "\"\n" );
+ if( addFrames ) {
+ newText.append( QString().sprintf(" INDEX 01 %02i:%02i:37\n",INDEX/60,INDEX%60) );
+ INDEX++;
+ addFrames = false;
+ }
+ else {
+ newText.append( QString().sprintf(" INDEX 01 %02i:%02i:00\n",INDEX/60,INDEX%60) );
+ addFrames = true;
+ }
+
+ performerIt++;
+ timeIt++;
+ TRACK++;
+ INDEX += (*timeIt);
+ }
+
+ tTextEdit->setText(newText);
+}
+
+void CuesheetEditor::convert()
+{
+ QString text=tTextEdit->text();
+ QString newText;
+ QString tmpText;
+ QString first, rest;
+ QStringList splinters;
+ int index;
+
+ while( index!=-1 )
+ {
+ index=text.find("\"");
+ if( index==-1 )
+ break;
+ newText+=text.left(index+1);
+ text.remove(0,index+1);
+ index=text.find("\"");
+ tmpText=text.left(index+1);
+ text.remove(0,index+1);
+ if( newText.right(6) == "FILE \"" )
+ {
+ newText+=tmpText;
+ continue;
+ }
+ splinters=QStringList::split(' ',tmpText);
+ for( QStringList::Iterator it=splinters.begin(); it!=splinters.end(); ++it )
+ {
+ for( uint i=0; i<(*it).length(); i++ )
+ {
+ if( (*it).left(i).lower() != (*it).left(i).upper() )
+ {
+ index=i;
+ break;
+ }
+ }
+ first=(*it).left(index);
+ first=first.upper();
+ rest=(*it).right((*it).length()-index);
+ rest=rest.lower();
+ for( uint i=0; i<rest.length(); i++ )
+ {
+ /*if( rest.mid(i,1).lower() == rest.mid(i,1).upper() )
+ {
+ rest=rest.left(i+1)+rest.mid(i+1,1).upper()+rest.right(rest.length()-i-2);
+ }*/
+ if( rest.mid(i,1) == "-" )
+ {
+ rest=rest.left(i+1)+rest.mid(i+1,1).upper()+rest.right(rest.length()-i-2);
+ }
+ if( rest.mid(i,1).lower() == "j" && rest.mid(i-1,1).lower() == "d" )
+ {
+ rest=rest.left(i-1)+rest.mid(i-1,2).upper()+rest.right(rest.length()-i-1);
+ }
+ if( rest.mid(i,1).lower() == "j" && first.right(1).lower() == "d" )
+ {
+ rest=rest.left(i)+rest.mid(i,1).upper()+rest.right(rest.length()-i-1);
+ }
+ }
+
+ newText += first + rest + " ";
+ }
+ newText.remove( newText.length() - 1, 1 );
+ }
+
+ newText += text;
+
+ tTextEdit->setText( newText );
+}
+
+void CuesheetEditor::shift() //move the title to "PERFORMER" and performer to "TITLE"
+{
+ QString text = tTextEdit->text();
+ QString newText;
+ QString line, title="", performer="";
+ int index;
+
+ while( index !=- 1 )
+ {
+ index = text.find("\n");
+ if( index == -1 )
+ break;
+ line = text.left(index+1);
+ text.remove(0,index+1);
+ // TODO clean up
+ if( line.find( "TITLE" ) != -1 ) {
+ line.replace( "TITLE", "PERFORMER" );
+ if( performer != "" ) newText += performer;
+ performer=line;
+ }
+ else if( line.find( "PERFORMER" ) != -1 ) {
+ line.replace( "PERFORMER", "TITLE" );
+ if( title != "" ) newText += title;
+ title = line;
+ }
+ else {
+ if( title != "" ) newText += title;
+ if( performer != "" ) newText += performer;
+ title = "";
+ performer = "";
+ newText += line;
+ }
+
+ if( title != "" && performer != "" ) {
+ newText += title;
+ newText += performer;
+ title = "";
+ performer = "";
+ }
+ }
+
+ tTextEdit->setText( newText );
+}
+
+
+/*
+void CuesheetEditor::shift() //replace title by performer and reverse
+{
+ QString text=tTextEdit->text();
+ QString newText;
+ QString line, title, performer;
+ int index;
+
+ while( index!=-1 )
+ {
+ index=text.find("\n");
+ if( index==-1 )
+ break;
+ line=text.left(index+1);
+ text.remove(0,index+1);
+ if( line.find(" TITLE \"") != -1 ) {
+ line.replace(" TITLE \""," PERFORMER \"");
+ newText+=line;
+ }
+ else if( line.find(" PERFORMER \"") != -1 ) {
+ line.replace(" PERFORMER \""," TITLE \"");
+ newText+=line;
+ }
+ else {
+ newText+=line;
+ }
+ }
+
+ tTextEdit->setText(newText);
+}
+*/
diff --git a/src/cuesheeteditor.h b/src/cuesheeteditor.h
new file mode 100755
index 0000000..db5d986
--- /dev/null
+++ b/src/cuesheeteditor.h
@@ -0,0 +1,50 @@
+
+
+#ifndef CUESHEETEDITOR_H
+#define CUESHEETEDITOR_H
+
+#include <kdialog.h>
+#include <ktextedit.h>
+
+
+//class KTextEdit;
+class KPushButton;
+
+/**
+ * @short The editor tool for cuesheet files
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class CuesheetEditor : public KDialog
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ CuesheetEditor( QWidget* parent=0, const char* name=0, bool modal=true, WFlags f=0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~CuesheetEditor();
+
+ void setContent( const QString& text ) { tTextEdit->setText( text ); }
+
+private slots:
+ void help();
+ void convert();
+ void generate();
+ void shift();
+
+private:
+ KTextEdit* tTextEdit;
+ KPushButton* pHelp;
+ KPushButton* pGenerate;
+ KPushButton* pConvert;
+ KPushButton* pShift;
+ KPushButton* pOk;
+
+};
+
+#endif // CUESHEETEDITOR_H
diff --git a/src/dcopinterface.h b/src/dcopinterface.h
new file mode 100755
index 0000000..18a097c
--- /dev/null
+++ b/src/dcopinterface.h
@@ -0,0 +1,58 @@
+
+
+#ifndef DCOPINTERFACE_H
+#define DCOPINTERFACE_H
+
+#include <dcopobject.h>
+
+#include <qstringlist.h>
+
+/**
+ * @short The soundKonverter DCOP interface
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class DCOPInterface : virtual public DCOPObject
+{
+ K_DCOP
+k_dcop:
+ /**
+ * When a new instance of soundKonverter should be created,
+ * this function is called and all @p files are passed, that should be opened (for conversion).
+ */
+ virtual void openArgFiles( const QStringList &files ) = 0;
+
+ /**
+ * When a new instance of soundKonverter should be created,
+ * this function is called and all @p files are passed, that should be opened for editing the replaygain tag.
+ */
+ virtual void openArgReplayGainFiles( const QStringList &files ) = 0;
+
+ /*
+ * When a new instance of soundKonverter should be created,
+ * this function is called and all @p files are passed, that should be opened for repair.
+ */
+// virtual void openArgRepairFiles( const QStringList &files ) = 0;
+
+
+// TODO code cleanups
+/*
+ virtual void openFiles(const QStringList &files) = 0;
+ virtual void openReplayGainFiles(const QStringList &files) = 0;
+ virtual void showFileDialog() = 0;
+ virtual void showDirDialog() = 0;
+ virtual void showCDDialog() = 0;
+ virtual void showURLDialog() = 0;
+ virtual void showReplayGainScanner() = 0;
+ virtual void showCuesheetEditor() = 0;
+ virtual void showConfigDialog() = 0;
+ virtual void showLogViewer() = 0;
+ virtual void startConversion() = 0;
+ virtual void stopConversion() = 0;
+ virtual void killConversion() = 0;
+ virtual void removeFiles(const QStringList &files) = 0;
+*/
+};
+
+
+#endif // DCOPINTERFACE_H
diff --git a/src/dirdialog.cpp b/src/dirdialog.cpp
new file mode 100755
index 0000000..924036d
--- /dev/null
+++ b/src/dirdialog.cpp
@@ -0,0 +1,154 @@
+
+#include "dirdialog.h"
+#include "config.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qdir.h>
+#include <qcheckbox.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <kpushbutton.h>
+// #include <kurlrequester.h>
+#include <klistbox.h>
+#include <kfiledialog.h>
+
+DirDialog::DirDialog( Config* config, Mode mode, QWidget *parent, const char *name, bool modal, WFlags f )
+ : KDialog( parent, name, modal, f )
+{
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ setCaption( i18n("Add folder") );
+ resize( 400, 235 );
+ setIcon( iconLoader->loadIcon("folder_open",KIcon::Small) );
+
+ QGridLayout* grid = new QGridLayout( this, 4, 1, 11, 6 );
+
+ QHBoxLayout* directoryBox = new QHBoxLayout();
+ grid->addLayout( directoryBox, 0, 0 );
+
+ QLabel* labelDirectory = new QLabel( i18n("Directory:"), this, "labelDirectory" );
+ directoryBox->addWidget( labelDirectory );
+
+// uDirectory = new KURLRequester( this, "uDirectory" );
+// uDirectory->setMode( KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly );
+// uDirectory->setURL( QDir::homeDirPath() );
+// directoryBox->addWidget( uDirectory );
+
+ lDirectory = new KLineEdit( this, "lDirectory" );
+ directoryBox->addWidget( lDirectory );
+
+ pDirectory = new KPushButton( iconLoader->loadIcon("folder_open",KIcon::Small), "", this, "pDirectory" );
+ directoryBox->addWidget( pDirectory );
+ connect( pDirectory, SIGNAL(clicked()),
+ this, SLOT(selectDirectoryClicked())
+ );
+
+ QHBoxLayout* fileTypesBox = new QHBoxLayout();
+ grid->addLayout( fileTypesBox, 1, 0 );
+
+ fileTypes = new KListBox( this, "fileTypes" );
+ if( mode == Convert ) fileTypes->insertStringList( config->fileTypes() );
+ else if( mode == ReplayGain ) fileTypes->insertStringList( config->replayGainFileTypes() );
+ fileTypes->setSelectionMode( QListBox::Multi );
+ for( int i = 0; i < fileTypes->count(); i++ ) fileTypes->setSelected( i, true );
+ fileTypesBox->addWidget( fileTypes );
+
+ QVBoxLayout* fileTypesButtonsBox = new QVBoxLayout();
+ fileTypesBox->addLayout( fileTypesButtonsBox );
+
+ pSelectAll = new KPushButton( iconLoader->loadIcon("font",KIcon::Small), i18n("Select all"), this, "pSelectAll" );
+ fileTypesButtonsBox->addWidget( pSelectAll );
+ connect( pSelectAll, SIGNAL(clicked()),
+ this, SLOT(selectAllClicked())
+ );
+
+ pSelectNone = new KPushButton( iconLoader->loadIcon("empty",KIcon::Small), i18n("Select none"), this, "pSelectNone" );
+ fileTypesButtonsBox->addWidget( pSelectNone );
+ connect( pSelectNone, SIGNAL(clicked()),
+ this, SLOT(selectNoneClicked())
+ );
+
+ cRecursive = new QCheckBox( i18n("Recursive"), this, "cRecursive" );
+ cRecursive->setChecked( true );
+ recursive = true;
+ fileTypesButtonsBox->addWidget( cRecursive );
+ connect( cRecursive, SIGNAL(toggled(bool)),
+ this, SLOT(recursiveToggled(bool))
+ );
+
+ fileTypesButtonsBox->addStretch();
+
+ QHBoxLayout* buttonBox = new QHBoxLayout();
+ grid->addLayout( buttonBox, 2, 0 );
+
+ pOk = new KPushButton( iconLoader->loadIcon("folder_open",KIcon::Small), i18n("Open"), this, "pOk" );
+ buttonBox->addWidget( pOk );
+ connect( pOk, SIGNAL(clicked()),
+ this, SLOT(okClicked())
+ );
+
+ buttonBox->addStretch();
+
+ pCancel = new KPushButton( iconLoader->loadIcon("cancel",KIcon::Small),i18n("Cancel"), this, "pCancel" );
+ pOk->setFocus();
+ buttonBox->addWidget( pCancel );
+ connect( pCancel, SIGNAL(clicked()),
+ this, SLOT(reject())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+
+ QString directory = KFileDialog::getExistingDirectory( ":file_open", this, i18n("Choose a directory") );
+ if( !directory.isEmpty() )
+ {
+ lDirectory->setText( directory );
+ }
+ else
+ {
+ lDirectory->setText( QDir::homeDirPath() );
+ }
+}
+
+DirDialog::~DirDialog()
+{}
+
+void DirDialog::okClicked()
+{
+ selectedFileTypes.clear();
+ for( int i = 0; i < fileTypes->count(); i++ ) {
+ if( fileTypes->isSelected(i) ) selectedFileTypes += QStringList::split(", ",fileTypes->text(i));
+ }
+ directory = lDirectory->text();
+
+ emit accept();
+}
+
+void DirDialog::selectDirectoryClicked()
+{
+ QString directory = KFileDialog::getExistingDirectory( lDirectory->text(), this, i18n("Choose a directory") );
+ if( !directory.isEmpty() )
+ {
+ lDirectory->setText( directory );
+ }
+}
+
+void DirDialog::selectAllClicked()
+{
+ for( int i = 0; i < fileTypes->count(); i++ ) fileTypes->setSelected( i, true );
+}
+
+void DirDialog::selectNoneClicked()
+{
+ for( int i = 0; i < fileTypes->count(); i++ ) fileTypes->setSelected( i, false );
+}
+
+void DirDialog::recursiveToggled( bool checked )
+{
+ recursive = checked;
+}
+
diff --git a/src/dirdialog.h b/src/dirdialog.h
new file mode 100755
index 0000000..4227a9e
--- /dev/null
+++ b/src/dirdialog.h
@@ -0,0 +1,65 @@
+
+#ifndef DIRDIALOG_H
+#define DIRDIALOG_H
+
+#include <kdialog.h>
+
+class Config;
+
+class QCheckBox;
+
+class KLineEdit;
+class KPushButton;
+// class KURLRequester;
+class KListBox;
+
+/**
+ * @short The Replay Gain Tool
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class DirDialog : public KDialog
+{
+ Q_OBJECT
+public:
+ enum Mode {
+ Convert = 0x0001,
+ ReplayGain = 0x0002
+ };
+
+ /**
+ * Constructor
+ */
+ DirDialog( Config*, Mode, QWidget* parent=0, const char* name=0, bool modal=true, WFlags f=0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~DirDialog();
+
+ QString directory;
+ QStringList selectedFileTypes;
+ bool recursive;
+
+private slots:
+ void selectDirectoryClicked();
+ void okClicked();
+ void selectAllClicked();
+ void selectNoneClicked();
+ void recursiveToggled( bool );
+
+private:
+// KURLRequester* uDirectory;
+ KLineEdit* lDirectory;
+ KPushButton* pDirectory;
+ KListBox* fileTypes;
+ KPushButton* pSelectAll;
+ KPushButton* pSelectNone;
+ QCheckBox* cRecursive;
+ KPushButton* pCancel;
+ KPushButton* pOk;
+
+};
+
+
+#endif
diff --git a/src/filelist.cpp b/src/filelist.cpp
new file mode 100755
index 0000000..c4e6b5d
--- /dev/null
+++ b/src/filelist.cpp
@@ -0,0 +1,1445 @@
+
+#include "filelist.h"
+#include "cdmanager.h"
+#include "tagengine.h"
+#include "options.h"
+#include "convert.h"
+#include "optionseditor.h"
+#include "outputdirectory.h"
+#include "config.h"
+#include "logger.h"
+#include "convertpluginloader.h" // NOTE DEBUG
+
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kactioncollection.h>
+#include <kmimetype.h>
+#include <kurl.h>
+#include <kmountpoint.h>
+#include <kstandarddirs.h>
+#include <kurldrag.h>
+#include <kapplication.h>
+
+#include <qlayout.h>
+#include <qfileinfo.h>
+#include <qsimplerichtext.h>
+#include <qpainter.h>
+#include <qapplication.h>
+#include <qdragobject.h>
+#include <qheader.h>
+#include <qdir.h>
+#include <kprogress.h>
+#include <kuser.h>
+
+// TODO when stopping items by using the context menu, the queue mode restarts that item
+
+// TODO when dropping items, don't let the user select the position
+
+// ### soundkonverter 0.4: draw tooltip like bubble info box
+
+FileListItem::FileListItem( KListView* parent, FileListItem* after )
+ : KListViewItem( parent, after )
+{
+ tags = 0;
+ converting = false;
+ time = 0;
+ ripping = false;
+}
+
+FileListItem::FileListItem( KListView* parent )
+ : KListViewItem( parent )
+{
+ tags = 0;
+ converting = false;
+ time = 0;
+ ripping = false;
+}
+
+FileListItem::~FileListItem()
+{}
+
+void FileListItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int alignment )
+{
+ // NOTE speed up this function
+ // NOTE calculate the red color
+
+ QColorGroup _cg( cg );
+ QColor c;
+
+ if( column == ((FileList*)listView())->columnByName(i18n("Input")) || column == ((FileList*)listView())->columnByName(i18n("Output")) )
+ {
+ int margin = listView()->itemMargin();
+ int w = width - 2*margin;
+ int h = height();
+ QRect textRect = p->boundingRect( margin, 0, w, h, alignment, text(column) );
+
+ if( textRect.width() > w ) {
+ alignment = Qt::AlignRight | Qt::SingleLine;
+ }
+
+ /*if ( textRect.width() <= w ) {
+ p->drawText( margin, 0, w, h, alignment | Qt::SingleLine | Qt::ExpandTabs, text(column), -1 );
+ }
+ else {
+ textRect = p->boundingRect( margin, 0, w, h, Qt::AlignLeft, "... " );
+ p->drawText( margin, 0, textRect.width(), h, Qt::AlignLeft | Qt::SingleLine | Qt::ExpandTabs, "...", -1 );
+ p->drawText( margin+textRect.width(), 0, w-textRect.width(), h, Qt::AlignRight | Qt::SingleLine | Qt::ExpandTabs, text(column), -1 );
+ }*/
+ }
+
+ if( isSelected() && converting ) {
+ _cg.setColor( QColorGroup::Highlight, QColor( 215, 62, 62 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( converting && column != listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 255, 234, 234 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( converting && column == listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 247, 227, 227 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+
+ KListViewItem::paintCell( p, _cg, column, width, alignment );
+}
+
+/*void FileListItem::updateOutputCell()
+{
+ setText( ((FileList*)listView())->columnByName(i18n("Output")), OutputDirectory::calcPath(this) ); // FIXME no config !!!
+}
+
+void FileListItem::updateOptionsCell()
+{
+ setText( ((FileList*)listView())->columnByName(i18n("Quality")), ((FileList*)listView())->config->getProfileName(options) );
+}*/
+
+FileList::FileList( CDManager* _cdManager, TagEngine* _tagEngine, Config* _config, Options* _options, Logger* _logger, QWidget* parent, const char* name )
+ : KListView( parent, name )
+{
+ cdManager = _cdManager;
+ tagEngine = _tagEngine;
+ config = _config;
+ options = _options;
+ logger = _logger;
+ optionsEditor = 0;
+
+ queue = false;
+ notify = "";
+
+ setAcceptDrops( true );
+ setDragEnabled( true );
+
+ addColumn( i18n("State"), 120 );
+ addColumn( i18n("Input"), 180 );
+ addColumn( i18n("Output"), 180 );
+ addColumn( i18n("Quality") );
+
+ header()->setClickEnabled( false );
+
+ setSelectionMode( QListView::Extended );
+ setAllColumnsShowFocus( true );
+ setResizeMode( LastColumn );
+ setSorting( -1 ); // NOTE if commented out, items aren't moveable anymore
+
+ setMinimumHeight( 200 );
+
+ QGridLayout* grid = new QGridLayout( this, 2, 1, 11, 6 );
+ grid->setRowStretch( 0, 1 );
+ grid->setRowStretch( 2, 1 );
+ grid->setColStretch( 0, 1 );
+ grid->setColStretch( 2, 1 );
+ pScanStatus = new KProgress( this, "pScanStatus" );
+ pScanStatus->setMinimumHeight( pScanStatus->height() );
+ pScanStatus->setFormat( "%v / %m" );
+ pScanStatus->hide();
+ grid->addWidget( pScanStatus, 1, 1 );
+ grid->setColStretch( 1, 2 );
+
+ contextMenu = new KPopupMenu( this );
+ connect( this, SIGNAL(contextMenuRequested( QListViewItem*, const QPoint&, int )),
+ this, SLOT(showContextMenu( QListViewItem*, const QPoint&, int ))
+ );
+
+ // we haven't got access to the action collection of soundKonverter, so let's create a new one
+ actionCollection = new KActionCollection( this );
+
+ edit = new KAction( i18n("Edit options ..."), "view_text", 0, this, SLOT(showOptionsEditorDialog()), actionCollection, "edit_options" );
+ start = new KAction( i18n("Start conversion"), "run", 0, this, SLOT(convertSelectedItems()), actionCollection, "start_conversion" );
+ stop = new KAction( i18n("Stop conversion"), "stop", 0, this, SLOT(stopSelectedItems()), actionCollection, "stop_conversion" );
+ remove = new KAction( i18n("Remove"), "edittrash", Key_Delete, this, SLOT(removeSelectedItems()), actionCollection, "remove" );
+ paste = new KAction( i18n("Paste"), "editpaste", 0, this, 0, actionCollection, "paste" ); // TODO paste
+
+ connect( this, SIGNAL(selectionChanged()),
+ this, SLOT(itemsSelected())
+ );
+
+// connect( this, SIGNAL(clicked(QListViewItem*,const QPoint&,int)),
+// this, SLOT(clickedSomewhere(QListViewItem*,const QPoint&,int))
+// );
+
+ bubble = new QSimpleRichText( i18n( "<div align=center>"
+ "<h3>File List</h3>"
+ "Select your desired output options in the form above and add some files.<br/>"
+ "You can add files by clicking on \"Add files ...\" or dropping them here."
+// "<br/><a href=\"documenation:about_compression\">Learn more about audio compression ...</a>"
+ "</div>" ), QApplication::font() );
+
+ connect( header(), SIGNAL(sizeChange( int, int, int )),
+ SLOT(columnResizeEvent( int, int, int ))
+ );
+ connect( this, SIGNAL( dropped(QDropEvent*, QListViewItem*, QListViewItem*) ),
+ SLOT( slotDropped(QDropEvent*, QListViewItem*, QListViewItem*) )
+ );
+
+// if( QFile::exists(locateLocal("data","soundkonverter/filelist.autosave.xml")) ) load( true );
+
+// debug(); // NOTE DEBUG
+}
+
+FileList::~FileList()
+{
+ delete optionsEditor;
+}
+
+int FileList::columnByName( const QString& name )
+{
+ for( int i = 0; i < columns(); ++i ) {
+ if( columnText( i ) == name ) return i;
+ }
+ return -1;
+}
+
+void FileList::viewportPaintEvent( QPaintEvent* e )
+{
+ KListView::viewportPaintEvent( e );
+
+ // the bubble help
+ if( childCount() == 0 ) {
+ QPainter p( viewport() );
+
+ bubble->setWidth( width() - 50 );
+
+ const uint w = bubble->width() + 20;
+ const uint h = bubble->height() + 20;
+
+ p.setBrush( colorGroup().background() );
+ p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h );
+ bubble->draw( &p, 20, 20, QRect(), colorGroup() );
+ }
+}
+
+void FileList::viewportResizeEvent( QResizeEvent* )
+{
+ // needed for correct redraw of bubble help
+ triggerUpdate();
+}
+
+void FileList::columnResizeEvent( int, int, int )
+{
+ // needed for correct redraw of bubble help
+ triggerUpdate();
+}
+
+// void FileList::clickedSomewhere( QListViewItem*, const QPoint& pos, int )
+// {
+// /* if( childCount() == 0 ) {
+// kdDebug() << "clicked: `" << bubble->anchorAt(mapFromGlobal(pos)-QPoint(24,0)) << " (" << pos.x() << " | " << pos.y() << ")'" << endl;
+// }*/
+// }
+
+bool FileList::acceptDrag( QDropEvent* e ) const
+{
+ return ( e->source() == viewport() || KURLDrag::canDecode(e) ); // TODO verify the files
+}
+
+void FileList::slotDropped( QDropEvent* e, QListViewItem*, QListViewItem* itemAfter )
+{
+ QString file;
+ KURL::List list;
+ QStringList files;
+ if( KURLDrag::decode( e, list ) ) // TODO local?
+ {
+ save( true );
+ for( KURL::List::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // TODO verify the files (necessary when multiple files are being dropped)
+ // TODO implement cdda:/
+ file = QDir::convertSeparators( (*it).pathOrURL() ); // TODO implement that in the url/file dialog, too?
+ QFileInfo fileInfo( file );
+ if( fileInfo.isDir() )
+ {
+ addDir( file );
+ }
+ else
+ {
+ files.append( (*it).url() );
+ }
+ }
+ addFiles( files, (FileListItem*)itemAfter, true );
+ save( true );
+ }
+}
+
+void FileList::showContextMenu( QListViewItem* item, const QPoint& point, int )
+{
+ // if item is null, we can abort here
+ if( !item ) return;
+
+ // remove all items from the context menu
+ contextMenu->clear();
+
+ // add a tilte to our context manu
+ //contextMenu->insertTitle( static_cast<FileListItem*>(item)->fileName ); // TODO sqeeze or something else
+
+ // TODO implement pasting, etc.
+
+ // is this file (of our item) beeing converted at the moment?
+ if( !static_cast<FileListItem*>(item)->converting ) {
+ edit->plug( contextMenu );
+ contextMenu->insertSeparator();
+ remove->plug( contextMenu );
+ //paste->plug( contextMenu );
+ contextMenu->insertSeparator();
+ start->plug( contextMenu );
+ }
+ else {
+ stop->plug( contextMenu );
+ //contextMenu->insertSeparator();
+ //remove->plug( contextMenu );
+ //paste->plug( contextMenu );
+ }
+
+ // show the popup menu
+ contextMenu->popup( point );
+}
+
+void FileList::removeSelectedItems()
+{
+ FileListItem *item = firstChild(), *nextitem = 0;
+
+ while( item != 0 ) {
+ if( item->isSelected() && !item->converting ) {
+ nextitem = item->nextSibling();
+ emit decreaseTime( item->time );
+ delete item;
+ item = nextitem;
+ }
+ else {
+ item = item->nextSibling();
+ }
+ }
+ emit fileCountChanged( childCount() );
+ itemsSelected();
+}
+
+void FileList::convertSelectedItems()
+{
+ FileListItem* item = firstChild();
+
+ while( item != 0 ) {
+ if( item->isSelected() && !item->converting ) {
+ emit convertItem( item );
+ }
+ item = item->nextSibling();
+ }
+ itemsSelected();
+ emit startedConversion();
+}
+
+void FileList::stopSelectedItems()
+{
+ FileListItem* item = firstChild();
+
+ while( item != 0 ) {
+ if( item->isSelected() && item->converting ) {
+ emit stopItem( item );
+ }
+ item = item->nextSibling();
+ }
+}
+
+int FileList::listDir( const QString& directory, QStringList filter, bool recursive, bool fast, int count )
+{ // NOTE speed up?
+ QDir dir( directory );
+ dir.setFilter( QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::Readable );
+
+ QStringList list = dir.entryList();
+
+ for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
+ if( *it == "." || *it == ".." ) continue;
+ QFileInfo fileInfo( directory + "/" + *it );
+ if( fast ) {
+ if( fileInfo.isDir() && recursive ) {
+ count = listDir( fileInfo.filePath(), filter, recursive, fast, count );
+ }
+ else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names
+ // NOTE filter feature
+ for( QStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) {
+ if( (*it).endsWith("."+(*jt),false) ) {
+ count++;
+ pScanStatus->setTotalSteps( count );
+ break;
+ }
+ }
+ if( filter.first() == "" ) {
+ count++;
+ pScanStatus->setTotalSteps( count );
+ }
+ }
+ }
+ else {
+ if( fileInfo.isDir() && recursive ) {
+ count = listDir( fileInfo.filePath(), filter, recursive, fast, count );
+ }
+ else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names
+ // NOTE filter feature
+ for( QStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) {
+ if( (*it).endsWith("."+(*jt),false) ) {
+ addFiles( KURL::encode_string(directory + "/" + *it) );
+ count++;
+ pScanStatus->setProgress( count );
+ break;
+ }
+ }
+ if( filter.first() == "" ) {
+ addFiles( KURL::encode_string(directory + "/" + *it) );
+ count++;
+ pScanStatus->setProgress( count );
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+// NOTE progressbar when adding files?
+void FileList::addFiles( QStringList fileList, FileListItem* after, bool enabled )
+{
+ // TODO test if everything works with remote files (http://) and local files (media://)
+ FileListItem* lastListItem;
+ if( !after && !enabled ) lastListItem = lastItem();
+ else lastListItem = after;
+ QString filePathName;
+ QString device;
+
+ for( QStringList::Iterator it = fileList.begin(); it != fileList.end(); ++it ) {
+ FileListItem* newItem = new FileListItem( this, lastListItem );
+ lastListItem = newItem;
+ newItem->options = options->getCurrentOptions(); // FIXME speed up
+ newItem->notify = notify;
+ newItem->local = false;
+ newItem->track = -1;
+ newItem->url = *it;
+
+ if( (*it).left( 1 ) == "/" ) {
+ filePathName = *it;
+ newItem->local = true;
+ }
+ else if( (*it).left( 7 ) == "file://" ) {
+ filePathName = *it;
+ filePathName.remove( 0, 7 );
+ newItem->local = true;
+ }
+ else if( (*it).left( 13 ) == "system:/home/" ) {
+ filePathName = *it;
+ filePathName.remove( 0, 13 );
+ filePathName = QDir::homeDirPath() + "/" + filePathName;
+ newItem->local = true;
+ }
+ else if( (*it).left( 14 ) == "system:/users/" || (*it).left( 6 ) == "home:/" ) {
+ int length = ( (*it).left(6) == "home:/" ) ? 6 : 14;
+ QString username = *it;
+ username.remove( 0, length );
+ username = username.left( username.find("/") );
+ filePathName = *it;
+ filePathName.remove( 0, length + username.length() );
+ KUser user( username );
+ filePathName = user.homeDir() + filePathName;
+ newItem->local = true;
+ }
+ else if( (*it).left( 14 ) == "system:/media/" || (*it).left( 7 ) == "media:/" ) {
+ int length = ( (*it).left(7) == "media:/" ) ? 7 : 14;
+ device = *it;
+ device.remove( 0, length );
+ device = "/dev/" + device.left( device.find( "/" ) );
+
+ KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
+
+ for( KMountPoint::List::ConstIterator jt = mountPoints.begin(); jt != mountPoints.end(); ++jt )
+ {
+ const KSharedPtr<KMountPoint> mp = *jt;
+ logger->log( 1000, mp->mountedFrom() + " : " + mp->mountPoint() );
+ if( mp->mountedFrom() == device )
+ {
+ filePathName = ( mp->mountPoint() == "/" ) ? mp->mountPoint() : mp->mountPoint() + "/";
+ filePathName += (*it).right( (*it).length() - device.length() - length + 4 );
+ }
+ }
+
+ newItem->local = true;
+ }
+// else if( (*it).left( 14 ) == "system:/trash/" || (*it).left( 7 ) == "trash:/" ) {
+// }
+
+ if( newItem->local == true ) {
+// logger->log( 1000, i18n("Adding file") + ": " + filePathName );
+ newItem->mimeType = KMimeType::findByFileContent( filePathName )->name();
+ newItem->fileFormat = KMimeType::findByFileContent( filePathName )->patterns().first().lower();
+ newItem->fileFormat.remove( 0, 2 );
+// logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
+// newItem->mimeType="";
+ if( newItem->mimeType.isEmpty() || newItem->mimeType == "application/octet-stream" || newItem->mimeType == "text/plain" ) {
+ newItem->mimeType = KMimeType::findByURL( filePathName.lower() )->name();
+ newItem->fileFormat = KMimeType::findByURL( filePathName.lower() )->patterns().first().lower();
+ newItem->fileFormat.remove( 0, 2 );
+// logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
+// newItem->mimeType="";
+ // HACK last choise is to use the extension without KDE's help
+ if( newItem->mimeType.isEmpty() || newItem->mimeType == "application/octet-stream" || newItem->mimeType == "text/plain" ) {
+ newItem->fileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 );
+ FormatItem *formatItem = config->getFormatItem( newItem->fileFormat );
+ if( formatItem ) newItem->mimeType = formatItem->mime_types.first();
+// logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
+ }
+ }
+// logger->log( 1000, " " + i18n("Mime type") + ": " + newItem->mimeType + " (" + i18n("Format") + ": " + newItem->fileFormat + ")" );
+ // check whether the mime type has a decoder registered
+ if( !config->acceptFile( newItem->mimeType ) || ( newItem->fileFormat == "wav" && newItem->options.encodingOptions.sFormat == "wav" ) ) {
+ delete newItem;
+ continue;
+ }
+ newItem->options.filePathName = filePathName;
+ QFileInfo fileInfo( filePathName );
+ newItem->fileName = fileInfo.fileName();
+ newItem->tags = tagEngine->readTags( KURL::decode_string(filePathName) );
+ if( newItem->tags == 0 ) {
+// logger->log( 1000, " " + i18n("Reading tags failed") );
+ // FIXME check for wav files
+ FormatItem* formatItem = config->getFormatItem( newItem->mimeType );
+ if( formatItem && formatItem->size > 0 ) {
+ newItem->time = fileInfo.size() / formatItem->size;
+ }
+ else {
+ newItem->time = 210;
+ }
+ }
+ else {
+// logger->log( 1000, " " + i18n("Tags successfully read") );
+ newItem->time = newItem->tags->length;
+ }
+ }
+ else {
+// logger->log( 1000, " File is remote (not yet implemented) (" + *it + ")" );
+ filePathName = *it;
+ newItem->fileName = filePathName.right( filePathName.length() - filePathName.findRev("/") - 1 );
+ newItem->fileFormat = newItem->fileName.right( newItem->fileName.length() - newItem->fileName.findRev(".") - 1 ).lower();
+ // NOTE http will not work with KMimeType - this just works
+ if( filePathName.startsWith("http://") ) {
+ newItem->mimeType = KMimeType::findByURL( "file:///" + newItem->fileName.lower() )->name();
+ }
+ else {
+ newItem->mimeType = KMimeType::findByURL( filePathName.lower() )->name();
+ }
+ // check whether the mime type has a decoder registered
+ if( newItem->mimeType == "" ) {
+ newItem->mimeType = newItem->fileFormat;
+ }
+ if( !config->acceptFile( newItem->mimeType ) || ( newItem->fileFormat == "wav" && newItem->options.encodingOptions.sFormat == "wav" ) ) {
+ delete newItem;
+ continue;
+ }
+ newItem->options.filePathName = filePathName;
+ newItem->tags = 0;
+ newItem->time = 210; // NOTE we can't neither get the length nor guess it without accessing the file
+ // TODO get the file size and guess the length
+ }
+
+ updateItem( newItem );
+ emit increaseTime( newItem->time );
+ }
+ emit fileCountChanged( childCount() );
+}
+
+void FileList::addDir( const QString& directory, const QStringList& filter, bool recursive )
+{
+ pScanStatus->setProgress( 0 );
+ pScanStatus->setTotalSteps( 0 );
+ pScanStatus->show(); // show the status while scanning the directories
+ kapp->processEvents();
+
+ int count = listDir( directory, filter, recursive, true );
+ listDir( directory, filter, recursive );
+
+ pScanStatus->hide(); // hide the status bar, when the scan is done
+}
+
+void FileList::addTracks( const QString& device, QValueList<int> trackList )
+{
+ for( QValueList<int>::Iterator it = trackList.begin(); it != trackList.end(); ++it ) {
+// logger->log( 1000, i18n("Adding track") + QString().sprintf(" %02i",*it) );
+ FileListItem* newItem = new FileListItem( this, lastItem() );
+ newItem->options = options->getCurrentOptions(); // FIXME speed up
+// if( newItem->options.outputOptions.mode != OutputDirectory::Specify ) newItem->options.outputOptions.mode = OutputDirectory::MetaData;
+ newItem->notify = notify;
+ newItem->track = (*it);
+ newItem->fileName = i18n("Audio CD (%1)").arg(device) + ": " + i18n("Track") + QString().sprintf(" %02i",newItem->track);
+ newItem->mimeType = "application/x-cda";
+ newItem->fileFormat = "cda";
+ newItem->local = true;
+ newItem->device = device;
+ newItem->tags = cdManager->getTags( device, newItem->track );
+ if( newItem->tags == 0 ) { // NOTE shouldn't happen
+// logger->log( 1000, " " + i18n("Reading tags failed") );
+ newItem->time = 210;
+ newItem->options.filePathName = QDir::homeDirPath() + "/" + i18n("Track") + QString().sprintf(" %02i",newItem->track) + "." + newItem->fileFormat;
+ }
+ else {
+// logger->log( 1000, " " + i18n("Tags successfully read") );
+ newItem->time = newItem->tags->length;
+ newItem->options.filePathName = QDir::homeDirPath() + "/" + QString().sprintf("%02i - ",newItem->track) + newItem->tags->title + "." + newItem->fileFormat;
+ newItem->fileName = i18n("Audio CD (%1)").arg(device) + ": " + QString().sprintf("%02i - ",newItem->track) + newItem->tags->title;
+ }
+ updateItem( newItem );
+ emit increaseTime( newItem->time );
+ }
+ emit fileCountChanged( childCount() );
+}
+
+void FileList::addDisc( const QString& device )
+{
+// logger->log( 1000, i18n("Adding full audio CD (%1)").arg(device) );
+ FileListItem* newItem = new FileListItem( this, lastItem() );
+ newItem->options = options->getCurrentOptions();
+// if( newItem->options.outputOptions.mode != OutputDirectory::Specify ) newItem->options.outputOptions.mode = OutputDirectory::MetaData;
+ newItem->notify = notify;
+ newItem->track = 0;
+ newItem->fileName = i18n("Full audio CD (%1)").arg(device);
+ newItem->mimeType = "application/x-cda";
+ newItem->fileFormat = "cda";
+ newItem->local = true;
+ newItem->device = device;
+ newItem->tags = cdManager->getTags( device, 0 );
+ if( newItem->tags == 0 ) { // NOTE shouldn't happen
+// logger->log( 1000, " " + i18n("Reading tags failed") );
+ newItem->time = 3600;
+ newItem->options.filePathName = QDir::homeDirPath() + "/" + i18n("Audio CD") + "." + newItem->fileFormat;
+ }
+ else {
+// logger->log( 1000, " " + i18n("Tags successfully read") );
+ newItem->time = newItem->tags->length;
+ newItem->options.filePathName = QDir::homeDirPath() + newItem->tags->title + "." + newItem->fileFormat;
+ newItem->fileName = i18n("Full audio CD (%1)").arg(device) + ": " + newItem->tags->title;
+ }
+ updateItem( newItem );
+ emit increaseTime( newItem->time );
+ emit fileCountChanged( childCount() );
+}
+
+void FileList::itemFinished( FileListItem* item, int state )
+{
+ if( state == 0 ) {
+ if( item ) delete item;
+ itemsSelected();
+ }
+ else if( state == 1 ) {
+ item->setText( columnByName(i18n("State")), i18n("Stopped") );
+ }
+ else {
+ item->setText( columnByName(i18n("State")), i18n("Failed") );
+ }
+
+ save( true );
+
+ FileListItem* it = firstChild();
+ int waitingCount = 0, convertingCount = 0;
+
+ while( it != 0 ) {
+ if( it->text(columnByName(i18n("State"))) != i18n("Failed") && it->text(columnByName(i18n("State"))) != i18n("Stopped") && it->text(columnByName(i18n("State"))) != i18n("Will be skipped") ) {
+ if( it->text(columnByName(i18n("State"))) == i18n("Waiting") /*|| it->text(columnByName(i18n("State"))) == i18n("Stopped")*/ ) {
+ waitingCount++;
+ }
+ else {
+ convertingCount++;
+ }
+ }
+ it = it->nextSibling();
+ }
+
+ if( waitingCount > 0 && queue ) {
+ convertNextItem();
+ }
+ else if( convertingCount == 0 ) {
+ queue = false;
+ float time = 0;
+ it = firstChild();
+ while( it != 0 ) {
+// it->setText( columnByName(i18n("State")), i18n("Waiting") );
+ updateItem( it );
+ time += it->time;
+ it = it->nextSibling();
+ }
+ emit finished( time );
+ emit stoppedConversion();
+ emit fileCountChanged( childCount() );
+ }
+}
+
+void FileList::rippingFinished( const QString& device )
+{
+ if( !queue ) return;
+
+ int count = 0;
+ FileListItem *item = firstChild();
+ while( item != 0 ) {
+ if( item->converting ) {
+ count++;
+ }
+ item = item->nextSibling();
+ }
+
+ // look for "Waiting" files first ...
+ item = firstChild();
+ while( item != 0 && count < config->data.general.numFiles ) {
+ if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Waiting") ) {
+ if( item->track >= 0 && item->device == device ) {
+ convertItem( item );
+ itemsSelected();
+ return;
+ }
+ }
+ item = item->nextSibling();
+ }
+
+ // ... then convert the stopped, too
+/* item = firstChild();
+ while( item != 0 && count < config->data.general.numFiles ) {
+ if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Stopped") ) {
+ if( item->track >= 0 && item->device == device ) {
+ convertItem( item );
+ itemsSelected();
+ return;
+ }
+ }
+ item = item->nextSibling();
+ }*/
+}
+
+void FileList::updateItem( FileListItem* item )
+{
+ if( !item ) return;
+
+ if( !item->converting ) {
+ if( config->data.general.conflictHandling == 1 && QFile::exists(KURL::decode_string(OutputDirectory::calcPath(item,config))) ) { // was: .replace("%2f","%252f")
+ item->setText( columnByName(i18n("State")), i18n("Will be skipped") );
+ }
+ else {
+ item->setText( columnByName(i18n("State")), i18n("Waiting") );
+ }
+ }
+ else {
+ item->setText( columnByName(i18n("State")), i18n("Converting") );
+ }
+
+ if( item->track >= 0 ) item->setText( columnByName(i18n("Input")), KURL::decode_string(item->fileName).replace("%2f","/").replace("%%","%") );
+ else item->setText( columnByName(i18n("Input")), KURL::decode_string(item->options.filePathName).replace("%2f","/").replace("%%","%") );
+
+ item->setText( columnByName(i18n("Output")), KURL::decode_string(OutputDirectory::uniqueFileName(OutputDirectory::calcPath(item,config))).replace("%2f","/").replace("%%","%") );
+
+ item->setText( columnByName(i18n("Quality")), config->getProfileName(item->options) );
+}
+
+void FileList::showOptionsEditorDialog()
+{
+ // FIXME options are set to defaults
+
+ if( optionsEditor == 0 ) {
+ optionsEditor = new OptionsEditor( tagEngine, config, this, 0, "optionsEditor" );
+ if( optionsEditor == 0 ) {
+ // TODO error message
+ return;
+ }
+// }
+ connect( this, SIGNAL(editItems(QValueList<FileListItem*>)),
+ optionsEditor, SLOT(itemsSelected(QValueList<FileListItem*>))
+ );
+ connect( this, SIGNAL(setPreviousItemEnabled(bool)),
+ optionsEditor, SLOT(setPreviousEnabled(bool))
+ );
+ connect( this, SIGNAL(setNextItemEnabled(bool)),
+ optionsEditor, SLOT(setNextEnabled(bool))
+ );
+ connect( optionsEditor, SIGNAL(user2Clicked()),
+ this, SLOT(selectPreviousItem())
+ );
+ connect( optionsEditor, SIGNAL(user1Clicked()),
+ this, SLOT(selectNextItem())
+ );
+ /*connect( this, SIGNAL(moveEditor(int,int)),
+ optionsEditor, SLOT(moveWindow(int,int))
+ );*/
+}
+ itemsSelected();
+ optionsEditor->show();
+}
+
+// FIXME still makes some troubles (wreaks some items) and crashes
+void FileList::selectPreviousItem()
+{
+ if( selectedFiles.first() == 0 ) return;
+ FileListItem* item = selectedFiles.first()->itemAbove();
+ if( item != 0 ) {
+ item->setSelected( true );
+ repaintItem( item );
+ ensureItemVisible( item );
+ }
+
+ for( QValueList<FileListItem*>::Iterator it = selectedFiles.begin(); it != selectedFiles.end(); ++it ) {
+ (*it)->setSelected( false );
+ repaintItem( *it );
+ }
+
+ itemsSelected();
+}
+
+void FileList::selectNextItem()
+{
+ if( selectedFiles.last() == 0 ) return;
+ FileListItem* item = selectedFiles.last()->itemBelow();
+ if( item != 0 ) {
+ item->setSelected( true );
+ repaintItem( item );
+ ensureItemVisible( item );
+ }
+
+ for( QValueList<FileListItem*>::Iterator it = selectedFiles.begin(); it != selectedFiles.end(); ++it ) {
+ (*it)->setSelected( false );
+ repaintItem( *it );
+ }
+
+ itemsSelected();
+}
+
+void FileList::itemsSelected()
+{
+ selectedFiles.clear();
+ for( FileListItem *item = firstChild(); item != NULL; item = item->nextSibling() ) {
+ if( item->isSelected() ) {
+ selectedFiles.append( item );
+ }
+ }
+
+ if( selectedFiles.count() > 0 ) {
+ if( selectedFiles.first()->itemAbove() != 0 ) emit setPreviousItemEnabled( true );
+ else emit setPreviousItemEnabled( false );
+ if( selectedFiles.last()->itemBelow() != 0 ) emit setNextItemEnabled( true );
+ else emit setNextItemEnabled( false );
+ }
+ else {
+ emit setPreviousItemEnabled( false );
+ emit setNextItemEnabled( false );
+ }
+
+ emit editItems( selectedFiles );
+}
+
+/*void FileList::moveOptionsEditor( int x, int y )
+{
+ emit moveEditor( x, y );
+}*/
+
+void FileList::startConversion()
+{
+ // iterate through all items and set the state to "Waiting"
+ FileListItem* it = firstChild();
+ while( it != 0 ) {
+// it->setText( columnByName(i18n("State")), i18n("Waiting") );
+ if( !it->converting && it->text(columnByName(i18n("State"))) != i18n("Will be skipped") ) {
+ it->setText( columnByName(i18n("State")), i18n("Waiting") );
+ }
+ it = it->nextSibling();
+ }
+ queue = true;
+ emit startedConversion();
+ convertNextItem();
+}
+
+void FileList::stopConversion()
+{
+ queue = false;
+ emit stopClicked();
+}
+
+void FileList::continueConversion()
+{
+ queue = true;
+ emit continueClicked();
+}
+
+void FileList::killConversion()
+{
+ queue = false;
+
+ FileListItem *item = firstChild();
+
+ while( item != 0 ) {
+ if( item->converting ) {
+ emit stopItem( item );
+ }
+ item = item->nextSibling();
+ }
+}
+
+void FileList::convertNextItem()
+{
+ if( !queue ) return;
+
+ QStringList devices;
+
+ // look for tracks that are being ripped
+ int count = 0;
+ FileListItem *item = firstChild();
+ while( item != 0 ) {
+ if( item->converting ) {
+ count++;
+ if( item->ripping ) {
+ devices += item->device;
+ }
+ }
+ item = item->nextSibling();
+ }
+
+ // look for "Waiting" files first ...
+ item = firstChild();
+ while( item != 0 && count < config->data.general.numFiles ) {
+ if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Waiting") ) {
+ if( item->track >= 0 && devices.findIndex(item->device) == -1 ) {
+ convertItem( item );
+ count++;
+ devices += item->device;
+ }
+ else if( item->track < 0 ) {
+ convertItem( item );
+ count++;
+ }
+ }
+ item = item->nextSibling();
+ }
+
+ // ... then convert the stopped, too
+// item = firstChild();
+// while( item != 0 && count < config->data.general.numFiles ) {
+// if( !item->converting && item->text(columnByName(i18n("State"))) == i18n("Stopped") ) {
+// if( item->track >= 0 && devices.findIndex(item->device) == -1 ) {
+// convertItem( item );
+// count++;
+// devices += item->device;
+// }
+// else if( item->track < 0 ) {
+// convertItem( item );
+// count++;
+// }
+// }
+// item = item->nextSibling();
+// }
+
+ itemsSelected();
+
+ FileListItem* it = firstChild();
+ int waitingCount = 0, convertingCount = 0;
+
+ while( it != 0 ) {
+ if( it->text(columnByName(i18n("State"))) != i18n("Failed") && it->text(columnByName(i18n("State"))) != i18n("Stopped") && it->text(columnByName(i18n("State"))) != i18n("Will be skipped") ) {
+ if( it->text(columnByName(i18n("State"))) == i18n("Waiting") /*|| it->text(columnByName(i18n("State"))) == i18n("Stopped")*/ ) {
+ waitingCount++;
+ }
+ else {
+ convertingCount++;
+ }
+ }
+ it = it->nextSibling();
+ }
+
+ if( waitingCount == 0 && convertingCount == 0 ) itemFinished( 0, 0 );
+}
+
+void FileList::load( bool autosave )
+{
+ // NOTE clear the file list befor adding the saved items?
+
+ int version;
+ QString name;
+ FileListItem* lastListItem = lastItem();
+ QString filename;
+ if( autosave ) filename = locateLocal("data","soundkonverter/filelist.autosave.xml");
+ else filename = locateLocal("data","soundkonverter/filelist.xml");
+
+ QDomDocument domTree;
+ QFile opmlFile( filename );
+ if( !opmlFile.open( IO_ReadOnly ) ) return;
+ if( !domTree.setContent( &opmlFile ) ) return;
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "filelist" ) return;
+ QDomNode node, sub1Node, sub2Node;
+ node = root.firstChild();
+
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+
+ version = node.toElement().attribute("version").toInt();
+
+ }
+ else if( node.isElement() && node.nodeName() == "file" ) {
+
+ FileListItem* item = new FileListItem( this, lastListItem );
+ lastListItem = item;
+
+ item->fileName = node.toElement().attribute("fileName");
+ item->mimeType = node.toElement().attribute("mimeType");
+ item->fileFormat = node.toElement().attribute("fileFormat");
+ item->local = node.toElement().attribute("local").toInt();
+ item->track = node.toElement().attribute("track").toInt();
+ item->time = node.toElement().attribute("time").toInt();
+
+ item->options.filePathName = node.toElement().attribute("filePathName");
+ item->options.outputFilePathName = node.toElement().attribute("outputFilePathName");
+
+ sub1Node = node.toElement().firstChild();
+ while( !sub1Node.isNull() ) {
+ if( sub1Node.isElement() && sub1Node.nodeName() == "encodingOptions" ) {
+
+ item->options.encodingOptions.sFormat = sub1Node.toElement().attribute("sFormat");
+ item->options.encodingOptions.sQualityMode = sub1Node.toElement().attribute("sQualityMode");
+ item->options.encodingOptions.iQuality = sub1Node.toElement().attribute("iQuality").toInt();
+ item->options.encodingOptions.sBitrateMode = sub1Node.toElement().attribute("sBitrateMode");
+ item->options.encodingOptions.bBitrateRange = sub1Node.toElement().attribute("bBitrateRange").toInt();
+ item->options.encodingOptions.iMinBitrate = sub1Node.toElement().attribute("iMinBitrate").toInt();
+ item->options.encodingOptions.iMaxBitrate = sub1Node.toElement().attribute("iMaxBitrate").toInt();
+ item->options.encodingOptions.sInOutFiles = sub1Node.toElement().attribute("sInOutFiles");
+
+ sub2Node = sub1Node.toElement().firstChild();
+ while( !sub2Node.isNull() ) {
+ if( sub2Node.isElement() && sub2Node.nodeName() == "samplingRate" ) {
+
+ item->options.encodingOptions.samplingRate.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
+ item->options.encodingOptions.samplingRate.iSamplingRate = sub2Node.toElement().attribute("iSamplingRate").toInt();
+
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "channels" ) {
+
+ item->options.encodingOptions.channels.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
+ item->options.encodingOptions.channels.sChannels = sub2Node.toElement().attribute("sChannels");
+
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "replaygain" ) {
+
+ item->options.encodingOptions.replaygain.bEnabled = sub2Node.toElement().attribute("bEnabled").toInt();
+
+ }
+ sub2Node = sub2Node.nextSibling();
+ }
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "outputOptions" ) {
+
+ item->options.outputOptions.mode = (OutputDirectory::Mode)sub1Node.toElement().attribute("mode").toInt();
+ item->options.outputOptions.directory = sub1Node.toElement().attribute("directory");
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "tags" ) {
+
+ item->tags = new TagData();
+
+ item->tags->artist = sub1Node.toElement().attribute("artist");
+ item->tags->composer = sub1Node.toElement().attribute("composer");
+ item->tags->album = sub1Node.toElement().attribute("album");
+ item->tags->title = sub1Node.toElement().attribute("title");
+ item->tags->genre = sub1Node.toElement().attribute("genre");
+ item->tags->comment = sub1Node.toElement().attribute("comment");
+ item->tags->track = sub1Node.toElement().attribute("track").toInt();
+ item->tags->disc = sub1Node.toElement().attribute("disc").toInt();
+ item->tags->year = sub1Node.toElement().attribute("year").toInt();
+ item->tags->track_gain = sub1Node.toElement().attribute("track_gain").toFloat();
+ item->tags->album_gain = sub1Node.toElement().attribute("album_gain").toFloat();
+ item->tags->length = sub1Node.toElement().attribute("length").toInt();
+ item->tags->fileSize = sub1Node.toElement().attribute("fileSize").toInt();
+ item->tags->bitrate = sub1Node.toElement().attribute("bitrate").toInt();
+ item->tags->samplingRate = sub1Node.toElement().attribute("samplingRate").toInt();
+
+ }
+ sub1Node = sub1Node.nextSibling();
+ }
+ updateItem( item );
+ emit increaseTime( item->time );
+ }
+ node = node.nextSibling();
+ }
+ emit fileCountChanged( childCount() );
+}
+
+void FileList::save( bool autosave )
+{
+ // NOTE filenames should be encoded before saving - but it works fine without it (hm...)
+
+ QTime time;
+ time.start();
+
+ QDomDocument domTree;
+ QDomElement root = domTree.createElement( "soundkonverter" );
+ root.setAttribute( "type", "filelist" );
+ domTree.appendChild( root );
+
+ QDomElement info = domTree.createElement( "info" );
+ info.setAttribute( "version", "300" );
+ root.appendChild( info );
+
+ for( FileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ QDomElement file = domTree.createElement( "file" );
+ file.setAttribute( "fileName", item->fileName );
+ file.setAttribute( "mimeType", item->mimeType );
+ file.setAttribute( "fileFormat", item->fileFormat );
+ file.setAttribute( "local", item->local );
+ file.setAttribute( "track", item->track );
+ file.setAttribute( "device", item->device );
+ file.setAttribute( "time", item->time );
+
+ file.setAttribute( "filePathName", item->options.filePathName );
+ file.setAttribute( "outputFilePathName", item->options.outputFilePathName );
+
+ QDomElement encodingOptions = domTree.createElement( "encodingOptions" );
+
+ encodingOptions.setAttribute( "sFormat", item->options.encodingOptions.sFormat );
+ encodingOptions.setAttribute( "sQualityMode", item->options.encodingOptions.sQualityMode );
+ encodingOptions.setAttribute( "iQuality", item->options.encodingOptions.iQuality );
+ encodingOptions.setAttribute( "sBitrateMode", item->options.encodingOptions.sBitrateMode );
+ encodingOptions.setAttribute( "bBitrateRange", item->options.encodingOptions.bBitrateRange );
+ encodingOptions.setAttribute( "iMinBitrate", item->options.encodingOptions.iMinBitrate );
+ encodingOptions.setAttribute( "iMaxBitrate", item->options.encodingOptions.iMaxBitrate );
+
+ QDomElement samplingRate = domTree.createElement( "samplingRate" );
+
+ samplingRate.setAttribute( "bEnabled", item->options.encodingOptions.samplingRate.bEnabled );
+ samplingRate.setAttribute( "iSamplingRate", item->options.encodingOptions.samplingRate.iSamplingRate );
+
+ encodingOptions.appendChild( samplingRate );
+
+ QDomElement channels = domTree.createElement( "channels" );
+
+ channels.setAttribute( "bEnabled", item->options.encodingOptions.channels.bEnabled );
+ channels.setAttribute( "sChannels", item->options.encodingOptions.channels.sChannels );
+
+ encodingOptions.appendChild( channels );
+
+ QDomElement replaygain = domTree.createElement( "replaygain" );
+
+ replaygain.setAttribute( "bEnabled", item->options.encodingOptions.replaygain.bEnabled );
+
+ encodingOptions.appendChild( replaygain );
+
+ encodingOptions.setAttribute( "sInOutFiles", item->options.encodingOptions.sInOutFiles );
+
+ file.appendChild( encodingOptions );
+
+ QDomElement outputOptions = domTree.createElement( "outputOptions" );
+
+ outputOptions.setAttribute( "mode", int(item->options.outputOptions.mode) );
+ outputOptions.setAttribute( "directory", item->options.outputOptions.directory );
+
+ file.appendChild( outputOptions );
+
+ if( item->tags )
+ {
+ QDomElement tags = domTree.createElement( "tags" );
+
+ tags.setAttribute( "artist", item->tags->artist );
+ tags.setAttribute( "composer", item->tags->composer );
+ tags.setAttribute( "album", item->tags->album );
+ tags.setAttribute( "title", item->tags->title );
+ tags.setAttribute( "genre", item->tags->genre );
+ tags.setAttribute( "comment", item->tags->comment );
+ tags.setAttribute( "track", item->tags->track );
+ tags.setAttribute( "disc", item->tags->disc );
+ tags.setAttribute( "year", item->tags->year );
+ tags.setAttribute( "track_gain", item->tags->track_gain );
+ tags.setAttribute( "album_gain", item->tags->album_gain );
+ tags.setAttribute( "length", item->tags->length );
+ tags.setAttribute( "fileSize", item->tags->fileSize );
+ tags.setAttribute( "bitrate", item->tags->bitrate );
+ tags.setAttribute( "samplingRate", item->tags->samplingRate );
+
+ file.appendChild( tags );
+ }
+
+ root.appendChild( file );
+ }
+
+ QString filename;
+ if( autosave ) filename = locateLocal("data","soundkonverter/filelist.autosave.xml");
+ else filename = locateLocal("data","soundkonverter/filelist.xml");
+
+ QFile opmlFile( filename );
+ if( !opmlFile.open( IO_WriteOnly ) ) return;
+
+ QTextStream ts( &opmlFile );
+ ts << domTree.toString();
+
+ opmlFile.close();
+
+ logger->log( 1000, "save file list: " + QString::number(time.elapsed()) );
+}
+
+void FileList::debug() // NOTE DEBUG
+{
+ logger->log( 1000, "DEBUG begin" );
+
+ ConversionOptions conversionOptions;
+
+ QStringList formats = config->allEncodableFormats();
+
+ for( QStringList::Iterator a = formats.begin(); a != formats.end(); ++a )
+ {
+ logger->log( 1000, "format: " + (*a) );
+ FormatItem* formatItem = config->getFormatItem( *a );
+ if( formatItem == 0 ) continue;
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->encoders.begin(); b != formatItem->encoders.end(); ++b ) {
+ logger->log( 1000, " encoder: " + (*b)->enc.bin );
+ formatItem->encoder = (*b);
+ if( (*b)->enc.strength.enabled ) {
+ formatItem->compressionLevel = (*b)->enc.strength.default_value;
+ }
+ if( (*b)->enc.replaygain.enabled ) {
+ formatItem->internalReplayGain = true;
+ }
+
+ if( (*b)->enc.lossless.enabled ) {
+ options->setProfile( "Lossless" );
+ options->setFormat( *a );
+ options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
+ conversionOptions = options->getCurrentOptions();
+ conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
+ options->setCurrentOptions( conversionOptions );
+ addFiles( "/home/daniel/soundKonverter/test.mp3" );
+ }
+ if( (*b)->enc.lossy.enabled ) {
+ options->setProfile( "Medium" );
+ if( (*b)->enc.lossy.bitrate.cbr.enabled ) {
+ options->setFormat( *a );
+ conversionOptions = options->getCurrentOptions();
+ conversionOptions.encodingOptions.sQualityMode = i18n("Bitrate");
+ conversionOptions.encodingOptions.sBitrateMode = "cbr";
+ conversionOptions.encodingOptions.iQuality = 128;
+ conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
+ options->setCurrentOptions( conversionOptions );
+ options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
+ addFiles( "/home/daniel/soundKonverter/test.mp3" );
+ }
+ if( (*b)->enc.lossy.bitrate.abr.enabled ) {
+ options->setFormat( *a );
+ conversionOptions = options->getCurrentOptions();
+ conversionOptions.encodingOptions.sQualityMode = i18n("Bitrate");
+ conversionOptions.encodingOptions.sBitrateMode = "abr";
+ conversionOptions.encodingOptions.iQuality = 128;
+ conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
+ options->setCurrentOptions( conversionOptions );
+ options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
+ addFiles( "/home/daniel/soundKonverter/test.mp3" );
+ if( (*b)->enc.lossy.bitrate.abr.bitrate_range.enabled ) {
+ options->setFormat( *a );
+ conversionOptions = options->getCurrentOptions();
+ conversionOptions.encodingOptions.sQualityMode = i18n("Bitrate");
+ conversionOptions.encodingOptions.sBitrateMode = "abr";
+ conversionOptions.encodingOptions.iQuality = 128;
+ conversionOptions.encodingOptions.bBitrateRange = true;
+ conversionOptions.encodingOptions.iMinBitrate = 64;
+ conversionOptions.encodingOptions.iMaxBitrate = 192;
+ conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
+ options->setCurrentOptions( conversionOptions );
+ options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
+ addFiles( "/home/daniel/soundKonverter/test.mp3" );
+ }
+ }
+ if( (*b)->enc.lossy.quality.enabled ) {
+ options->setFormat( *a );
+ conversionOptions = options->getCurrentOptions();
+ conversionOptions.encodingOptions.sQualityMode = i18n("Quality");
+ conversionOptions.encodingOptions.iQuality = 40;
+ conversionOptions.encodingOptions.sInOutFiles = debug_params( conversionOptions, formatItem );
+ options->setCurrentOptions( conversionOptions );
+ options->setOutputDirectory( "/home/daniel/soundKonverter/test_output" );
+ addFiles( "/home/daniel/soundKonverter/test.mp3" );
+ }
+ }
+ }
+
+ for( QValueList<ConvertPlugin*>::Iterator b = formatItem->decoders.begin(); b != formatItem->decoders.end(); ++b ) {
+ formatItem->decoder = (*b);
+ }
+
+ for( QValueList<ReplayGainPlugin*>::Iterator b = formatItem->replaygains.begin(); b != formatItem->replaygains.end(); ++b ) {
+ formatItem->replaygain = (*b);
+ }
+ }
+
+ logger->log( 1000, "DEBUG end" );
+}
+
+QString FileList::debug_params( ConversionOptions conversionOptions, FormatItem* formatItem ) // NOTE DEBUG
+{
+ ConvertPlugin* plugin = formatItem->encoder;
+
+ QString sStrength;
+ QString sBitrate;
+ QString sQuality;
+ QString sMinBitrate;
+ QString sMaxBitrate;
+ QString sSamplingRate;
+
+ int t_int;
+ float t_float;
+
+ QString param = "";
+ if( !plugin->enc.param.isEmpty() ) param.append( " " + plugin->enc.param );
+ if( !plugin->enc.overwrite.isEmpty() ) param.append( " " + plugin->enc.overwrite );
+
+ if( plugin->enc.strength.enabled ) {
+ param.append( " " + plugin->enc.strength.param );
+ int compressionLevel = formatItem->compressionLevel;
+
+ if( plugin->enc.strength.profiles.empty() ) {
+ if( plugin->enc.strength.step < 1 ) {
+ if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min )
+ sStrength = QString::number( compressionLevel );
+ else
+ sStrength = QString::number( plugin->enc.strength.range_min - compressionLevel );
+ }
+ else {
+ if( plugin->enc.strength.range_max >= plugin->enc.strength.range_min )
+ sStrength = QString::number( int(compressionLevel) );
+ else
+ sStrength = QString::number( int(plugin->enc.strength.range_min - compressionLevel) );
+ }
+ if( plugin->enc.strength.separator != '.' ) sStrength.replace( QChar('.'), plugin->enc.strength.separator );
+ }
+ else {
+ QStringList::Iterator it = plugin->enc.strength.profiles.at( compressionLevel );
+ sStrength = *it;
+ }
+ }
+
+ if( conversionOptions.encodingOptions.sQualityMode == i18n("Bitrate") ) {
+ if( conversionOptions.encodingOptions.sBitrateMode == "cbr" && plugin->enc.lossy.bitrate.cbr.enabled ) {
+ param.append( " " + plugin->enc.lossy.bitrate.cbr.param );
+ sBitrate = QString::number( conversionOptions.encodingOptions.iQuality );
+ }
+ else if( conversionOptions.encodingOptions.sBitrateMode == "abr" && plugin->enc.lossy.bitrate.abr.enabled ) {
+ param.append( " " + plugin->enc.lossy.bitrate.abr.param );
+ sBitrate = QString::number( conversionOptions.encodingOptions.iQuality );
+ if( conversionOptions.encodingOptions.bBitrateRange && plugin->enc.lossy.bitrate.abr.bitrate_range.enabled ) {
+ param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_min );
+ sMinBitrate = QString::number( conversionOptions.encodingOptions.iMinBitrate );
+ param.append( " " + plugin->enc.lossy.bitrate.abr.bitrate_range.param_max );
+ sMaxBitrate = QString::number( conversionOptions.encodingOptions.iMaxBitrate );
+ }
+ }
+ }
+ else if( conversionOptions.encodingOptions.sQualityMode == i18n("Quality") && plugin->enc.lossy.quality.enabled ) {
+ param.append( " " + plugin->enc.lossy.quality.param );
+ if( plugin->enc.lossy.quality.profiles.empty() ) {
+ if( plugin->enc.lossy.quality.step < 1 ) {
+ if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
+ t_float = ( (float)conversionOptions.encodingOptions.iQuality * ( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100 ) + plugin->enc.lossy.quality.range_min;
+ else
+ t_float = ( (100.0f - (float)conversionOptions.encodingOptions.iQuality) * ( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100 ) + plugin->enc.lossy.quality.range_max;
+ //t_float -= t_float%plugin->enc.quality.step;
+ //sQuality = QString().sprintf( "%.2f", t_float );
+ sQuality = QString::number( t_float );
+ }
+ else {
+ if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
+ t_int = ( conversionOptions.encodingOptions.iQuality * (int)( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100) + (int)plugin->enc.lossy.quality.range_min;
+ else
+ t_int = ( (100 - conversionOptions.encodingOptions.iQuality) * (int)( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100) + (int)plugin->enc.lossy.quality.range_max;
+ //t_int -= t_int%plugin->enc.quality.step;
+ sQuality = QString::number( t_int );
+ }
+ if( plugin->enc.bin == "oggenc" ) sQuality.replace(QChar('.'),KGlobal::locale()->decimalSymbol());
+ else if( plugin->enc.lossy.quality.separator != '.' ) sQuality.replace(QChar('.'),plugin->enc.lossy.quality.separator);
+ }
+ else {
+ QStringList::Iterator it = plugin->enc.lossy.quality.profiles.at( int(conversionOptions.encodingOptions.iQuality*plugin->enc.lossy.quality.range_max/100) );
+ sQuality = *it;
+ }
+ }
+ else if( conversionOptions.encodingOptions.sQualityMode == i18n("Lossless") && plugin->enc.lossless.enabled ) {
+ param.append( " " + plugin->enc.lossless.param );
+ }
+ else if( conversionOptions.encodingOptions.sQualityMode == i18n("Hybrid") && plugin->enc.hybrid.enabled ) {
+ param.append( " " + plugin->enc.hybrid.param );
+ sBitrate = QString::number( conversionOptions.encodingOptions.iQuality );
+ }
+
+ if( conversionOptions.encodingOptions.samplingRate.bEnabled && plugin->enc.lossy.samplingrate.enabled ) {
+ param.append( " " + plugin->enc.lossy.samplingrate.param );
+ if( plugin->enc.lossy.samplingrate.unit == PluginLoaderBase::Hz ) {
+ sSamplingRate = QString::number( conversionOptions.encodingOptions.samplingRate.iSamplingRate );
+ }
+ else {
+ sSamplingRate = QString::number( (float)conversionOptions.encodingOptions.samplingRate.iSamplingRate/1000 );
+ }
+ }
+
+ if( conversionOptions.encodingOptions.channels.bEnabled ) {
+ if( conversionOptions.encodingOptions.channels.sChannels == i18n("Mono") && plugin->enc.lossy.channels.mono_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.mono_param );
+ }
+ else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Stereo") && plugin->enc.lossy.channels.stereo_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.stereo_param );
+ }
+ else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Joint-Stereo") && plugin->enc.lossy.channels.joint_stereo_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.joint_stereo_param );
+ }
+ else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Forced Joint-Stereo") && plugin->enc.lossy.channels.forced_joint_stereo_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.forced_joint_stereo_param );
+ }
+ else if( conversionOptions.encodingOptions.channels.sChannels == i18n("Dual Channels") && plugin->enc.lossy.channels.dual_channels_enabled ) {
+ param.append( " " + plugin->enc.lossy.channels.dual_channels_param );
+ }
+ }
+
+ if( conversionOptions.encodingOptions.replaygain.bEnabled && plugin->enc.replaygain.enabled && plugin->enc.replaygain.use && formatItem->internalReplayGain ) {
+ param.append( " " + plugin->enc.replaygain.use );
+ }
+ else if( plugin->enc.replaygain.enabled && plugin->enc.replaygain.avoid ) {
+ param.append( " " + plugin->enc.replaygain.avoid );
+ }
+
+// if( !tagEngine->canWrite(conversionOptions.encodingOptions.sFormat) && item->fileListItem->tags && plugin->enc.tag.enabled ) {
+ if( plugin->enc.tag.enabled && conversionOptions.encodingOptions.sFormat != "aac" ) { // HACK don't write metadata to aac
+ if( !plugin->enc.tag.param.isEmpty() ) param.append( " " + plugin->enc.tag.param );
+ if( !plugin->enc.tag.artist.isEmpty() ) param.append( " " + plugin->enc.tag.artist );
+ if( !plugin->enc.tag.album.isEmpty() ) param.append( " " + plugin->enc.tag.album );
+ if( !plugin->enc.tag.comment.isEmpty() ) param.append( " " + plugin->enc.tag.comment );
+ if( !plugin->enc.tag.disc.isEmpty() ) param.append( " " + plugin->enc.tag.disc );
+ if( !plugin->enc.tag.genre.isEmpty() ) param.append( " " + plugin->enc.tag.genre );
+ if( !plugin->enc.tag.track.isEmpty() ) param.append( " " + plugin->enc.tag.track );
+ if( !plugin->enc.tag.composer.isEmpty() ) param.append( " " + plugin->enc.tag.composer );
+ if( !plugin->enc.tag.title.isEmpty() ) param.append( " " + plugin->enc.tag.title );
+ if( !plugin->enc.tag.year.isEmpty() ) param.append( " " + plugin->enc.tag.year );
+ }
+
+ param.replace( "%c", sStrength );
+ param.replace( "%b", sBitrate );
+ param.replace( "%q", sQuality );
+ param.replace( "%m", sMinBitrate );
+ param.replace( "%M", sMaxBitrate );
+ param.replace( "%s", sSamplingRate );
+
+ return conversionOptions.encodingOptions.sInOutFiles.replace( "%p", param );
+}
diff --git a/src/filelist.h b/src/filelist.h
new file mode 100755
index 0000000..484eedc
--- /dev/null
+++ b/src/filelist.h
@@ -0,0 +1,235 @@
+
+
+#ifndef FILELIST_H
+#define FILELIST_H
+
+#include <klistview.h>
+
+#include "conversionoptions.h"
+
+class CDManager;
+class TagEngine;
+class TagData;
+class Options;
+class OptionsEditor;
+class Config;
+class Logger;
+class FormatItem; // NOTE DEBUG
+
+class QPainter;
+class QColorGroup;
+class QSimpleRichText;
+class KProgress;
+class KPopupMenu;
+class KAction;
+class KActionCollection;
+
+/**
+ * @short The items for the file list
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class FileListItem : public KListViewItem
+{
+public:
+ /**
+ * Constructor
+ * @param parent The parent list view
+ */
+ FileListItem( KListView* parent );
+
+ /**
+ * Constructor
+ * @param parent The parent list view
+ * @param after The item, the new item should be placed after
+ */
+ FileListItem( KListView* parent, FileListItem* after );
+
+ /**
+ * Destructor
+ */
+ virtual ~FileListItem();
+
+ virtual void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int alignment );
+
+ //void updateOutputCell();
+ //void updateOptionsCell();
+
+ FileListItem* nextSibling() const { return static_cast<FileListItem*>( KListViewItem::nextSibling() ); }
+ FileListItem* itemAbove() { return static_cast<FileListItem*>( KListViewItem::itemAbove() ); }
+ FileListItem* itemBelow() { return static_cast<FileListItem*>( KListViewItem::itemBelow() ); }
+ //FileList* listView() const { return static_cast<FileList*>( KListViewItem::listView() ); }
+
+ ConversionOptions options; // the information we get from the options class for creating the item
+ TagData* tags; // we need to instruct the tagengine to read the tags from the file!
+ // and the user can change them!
+ QString fileName; // just the _name_ of the file
+ QString mimeType; // the mime type of the file
+ QString fileFormat; // just the _format_ of the file (for easier use)
+ QString url; // the original input file name string
+ bool converting; // is this item being converted at the moment?
+ bool local; // is this file a local one?
+ int track; // the number of the track, if it is on an audio cd
+ // if it is lower than 0, it isn't an audio cd track at all
+ QString device; // the device of the audio cd
+ bool ripping; // is this track currently being ripped?
+
+ float time; // the duration of the track, used for the calculation of the progress bar
+ QString notify; // execute this command, when the file is converted
+};
+
+/**
+ * @short The file list
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class FileList : public KListView
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ * @param parent The parent widget
+ * @param name The name of the file list
+ */
+ FileList( CDManager*, TagEngine*, Config*, Options*, Logger*, QWidget *parent = 0, const char* name = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~FileList();
+
+ FileListItem* firstChild() const { return static_cast<FileListItem*>( KListView::firstChild() ); }
+ FileListItem* lastItem() const { return static_cast<FileListItem*>( KListView::lastItem() ); }
+
+ int columnByName( const QString& name );
+ void updateItem( FileListItem* item );
+
+ bool queueEnabled() { return queue; }
+
+ void setNotify( const QString& cmd ) { notify = cmd; }
+
+protected:
+ virtual bool acceptDrag( QDropEvent *e ) const;
+
+private:
+ /** Lists all file in a directory and adds them to the file list, if @p fast is false. The number of listed files is returned */
+ int listDir( const QString& directory, QStringList filter, bool recursive = true, bool fast = false, int count = 0 );
+
+ /** A progressbar, that is shown, when a directory is added recursive */
+ KProgress* pScanStatus;
+
+ void convertNextItem();
+
+ void viewportPaintEvent( QPaintEvent* );
+ void viewportResizeEvent( QResizeEvent* );
+
+ void debug(); // NOTE DEBUG
+ QString debug_params( ConversionOptions conversionOptions, FormatItem* formatItem ); // NOTE DEBUG
+
+ QSimpleRichText* bubble;
+
+ /** The context menu for editing or starting the files */
+ KPopupMenu* contextMenu;
+
+ KActionCollection* actionCollection;
+ KAction* edit;
+ KAction* start;
+ KAction* stop;
+ KAction* remove;
+ KAction* paste;
+
+ CDManager* cdManager;
+ TagEngine* tagEngine;
+ OptionsEditor* optionsEditor;
+
+ Options* options;
+ Config* config;
+ Logger* logger;
+
+ QValueList<FileListItem*> selectedFiles;
+
+ bool queue;
+
+ /**
+ * A command that should be executed after the conversion of a file is complete
+ * %i will be replaced by the input file path
+ * %o " " " " " output " "
+ */
+ QString notify;
+
+private slots:
+ /*
+ * The user clicked somewhere into the list view (e.g. the link in the bubble)
+ */
+// void clickedSomewhere( QListViewItem*, const QPoint&, int );
+
+ /**
+ * We'll recive a signal, when we should show the context menu
+ * @p item The item, that's context menu should be shown
+ * @p point The position of the click (start position of the context menu)
+ */
+ void showContextMenu( QListViewItem* item, const QPoint& point, int );
+
+ /**
+ * Remove selected items from the file list
+ */
+ void removeSelectedItems();
+
+ /**
+ * Add selected items to the conversion list
+ */
+ void convertSelectedItems();
+
+ /**
+ * Stop the conversion of all selected items
+ */
+ void stopSelectedItems();
+
+ void columnResizeEvent( int, int, int );
+ void slotDropped( QDropEvent*, QListViewItem*, QListViewItem* ); // NOTE rename?
+
+public slots:
+ void addFiles( QStringList, FileListItem* after = 0, bool enabled = false ); // NOTE const QStringList& ?
+ void addDir( const QString&, const QStringList& filter = "", bool recursive = true );
+ void addTracks( const QString& device, QValueList<int> );
+ void addDisc( const QString& device );
+ /**
+ * The conversion of an item has finished and the state is reported ( 0 = ok, -1 = error, 1 = aborted )
+ */
+ void itemFinished( FileListItem*, int );
+ void rippingFinished( const QString& device );
+ void showOptionsEditorDialog();
+ void selectPreviousItem();
+ void selectNextItem();
+ void itemsSelected();
+ //void moveOptionsEditor( int x, int y );
+ void startConversion();
+ void stopConversion();
+ void continueConversion();
+ void killConversion();
+
+ void load( bool autosave = false );
+ void save( bool autosave = false );
+
+signals:
+ void convertItem( FileListItem* );
+ void stopItem( FileListItem* );
+ void editItems( QValueList<FileListItem*> );
+ void setPreviousItemEnabled( bool );
+ void setNextItemEnabled( bool );
+ //void moveEditor( int x, int y );
+ void fileCountChanged( int );
+ void startedConversion();
+ void stopClicked();
+ void continueClicked();
+ void stoppedConversion();
+
+ void increaseTime( float );
+ void decreaseTime( float );
+ //void countTime( float );
+ //void setTime( float );
+ void finished( float );
+};
+
+#endif // FILELIST_H
diff --git a/src/hi16-app-soundkonverter.png b/src/hi16-app-soundkonverter.png
new file mode 100755
index 0000000..2350f15
--- /dev/null
+++ b/src/hi16-app-soundkonverter.png
Binary files differ
diff --git a/src/hi16-app-soundkonverter_replaygain.png b/src/hi16-app-soundkonverter_replaygain.png
new file mode 100755
index 0000000..bd2821e
--- /dev/null
+++ b/src/hi16-app-soundkonverter_replaygain.png
Binary files differ
diff --git a/src/hi22-app-soundkonverter.png b/src/hi22-app-soundkonverter.png
new file mode 100755
index 0000000..3896959
--- /dev/null
+++ b/src/hi22-app-soundkonverter.png
Binary files differ
diff --git a/src/hi22-app-soundkonverter_replaygain.png b/src/hi22-app-soundkonverter_replaygain.png
new file mode 100755
index 0000000..b76cf9c
--- /dev/null
+++ b/src/hi22-app-soundkonverter_replaygain.png
Binary files differ
diff --git a/src/hi32-app-soundkonverter.png b/src/hi32-app-soundkonverter.png
new file mode 100755
index 0000000..fa39d12
--- /dev/null
+++ b/src/hi32-app-soundkonverter.png
Binary files differ
diff --git a/src/hi32-app-soundkonverter_replaygain.png b/src/hi32-app-soundkonverter_replaygain.png
new file mode 100755
index 0000000..ce3f56e
--- /dev/null
+++ b/src/hi32-app-soundkonverter_replaygain.png
Binary files differ
diff --git a/src/hi48-app-soundkonverter.png b/src/hi48-app-soundkonverter.png
new file mode 100755
index 0000000..475589a
--- /dev/null
+++ b/src/hi48-app-soundkonverter.png
Binary files differ
diff --git a/src/hi48-app-soundkonverter_replaygain.png b/src/hi48-app-soundkonverter_replaygain.png
new file mode 100755
index 0000000..0b0cc5a
--- /dev/null
+++ b/src/hi48-app-soundkonverter_replaygain.png
Binary files differ
diff --git a/src/hi64-app-soundkonverter.png b/src/hi64-app-soundkonverter.png
new file mode 100755
index 0000000..abf4714
--- /dev/null
+++ b/src/hi64-app-soundkonverter.png
Binary files differ
diff --git a/src/hi64-app-soundkonverter_replaygain.png b/src/hi64-app-soundkonverter_replaygain.png
new file mode 100755
index 0000000..29834c4
--- /dev/null
+++ b/src/hi64-app-soundkonverter_replaygain.png
Binary files differ
diff --git a/src/logger.cpp b/src/logger.cpp
new file mode 100755
index 0000000..7b19fff
--- /dev/null
+++ b/src/logger.cpp
@@ -0,0 +1,149 @@
+
+#include "logger.h"
+
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+#include <cstdlib>
+#include <ctime>
+
+
+LoggerItem::LoggerItem()
+{}
+
+LoggerItem::~LoggerItem()
+{}
+
+
+Logger::Logger()
+{
+ QValueList<LoggerItem*>::Iterator item = processes.append( new LoggerItem() );
+ (*item)->filename = "soundKonverter";
+ (*item)->id = 1000;
+ (*item)->completed = true;
+ (*item)->state = 1;
+ (*item)->file.setName( locateLocal("data",QString("soundkonverter/log/%1.log").arg((*item)->id)) );
+ // TODO error handling
+ (*item)->file.open( IO_WriteOnly );
+ (*item)->textStream.setDevice( &((*item)->file) );
+ srand( (unsigned)time(NULL) );
+}
+
+Logger::~Logger()
+{}
+
+void Logger::cleanUp()
+{
+ for( QValueList<LoggerItem*>::Iterator it = processes.begin(); it != processes.end(); ++it ) {
+ if( (*it)->id != 1000 ) {
+ emit removedProcess( (*it)->id );
+ (*it)->file.close();
+ (*it)->file.remove();
+ delete *it;
+ }
+ }
+ processes.clear();
+}
+
+int Logger::registerProcess( const QString& filename )
+{
+ // NOTE ok, it works now, but why prepend() and not append()?
+ QValueList<LoggerItem*>::Iterator item = processes.append( new LoggerItem() );
+ (*item)->filename = filename;
+ (*item)->id = getNewID();
+ (*item)->completed = false;
+ (*item)->file.setName( locateLocal("data",QString("soundkonverter/log/%1.log").arg((*item)->id)) );
+ // TODO error handling
+ (*item)->file.open( IO_WriteOnly );
+ (*item)->textStream.setDevice( &((*item)->file) );
+
+ emit updateProcess( (*item)->id );
+
+ return (*item)->id;
+}
+
+void Logger::log( int id, const QString& data )
+{
+ for( QValueList<LoggerItem*>::Iterator it = processes.begin(); it != processes.end(); ++it ) {
+ if( (*it)->id == id ) {
+ (*it)->data.append( data );
+ if( (*it)->data.count() > 100000 ) (*it)->data.erase( (*it)->data.at(0) );
+ (*it)->textStream << data;
+ (*it)->textStream << "\n";
+ (*it)->file.flush();
+ if( id == 1000 ) emit updateProcess( 1000 );
+ return;
+ }
+ }
+}
+
+int Logger::getNewID()
+{
+ bool ok;
+ int id;
+
+ do {
+ id = rand();
+ ok = true;
+
+ for( QValueList<LoggerItem*>::Iterator it = processes.begin(); it != processes.end(); ++it ) {
+ if( (*it)->id == id ) ok = false;
+ }
+
+ } while( !ok );
+
+ return id;
+}
+
+LoggerItem* Logger::getLog( int id )
+{
+ for( QValueList<LoggerItem*>::Iterator it = processes.begin(); it != processes.end(); ++it ) {
+ if( (*it)->id == id ) return *it;
+ }
+
+ return 0;
+}
+
+QValueList<LoggerItem*> Logger::getLogs()
+{
+/* QValueList<LoggerItem*> items;
+
+ for( QValueList<LoggerItem*>::Iterator it = processes.begin(); it != processes.end(); ++it ) {
+ if( (*it)->completed ) items.append( *it );
+ }
+
+ return items;*/
+ return processes;
+}
+
+void Logger::processCompleted( int id, int state )
+{
+ LoggerItem* item = 0;
+ QTime time = QTime::currentTime();
+ bool remove = false;
+
+ for( QValueList<LoggerItem*>::Iterator it = processes.begin(); it != processes.end(); ++it ) {
+ // TODO test
+ if( time >= (*it)->time && (*it)->completed && (*it)->state == 0 ) {
+ time = (*it)->time;
+ item = *it;
+ remove = true;
+ }
+ else if( (*it)->id == id ) {
+ (*it)->state = state;
+ (*it)->completed = true;
+ (*it)->time = (*it)->time.currentTime();
+ (*it)->textStream << i18n("Finished logging");
+ (*it)->file.close();
+ emit updateProcess( id );
+ }
+ }
+
+ if( remove && processes.count() > 11 ) {
+ emit removedProcess( item->id );
+ item->file.remove();
+ processes.remove( item );
+ delete item;
+ }
+}
+
diff --git a/src/logger.h b/src/logger.h
new file mode 100755
index 0000000..c8d0076
--- /dev/null
+++ b/src/logger.h
@@ -0,0 +1,100 @@
+
+
+#ifndef LOGGER_H
+#define LOGGER_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qdatetime.h>
+#include <qfile.h>
+#include <qtextstream.h>
+
+
+/**
+ * @short An item for every process that is logged
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class LoggerItem
+{
+public:
+ /**
+ * Constructor
+ */
+ LoggerItem();
+
+ /**
+ * Destructor
+ */
+ virtual ~LoggerItem();
+
+ QString filename;
+ int id;
+ QStringList data;
+ bool completed;
+ int state; // 0 = ok, -1 = failed, 1 = other (soundKonverter)
+ QTime time;
+ QFile file;
+ QTextStream textStream;
+
+};
+
+
+/**
+ * @short All data about the processes are collected here
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class Logger : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ Logger();
+
+ /**
+ * Destructor
+ */
+ virtual ~Logger();
+
+ void cleanUp();
+
+ /**
+ * Creates a new logger item and returns the id of it, @p filename is added to the new logger item
+ */
+ int registerProcess( const QString& filename );
+
+ /**
+ * Adds the string @p data to the data of the logger item with id @p id
+ */
+ void log( int id, const QString& data );
+
+ /**
+ * Returns the logger item with id @p id
+ */
+ LoggerItem* getLog( int id );
+
+ /**
+ * Returns a list of all logger items
+ */
+ QValueList<LoggerItem*> getLogs();
+
+private:
+ /** the list of all logger items */
+ QValueList<LoggerItem*> processes;
+
+ /** returns an unused random id */
+ int getNewID();
+
+public slots:
+ void processCompleted( int id, int state );
+
+signals:
+ void removedProcess( int id );
+ void updateProcess( int id );
+
+};
+
+#endif // LOGGER_H
diff --git a/src/logviewer.cpp b/src/logviewer.cpp
new file mode 100755
index 0000000..c76f7e6
--- /dev/null
+++ b/src/logviewer.cpp
@@ -0,0 +1,204 @@
+
+#include "logviewer.h"
+#include "logger.h"
+
+#include <qlayout.h>
+#include <qstring.h>
+#include <qheader.h>
+#include <qcolor.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <kpushbutton.h>
+#include <kurl.h>
+//#include <kdebug.h>
+
+// ### soundkonverter 0.4: make sub items for the output
+
+LogViewerItem::LogViewerItem( KListView* parent, LogViewerItem* after, QString label1 )
+ : KListViewItem( parent, after, label1 )
+{
+ converting = false;
+}
+
+LogViewerItem::LogViewerItem( LogViewerItem* parent, LogViewerItem* after, QString label1 )
+ : KListViewItem( parent, after, label1 )
+{
+ converting = false;
+}
+
+LogViewerItem::~LogViewerItem()
+{}
+
+void LogViewerItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int alignment )
+{
+ // NOTE calculate the red color
+
+ QColorGroup _cg( cg );
+ QColor c;
+
+ if( isSelected() && converting ) {
+ _cg.setColor( QColorGroup::Highlight, QColor( 215, 62, 62 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( converting && column != listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 255, 234, 234 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( converting && column == listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 247, 227, 227 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+
+ KListViewItem::paintCell( p, _cg, column, width, alignment );
+}
+
+
+LogViewerList::LogViewerList( QWidget* parent, const char* name )
+ : KListView( parent, name )
+{}
+
+LogViewerList::~LogViewerList()
+{}
+
+
+LogViewer::LogViewer( Logger* _logger, QWidget *parent, const char *name, bool modal, WFlags f )
+ : KDialog( parent, name, modal, f )
+{
+ logger = _logger;
+ connect( logger, SIGNAL(removedProcess(int)),
+ this, SLOT(processRemoved(int))
+ );
+ connect( logger, SIGNAL(updateProcess(int)),
+ this, SLOT(updateProcess(int))
+ );
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ setCaption( i18n("Log Viewer") );
+ resize( 600, 400 );
+ setIcon( iconLoader->loadIcon("view_text",KIcon::Small) );
+
+ QGridLayout *grid = new QGridLayout( this, 4, 1, 11, 6 );
+
+ lLogs = new LogViewerList( this, "lLogs" );
+ lLogs->addColumn( i18n("Job/File") );
+ //lLogs->setSelectionMode( QListView::Extended );
+ //lLogs->setAllColumnsShowFocus( true );
+ lLogs->setResizeMode( QListView::LastColumn );
+ lLogs->setSorting( -1 );
+ lLogs->setRootIsDecorated( true );
+ lLogs->header()->setClickEnabled( false );
+ grid->addWidget( lLogs, 0, 0 );
+
+ QHBoxLayout *buttonBox = new QHBoxLayout();
+ grid->addLayout( buttonBox, 3, 0 );
+
+ pReload = new KPushButton(iconLoader->loadIcon("reload",KIcon::Small), i18n("Reload"), this, "pReload" );
+ buttonBox->addWidget( pReload );
+ connect( pReload, SIGNAL(clicked()),
+ this, SLOT(refillLogs())
+ );
+
+ buttonBox->addStretch();
+
+ pOk = new KPushButton(iconLoader->loadIcon("exit",KIcon::Small), i18n("Close"), this, "pOk" );
+ pOk->setFocus();
+ buttonBox->addWidget( pOk );
+ connect( pOk, SIGNAL(clicked()),
+ this, SLOT(accept())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+
+ refillLogs();
+}
+
+LogViewer::~LogViewer()
+{}
+
+void LogViewer::refillLogs()
+{
+ LogViewerItem *parent = 0, *last = 0;
+
+ lLogs->clear();
+ QValueList<LoggerItem*> logs = logger->getLogs();
+ for( QValueList<LoggerItem*>::Iterator a = logs.begin(); a != logs.end(); ++a ) {
+ parent = new LogViewerItem( lLogs, parent, KURL::decode_string((*a)->filename).replace("%2f","/").replace("%%","%") + " - " + QString::number((*a)->id) );
+ parent->converting = !(*a)->completed;
+ //parent->setOpen( true );
+ last = 0;
+ for( QStringList::Iterator b = (*a)->data.begin(); b != (*a)->data.end(); ++b ) {
+ last = new LogViewerItem( parent, last, *b );
+ last->setMultiLinesEnabled( true );
+ last->converting = !(*a)->completed;
+ }
+ }
+}
+
+void LogViewer::processRemoved( int id )
+{
+ LoggerItem* item = logger->getLog( id ); // this is ok, because the item still exists.
+ // it will be deleted after is function is completed
+
+ QListViewItem* it = lLogs->firstChild();
+
+ while( it != 0 ) {
+ if( it->text(0) == KURL::decode_string(item->filename).replace("%2f","/").replace("%%","%") + " - " + QString::number(item->id) ) {
+ delete it;
+ return;
+ }
+ it = it->nextSibling();
+ }
+}
+
+void LogViewer::updateProcess( int id )
+{
+ LoggerItem* item = logger->getLog( id );
+
+ LogViewerItem* it = lLogs->firstChild();
+
+ LogViewerItem *lastItem = 0, *oldItem = 0;
+
+ while( it != 0 ) {
+ if( it->text(0) == KURL::decode_string(item->filename).replace("%2f","/").replace("%%","%") + " - " + QString::number(item->id) ) {
+ LogViewerItem *a = it->firstChild(), *b;
+ while( a != 0 ) {
+ b = a->nextSibling();
+ delete a;
+ a = b;
+ }
+ it->converting = !item->completed;
+ LogViewerItem* last = 0;
+ for( QStringList::Iterator b = item->data.begin(); b != item->data.end(); ++b ) {
+ last = new LogViewerItem( (LogViewerItem*)it, last, *b );
+ last->setMultiLinesEnabled( true );
+ }
+ return;
+ }
+ it = it->nextSibling();
+ }
+
+ LogViewerItem *parent = 0;
+
+ // get the last list view item
+ for( QListViewItem* it = lLogs->firstChild(); it != 0; it = it->nextSibling() ) {
+ lastItem = (LogViewerItem*)it;
+ }
+
+ parent = new LogViewerItem( lLogs, lastItem, KURL::decode_string(item->filename).replace("%2f","/").replace("%%","%") + " - " + QString::number(item->id) );
+ parent->converting = !item->completed;
+ LogViewerItem* last = 0;
+ for( QStringList::Iterator b = item->data.begin(); b != item->data.end(); ++b ) {
+ last = new LogViewerItem( parent, last, *b );
+ last->setMultiLinesEnabled( true );
+ }
+}
+
+
diff --git a/src/logviewer.h b/src/logviewer.h
new file mode 100755
index 0000000..be3a8a3
--- /dev/null
+++ b/src/logviewer.h
@@ -0,0 +1,111 @@
+
+
+#ifndef LOGVIEWER_H
+#define LOGVIEWER_H
+
+#include <klistview.h>
+#include <kdialog.h>
+
+
+class KPushButton;
+class Logger;
+
+/**
+ * @short The items for the log viewer list
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class LogViewerItem : public KListViewItem
+{
+public:
+ /**
+ * Constructor
+ * @param parent The parent list view
+ * @param after The item, the new item should be placed after
+ * @param label1 The text for the first column
+ */
+ LogViewerItem( KListView* parent, LogViewerItem* after, QString label1 );
+
+ /**
+ * Constructor
+ * @param parent The parent list view item
+ * @param after The item, the new item should be placed after
+ * @param label1 The text for the first column
+ */
+ LogViewerItem( LogViewerItem* parent, LogViewerItem* after, QString label1 );
+
+ /**
+ * Destructor
+ */
+ virtual ~LogViewerItem();
+
+ virtual void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int alignment );
+
+ LogViewerItem* nextSibling() const { return static_cast<LogViewerItem*>( KListViewItem::nextSibling() ); }
+ LogViewerItem* firstChild() const { return static_cast<LogViewerItem*>( KListViewItem::firstChild() ); }
+
+ bool converting;
+};
+
+
+/**
+ * @short The log viewer list
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class LogViewerList : public KListView
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ * @param parent The parent widget
+ * @param name The name of the file list
+ */
+ LogViewerList( QWidget * parent = 0, const char* name = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~LogViewerList();
+
+ LogViewerItem* firstChild() const { return static_cast<LogViewerItem*>( KListView::firstChild() ); }
+};
+
+
+/**
+ * @short Shows the logs that are collected by the logger
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class LogViewer : public KDialog
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ LogViewer( Logger* _logger, QWidget* parent=0, const char* name = 0, bool modal = true, WFlags f = 0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~LogViewer();
+
+private:
+ Logger* logger;
+ LogViewerList* lLogs;
+ KPushButton* pOk;
+ KPushButton* pReload;
+
+private slots:
+ void refillLogs();
+ void updateProcess( int id );
+
+public slots:
+ /** get notification when a job has been removed */
+ void processRemoved( int id );
+
+};
+
+#endif // LOGVIEWER_H
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100755
index 0000000..7b42a3c
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,75 @@
+
+
+#include "soundkonverterapp.h"
+
+#include "tplugins.h"
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <klocale.h>
+
+static const char description[] =
+ I18N_NOOP("soundKonverter is a frontend to various audio converters, Replay Gain tools and CD rippers.\n\nPlease file bug reports at https://bugs.launchpad.net/ubuntu/+source/soundkonverter\nor simply send me a mail to [email protected]");
+
+static const char version[] = "0.3.8";
+
+static KCmdLineOptions options[] =
+{
+ { "replaygain", I18N_NOOP("Open the Replay Gain tool an add all given files"), 0 },
+// { "repair", I18N_NOOP("Open the repair files tool an add all given files"), 0 },
+ { "rip <device>", I18N_NOOP("List all tracks on the cd drive <device>, 'auto' will search for a cd"), 0 },
+ { "profile <profile>", I18N_NOOP("Add all files using the given profile"), 0 },
+ { "format <format>", I18N_NOOP("Add all files using the given format"), 0 },
+ { "output <directory>", I18N_NOOP("Output all files to <directory>"), 0 },
+ { "invisible", I18N_NOOP("Start soundKonverter invisible"), 0 },
+ { "autoclose", I18N_NOOP("Close soundKonverter after all files are converted (enabled when using '--invisible')"), 0 },
+ { "command <command>", I18N_NOOP("Execute <command> after each file has been converted"), 0 },
+ { "+[files]", I18N_NOOP("Audio file(s) to append to the file list"), 0 },
+ KCmdLineLastOption
+};
+
+int main(int argc, char **argv)
+{
+ KAboutData about("soundkonverter", I18N_NOOP("soundKonverter"), version, description,
+ KAboutData::License_GPL, "(C) 2008 Daniel Faust", 0, 0, "[email protected]");
+ about.addAuthor( "Daniel Faust", 0, "[email protected]" );
+ about.addCredit( "David Vignoni", "Nuvola icon theme", 0, "http://www.icon-king.com" );
+ about.addCredit( "Scott Wheeler", "TagLib", "[email protected]", "http://ktown.kde.org/~wheeler" );
+ about.addCredit( "Amarok developers", "Amarok", 0, "http://amarok.kde.org" );
+ about.addCredit( "Kaffeine developers", "Kaffeine", 0, "http://kaffeine.sourceforge.net" );
+ about.addCredit( "All programmers of audioconverters", "Backends" );
+ KCmdLineArgs::init(argc, argv, &about);
+ KCmdLineArgs::addCmdLineOptions( options );
+
+ soundKonverterApp::addCmdLineOptions();
+ if( !soundKonverterApp::start() ) {
+ return 0;
+ }
+
+ soundKonverterApp app;
+
+ registerTaglibPlugins();
+
+ // mainWin has WDestructiveClose flag by default, so it will delete itself.
+ return app.exec();
+}
+
+/*
+methods and files to update:
+outputdirectory.cpp: vfatPath( const QString& path )
+metadata/
+cddb.cpp, cddb.h
+cdmanager.cpp, cdmanager.h
+
+
+audio/x-musepack;application/x-musepack;audio/musepack;application/musepack;
+application/x-ape;audio/ape;audio/x-ape;
+audio/x-mp3;application/x-id3;audio/mpeg;audio/x-mpeg;audio/x-mpeg-3;audio/mpeg3;audio/mp3;audio/x-mp3;application/x-id3;audio/mpeg;audio/x-mpeg;audio/x-mpeg-3;audio/mpeg3;audio/mp3;
+audio/mp4;audio/x-m4a;audio/x-m4a;
+audio/mpc;audio/x-mpc;audio/mp;audio/x-mp;audio/mpc;audio/x-mpc;audio/mp;audio/x-mp;
+application/ogg;application/x-ogg;application/x-vorbis+ogg;audio/x-vorbis+ogg;audio/vorbis;audio/x-vorbis;audio/ogg;audio/x-ogg;application/ogg;application/x-ogg;audio/vorbis;audio/x-vorbis;audio/ogg;audio/x-ogg;
+audio/x-flac;application/x-flac;audio/flac;audio/x-flac;application/x-flac;audio/flac;
+audio/x-s3m;audio/x-mod;audio/x-xm;audio/x-it;audio/x-s3m;audio/x-mod;audio/x-xm;audio/x-it
+
+*/
+
diff --git a/src/metadata/Makefile.am b/src/metadata/Makefile.am
new file mode 100644
index 0000000..8e7209f
--- /dev/null
+++ b/src/metadata/Makefile.am
@@ -0,0 +1,37 @@
+if with_mp4v2
+MP4_SUBDIR = mp4
+MP4_LDADD = mp4/libtagmp4.la
+else
+MP4_SUBDIR = m4a
+MP4_LDADD = m4a/libtagm4a.la
+endif
+
+SUBDIRS = speex wavpack trueaudio asf audible rmff $(MP4_SUBDIR) aac wav ape optimfrog
+
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libmetadata_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libmetadata.la
+
+libmetadata_la_SOURCES = \
+ tplugins.cpp \
+ tagengine.cpp
+
+noinst_HEADERS = \
+ tplugins.h \
+ tagengine.h
+
+libmetadata_la_LIBADD = \
+ speex/libtagspeex.la \
+ trueaudio/libtagtrueaudio.la \
+ wavpack/libtagwavpack.la \
+ asf/libtagasf.la \
+ wav/libtagwav.la \
+ rmff/libtagrealmedia.la \
+ $(MP4_LDADD) \
+ $(taglib_libs) \
+ aac/libtagaac.la \
+ audible/libtagaudible.la \
+ ape/libtagmonkeysaudio.la \
+ optimfrog/libtagoptimfrog.la
diff --git a/src/metadata/aac/Makefile.am b/src/metadata/aac/Makefile.am
new file mode 100644
index 0000000..b6b0f43
--- /dev/null
+++ b/src/metadata/aac/Makefile.am
@@ -0,0 +1,12 @@
+SUBDIRS =
+METASOURCES = AUTO
+INCLUDES = $(all_includes) $(taglib_includes)
+
+libtagaac_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagaac.la
+
+libtagaac_la_SOURCES = \
+ aacfiletyperesolver.cpp
+
+noinst_HEADERS = \
+ aacfiletyperesolver.h
diff --git a/src/metadata/aac/aacfiletyperesolver.cpp b/src/metadata/aac/aacfiletyperesolver.cpp
new file mode 100644
index 0000000..fcc2a86
--- /dev/null
+++ b/src/metadata/aac/aacfiletyperesolver.cpp
@@ -0,0 +1,38 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "aacfiletyperesolver.h"
+#include <taglib/mpegfile.h>
+
+#include <string.h>
+
+TagLib::File *AACFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".aac"))
+ {
+ return new TagLib::MPEG::File(fileName, readProperties, propertiesStyle);
+ }
+
+ return 0;
+}
diff --git a/src/metadata/aac/aacfiletyperesolver.h b/src/metadata/aac/aacfiletyperesolver.h
new file mode 100644
index 0000000..6c33b71
--- /dev/null
+++ b/src/metadata/aac/aacfiletyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_AACFILETYPERESOLVER_H
+#define TAGLIB_AACFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class AACFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/ape/Makefile.am b/src/metadata/ape/Makefile.am
new file mode 100755
index 0000000..156cf91
--- /dev/null
+++ b/src/metadata/ape/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagmonkeysaudio_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagmonkeysaudio.la
+noinst_HEADERS = taglib_monkeysaudiofiletyperesolver.h
+libtagmonkeysaudio_la_SOURCES = taglib_monkeysaudiofiletyperesolver.cpp
diff --git a/src/metadata/ape/taglib_monkeysaudiofiletyperesolver.cpp b/src/metadata/ape/taglib_monkeysaudiofiletyperesolver.cpp
new file mode 100755
index 0000000..fea0344
--- /dev/null
+++ b/src/metadata/ape/taglib_monkeysaudiofiletyperesolver.cpp
@@ -0,0 +1,21 @@
+// (c) 2006 Martin Aumueller <[email protected]>
+// modified 2006 Daniel Faust <[email protected]>
+// See COPYING file for licensing information
+
+#include "taglib_monkeysaudiofiletyperesolver.h"
+#include <taglib/mpcfile.h>
+
+#include <string.h>
+
+TagLib::File *MonkeysAudioFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && (!strcasecmp(ext, ".ape") || !strcasecmp(ext, ".mac")))
+ {
+ return new TagLib::MPC::File(fileName, readProperties, propertiesStyle);
+ }
+
+ return 0;
+}
diff --git a/src/metadata/ape/taglib_monkeysaudiofiletyperesolver.h b/src/metadata/ape/taglib_monkeysaudiofiletyperesolver.h
new file mode 100755
index 0000000..4af4a7b
--- /dev/null
+++ b/src/metadata/ape/taglib_monkeysaudiofiletyperesolver.h
@@ -0,0 +1,19 @@
+// (c) 2006 Martin Aumueller <[email protected]>
+// modified 2006 Daniel Faust <[email protected]>
+// See COPYING file for licensing information
+
+#ifndef TAGLIB_MONKEYSAUDIOFILETYPERESOLVER_H
+#define TAGLIB_MONKEYSAUDIOFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class MonkeysAudioFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/asf/Makefile.am b/src/metadata/asf/Makefile.am
new file mode 100644
index 0000000..63fa0e1
--- /dev/null
+++ b/src/metadata/asf/Makefile.am
@@ -0,0 +1,20 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagasf_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagasf.la
+
+libtagasf_la_SOURCES = \
+ asfattribute.cpp \
+ asfproperties.cpp \
+ asftag.cpp \
+ asffile.cpp \
+ taglib_asffiletyperesolver.cpp
+
+noinst_HEADERS = \
+ asfattribute.h \
+ asfproperties.h \
+ asftag.h \
+ asffile.h \
+ taglib_asffiletyperesolver.h
diff --git a/src/metadata/asf/asfattribute.cpp b/src/metadata/asf/asfattribute.cpp
new file mode 100644
index 0000000..bfe81c5
--- /dev/null
+++ b/src/metadata/asf/asfattribute.cpp
@@ -0,0 +1,304 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <taglib.h>
+#include "asfattribute.h"
+#include "asffile.h"
+
+using namespace TagLib;
+
+class ASF::Attribute::AttributePrivate : public RefCounter
+{
+public:
+ AttributePrivate()
+ : stream(0),
+ language(0) {}
+ AttributeTypes type;
+ String stringValue;
+ ByteVector byteVectorValue;
+ union {
+ unsigned int intValue;
+ unsigned short shortValue;
+ unsigned long long longLongValue;
+ bool boolValue;
+ };
+ int stream;
+ int language;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::Attribute::Attribute()
+{
+ d = new AttributePrivate;
+ d->type = UnicodeType;
+}
+
+ASF::Attribute::Attribute(const ASF::Attribute &other)
+ : d(other.d)
+{
+ d->ref();
+}
+
+ASF::Attribute &
+ASF::Attribute::operator=(const ASF::Attribute &other)
+{
+ if(d->deref())
+ delete d;
+ d = other.d;
+ d->ref();
+ return *this;
+}
+
+ASF::Attribute::~Attribute()
+{
+ if(d->deref())
+ delete d;
+}
+
+ASF::Attribute::Attribute(const String &value)
+{
+ d = new AttributePrivate;
+ d->type = UnicodeType;
+ d->stringValue = value;
+}
+
+ASF::Attribute::Attribute(const ByteVector &value)
+{
+ d = new AttributePrivate;
+ d->type = BytesType;
+ d->byteVectorValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned int value)
+{
+ d = new AttributePrivate;
+ d->type = DWordType;
+ d->intValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned long long value)
+{
+ d = new AttributePrivate;
+ d->type = QWordType;
+ d->longLongValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned short value)
+{
+ d = new AttributePrivate;
+ d->type = WordType;
+ d->shortValue = value;
+}
+
+ASF::Attribute::Attribute(bool value)
+{
+ d = new AttributePrivate;
+ d->type = BoolType;
+ d->boolValue = value;
+}
+
+ASF::Attribute::AttributeTypes
+ASF::Attribute::type() const
+{
+ return d->type;
+}
+
+String
+ASF::Attribute::toString() const
+{
+ return d->stringValue;
+}
+
+ByteVector
+ASF::Attribute::toByteVector() const
+{
+ return d->byteVectorValue;
+}
+
+unsigned short
+ASF::Attribute::toBool() const
+{
+ return d->shortValue;
+}
+
+unsigned short
+ASF::Attribute::toUShort() const
+{
+ return d->shortValue;
+}
+
+unsigned int
+ASF::Attribute::toUInt() const
+{
+ return d->intValue;
+}
+
+unsigned long long
+ASF::Attribute::toULongLong() const
+{
+ return d->longLongValue;
+}
+
+String
+ASF::Attribute::parse(ASF::File &f, int kind)
+{
+ int size, nameLength;
+ String name;
+
+ // extended content descriptor
+ if(kind == 0) {
+ nameLength = f.readWORD();
+ name = f.readString(nameLength);
+ d->type = ASF::Attribute::AttributeTypes(f.readWORD());
+ size = f.readWORD();
+ }
+ // metadata & metadata library
+ else {
+ int temp = f.readWORD();
+ // metadata library
+ if(kind == 2) {
+ d->language = temp;
+ }
+ d->stream = f.readWORD();
+ nameLength = f.readWORD();
+ d->type = ASF::Attribute::AttributeTypes(f.readWORD());
+ size = f.readDWORD();
+ name = f.readString(nameLength);
+ }
+
+ switch(d->type) {
+ case WordType:
+ d->shortValue = f.readWORD();
+ break;
+
+ case BoolType:
+ if(kind == 0) {
+ d->boolValue = f.readDWORD() == 1;
+ }
+ else {
+ d->boolValue = f.readWORD() == 1;
+ }
+ break;
+
+ case DWordType:
+ d->intValue = f.readDWORD();
+ break;
+
+ case QWordType:
+ d->longLongValue = f.readQWORD();
+ break;
+
+ case UnicodeType:
+ d->stringValue = f.readString(size);
+ break;
+
+ case BytesType:
+ case GuidType:
+ d->byteVectorValue = f.readBlock(size);
+ break;
+ }
+
+ return name;
+}
+
+ByteVector
+ASF::Attribute::render(const String &name, int kind) const
+{
+ ByteVector data;
+
+ switch (d->type) {
+ case WordType:
+ data.append(ByteVector::fromShort(d->shortValue, false));
+ break;
+
+ case BoolType:
+ if(kind == 0) {
+ data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
+ }
+ else {
+ data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
+ }
+ break;
+
+ case DWordType:
+ data.append(ByteVector::fromUInt(d->intValue, false));
+ break;
+
+ case QWordType:
+ data.append(ByteVector::fromLongLong(d->longLongValue, false));
+ break;
+
+ case UnicodeType:
+ data.append(File::renderString(d->stringValue));
+ break;
+
+ case BytesType:
+ case GuidType:
+ data.append(d->byteVectorValue);
+ break;
+ }
+
+ if(kind == 0) {
+ data = File::renderString(name, true) +
+ ByteVector::fromShort((int)d->type, false) +
+ ByteVector::fromShort(data.size(), false) +
+ data;
+ }
+ else {
+ ByteVector nameData = File::renderString(name);
+ data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
+ ByteVector::fromShort(d->stream, false) +
+ ByteVector::fromShort(nameData.size(), false) +
+ ByteVector::fromShort((int)d->type, false) +
+ ByteVector::fromUInt(data.size(), false) +
+ nameData +
+ data;
+ }
+
+ return data;
+}
+
+int
+ASF::Attribute::language() const
+{
+ return d->language;
+}
+
+void
+ASF::Attribute::setLanguage(int value)
+{
+ d->language = value;
+}
+
+int
+ASF::Attribute::stream() const
+{
+ return d->stream;
+}
+
+void
+ASF::Attribute::setStream(int value)
+{
+ d->stream = value;
+}
diff --git a/src/metadata/asf/asfattribute.h b/src/metadata/asf/asfattribute.h
new file mode 100644
index 0000000..9a8e3c9
--- /dev/null
+++ b/src/metadata/asf/asfattribute.h
@@ -0,0 +1,176 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_ASFATTRIBUTE_H
+#define TAGLIB_ASFATTRIBUTE_H
+
+#include <tstring.h>
+#include <tbytevector.h>
+
+namespace TagLib
+{
+
+ namespace ASF
+ {
+
+ class File;
+
+ class Attribute
+ {
+ public:
+
+ /*!
+ * Enum of types an Attribute can have.
+ */
+ enum AttributeTypes {
+ UnicodeType = 0,
+ BytesType = 1,
+ BoolType = 2,
+ DWordType = 3,
+ QWordType = 4,
+ WordType = 5,
+ GuidType = 6
+ };
+
+ /*!
+ * Constructs an empty attribute.
+ */
+ Attribute();
+
+ /*!
+ * Constructs an attribute with \a key and a UnicodeType \a value.
+ */
+ Attribute(const String &value);
+
+ /*!
+ * Constructs an attribute with \a key and a BytesType \a value.
+ */
+ Attribute(const ByteVector &value);
+
+ /*!
+ * Constructs an attribute with \a key and a DWordType \a value.
+ */
+ Attribute(unsigned int value);
+
+ /*!
+ * Constructs an attribute with \a key and a QWordType \a value.
+ */
+ Attribute(unsigned long long value);
+
+ /*!
+ * Constructs an attribute with \a key and a WordType \a value.
+ */
+ Attribute(unsigned short value);
+
+ /*!
+ * Constructs an attribute with \a key and a BoolType \a value.
+ */
+ Attribute(bool value);
+
+ /*!
+ * Construct an attribute as a copy of \a other.
+ */
+ Attribute(const Attribute &item);
+
+ /*!
+ * Copies the contents of \a other into this item.
+ */
+ ASF::Attribute &operator=(const Attribute &other);
+
+ /*!
+ * Destroys the attribute.
+ */
+ virtual ~Attribute();
+
+ /*!
+ * Returns type of the value.
+ */
+ AttributeTypes type() const;
+
+ /*!
+ * Returns the BoolType \a value.
+ */
+ unsigned short toBool() const;
+
+ /*!
+ * Returns the WordType \a value.
+ */
+ unsigned short toUShort() const;
+
+ /*!
+ * Returns the DWordType \a value.
+ */
+ unsigned int toUInt() const;
+
+ /*!
+ * Returns the QWordType \a value.
+ */
+ unsigned long long toULongLong() const;
+
+ /*!
+ * Returns the UnicodeType \a value.
+ */
+ String toString() const;
+
+ /*!
+ * Returns the BytesType \a value.
+ */
+ ByteVector toByteVector() const;
+
+ /*!
+ * Returns the language number, or 0 is no stream number was set.
+ */
+ int language() const;
+
+ /*!
+ * Sets the language number.
+ */
+ void setLanguage(int value);
+
+ /*!
+ * Returns the stream number, or 0 is no stream number was set.
+ */
+ int stream() const;
+
+ /*!
+ * Sets the stream number.
+ */
+ void setStream(int value);
+
+#ifndef DO_NOT_DOCUMENT
+ /* THIS IS PRIVATE, DON'T TOUCH IT! */
+ String parse(ASF::File &file, int kind = 0);
+#endif
+
+ private:
+ friend class File;
+
+ ByteVector render(const String &name, int kind = 0) const;
+
+ class AttributePrivate;
+ AttributePrivate *d;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/src/metadata/asf/asffile.cpp b/src/metadata/asf/asffile.cpp
new file mode 100644
index 0000000..55a12cc
--- /dev/null
+++ b/src/metadata/asf/asffile.cpp
@@ -0,0 +1,547 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tbytevectorlist.h>
+#include <tstring.h>
+#include "asffile.h"
+#include "asftag.h"
+#include "asfproperties.h"
+
+using namespace TagLib;
+
+class ASF::File::FilePrivate
+{
+public:
+ FilePrivate():
+ size(0),
+ tag(0),
+ properties(0),
+ contentDescriptionObject(0),
+ extendedContentDescriptionObject(0),
+ headerExtensionObject(0),
+ metadataObject(0),
+ metadataLibraryObject(0) {}
+ unsigned long long size;
+ ASF::Tag *tag;
+ ASF::Properties *properties;
+ List<ASF::File::BaseObject *> objects;
+ ASF::File::ContentDescriptionObject *contentDescriptionObject;
+ ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
+ ASF::File::HeaderExtensionObject *headerExtensionObject;
+ ASF::File::MetadataObject *metadataObject;
+ ASF::File::MetadataLibraryObject *metadataLibraryObject;
+};
+
+static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
+static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
+static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
+static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
+static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
+static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
+
+class ASF::File::BaseObject
+{
+public:
+ ByteVector data;
+ virtual ~BaseObject() {}
+ virtual ByteVector guid() = 0;
+ virtual void parse(ASF::File *file, unsigned int size);
+ virtual ByteVector render(ASF::File *file);
+};
+
+class ASF::File::UnknownObject : public ASF::File::BaseObject
+{
+ ByteVector myGuid;
+public:
+ UnknownObject(const ByteVector &guid);
+ ByteVector guid();
+};
+
+class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
+{
+public:
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+};
+
+class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
+{
+public:
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+};
+
+class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
+{
+public:
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+ ByteVector render(ASF::File *file);
+};
+
+class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
+{
+public:
+ ByteVectorList attributeData;
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+ ByteVector render(ASF::File *file);
+};
+
+class ASF::File::MetadataObject : public ASF::File::BaseObject
+{
+public:
+ ByteVectorList attributeData;
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+ ByteVector render(ASF::File *file);
+};
+
+class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
+{
+public:
+ ByteVectorList attributeData;
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+ ByteVector render(ASF::File *file);
+};
+
+class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
+{
+public:
+ List<ASF::File::BaseObject *> objects;
+ ByteVector guid();
+ void parse(ASF::File *file, uint size);
+ ByteVector render(ASF::File *file);
+};
+
+void
+ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
+{
+ data = file->readBlock(size - 24);
+}
+
+ByteVector
+ASF::File::BaseObject::render(ASF::File * /*file*/)
+{
+ return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
+}
+
+ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
+{
+}
+
+ByteVector
+ASF::File::UnknownObject::guid()
+{
+ return myGuid;
+}
+
+ByteVector
+ASF::File::FilePropertiesObject::guid()
+{
+ return filePropertiesGuid;
+}
+
+void
+ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
+{
+ BaseObject::parse(file, size);
+ file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
+}
+
+ByteVector
+ASF::File::StreamPropertiesObject::guid()
+{
+ return streamPropertiesGuid;
+}
+
+void
+ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
+{
+ BaseObject::parse(file, size);
+ file->d->properties->setChannels(data.mid(56, 2).toShort(false));
+ file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
+ file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
+}
+
+ByteVector
+ASF::File::ContentDescriptionObject::guid()
+{
+ return contentDescriptionGuid;
+}
+
+void
+ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
+{
+ file->d->contentDescriptionObject = this;
+ int titleLength = file->readWORD();
+ int artistLength = file->readWORD();
+ int copyrightLength = file->readWORD();
+ int commentLength = file->readWORD();
+ int ratingLength = file->readWORD();
+ file->d->tag->setTitle(file->readString(titleLength));
+ file->d->tag->setArtist(file->readString(artistLength));
+ file->d->tag->setCopyright(file->readString(copyrightLength));
+ file->d->tag->setComment(file->readString(commentLength));
+ file->d->tag->setRating(file->readString(ratingLength));
+}
+
+ByteVector
+ASF::File::ContentDescriptionObject::render(ASF::File *file)
+{
+ ByteVector v1 = file->renderString(file->d->tag->title());
+ ByteVector v2 = file->renderString(file->d->tag->artist());
+ ByteVector v3 = file->renderString(file->d->tag->copyright());
+ ByteVector v4 = file->renderString(file->d->tag->comment());
+ ByteVector v5 = file->renderString(file->d->tag->rating());
+ data.clear();
+ data.append(ByteVector::fromShort(v1.size(), false));
+ data.append(ByteVector::fromShort(v2.size(), false));
+ data.append(ByteVector::fromShort(v3.size(), false));
+ data.append(ByteVector::fromShort(v4.size(), false));
+ data.append(ByteVector::fromShort(v5.size(), false));
+ data.append(v1);
+ data.append(v2);
+ data.append(v3);
+ data.append(v4);
+ data.append(v5);
+ return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::ExtendedContentDescriptionObject::guid()
+{
+ return extendedContentDescriptionGuid;
+}
+
+void
+ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
+{
+ file->d->extendedContentDescriptionObject = this;
+ int count = file->readWORD();
+ while(count--) {
+ ASF::Attribute attribute;
+ String name = attribute.parse(*file);
+ file->d->tag->addAttribute(name, attribute);
+ }
+}
+
+ByteVector
+ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
+{
+ data.clear();
+ data.append(ByteVector::fromShort(attributeData.size(), false));
+ data.append(attributeData.toByteVector(ByteVector::null));
+ return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::MetadataObject::guid()
+{
+ return metadataGuid;
+}
+
+void
+ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
+{
+ file->d->metadataObject = this;
+ int count = file->readWORD();
+ while(count--) {
+ ASF::Attribute attribute;
+ String name = attribute.parse(*file, 1);
+ file->d->tag->addAttribute(name, attribute);
+ }
+}
+
+ByteVector
+ASF::File::MetadataObject::render(ASF::File *file)
+{
+ data.clear();
+ data.append(ByteVector::fromShort(attributeData.size(), false));
+ data.append(attributeData.toByteVector(ByteVector::null));
+ return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::MetadataLibraryObject::guid()
+{
+ return metadataLibraryGuid;
+}
+
+void
+ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
+{
+ file->d->metadataLibraryObject = this;
+ int count = file->readWORD();
+ while(count--) {
+ ASF::Attribute attribute;
+ String name = attribute.parse(*file, 2);
+ file->d->tag->addAttribute(name, attribute);
+ }
+}
+
+ByteVector
+ASF::File::MetadataLibraryObject::render(ASF::File *file)
+{
+ data.clear();
+ data.append(ByteVector::fromShort(attributeData.size(), false));
+ data.append(attributeData.toByteVector(ByteVector::null));
+ return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::HeaderExtensionObject::guid()
+{
+ return headerExtensionGuid;
+}
+
+void
+ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
+{
+ file->d->headerExtensionObject = this;
+ file->seek(18, File::Current);
+ long long dataSize = file->readDWORD();
+ long long dataPos = 0;
+ while(dataPos < dataSize) {
+ ByteVector guid = file->readBlock(16);
+ long long size = file->readQWORD();
+ BaseObject *obj;
+ if(guid == metadataGuid) {
+ obj = new MetadataObject();
+ }
+ else if(guid == metadataLibraryGuid) {
+ obj = new MetadataLibraryObject();
+ }
+ else {
+ obj = new UnknownObject(guid);
+ }
+ obj->parse(file, size);
+ objects.append(obj);
+ dataPos += size;
+ }
+}
+
+ByteVector
+ASF::File::HeaderExtensionObject::render(ASF::File *file)
+{
+ data.clear();
+ for(unsigned int i = 0; i < objects.size(); i++) {
+ data.append(objects[i]->render(file));
+ }
+ data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
+ return BaseObject::render(file);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::File::File(const char *file, bool readProperties, Properties::ReadStyle propertiesStyle)
+ : TagLib::File(file)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+ASF::File::~File()
+{
+ for(unsigned int i = 0; i < d->objects.size(); i++) {
+ delete d->objects[i];
+ }
+ if(d->tag) {
+ delete d->tag;
+ }
+ if(d->properties) {
+ delete d->properties;
+ }
+ delete d;
+}
+
+ASF::Tag *ASF::File::tag() const
+{
+ return d->tag;
+}
+
+ASF::Properties *ASF::File::audioProperties() const
+{
+ return d->properties;
+}
+
+void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
+{
+ if(!isValid())
+ return;
+
+ ByteVector guid = readBlock(16);
+ if(guid != headerGuid) {
+ return;
+ }
+
+ d->tag = new ASF::Tag();
+ d->properties = new ASF::Properties();
+
+ d->size = readQWORD();
+ int numObjects = readDWORD();
+ seek(2, Current);
+
+ for(int i = 0; i < numObjects; i++) {
+ ByteVector guid = readBlock(16);
+ long size = (long)readQWORD();
+ BaseObject *obj;
+ if(guid == filePropertiesGuid) {
+ obj = new FilePropertiesObject();
+ }
+ else if(guid == streamPropertiesGuid) {
+ obj = new StreamPropertiesObject();
+ }
+ else if(guid == contentDescriptionGuid) {
+ obj = new ContentDescriptionObject();
+ }
+ else if(guid == extendedContentDescriptionGuid) {
+ obj = new ExtendedContentDescriptionObject();
+ }
+ else if(guid == headerExtensionGuid) {
+ obj = new HeaderExtensionObject();
+ }
+ else {
+ obj = new UnknownObject(guid);
+ }
+ obj->parse(this, size);
+ d->objects.append(obj);
+ }
+}
+
+bool ASF::File::save()
+{
+ if(readOnly()) {
+ return false;
+ }
+
+ if(!d->contentDescriptionObject) {
+ d->contentDescriptionObject = new ContentDescriptionObject();
+ d->objects.append(d->contentDescriptionObject);
+ }
+ if(!d->extendedContentDescriptionObject) {
+ d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
+ d->objects.append(d->extendedContentDescriptionObject);
+ }
+ if(!d->headerExtensionObject) {
+ d->headerExtensionObject = new HeaderExtensionObject();
+ d->objects.append(d->headerExtensionObject);
+ }
+ if(!d->metadataObject) {
+ d->metadataObject = new MetadataObject();
+ d->headerExtensionObject->objects.append(d->metadataObject);
+ }
+ if(!d->metadataLibraryObject) {
+ d->metadataLibraryObject = new MetadataLibraryObject();
+ d->headerExtensionObject->objects.append(d->metadataLibraryObject);
+ }
+
+ ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
+ for(; it != d->tag->attributeListMap().end(); it++) {
+ const String &name = it->first;
+ const AttributeList &attributes = it->second;
+ bool inExtendedContentDescriptionObject = false;
+ bool inMetadataObject = false;
+ for(unsigned int j = 0; j < attributes.size(); j++) {
+ const Attribute &attribute = attributes[j];
+ if(!inExtendedContentDescriptionObject && attribute.language() == 0 && attribute.stream() == 0) {
+ d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
+ inExtendedContentDescriptionObject = true;
+ }
+ else if(!inMetadataObject && attribute.language() == 0 && attribute.stream() != 0) {
+ d->metadataObject->attributeData.append(attribute.render(name, 1));
+ inMetadataObject = true;
+ }
+ else {
+ d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
+ }
+ }
+ }
+
+ ByteVector data;
+ for(unsigned int i = 0; i < d->objects.size(); i++) {
+ data.append(d->objects[i]->render(this));
+ }
+ data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
+ insert(data, 0, d->size);
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+int ASF::File::readBYTE()
+{
+ ByteVector v = readBlock(1);
+ return v[0];
+}
+
+int ASF::File::readWORD()
+{
+ ByteVector v = readBlock(2);
+ return v.toShort(false);
+}
+
+unsigned int ASF::File::readDWORD()
+{
+ ByteVector v = readBlock(4);
+ return v.toUInt(false);
+}
+
+long long ASF::File::readQWORD()
+{
+ ByteVector v = readBlock(8);
+ return v.toLongLong(false);
+}
+
+String
+ASF::File::readString(int length)
+{
+ ByteVector data = readBlock(length);
+ unsigned int size = data.size();
+ while (size >= 2) {
+ if(data[size - 1] != '\0' || data[size - 2] != '\0') {
+ break;
+ }
+ size -= 2;
+ }
+ if(size != data.size()) {
+ data.resize(size);
+ }
+ return String(data, String::UTF16LE);
+}
+
+ByteVector
+ASF::File::renderString(const String &str, bool includeLength)
+{
+ ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
+ if(includeLength) {
+ data = ByteVector::fromShort(data.size(), false) + data;
+ }
+ return data;
+}
diff --git a/src/metadata/asf/asffile.h b/src/metadata/asf/asffile.h
new file mode 100644
index 0000000..db97331
--- /dev/null
+++ b/src/metadata/asf/asffile.h
@@ -0,0 +1,114 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_ASFFILE_H
+#define TAGLIB_ASFFILE_H
+
+#include <tag.h>
+#include <tfile.h>
+#include "asfproperties.h"
+#include "asftag.h"
+
+namespace TagLib {
+
+ //! An implementation of ASF (WMA) metadata
+ namespace ASF {
+
+ /*!
+ * This implements and provides an interface for ASF files to the
+ * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+ * the abstract TagLib::File API as well as providing some additional
+ * information specific to ASF files.
+ */
+ class File : public TagLib::File
+ {
+ public:
+
+ /*!
+ * Contructs an ASF file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ *
+ * \note In the current implementation, both \a readProperties and
+ * \a propertiesStyle are ignored.
+ */
+ File(const char *file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns a pointer to the ASF tag of the file.
+ *
+ * ASF::Tag implements the tag interface, so this serves as the
+ * reimplementation of TagLib::File::tag().
+ *
+ * \note The Tag <b>is still</b> owned by the ASF::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ virtual Tag *tag() const;
+
+ /*!
+ * Returns the ASF audio properties for this file.
+ */
+ virtual Properties *audioProperties() const;
+
+ /*!
+ * Save the file.
+ *
+ * This returns true if the save was successful.
+ */
+ virtual bool save();
+
+ private:
+
+ int readBYTE();
+ int readWORD();
+ unsigned int readDWORD();
+ long long readQWORD();
+ static ByteVector renderString(const String &str, bool includeLength = false);
+ String readString(int len);
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+ friend class Attribute;
+
+ class BaseObject;
+ class UnknownObject;
+ class FilePropertiesObject;
+ class StreamPropertiesObject;
+ class ContentDescriptionObject;
+ class ExtendedContentDescriptionObject;
+ class HeaderExtensionObject;
+ class MetadataObject;
+ class MetadataLibraryObject;
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/src/metadata/asf/asfproperties.cpp b/src/metadata/asf/asfproperties.cpp
new file mode 100644
index 0000000..ca1d983
--- /dev/null
+++ b/src/metadata/asf/asfproperties.cpp
@@ -0,0 +1,95 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include "asfproperties.h"
+
+using namespace TagLib;
+
+class ASF::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0) {}
+ int length;
+ int bitrate;
+ int sampleRate;
+ int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
+{
+ d = new PropertiesPrivate;
+}
+
+ASF::Properties::~Properties()
+{
+ if(d)
+ delete d;
+}
+
+int ASF::Properties::length() const
+{
+ return d->length;
+}
+
+int ASF::Properties::bitrate() const
+{
+ return d->bitrate;
+}
+
+int ASF::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int ASF::Properties::channels() const
+{
+ return d->channels;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void ASF::Properties::setLength(int length)
+{
+ d->length = length;
+}
+
+void ASF::Properties::setBitrate(int length)
+{
+ d->bitrate = length;
+}
+
+void ASF::Properties::setSampleRate(int length)
+{
+ d->sampleRate = length;
+}
+
+void ASF::Properties::setChannels(int length)
+{
+ d->channels = length;
+}
+
diff --git a/src/metadata/asf/asfproperties.h b/src/metadata/asf/asfproperties.h
new file mode 100644
index 0000000..150db8a
--- /dev/null
+++ b/src/metadata/asf/asfproperties.h
@@ -0,0 +1,69 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_ASFPROPERTIES_H
+#define TAGLIB_ASFPROPERTIES_H
+
+#include <audioproperties.h>
+#include <tstring.h>
+
+namespace TagLib {
+
+ namespace ASF {
+
+ //! An implementation of ASF audio properties
+ class Properties : public AudioProperties
+ {
+ public:
+
+ /*!
+ * Create an instance of ASF::Properties.
+ */
+ Properties();
+
+ /*!
+ * Destroys this ASF::Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+#ifndef DO_NOT_DOCUMENT
+ void setLength(int value);
+ void setBitrate(int value);
+ void setSampleRate(int value);
+ void setChannels(int value);
+#endif
+
+ private:
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/src/metadata/asf/asftag.cpp b/src/metadata/asf/asftag.cpp
new file mode 100644
index 0000000..300887e
--- /dev/null
+++ b/src/metadata/asf/asftag.cpp
@@ -0,0 +1,202 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "asftag.h"
+
+using namespace TagLib;
+
+class ASF::Tag::TagPrivate
+{
+public:
+ String title;
+ String artist;
+ String copyright;
+ String comment;
+ String rating;
+ AttributeListMap attributeListMap;
+};
+
+ASF::Tag::Tag()
+: TagLib::Tag()
+{
+ d = new TagPrivate;
+}
+
+ASF::Tag::~Tag()
+{
+ if(d)
+ delete d;
+}
+
+String
+ASF::Tag::title() const
+{
+ return d->title;
+}
+
+String
+ASF::Tag::artist() const
+{
+ return d->artist;
+}
+
+String
+ASF::Tag::album() const
+{
+ if(d->attributeListMap.contains("WM/AlbumTitle"))
+ return d->attributeListMap["WM/AlbumTitle"][0].toString();
+ return String::null;
+}
+
+String
+ASF::Tag::copyright() const
+{
+ return d->copyright;
+}
+
+String
+ASF::Tag::comment() const
+{
+ return d->comment;
+}
+
+String
+ASF::Tag::rating() const
+{
+ return d->rating;
+}
+
+unsigned int
+ASF::Tag::year() const
+{
+ if(d->attributeListMap.contains("WM/Year"))
+ return d->attributeListMap["WM/Year"][0].toString().toInt();
+ return 0;
+}
+
+unsigned int
+ASF::Tag::track() const
+{
+ if(d->attributeListMap.contains("WM/TrackNumber"))
+ return d->attributeListMap["WM/TrackNumber"][0].toString().toInt();
+ if(d->attributeListMap.contains("WM/Track"))
+ return d->attributeListMap["WM/Track"][0].toUInt();
+ return 0;
+}
+
+String
+ASF::Tag::genre() const
+{
+ if(d->attributeListMap.contains("WM/Genre"))
+ return d->attributeListMap["WM/Genre"][0].toString();
+ return String::null;
+}
+
+void
+ASF::Tag::setTitle(const String &value)
+{
+ d->title = value;
+}
+
+void
+ASF::Tag::setArtist(const String &value)
+{
+ d->artist = value;
+}
+
+void
+ASF::Tag::setCopyright(const String &value)
+{
+ d->copyright = value;
+}
+
+void
+ASF::Tag::setComment(const String &value)
+{
+ d->comment = value;
+}
+
+void
+ASF::Tag::setRating(const String &value)
+{
+ d->rating = value;
+}
+
+void
+ASF::Tag::setAlbum(const String &value)
+{
+ setAttribute("WM/AlbumTitle", value);
+}
+
+void
+ASF::Tag::setGenre(const String &value)
+{
+ setAttribute("WM/Genre", value);
+}
+
+void
+ASF::Tag::setYear(uint value)
+{
+ setAttribute("WM/Year", String::number(value));
+}
+
+void
+ASF::Tag::setTrack(uint value)
+{
+ setAttribute("WM/TrackNumber", String::number(value));
+}
+
+ASF::AttributeListMap&
+ASF::Tag::attributeListMap()
+{
+ return d->attributeListMap;
+}
+
+void ASF::Tag::removeItem(const String &key)
+{
+ AttributeListMap::Iterator it = d->attributeListMap.find(key);
+ if(it != d->attributeListMap.end())
+ d->attributeListMap.erase(it);
+}
+
+void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
+{
+ AttributeList value;
+ value.append(attribute);
+ d->attributeListMap.insert(name, value);
+}
+
+void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
+{
+ if(d->attributeListMap.contains(name)) {
+ d->attributeListMap[name].append(attribute);
+ }
+ else {
+ setAttribute(name, attribute);
+ }
+}
+
+bool ASF::Tag::isEmpty() const {
+ return TagLib::Tag::isEmpty() &&
+ copyright().isEmpty() &&
+ rating().isEmpty() &&
+ d->attributeListMap.isEmpty();
+}
diff --git a/src/metadata/asf/asftag.h b/src/metadata/asf/asftag.h
new file mode 100644
index 0000000..f282dee
--- /dev/null
+++ b/src/metadata/asf/asftag.h
@@ -0,0 +1,181 @@
+/**************************************************************************
+ copyright : (C) 2005-2007 by Lukáš Lalinský
+ **************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_ASFTAG_H
+#define TAGLIB_ASFTAG_H
+
+#include <tag.h>
+#include <tlist.h>
+#include <tmap.h>
+#include "asfattribute.h"
+
+namespace TagLib {
+
+ namespace ASF {
+
+ typedef List<Attribute> AttributeList;
+ typedef Map<String, AttributeList> AttributeListMap;
+
+ class Tag : public TagLib::Tag {
+
+ friend class File;
+
+ public:
+
+ Tag();
+
+ virtual ~Tag();
+
+ /*!
+ * Returns the track name.
+ */
+ virtual String title() const;
+
+ /*!
+ * Returns the artist name.
+ */
+ virtual String artist() const;
+
+ /*!
+ * Returns the album name; if no album name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String album() const;
+
+ /*!
+ * Returns the track comment.
+ */
+ virtual String comment() const;
+
+ /*!
+ * Returns the genre name; if no genre is present in the tag String::null
+ * will be returned.
+ */
+ virtual String genre() const;
+
+ /*!
+ * Returns the rating.
+ */
+ virtual String rating() const;
+
+ /*!
+ * Returns the genre name; if no genre is present in the tag String::null
+ * will be returned.
+ */
+ virtual String copyright() const;
+
+ /*!
+ * Returns the year; if there is no year set, this will return 0.
+ */
+ virtual uint year() const;
+
+ /*!
+ * Returns the track number; if there is no track number set, this will
+ * return 0.
+ */
+ virtual uint track() const;
+
+ /*!
+ * Sets the title to \a s.
+ */
+ virtual void setTitle(const String &s);
+
+ /*!
+ * Sets the artist to \a s.
+ */
+ virtual void setArtist(const String &s);
+
+ /*!
+ * Sets the album to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setAlbum(const String &s);
+
+ /*!
+ * Sets the comment to \a s.
+ */
+ virtual void setComment(const String &s);
+
+ /*!
+ * Sets the rating to \a s.
+ */
+ virtual void setRating(const String &s);
+
+ /*!
+ * Sets the copyright to \a s.
+ */
+ virtual void setCopyright(const String &s);
+
+ /*!
+ * Sets the genre to \a s.
+ */
+ virtual void setGenre(const String &s);
+
+ /*!
+ * Sets the year to \a i. If \a s is 0 then this value will be cleared.
+ */
+ virtual void setYear(uint i);
+
+ /*!
+ * Sets the track to \a i. If \a s is 0 then this value will be cleared.
+ */
+ virtual void setTrack(uint i);
+
+ /*!
+ * Returns true if the tag does not contain any data. This should be
+ * reimplemented in subclasses that provide more than the basic tagging
+ * abilities in this class.
+ */
+ virtual bool isEmpty() const;
+
+ /*!
+ * Returns a reference to the item list map. This is an AttributeListMap of
+ * all of the items in the tag.
+ *
+ * This is the most powerfull structure for accessing the items of the tag.
+ */
+ AttributeListMap &attributeListMap();
+
+ /*!
+ * Removes the \a key attribute from the tag
+ */
+ void removeItem(const String &name);
+
+ /*!
+ * Sets the \a key attribute to the value of \a attribute. If an attribute
+ * with the \a key is already present, it will be replaced.
+ */
+ void setAttribute(const String &name, const Attribute &attribute);
+
+ /*!
+ * Sets the \a key attribute to the value of \a attribute. If an attribute
+ * with the \a key is already present, it will be added to the list.
+ */
+ void addAttribute(const String &name, const Attribute &attribute);
+
+ private:
+
+ class TagPrivate;
+ TagPrivate *d;
+ };
+ }
+}
+#endif
diff --git a/src/metadata/asf/taglib_asffiletyperesolver.cpp b/src/metadata/asf/taglib_asffiletyperesolver.cpp
new file mode 100644
index 0000000..f9ed059
--- /dev/null
+++ b/src/metadata/asf/taglib_asffiletyperesolver.cpp
@@ -0,0 +1,47 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+// (c) 2005 Martin Aumueller <[email protected]>
+// See COPYING file for licensing information
+
+#include "taglib_asffiletyperesolver.h"
+#include "asffile.h"
+
+#include <string.h>
+
+TagLib::File *ASFFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && (!strcasecmp(ext, ".wma") || !strcasecmp(ext, ".asf")))
+ {
+ TagLib::ASF::File *f = new TagLib::ASF::File(fileName, readProperties, propertiesStyle);
+ if(f->isValid())
+ return f;
+ else
+ {
+ delete f;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/metadata/asf/taglib_asffiletyperesolver.h b/src/metadata/asf/taglib_asffiletyperesolver.h
new file mode 100644
index 0000000..ab524b4
--- /dev/null
+++ b/src/metadata/asf/taglib_asffiletyperesolver.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+// (c) 2005 Martin Aumueller <[email protected]>
+// See COPYING file for licensing information
+
+#ifndef TAGLIB_ASFFILETYPERESOLVER_H
+#define TAGLIB_ASFFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class ASFFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+
+public:
+ virtual ~ASFFileTypeResolver() {}
+};
+
+#endif
diff --git a/src/metadata/audible/Makefile.am b/src/metadata/audible/Makefile.am
new file mode 100644
index 0000000..dcade34
--- /dev/null
+++ b/src/metadata/audible/Makefile.am
@@ -0,0 +1,17 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagaudible_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagaudible.la
+
+libtagaudible_la_SOURCES = audibleproperties.cpp \
+ audibletag.cpp \
+ taglib_audiblefile.cpp \
+ taglib_audiblefiletyperesolver.cpp
+
+noinst_HEADERS = audibleproperties.h \
+ audibletag.h \
+ taglib_audiblefile.h \
+ taglib_audiblefiletyperesolver.h
+
diff --git a/src/metadata/audible/audibleproperties.cpp b/src/metadata/audible/audibleproperties.cpp
new file mode 100644
index 0000000..4f39322
--- /dev/null
+++ b/src/metadata/audible/audibleproperties.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "audibleproperties.h"
+
+#include <taglib/tstring.h>
+
+#include "taglib_audiblefile.h"
+
+#include <netinet/in.h> // ntohl
+
+using namespace TagLib;
+
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Audible::Properties::Properties(Properties::ReadStyle style) : AudioProperties(style)
+{
+ m_length = 0;
+ m_bitrate = 0;
+ m_sampleRate = 0;
+ m_channels = 0;
+}
+
+Audible::Properties::~Properties()
+{
+}
+
+int Audible::Properties::length() const
+{
+ return m_length;
+}
+
+int Audible::Properties::bitrate() const
+{
+ return m_bitrate;
+}
+
+int Audible::Properties::sampleRate() const
+{
+ return m_sampleRate;
+}
+
+int Audible::Properties::channels() const
+{
+ return m_channels;
+}
+
+#define LENGTH_OFF 61
+
+void Audible::Properties::readAudibleProperties( FILE *fp, int off )
+{
+ fseek(fp, off+LENGTH_OFF, SEEK_SET );
+ fread(&m_length, sizeof(m_length), 1, fp);
+ m_length = ntohl(m_length);
+ //fprintf(stderr, "len (sec): %d\n", m_length);
+ m_bitrate = 0;
+ m_sampleRate = 0;
+ m_channels = 1;
+}
diff --git a/src/metadata/audible/audibleproperties.h b/src/metadata/audible/audibleproperties.h
new file mode 100644
index 0000000..948fc30
--- /dev/null
+++ b/src/metadata/audible/audibleproperties.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_AUDIBLEPROPERTIES_H
+#define TAGLIB_AUDIBLEPROPERTIES_H
+
+#include <config.h>
+
+#include <taglib/audioproperties.h>
+#include <taglib/tstring.h>
+
+namespace TagLib {
+
+ namespace Audible {
+
+ class File;
+
+ /*!
+ * This reads the data from a Audible stream to support the
+ * AudioProperties API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Initialize this structure
+ */
+ Properties(Properties::ReadStyle style);
+
+ /*!
+ * Destroys this Audible Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ void readAudibleProperties(FILE *file, int off);
+
+
+ private:
+ void readAudioTrackProperties(FILE *file);
+ friend class Audible::File;
+
+ int m_length;
+ int m_bitrate;
+ int m_sampleRate;
+ int m_channels;
+
+ Properties(const Properties &);
+ Properties &operator=(const Properties &);
+
+ void read();
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/audible/audibletag.cpp b/src/metadata/audible/audibletag.cpp
new file mode 100644
index 0000000..0fe786f
--- /dev/null
+++ b/src/metadata/audible/audibletag.cpp
@@ -0,0 +1,163 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "audibletag.h"
+
+#include <taglib/tag.h>
+
+#include <netinet/in.h> // ntohl
+#include <stdlib.h>
+#include <string.h>
+
+using namespace TagLib;
+
+Audible::Tag::Tag() : TagLib::Tag::Tag() {
+ m_title = String::null;
+ m_artist = String::null;
+ m_album = String::null;
+ m_comment = String::null;
+ m_genre = String::null;
+ m_year = 0;
+ m_track = 0;
+ m_userID = 0;
+ m_tagsEndOffset = -1;
+}
+
+Audible::Tag::~Tag() {
+}
+
+bool Audible::Tag::isEmpty() const {
+ return m_title == String::null &&
+ m_artist == String::null &&
+ m_album == String::null &&
+ m_comment == String::null &&
+ m_genre == String::null &&
+ m_year == 0 &&
+ m_track == 0 &&
+ m_userID == 0;
+}
+
+void Audible::Tag::duplicate(const Tag *source, Tag *target, bool overwrite) {
+ // No nonstandard information stored yet
+ Tag::duplicate(source, target, overwrite);
+}
+
+#define OFF_PRODUCT_ID 197
+#define OFF_TAGS 189
+
+void Audible::Tag::readTags( FILE *fp )
+{
+ char buf[1023];
+ fseek(fp, OFF_PRODUCT_ID, SEEK_SET);
+ fread(buf, strlen("product_id"), 1, fp);
+ if(memcmp(buf, "product_id", strlen("product_id")))
+ {
+ buf[20]='\0';
+ fprintf(stderr, "no valid Audible aa file: %s\n", buf);
+ return;
+ }
+
+ // Now parse tag.
+
+ fseek(fp, OFF_TAGS, SEEK_SET);
+ char *name, *value;
+
+ m_tagsEndOffset = OFF_TAGS;
+
+ bool lasttag = false;
+ while(!lasttag)
+ {
+ lasttag = !readTag(fp, &name, &value);
+ if(!strcmp(name, "title"))
+ {
+ m_title = String(value, String::Latin1);
+ }
+ else if(!strcmp(name, "author"))
+ {
+ m_artist = String(value, String::Latin1);
+ }
+ else if(!strcmp(name, "long_description"))
+ {
+ m_comment = String(value, String::Latin1);
+ }
+ else if(!strcmp(name, "description"))
+ {
+ if( m_comment.isNull() )
+ m_comment = String(value, String::Latin1);
+ }
+ else if(!strcmp(name, "pubdate"))
+ {
+ m_year = 0;
+ char *p = strrchr(value, '-');
+ if(p)
+ m_year = strtol(p+1, NULL, 10);
+ }
+ else if(!strcmp(name, "user_id"))
+ {
+ m_userID = strtol(value, NULL, 10);
+ }
+
+ delete[] name;
+ delete[] value;
+ }
+
+ m_album = String("", String::Latin1);
+ m_track = 0;
+ m_genre = String("Audiobook", String::Latin1);
+}
+
+bool Audible::Tag::readTag( FILE *fp, char **name, char **value)
+{
+ uint32_t nlen;
+ fread(&nlen, sizeof(nlen), 1, fp);
+ nlen = ntohl(nlen);
+ //fprintf(stderr, "tagname len=%x\n", (unsigned)nlen);
+ *name = new char[nlen+1];
+ (*name)[nlen] = '\0';
+
+ uint32_t vlen;
+ fread(&vlen, sizeof(vlen), 1, fp);
+ vlen = ntohl(vlen);
+ //fprintf(stderr, "tag len=%x\n", (unsigned)vlen);
+ *value = new char[vlen+1];
+ (*value)[vlen] = '\0';
+
+ fread(*name, nlen, 1, fp);
+ fread(*value, vlen, 1, fp);
+ char lasttag;
+ fread(&lasttag, 1, 1, fp);
+ //fprintf(stderr, "%s: \"%s\"\n", *name, *value);
+
+ m_tagsEndOffset += 2 * 4 + nlen + vlen + 1;
+
+ return !lasttag;
+}
+
+int Audible::Tag::getTagsEndOffset()
+{
+ return m_tagsEndOffset;
+}
diff --git a/src/metadata/audible/audibletag.h b/src/metadata/audible/audibletag.h
new file mode 100644
index 0000000..f03966f
--- /dev/null
+++ b/src/metadata/audible/audibletag.h
@@ -0,0 +1,185 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_AUDIBLETAG_H
+#define TAGLIB_AUDIBLETAG_H
+
+#include <config.h>
+
+#include <taglib/tag.h>
+#include "taglib_audiblefile.h"
+
+namespace TagLib {
+
+ namespace Audible {
+ /*!
+ * This implements the generic TagLib::Tag API
+ */
+ class Tag : public TagLib::Tag
+ {
+ public:
+ Tag();
+
+ /*!
+ * read tags from the aa file.
+ */
+ void readTags( FILE *file );
+
+ /*!
+ * Destroys this AudibleTag instance.
+ */
+ virtual ~Tag();
+
+ /*!
+ * Returns the track name; if no track name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String title() const { return m_title; }
+
+ /*!
+ * Returns the artist name; if no artist name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String artist() const { return m_artist; }
+
+ /*!
+ * Returns the album name; if no album name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String album() const { return m_album; }
+
+ /*!
+ * Returns the track comment; if no comment is present in the tag
+ * String::null will be returned.
+ */
+ virtual String comment() const { return m_comment; }
+
+ /*!
+ * Returns the genre name; if no genre is present in the tag String::null
+ * will be returned.
+ */
+ virtual String genre() const { return m_genre; }
+
+ /*!
+ * Returns the year; if there is no year set, this will return 0.
+ */
+ virtual uint year() const { return m_year; }
+
+ /*!
+ * Returns the track number; if there is no track number set, this will
+ * return 0.
+ */
+ virtual uint track() const { return m_track; }
+
+ /*!
+ * Returns the user id for this file.
+ */
+ virtual uint userID() const { return m_userID; }
+
+ /*!
+ * Sets the title to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setTitle(const String &s) { m_title = s; }
+
+ /*!
+ * Sets the artist to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setArtist(const String &s) { m_artist = s; }
+
+ /*!
+ * Sets the album to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setAlbum(const String &s) { m_album = s; }
+
+ /*!
+ * Sets the album to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setComment(const String &s) { m_comment = s; }
+
+ /*!
+ * Sets the genre to \a s. If \a s is String::null then this value will be
+ * cleared. For tag formats that use a fixed set of genres, the appropriate
+ * value will be selected based on a string comparison. A list of available
+ * genres for those formats should be available in that type's
+ * implementation.
+ */
+ virtual void setGenre(const String &s) { m_genre = s; }
+
+ /*!
+ * Sets the year to \a i. If \a s is 0 then this value will be cleared.
+ */
+ virtual void setYear(uint i) { m_year = i; }
+
+ /*!
+ * Sets the track to \a i. If \a s is 0 then this value will be cleared.
+ */
+ virtual void setTrack(uint i) { m_track = i; }
+
+ /*!
+ * Returns true if the tag does not contain any data. This should be
+ * reimplemented in subclasses that provide more than the basic tagging
+ * abilities in this class.
+ */
+ virtual bool isEmpty() const;
+
+ /*!
+ * Copies the generic data from one tag to another.
+ *
+ * \note This will not affect any of the lower level details of the tag. For
+ * instance if any of the tag type specific data (maybe a URL for a band) is
+ * set, this will not modify or copy that. This just copies using the API
+ * in this class.
+ *
+ * If \a overwrite is true then the values will be unconditionally copied.
+ * If false only empty values will be overwritten.
+ */
+ static void duplicate(const Tag *source, Tag *target, bool overwrite = true);
+
+ virtual void setUserID(uint id) { m_userID = id; }
+
+ int getTagsEndOffset();
+
+
+
+ protected:
+ String m_title;
+ String m_artist;
+ String m_album;
+ String m_comment;
+ String m_genre;
+ uint m_year;
+ uint m_track;
+ uint m_userID;
+ bool readTag( FILE *fp, char **name, char **value);
+ int m_tagsEndOffset;
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/audible/taglib_audiblefile.cpp b/src/metadata/audible/taglib_audiblefile.cpp
new file mode 100644
index 0000000..47f5182
--- /dev/null
+++ b/src/metadata/audible/taglib_audiblefile.cpp
@@ -0,0 +1,121 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "taglib_audiblefile.h"
+
+#include "audibletag.h"
+#include <taglib/tfile.h>
+#include <taglib/audioproperties.h>
+
+namespace TagLib {
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Audible::File::File(const char *file,
+ bool readProperties,
+ Properties::ReadStyle propertiesStyle,
+ FILE *fp)
+ : TagLib::File(file)
+ , audibletag( NULL )
+ , properties( NULL )
+{
+
+ // debug ("Audible::File: create new file object.");
+ //debug ( file );
+
+ /**
+ * Create the Audible file.
+ */
+
+ if(fp)
+ audiblefile = fp;
+ else
+ audiblefile = fopen(file, "rb");
+
+ if( isOpen() )
+ {
+ read(readProperties, propertiesStyle );
+ }
+}
+
+Audible::File::~File()
+{
+ if(audiblefile)
+ fclose(audiblefile);
+ delete audibletag;
+ delete properties;
+}
+
+TagLib::Tag *Audible::File::tag() const
+{
+ return audibletag;
+}
+
+TagLib::Audible::Tag *Audible::File::getAudibleTag() const
+{
+ return audibletag;
+}
+
+Audible::Properties *Audible::File::audioProperties() const
+{
+ return properties;
+}
+
+bool Audible::File::save()
+{
+ return false;
+}
+
+bool Audible::File::isOpen()
+{
+ return audiblefile != NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Audible::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ properties = new Audible::Properties(propertiesStyle);
+ audibletag = new Audible::Tag();
+
+ if (audiblefile != NULL) {
+ audibletag->readTags( audiblefile );
+ int off = audibletag->getTagsEndOffset();
+ //fprintf(stderr, "off=%d\n", off);
+
+ if(readProperties)
+ {
+ // Parse bitrate etc.
+ properties->readAudibleProperties( audiblefile, off );
+ }
+ }
+}
+
+}
diff --git a/src/metadata/audible/taglib_audiblefile.h b/src/metadata/audible/taglib_audiblefile.h
new file mode 100644
index 0000000..c1860ae
--- /dev/null
+++ b/src/metadata/audible/taglib_audiblefile.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_AUDIBLEFILE_H
+#define TAGLIB_AUDIBLEFILE_H
+
+#include <taglib/tfile.h>
+#include "audibleproperties.h"
+#include "audibletag.h"
+
+namespace TagLib {
+
+ namespace Audible {
+
+ class Tag;
+
+ class File : public TagLib::File
+ {
+ public:
+ /*!
+ * Contructs a Audible file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(const char *file, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average,
+ FILE *fp=NULL);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+
+ virtual TagLib::Tag *tag() const;
+
+ /*!
+ * Returns the Audible::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Audible::Properties *audioProperties() const;
+
+ /*!
+ * Save the file.
+ * This is the same as calling save(AllTags);
+ *
+ * \note As of now, saving Audible tags is not supported.
+ */
+ virtual bool save();
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+ Audible::Tag *getAudibleTag() const;
+
+ bool isAudibleFile() const;
+
+ protected:
+ File(const File &);
+ File &operator=(const File &);
+ bool isOpen();
+
+
+ Audible::Tag *audibletag;
+ Audible::Properties *properties;
+
+ FILE *audiblefile;
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/audible/taglib_audiblefiletyperesolver.cpp b/src/metadata/audible/taglib_audiblefiletyperesolver.cpp
new file mode 100644
index 0000000..152b17c
--- /dev/null
+++ b/src/metadata/audible/taglib_audiblefiletyperesolver.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "taglib_audiblefiletyperesolver.h"
+#include "taglib_audiblefile.h"
+
+#include <string.h>
+
+TagLib::File *AudibleFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".aa"))
+ {
+ FILE *fp = fopen(fileName, "rb");
+ if(!fp)
+ return 0;
+
+ return new TagLib::Audible::File(fileName, readProperties, propertiesStyle, fp);
+ }
+
+ return 0;
+}
diff --git a/src/metadata/audible/taglib_audiblefiletyperesolver.h b/src/metadata/audible/taglib_audiblefiletyperesolver.h
new file mode 100644
index 0000000..6f1e705
--- /dev/null
+++ b/src/metadata/audible/taglib_audiblefiletyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_AUDIBLEFILETYPERESOLVER_H
+#define TAGLIB_AUDIBLEFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class AudibleFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/m4a/Makefile.am b/src/metadata/m4a/Makefile.am
new file mode 100644
index 0000000..34d9af4
--- /dev/null
+++ b/src/metadata/m4a/Makefile.am
@@ -0,0 +1,84 @@
+SUBDIRS =
+METASOURCES = AUTO
+INCLUDES = $(all_includes) $(taglib_includes)
+
+libtagm4a_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagm4a.la
+
+libtagm4a_la_SOURCES = \
+ taglib_mp4filetyperesolver.cpp \
+ mp4file.cpp \
+ mp4itunestag.cpp \
+ mp4isobox.cpp \
+ mp4isofullbox.cpp \
+ mp4skipbox.cpp \
+ mp4moovbox.cpp \
+ mp4mvhdbox.cpp \
+ mp4ilstbox.cpp \
+ boxfactory.cpp \
+ mp4fourcc.cpp \
+ mp4udtabox.cpp \
+ mp4metabox.cpp \
+ mp4tagsproxy.cpp \
+ mp4mdiabox.cpp \
+ mp4minfbox.cpp \
+ mp4audioproperties.cpp \
+ mp4hdlrbox.cpp \
+ mp4stblbox.cpp \
+ mp4audiosampleentry.cpp \
+ mp4stsdbox.cpp \
+ mp4sampleentry.cpp \
+ mp4trakbox.cpp \
+ mp4propsproxy.cpp \
+ itunesnambox.cpp \
+ itunesartbox.cpp \
+ itunesalbbox.cpp \
+ itunescvrbox.cpp \
+ itunesgenbox.cpp \
+ itunestrknbox.cpp \
+ itunesdaybox.cpp \
+ itunescmtbox.cpp \
+ itunesgrpbox.cpp \
+ ituneswrtbox.cpp \
+ itunesdiskbox.cpp \
+ itunestmpobox.cpp \
+ itunesdatabox.cpp
+
+noinst_HEADERS = \
+ taglib_mp4filetyperesolver.h \
+ mp4file.h \
+ mp4itunestag.h \
+ mp4isobox.h \
+ mp4isofullbox.h \
+ mp4skipbox.h \
+ mp4moovbox.h \
+ mp4mvhdbox.h \
+ mp4ilstbox.h \
+ boxfactory.h \
+ mp4fourcc.h \
+ mp4udtabox.h \
+ mp4metabox.h \
+ mp4tagsproxy.h \
+ mp4audioproperties.h \
+ mp4hdlrbox.h \
+ mp4propsproxy.h \
+ mp4mdiabox.h \
+ mp4stsdbox.h \
+ mp4trakbox.h \
+ mp4stblbox.h \
+ mp4audiosampleentry.h \
+ mp4minfbox.h \
+ mp4sampleentry.h \
+ itunesnambox.h \
+ itunesartbox.h \
+ itunesalbbox.h \
+ itunesgenbox.h \
+ itunestrknbox.h \
+ itunesdaybox.h \
+ itunescmtbox.h \
+ itunescvrbox.h \
+ itunesgrpbox.h \
+ ituneswrtbox.h \
+ itunesdiskbox.h \
+ itunestmpobox.h \
+ itunesdatabox.h
diff --git a/src/metadata/m4a/boxfactory.cpp b/src/metadata/m4a/boxfactory.cpp
new file mode 100644
index 0000000..0fc8eb4
--- /dev/null
+++ b/src/metadata/m4a/boxfactory.cpp
@@ -0,0 +1,150 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "tstring.h"
+#include "boxfactory.h"
+#include "mp4skipbox.h"
+#include "mp4moovbox.h"
+#include "mp4mvhdbox.h"
+#include "mp4trakbox.h"
+#include "mp4mdiabox.h"
+#include "mp4minfbox.h"
+#include "mp4stblbox.h"
+#include "mp4stsdbox.h"
+#include "mp4hdlrbox.h"
+#include "mp4udtabox.h"
+#include "mp4metabox.h"
+#include "mp4ilstbox.h"
+#include "itunesnambox.h"
+#include "itunesartbox.h"
+#include "itunesalbbox.h"
+#include "itunesgenbox.h"
+#include "itunesdaybox.h"
+#include "itunestrknbox.h"
+#include "itunescmtbox.h"
+#include "itunesgrpbox.h"
+#include "ituneswrtbox.h"
+#include "itunesdiskbox.h"
+#include "itunestmpobox.h"
+#include "itunescvrbox.h"
+#include "itunesdatabox.h"
+
+using namespace TagLib;
+
+MP4::BoxFactory::BoxFactory()
+{
+}
+
+MP4::BoxFactory::~BoxFactory()
+{
+}
+
+//! factory function
+MP4::Mp4IsoBox* MP4::BoxFactory::createInstance( TagLib::File* anyfile, MP4::Fourcc fourcc, uint size, long offset ) const
+{
+ MP4::File * file = dynamic_cast<MP4::File *>(anyfile);
+ if(!file)
+ return 0;
+
+ //std::cout << "creating box for: " << fourcc.toString() << std::endl;
+
+ switch( fourcc )
+ {
+ case 0x6d6f6f76: // 'moov'
+ return new MP4::Mp4MoovBox( file, fourcc, size, offset );
+ break;
+ case 0x6d766864: // 'mvhd'
+ return new MP4::Mp4MvhdBox( file, fourcc, size, offset );
+ break;
+ case 0x7472616b: // 'trak'
+ return new MP4::Mp4TrakBox( file, fourcc, size, offset );
+ break;
+ case 0x6d646961: // 'mdia'
+ return new MP4::Mp4MdiaBox( file, fourcc, size, offset );
+ break;
+ case 0x6d696e66: // 'minf'
+ return new MP4::Mp4MinfBox( file, fourcc, size, offset );
+ break;
+ case 0x7374626c: // 'stbl'
+ return new MP4::Mp4StblBox( file, fourcc, size, offset );
+ break;
+ case 0x73747364: // 'stsd'
+ return new MP4::Mp4StsdBox( file, fourcc, size, offset );
+ break;
+ case 0x68646c72: // 'hdlr'
+ return new MP4::Mp4HdlrBox( file, fourcc, size, offset );
+ break;
+ case 0x75647461: // 'udta'
+ return new MP4::Mp4UdtaBox( file, fourcc, size, offset );
+ break;
+ case 0x6d657461: // 'meta'
+ return new MP4::Mp4MetaBox( file, fourcc, size, offset );
+ break;
+ case 0x696c7374: // 'ilst'
+ return new MP4::Mp4IlstBox( file, fourcc, size, offset );
+ break;
+ case 0xa96e616d: // '_nam'
+ return new MP4::ITunesNamBox( file, fourcc, size, offset );
+ break;
+ case 0xa9415254: // '_ART'
+ return new MP4::ITunesArtBox( file, fourcc, size, offset );
+ break;
+ case 0xa9616c62: // '_alb'
+ return new MP4::ITunesAlbBox( file, fourcc, size, offset );
+ break;
+ case 0xa967656e: // '_gen'
+ return new MP4::ITunesGenBox( file, fourcc, size, offset );
+ break;
+ case 0x676e7265: // 'gnre'
+ return new MP4::ITunesGenBox( file, fourcc, size, offset );
+ break;
+ case 0xa9646179: // '_day'
+ return new MP4::ITunesDayBox( file, fourcc, size, offset );
+ break;
+ case 0x74726b6e: // 'trkn'
+ return new MP4::ITunesTrknBox( file, fourcc, size, offset );
+ break;
+ case 0xa9636d74: // '_cmt'
+ return new MP4::ITunesCmtBox( file, fourcc, size, offset );
+ break;
+ case 0xa9677270: // '_grp'
+ return new MP4::ITunesGrpBox( file, fourcc, size, offset );
+ break;
+ case 0xa9777274: // '_wrt'
+ return new MP4::ITunesWrtBox( file, fourcc, size, offset );
+ break;
+ case 0x6469736b: // 'disk'
+ return new MP4::ITunesDiskBox( file, fourcc, size, offset );
+ break;
+ case 0x746d706f: // 'tmpo'
+ return new MP4::ITunesTmpoBox( file, fourcc, size, offset );
+ break;
+ case 0x636f7672: // 'covr'
+ return new MP4::ITunesCvrBox( file, fourcc, size, offset );
+ break;
+ case 0x64616461: // 'data'
+ return new MP4::ITunesDataBox( file, fourcc, size, offset );
+ break;
+ default:
+ return new MP4::Mp4SkipBox( file, fourcc, size, offset );
+ }
+}
diff --git a/src/metadata/m4a/boxfactory.h b/src/metadata/m4a/boxfactory.h
new file mode 100644
index 0000000..7edcd6c
--- /dev/null
+++ b/src/metadata/m4a/boxfactory.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 BOXFACTORY_H
+#define BOXFACTORY_H
+
+#include "taglib.h"
+#include "mp4isobox.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class BoxFactory
+ {
+ public:
+ BoxFactory();
+ ~BoxFactory();
+
+ //! factory function
+ Mp4IsoBox* createInstance( TagLib::File* anyfile, MP4::Fourcc fourcc, uint size, long offset ) const;
+ }; // class BoxFactory
+
+ } // namepace MP4
+} // namepace TagLib
+
+#endif // BOXFACTORY_H
diff --git a/src/metadata/m4a/itunesalbbox.cpp b/src/metadata/m4a/itunesalbbox.cpp
new file mode 100644
index 0000000..5832fa0
--- /dev/null
+++ b/src/metadata/m4a/itunesalbbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesalbbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesAlbBox::ITunesAlbBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesAlbBox::ITunesAlbBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesAlbBox::ITunesAlbBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesAlbBox::~ITunesAlbBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesAlbBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesAlbBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::album, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of album box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesalbbox.h b/src/metadata/m4a/itunesalbbox.h
new file mode 100644
index 0000000..f2462c2
--- /dev/null
+++ b/src/metadata/m4a/itunesalbbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESALBBOX_H
+#define ITUNESALBBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesAlbBox: public Mp4IsoBox
+ {
+ public:
+ ITunesAlbBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesAlbBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesAlbBoxPrivate;
+ ITunesAlbBoxPrivate* d;
+ }; // class ITunesAlbBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESALBBOX_H
diff --git a/src/metadata/m4a/itunesartbox.cpp b/src/metadata/m4a/itunesartbox.cpp
new file mode 100644
index 0000000..19e717d
--- /dev/null
+++ b/src/metadata/m4a/itunesartbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesartbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesArtBox::ITunesArtBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesArtBox::ITunesArtBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesArtBox::ITunesArtBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesArtBox::~ITunesArtBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesArtBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesArtBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::artist, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of artist box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesartbox.h b/src/metadata/m4a/itunesartbox.h
new file mode 100644
index 0000000..5d197aa
--- /dev/null
+++ b/src/metadata/m4a/itunesartbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESARTBOX_H
+#define ITUNESARTBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesArtBox: public Mp4IsoBox
+ {
+ public:
+ ITunesArtBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesArtBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesArtBoxPrivate;
+ ITunesArtBoxPrivate* d;
+ }; // class ITunesArtBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESARTBOX_H
diff --git a/src/metadata/m4a/itunescmtbox.cpp b/src/metadata/m4a/itunescmtbox.cpp
new file mode 100644
index 0000000..c79f0f7
--- /dev/null
+++ b/src/metadata/m4a/itunescmtbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunescmtbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesCmtBox::ITunesCmtBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesCmtBox::ITunesCmtBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesCmtBox::ITunesCmtBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesCmtBox::~ITunesCmtBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesCmtBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesCmtBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::comment, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of title box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunescmtbox.h b/src/metadata/m4a/itunescmtbox.h
new file mode 100644
index 0000000..83ad65d
--- /dev/null
+++ b/src/metadata/m4a/itunescmtbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESCMTBOX_H
+#define ITUNESCMTBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesCmtBox: public Mp4IsoBox
+ {
+ public:
+ ITunesCmtBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesCmtBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesCmtBoxPrivate;
+ ITunesCmtBoxPrivate* d;
+ }; // class ITunesCmtBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESCMTBOX_H
diff --git a/src/metadata/m4a/itunescvrbox.cpp b/src/metadata/m4a/itunescvrbox.cpp
new file mode 100644
index 0000000..4a7b3db
--- /dev/null
+++ b/src/metadata/m4a/itunescvrbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunescvrbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesCvrBox::ITunesCvrBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesCvrBox::ITunesCvrBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesCvrBox::ITunesCvrBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesCvrBox::~ITunesCvrBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesCvrBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesCvrBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::cover, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of album box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunescvrbox.h b/src/metadata/m4a/itunescvrbox.h
new file mode 100644
index 0000000..a2693c2
--- /dev/null
+++ b/src/metadata/m4a/itunescvrbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESCVRBOX_H
+#define ITUNESCVRBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesCvrBox: public Mp4IsoBox
+ {
+ public:
+ ITunesCvrBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesCvrBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesCvrBoxPrivate;
+ ITunesCvrBoxPrivate* d;
+ }; // class ITunesCvrBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESCVRBOX_H
diff --git a/src/metadata/m4a/itunesdatabox.cpp b/src/metadata/m4a/itunesdatabox.cpp
new file mode 100644
index 0000000..7565a42
--- /dev/null
+++ b/src/metadata/m4a/itunesdatabox.cpp
@@ -0,0 +1,63 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesdatabox.h"
+#include "tbytevector.h"
+#include "mp4isobox.h"
+#include "tfile.h"
+
+using namespace TagLib;
+
+class MP4::ITunesDataBox::ITunesDataBoxPrivate
+{
+public:
+ ByteVector data;
+};
+
+MP4::ITunesDataBox::ITunesDataBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoFullBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesDataBox::ITunesDataBoxPrivate();
+}
+
+MP4::ITunesDataBox::~ITunesDataBox()
+{
+ delete d;
+}
+
+ByteVector MP4::ITunesDataBox::data() const
+{
+ return d->data;
+}
+
+//! parse the content of the box
+void MP4::ITunesDataBox::parse()
+{
+ // skip first 4 byte - don't know what they are supposed to be for - simply 4 zeros
+ file()->seek( 4, TagLib::File::Current );
+ // read contents - remaining size is box_size-12-4 (12:fullbox header, 4:starting zeros of data box)
+#if 0
+ std::cout << " reading data box with data length: " << size()-16 << std::endl;
+#endif
+ d->data = file()->readBlock( size()-12-4 );
+}
+
diff --git a/src/metadata/m4a/itunesdatabox.h b/src/metadata/m4a/itunesdatabox.h
new file mode 100644
index 0000000..d0c802c
--- /dev/null
+++ b/src/metadata/m4a/itunesdatabox.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESDATABOX_H
+#define ITUNESDATABOX_H
+
+#include "mp4isofullbox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesDataBox: public Mp4IsoFullBox
+ {
+ public:
+ ITunesDataBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesDataBox();
+
+ //! get the internal data, which can be txt or binary data as well
+ ByteVector data() const;
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesDataBoxPrivate;
+ ITunesDataBoxPrivate* d;
+ }; // class ITunesDataBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESDATABOX_H
diff --git a/src/metadata/m4a/itunesdaybox.cpp b/src/metadata/m4a/itunesdaybox.cpp
new file mode 100644
index 0000000..16568d7
--- /dev/null
+++ b/src/metadata/m4a/itunesdaybox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesdaybox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesDayBox::ITunesDayBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesDayBox::ITunesDayBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesDayBox::ITunesDayBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesDayBox::~ITunesDayBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesDayBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesDayBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::year, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of day box: " << dataString.substr(0,4) << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesdaybox.h b/src/metadata/m4a/itunesdaybox.h
new file mode 100644
index 0000000..7291363
--- /dev/null
+++ b/src/metadata/m4a/itunesdaybox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESDAYBOX_H
+#define ITUNESDAYBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesDayBox: public Mp4IsoBox
+ {
+ public:
+ ITunesDayBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesDayBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesDayBoxPrivate;
+ ITunesDayBoxPrivate* d;
+ }; // class ITunesDayBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESDAYBOX_H
diff --git a/src/metadata/m4a/itunesdiskbox.cpp b/src/metadata/m4a/itunesdiskbox.cpp
new file mode 100644
index 0000000..93c47f2
--- /dev/null
+++ b/src/metadata/m4a/itunesdiskbox.cpp
@@ -0,0 +1,93 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesdiskbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesDiskBox::ITunesDiskBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesDiskBox::ITunesDiskBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesDiskBox::ITunesDiskBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesDiskBox::~ITunesDiskBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesDiskBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesDiskBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::disk, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::ByteVector trknData = d->dataBox->data();
+ TagLib::String trknumber = TagLib::String::number( static_cast<int>( static_cast<unsigned char>(trknData[0]) << 24 |
+ static_cast<unsigned char>(trknData[1]) << 16 |
+ static_cast<unsigned char>(trknData[2]) << 8 |
+ static_cast<unsigned char>(trknData[3]) ) );
+ std::cout << "Content of tracknumber box: " << trknumber << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesdiskbox.h b/src/metadata/m4a/itunesdiskbox.h
new file mode 100644
index 0000000..bad73da
--- /dev/null
+++ b/src/metadata/m4a/itunesdiskbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESDISKBOX_H
+#define ITUNESDISKBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesDiskBox: public Mp4IsoBox
+ {
+ public:
+ ITunesDiskBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesDiskBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesDiskBoxPrivate;
+ ITunesDiskBoxPrivate* d;
+ }; // class ITunesDiskBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESDISKBOX_H
diff --git a/src/metadata/m4a/itunesgenbox.cpp b/src/metadata/m4a/itunesgenbox.cpp
new file mode 100644
index 0000000..08708bc
--- /dev/null
+++ b/src/metadata/m4a/itunesgenbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesgenbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesGenBox::ITunesGenBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesGenBox::ITunesGenBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesGenBox::ITunesGenBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesGenBox::~ITunesGenBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesGenBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesGenBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::genre, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of genre box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesgenbox.h b/src/metadata/m4a/itunesgenbox.h
new file mode 100644
index 0000000..deb7fe3
--- /dev/null
+++ b/src/metadata/m4a/itunesgenbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESGENBOX_H
+#define ITUNESGENBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesGenBox: public Mp4IsoBox
+ {
+ public:
+ ITunesGenBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesGenBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesGenBoxPrivate;
+ ITunesGenBoxPrivate* d;
+ }; // class ITunesGenBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESGENBOX_H
diff --git a/src/metadata/m4a/itunesgrpbox.cpp b/src/metadata/m4a/itunesgrpbox.cpp
new file mode 100644
index 0000000..061b6bd
--- /dev/null
+++ b/src/metadata/m4a/itunesgrpbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesgrpbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesGrpBox::ITunesGrpBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesGrpBox::ITunesGrpBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesGrpBox::ITunesGrpBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesGrpBox::~ITunesGrpBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesGrpBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesGrpBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::grouping, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of title box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesgrpbox.h b/src/metadata/m4a/itunesgrpbox.h
new file mode 100644
index 0000000..62922f0
--- /dev/null
+++ b/src/metadata/m4a/itunesgrpbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESGRPBOX_H
+#define ITUNESGRPBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesGrpBox: public Mp4IsoBox
+ {
+ public:
+ ITunesGrpBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesGrpBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesGrpBoxPrivate;
+ ITunesGrpBoxPrivate* d;
+ }; // class ITunesGrpBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESGRPBOX_H
diff --git a/src/metadata/m4a/itunesnambox.cpp b/src/metadata/m4a/itunesnambox.cpp
new file mode 100644
index 0000000..6cc954b
--- /dev/null
+++ b/src/metadata/m4a/itunesnambox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunesnambox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesNamBox::ITunesNamBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesNamBox::ITunesNamBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesNamBox::ITunesNamBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesNamBox::~ITunesNamBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesNamBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesNamBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::title, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of title box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunesnambox.h b/src/metadata/m4a/itunesnambox.h
new file mode 100644
index 0000000..434fd84
--- /dev/null
+++ b/src/metadata/m4a/itunesnambox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESNAMBOX_H
+#define ITUNESNAMBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesNamBox: public Mp4IsoBox
+ {
+ public:
+ ITunesNamBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesNamBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesNamBoxPrivate;
+ ITunesNamBoxPrivate* d;
+ }; // class ITunesNamBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESNAMBOX_H
diff --git a/src/metadata/m4a/itunestmpobox.cpp b/src/metadata/m4a/itunestmpobox.cpp
new file mode 100644
index 0000000..3d0ad2d
--- /dev/null
+++ b/src/metadata/m4a/itunestmpobox.cpp
@@ -0,0 +1,93 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunestmpobox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesTmpoBox::ITunesTmpoBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesTmpoBox::ITunesTmpoBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesTmpoBox::ITunesTmpoBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesTmpoBox::~ITunesTmpoBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesTmpoBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesTmpoBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::bpm, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::ByteVector trknData = d->dataBox->data();
+ TagLib::String trknumber = TagLib::String::number( static_cast<int>( static_cast<unsigned char>(trknData[0]) << 24 |
+ static_cast<unsigned char>(trknData[1]) << 16 |
+ static_cast<unsigned char>(trknData[2]) << 8 |
+ static_cast<unsigned char>(trknData[3]) ) );
+ std::cout << "Content of tracknumber box: " << trknumber << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunestmpobox.h b/src/metadata/m4a/itunestmpobox.h
new file mode 100644
index 0000000..981f938
--- /dev/null
+++ b/src/metadata/m4a/itunestmpobox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESTMPOBOX_H
+#define ITUNESTMPOBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesTmpoBox: public Mp4IsoBox
+ {
+ public:
+ ITunesTmpoBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesTmpoBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesTmpoBoxPrivate;
+ ITunesTmpoBoxPrivate* d;
+ }; // class ITunesTmpoBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESTMPOBOX_H
diff --git a/src/metadata/m4a/itunestrknbox.cpp b/src/metadata/m4a/itunestrknbox.cpp
new file mode 100644
index 0000000..f8d36cb
--- /dev/null
+++ b/src/metadata/m4a/itunestrknbox.cpp
@@ -0,0 +1,93 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "itunestrknbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesTrknBox::ITunesTrknBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesTrknBox::ITunesTrknBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesTrknBox::ITunesTrknBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesTrknBox::~ITunesTrknBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesTrknBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesTrknBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::trackno, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::ByteVector trknData = d->dataBox->data();
+ TagLib::String trknumber = TagLib::String::number( static_cast<int>( static_cast<unsigned char>(trknData[0]) << 24 |
+ static_cast<unsigned char>(trknData[1]) << 16 |
+ static_cast<unsigned char>(trknData[2]) << 8 |
+ static_cast<unsigned char>(trknData[3]) ) );
+ std::cout << "Content of tracknumber box: " << trknumber << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/itunestrknbox.h b/src/metadata/m4a/itunestrknbox.h
new file mode 100644
index 0000000..f603e1f
--- /dev/null
+++ b/src/metadata/m4a/itunestrknbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESTRKNBOX_H
+#define ITUNESTRKNBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesTrknBox: public Mp4IsoBox
+ {
+ public:
+ ITunesTrknBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesTrknBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesTrknBoxPrivate;
+ ITunesTrknBoxPrivate* d;
+ }; // class ITunesTrknBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESTRKNBOX_H
diff --git a/src/metadata/m4a/ituneswrtbox.cpp b/src/metadata/m4a/ituneswrtbox.cpp
new file mode 100644
index 0000000..ecf3c43
--- /dev/null
+++ b/src/metadata/m4a/ituneswrtbox.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "ituneswrtbox.h"
+#include "itunesdatabox.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "tfile.h"
+#include "mp4tagsproxy.h"
+
+using namespace TagLib;
+
+class MP4::ITunesWrtBox::ITunesWrtBoxPrivate
+{
+public:
+ ITunesDataBox* dataBox;
+};
+
+MP4::ITunesWrtBox::ITunesWrtBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::ITunesWrtBox::ITunesWrtBoxPrivate();
+ d->dataBox = 0;
+}
+
+MP4::ITunesWrtBox::~ITunesWrtBox()
+{
+ if( d->dataBox != 0 )
+ delete d->dataBox;
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::ITunesWrtBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ // parse data box
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ if(mp4file->readSizeAndType( size, fourcc ) == true)
+ {
+ // check for type - must be 'data'
+ if( fourcc != MP4::Fourcc("data") )
+ {
+ std::cerr << "bad atom in itunes tag - skipping it." << std::endl;
+ // jump over data tag
+ mp4file->seek( size-8, TagLib::File::Current );
+ return;
+ }
+ d->dataBox = new ITunesDataBox( mp4file, fourcc, size, mp4file->tell() );
+ d->dataBox->parsebox();
+ }
+ else
+ {
+ // reading unsuccessful - serious error!
+ std::cerr << "Error in parsing ITunesWrtBox - serious Error in taglib!" << std::endl;
+ return;
+ }
+ // register data box
+ mp4file->tagProxy()->registerBox( Mp4TagsProxy::composer, d->dataBox );
+
+#if 0
+ // get data pointer - just for debugging...
+ TagLib::String dataString( d->dataBox->data() );
+ std::cout << "Content of title box: " << dataString << std::endl;
+#endif
+}
+
diff --git a/src/metadata/m4a/ituneswrtbox.h b/src/metadata/m4a/ituneswrtbox.h
new file mode 100644
index 0000000..740d94c
--- /dev/null
+++ b/src/metadata/m4a/ituneswrtbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 ITUNESWRTBOX_H
+#define ITUNESWRTBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class ITunesWrtBox: public Mp4IsoBox
+ {
+ public:
+ ITunesWrtBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~ITunesWrtBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class ITunesWrtBoxPrivate;
+ ITunesWrtBoxPrivate* d;
+ }; // class ITunesWrtBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // ITUNESWRTBOX_H
diff --git a/src/metadata/m4a/mp4audioproperties.cpp b/src/metadata/m4a/mp4audioproperties.cpp
new file mode 100644
index 0000000..77afab1
--- /dev/null
+++ b/src/metadata/m4a/mp4audioproperties.cpp
@@ -0,0 +1,75 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4audioproperties.h"
+#include "mp4propsproxy.h"
+
+using namespace TagLib;
+
+class MP4::AudioProperties::AudioPropertiesPrivate
+{
+public:
+ MP4::Mp4PropsProxy* propsproxy;
+}; // AudioPropertiesPrivate
+
+MP4::AudioProperties::AudioProperties():TagLib::AudioProperties(TagLib::AudioProperties::Average)
+{
+ d = new MP4::AudioProperties::AudioPropertiesPrivate();
+}
+
+MP4::AudioProperties::~AudioProperties()
+{
+ delete d;
+}
+
+void MP4::AudioProperties::setProxy( Mp4PropsProxy* proxy )
+{
+ d->propsproxy = proxy;
+}
+
+int MP4::AudioProperties::length() const
+{
+ if( d->propsproxy == 0 )
+ return 0;
+ return d->propsproxy->seconds();
+}
+
+int MP4::AudioProperties::bitrate() const
+{
+ if( d->propsproxy == 0 )
+ return 0;
+ return d->propsproxy->bitRate()/1000;
+}
+
+int MP4::AudioProperties::sampleRate() const
+{
+ if( d->propsproxy == 0 )
+ return 0;
+ return d->propsproxy->sampleRate();
+}
+
+int MP4::AudioProperties::channels() const
+{
+ if( d->propsproxy == 0 )
+ return 0;
+ return d->propsproxy->channels();
+}
+
diff --git a/src/metadata/m4a/mp4audioproperties.h b/src/metadata/m4a/mp4audioproperties.h
new file mode 100644
index 0000000..4e61790
--- /dev/null
+++ b/src/metadata/m4a/mp4audioproperties.h
@@ -0,0 +1,73 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4AUDIOPROPERTIES_H
+#define MP4AUDIOPROPERTIES_H MP4AUDIOPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4PropsProxy;
+
+ class AudioProperties : public TagLib::AudioProperties
+ {
+ public:
+ //! constructor
+ AudioProperties();
+ //! destructor
+ ~AudioProperties();
+
+ //! function to set the proxy
+ void setProxy( Mp4PropsProxy* proxy );
+
+ /*!
+ * Returns the length of the file in seconds.
+ */
+ int length() const;
+
+ /*!
+ * Returns the most appropriate bit rate for the file in kb/s. For constant
+ * bitrate formats this is simply the bitrate of the file. For variable
+ * bitrate formats this is either the average or nominal bitrate.
+ */
+ int bitrate() const;
+
+ /*!
+ * Returns the sample rate in Hz.
+ */
+ int sampleRate() const;
+
+ /*!
+ * Returns the number of audio channels.
+ */
+ int channels() const;
+
+ private:
+ class AudioPropertiesPrivate;
+ AudioPropertiesPrivate* d;
+ };
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4AUDIOPROPERTIES_H
diff --git a/src/metadata/m4a/mp4audiosampleentry.cpp b/src/metadata/m4a/mp4audiosampleentry.cpp
new file mode 100644
index 0000000..fb87547
--- /dev/null
+++ b/src/metadata/m4a/mp4audiosampleentry.cpp
@@ -0,0 +1,146 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <iostream>
+#include "mp4audiosampleentry.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+#include "mp4propsproxy.h"
+
+using namespace TagLib;
+
+class MP4::Mp4AudioSampleEntry::Mp4AudioSampleEntryPrivate
+{
+public:
+ TagLib::uint channelcount;
+ TagLib::uint samplerate;
+ TagLib::uint bitrate;
+};
+
+MP4::Mp4AudioSampleEntry::Mp4AudioSampleEntry( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4SampleEntry(file, fourcc, size, offset)
+{
+ d = new MP4::Mp4AudioSampleEntry::Mp4AudioSampleEntryPrivate();
+}
+
+MP4::Mp4AudioSampleEntry::~Mp4AudioSampleEntry()
+{
+ delete d;
+}
+
+TagLib::uint MP4::Mp4AudioSampleEntry::channels() const
+{
+ return d->channelcount;
+}
+
+TagLib::uint MP4::Mp4AudioSampleEntry::samplerate() const
+{
+ return d->samplerate;
+}
+
+TagLib::uint MP4::Mp4AudioSampleEntry::bitrate() const
+{
+ return d->bitrate;
+}
+
+void MP4::Mp4AudioSampleEntry::parseEntry()
+{
+ TagLib::MP4::File* mp4file = dynamic_cast<TagLib::MP4::File*>(file());
+ if(!mp4file)
+ return;
+
+ // read 8 reserved bytes
+ mp4file->seek( 8, TagLib::File::Current );
+ // read channelcount
+ if(!mp4file->readShort( d->channelcount ))
+ return;
+ // seek over samplesize, pre_defined and reserved
+ mp4file->seek( 6, TagLib::File::Current );
+ // read samplerate
+ if(!mp4file->readInt( d->samplerate ))
+ return;
+
+ // register box at proxy
+ mp4file->propProxy()->registerAudioSampleEntry( this );
+
+
+ //std::cout << "fourcc of audio sample entry: " << fourcc().toString() << std::endl;
+ // check for both mp4a (plain files) and drms (encrypted files)
+ if( (fourcc() == MP4::Fourcc("mp4a")) ||
+ (fourcc() == MP4::Fourcc("drms")) )
+ {
+ TagLib::MP4::Fourcc fourcc;
+ TagLib::uint esds_size;
+
+ if (!mp4file->readSizeAndType( esds_size, fourcc ))
+ return;
+
+ // read esds' main parts
+ if( size()-48 > 0 )
+ ByteVector flags_version = mp4file->readBlock(4);
+ else
+ return;
+
+ ByteVector EsDescrTag = mp4file->readBlock(1);
+ // first 4 bytes contain full box specifics (version & flags)
+ // upcoming byte must be ESDescrTag (0x03)
+ if( EsDescrTag[0] == 0x03 )
+ {
+ TagLib::uint descr_len = mp4file->readSystemsLen();
+ TagLib::uint EsId;
+ if( !mp4file->readShort( EsId ) )
+ return;
+ ByteVector priority = mp4file->readBlock(1);
+ if( descr_len < 20 )
+ return;
+ }
+ else
+ {
+ TagLib::uint EsId;
+ if( !mp4file->readShort( EsId ) )
+ return;
+ }
+ // read decoder configuration tag (0x04)
+ ByteVector DecCfgTag = mp4file->readBlock(1);
+ if( DecCfgTag[0] != 0x04 )
+ return;
+ // read decoder configuration length
+ // TagLib::uint deccfg_len = mp4file->readSystemsLen();
+ // read object type Id
+ ByteVector objId = mp4file->readBlock(1);
+ // read stream type id
+ ByteVector strId = mp4file->readBlock(1);
+ // read buffer Size DB
+ ByteVector bufferSizeDB = mp4file->readBlock(3);
+ // read max bitrate
+ TagLib::uint max_bitrate;
+ if( !mp4file->readInt( max_bitrate ) )
+ return;
+ // read average bitrate
+ if( !mp4file->readInt( d->bitrate ) )
+ return;
+ // skip the rest
+ mp4file->seek( offset()+size()-8, File::Beginning );
+ }
+ else
+ mp4file->seek( size()-36, File::Current );
+}
+
diff --git a/src/metadata/m4a/mp4audiosampleentry.h b/src/metadata/m4a/mp4audiosampleentry.h
new file mode 100644
index 0000000..c39c5e3
--- /dev/null
+++ b/src/metadata/m4a/mp4audiosampleentry.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4AUDIOSAMPLEENTRY_H
+#define MP4AUDIOSAMPLEENTRY_H
+
+#include "mp4sampleentry.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4AudioSampleEntry: public Mp4SampleEntry
+ {
+ public:
+ Mp4AudioSampleEntry( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~Mp4AudioSampleEntry();
+
+ //! function to get the number of channels
+ TagLib::uint channels() const;
+ //! function to get the sample rate
+ TagLib::uint samplerate() const;
+ //! function to get the average bitrate of the audio stream
+ TagLib::uint bitrate() const;
+
+ private:
+ //! parse the content of the box
+ void parseEntry();
+
+ protected:
+ class Mp4AudioSampleEntryPrivate;
+ Mp4AudioSampleEntryPrivate* d;
+ }; // class Mp4AudioSampleEntry
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4AUDIOSAMPLEENTRY_H
diff --git a/src/metadata/m4a/mp4file.cpp b/src/metadata/m4a/mp4file.cpp
new file mode 100644
index 0000000..7d37d59
--- /dev/null
+++ b/src/metadata/m4a/mp4file.cpp
@@ -0,0 +1,377 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include "tlist.h"
+
+#include "mp4itunestag.h"
+#include "mp4file.h"
+#include "boxfactory.h"
+#include "mp4tagsproxy.h"
+#include "mp4propsproxy.h"
+#include "mp4audioproperties.h"
+#include "itunesdatabox.h"
+
+using namespace TagLib;
+
+class MP4::File::FilePrivate
+{
+public:
+ //! list for all boxes of the mp4 file
+ TagLib::List<MP4::Mp4IsoBox*> boxes;
+ //! the box factory - will create all boxes by tag and size
+ MP4::BoxFactory boxfactory;
+ //! proxy for the tags is filled after parsing
+ MP4::Mp4TagsProxy tagsProxy;
+ //! proxy for audio properties
+ MP4::Mp4PropsProxy propsProxy;
+ //! the tag returned by tag() function
+ MP4::Tag mp4tag;
+ //! container for the audio properties returned by properties() function
+ MP4::AudioProperties mp4audioproperties;
+ //! is set to valid after successfully parsing
+ bool isValid;
+};
+
+//! function to fill the tags with converted proxy data, which has been parsed out of the file previously
+static void fillTagFromProxy( MP4::Mp4TagsProxy& proxy, MP4::Tag& mp4tag );
+
+MP4::File::File(const char *file, bool , AudioProperties::ReadStyle )
+ :TagLib::File( file )
+{
+ // create member container
+ d = new MP4::File::FilePrivate();
+
+
+ d->isValid = false;
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ while( readSizeAndType( size, fourcc ) == true )
+ {
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( this, fourcc, size, tell() );
+ curbox->parsebox();
+ d->boxes.append( curbox );
+ }
+
+ for( TagLib::List<MP4::Mp4IsoBox*>::Iterator iter = d->boxes.begin();
+ iter != d->boxes.end();
+ iter++ )
+ {
+ if( (*iter)->fourcc() == MP4::Fourcc("moov") )
+ {
+ d->isValid = true;
+ break;
+ }
+ }
+
+ //if( d->isValid )
+ //debug( "file is valid" );
+ //else
+ //debug( "file is NOT valid" );
+
+ // fill tags from proxy data
+ fillTagFromProxy( d->tagsProxy, d->mp4tag );
+}
+
+MP4::File::~File()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->boxes.begin();
+ delIter != d->boxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+Tag *MP4::File::tag() const
+{
+ return &d->mp4tag;
+}
+
+AudioProperties * MP4::File::audioProperties() const
+{
+ d->mp4audioproperties.setProxy( &d->propsProxy );
+ return &d->mp4audioproperties;
+}
+
+bool MP4::File::save()
+{
+ return false;
+}
+
+void MP4::File::remove()
+{
+}
+
+TagLib::uint MP4::File::readSystemsLen()
+{
+ TagLib::uint length = 0;
+ TagLib::uint nbytes = 0;
+ ByteVector input;
+ TagLib::uchar tmp_input;
+
+ do
+ {
+ input = readBlock(1);
+ tmp_input = static_cast<TagLib::uchar>(input[0]);
+ nbytes++;
+ length = (length<<7) | (tmp_input&0x7F);
+ } while( (tmp_input&0x80) && (nbytes<4) );
+
+ return length;
+}
+
+bool MP4::File::readSizeAndType( TagLib::uint& size, MP4::Fourcc& fourcc )
+{
+ // read the two blocks from file
+ ByteVector readsize = readBlock(4);
+ ByteVector readtype = readBlock(4);
+
+ if( (readsize.size() != 4) || (readtype.size() != 4) )
+ return false;
+
+ // set size
+ size = static_cast<unsigned char>(readsize[0]) << 24 |
+ static_cast<unsigned char>(readsize[1]) << 16 |
+ static_cast<unsigned char>(readsize[2]) << 8 |
+ static_cast<unsigned char>(readsize[3]);
+
+ // type and size seem to be part of the stored size
+ if( size < 8 )
+ return false;
+
+ // set fourcc
+ fourcc = readtype.data();
+
+ return true;
+}
+
+bool MP4::File::readInt( TagLib::uint& toRead )
+{
+ ByteVector readbuffer = readBlock(4);
+ if( readbuffer.size() != 4 )
+ return false;
+
+ toRead = static_cast<unsigned char>(readbuffer[0]) << 24 |
+ static_cast<unsigned char>(readbuffer[1]) << 16 |
+ static_cast<unsigned char>(readbuffer[2]) << 8 |
+ static_cast<unsigned char>(readbuffer[3]);
+ return true;
+}
+
+bool MP4::File::readShort( TagLib::uint& toRead )
+{
+ ByteVector readbuffer = readBlock(2);
+ if( readbuffer.size() != 2 )
+ return false;
+
+ toRead = static_cast<unsigned char>(readbuffer[0]) << 8 |
+ static_cast<unsigned char>(readbuffer[1]);
+ return true;
+}
+
+bool MP4::File::readLongLong( TagLib::ulonglong& toRead )
+{
+ ByteVector readbuffer = readBlock(8);
+ if( readbuffer.size() != 8 )
+ return false;
+
+ toRead = static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[0])) << 56 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[1])) << 48 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[2])) << 40 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[3])) << 32 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[4])) << 24 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[5])) << 16 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[6])) << 8 |
+ static_cast<ulonglong>(static_cast<unsigned char>(readbuffer[7]));
+ return true;
+}
+
+bool MP4::File::readFourcc( TagLib::MP4::Fourcc& fourcc )
+{
+ ByteVector readtype = readBlock(4);
+
+ if( readtype.size() != 4)
+ return false;
+
+ // set fourcc
+ fourcc = readtype.data();
+
+ return true;
+}
+
+MP4::Mp4TagsProxy* MP4::File::tagProxy() const
+{
+ return &d->tagsProxy;
+}
+
+MP4::Mp4PropsProxy* MP4::File::propProxy() const
+{
+ return &d->propsProxy;
+}
+
+void fillTagFromProxy( MP4::Mp4TagsProxy& proxy, MP4::Tag& mp4tag )
+{
+ // tmp buffer for each tag
+ MP4::ITunesDataBox* databox;
+
+ databox = proxy.titleData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setTitle( datastring );
+ }
+
+ databox = proxy.artistData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setArtist( datastring );
+ }
+
+ databox = proxy.albumData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setAlbum( datastring );
+ }
+
+ databox = proxy.genreData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setGenre( datastring );
+ }
+
+ databox = proxy.yearData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setYear( datastring.toInt() );
+ }
+
+ databox = proxy.trknData();
+ if( databox != 0 )
+ {
+ // convert data to uint
+ TagLib::ByteVector datavec = databox->data();
+ if( datavec.size() >= 4 )
+ {
+ TagLib::uint trackno = static_cast<TagLib::uint>( static_cast<unsigned char>(datavec[0]) << 24 |
+ static_cast<unsigned char>(datavec[1]) << 16 |
+ static_cast<unsigned char>(datavec[2]) << 8 |
+ static_cast<unsigned char>(datavec[3]) );
+ mp4tag.setTrack( trackno );
+ }
+ else
+ mp4tag.setTrack( 0 );
+ }
+
+ databox = proxy.commentData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setComment( datastring );
+ }
+
+ databox = proxy.groupingData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setGrouping( datastring );
+ }
+
+ databox = proxy.composerData();
+ if( databox != 0 )
+ {
+ // convert data to string
+ TagLib::String datastring( databox->data(), String::UTF8 );
+ // check if string was set
+ if( !datastring.isEmpty() )
+ mp4tag.setComposer( datastring );
+ }
+
+ databox = proxy.diskData();
+ if( databox != 0 )
+ {
+ // convert data to uint
+ TagLib::ByteVector datavec = databox->data();
+ if( datavec.size() >= 4 )
+ {
+ TagLib::uint discno = static_cast<TagLib::uint>( static_cast<unsigned char>(datavec[0]) << 24 |
+ static_cast<unsigned char>(datavec[1]) << 16 |
+ static_cast<unsigned char>(datavec[2]) << 8 |
+ static_cast<unsigned char>(datavec[3]) );
+ mp4tag.setDisk( discno );
+ }
+ else
+ mp4tag.setDisk( 0 );
+ }
+
+ databox = proxy.bpmData();
+ if( databox != 0 )
+ {
+ // convert data to uint
+ TagLib::ByteVector datavec = databox->data();
+
+ if( datavec.size() >= 2 )
+ {
+ TagLib::uint bpm = static_cast<TagLib::uint>( static_cast<unsigned char>(datavec[0]) << 8 |
+ static_cast<unsigned char>(datavec[1]) );
+ mp4tag.setBpm( bpm );
+ }
+ else
+ mp4tag.setBpm( 0 );
+ }
+
+ databox = proxy.coverData();
+ if( databox != 0 )
+ {
+ // get byte vector
+ mp4tag.setCover( databox->data() );
+ }
+}
diff --git a/src/metadata/m4a/mp4file.h b/src/metadata/m4a/mp4file.h
new file mode 100644
index 0000000..9e40dbc
--- /dev/null
+++ b/src/metadata/m4a/mp4file.h
@@ -0,0 +1,169 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_MP4FILE_H
+#define TAGLIB_MP4FILE_H
+
+#include <tfile.h>
+#include <audioproperties.h>
+
+#include "mp4fourcc.h"
+
+namespace TagLib {
+
+ typedef unsigned long long ulonglong;
+
+ class Tag;
+
+ namespace MP4
+ {
+ class Mp4TagsProxy;
+ class Mp4PropsProxy;
+
+ //! An implementation of TagLib::File with mp4 itunes specific methods
+
+ /*!
+ * This implements and provides an interface for mp4 itunes files to the
+ * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+ * the abstract TagLib::File API as well as providing some additional
+ * information specific to mp4 itunes files. (TODO)
+ */
+
+ class File : public TagLib::File
+ {
+ public:
+ /*!
+ * Contructs an mp4 itunes file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(const char *file, bool readProperties = true,
+ AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
+ * or a combination of the two.
+ */
+ virtual TagLib::Tag *tag() const;
+
+ /*!
+ * Returns the mp4 itunes::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual AudioProperties *audioProperties() const;
+
+ /*!
+ * Saves the file.
+ */
+ virtual bool save();
+
+ /*!
+ * This will remove all tags.
+ *
+ * \note This will also invalidate pointers to the tags
+ * as their memory will be freed.
+ * \note In order to make the removal permanent save() still needs to be called
+ */
+ void remove();
+
+ /*!
+ * Helper function for parsing the MP4 file - reads the size and type of the next box.
+ * Returns true if read succeeded - not at EOF
+ */
+ bool readSizeAndType( TagLib::uint& size, MP4::Fourcc& fourcc );
+
+ /*!
+ * Helper function to read the length of an descriptor in systems manner
+ */
+ TagLib::uint readSystemsLen();
+
+ /*!
+ * Helper function for reading an unsigned int out of the file (big endian method)
+ */
+ bool readInt( TagLib::uint& toRead );
+
+ /*!
+ * Helper function for reading an unsigned short out of the file (big endian method)
+ */
+ bool readShort( TagLib::uint& toRead );
+
+ /*!
+ * Helper function for reading an unsigned long long (64bit) out of the file (big endian method)
+ */
+ bool readLongLong( TagLib::ulonglong& toRead );
+
+ /*!
+ * Helper function to read a fourcc code
+ */
+ bool readFourcc( TagLib::MP4::Fourcc& fourcc );
+
+ /*!
+ * Function to get the tags proxy for registration of the tags boxes.
+ * The proxy provides direct access to the data boxes of the certain tags - normally
+ * covered by several levels of subboxes
+ */
+ Mp4TagsProxy* tagProxy() const;
+
+ /*!
+ * Function to get the properties proxy for registration of the properties boxes.
+ * The proxy provides direct access to the needed boxes describing audio properties.
+ */
+ Mp4PropsProxy* propProxy() const;
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+
+ } // namespace MP4
+
+} // namespace TagLib
+
+#endif // TAGLIB_MP4FILE_H
diff --git a/src/metadata/m4a/mp4fourcc.cpp b/src/metadata/m4a/mp4fourcc.cpp
new file mode 100644
index 0000000..dac0f99
--- /dev/null
+++ b/src/metadata/m4a/mp4fourcc.cpp
@@ -0,0 +1,84 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4fourcc.h"
+
+using namespace TagLib;
+
+MP4::Fourcc::Fourcc()
+{
+ m_fourcc = 0U;
+}
+
+MP4::Fourcc::Fourcc( TagLib::String fourcc )
+{
+ m_fourcc = 0U;
+
+ if( fourcc.size() >= 4 )
+ m_fourcc = static_cast<unsigned char>(fourcc[0]) << 24 |
+ static_cast<unsigned char>(fourcc[1]) << 16 |
+ static_cast<unsigned char>(fourcc[2]) << 8 |
+ static_cast<unsigned char>(fourcc[3]);
+}
+
+MP4::Fourcc::~Fourcc()
+{}
+
+TagLib::String MP4::Fourcc::toString() const
+{
+ TagLib::String fourcc;
+ fourcc.append(static_cast<char>(m_fourcc >> 24 & 0xFF));
+ fourcc.append(static_cast<char>(m_fourcc >> 16 & 0xFF));
+ fourcc.append(static_cast<char>(m_fourcc >> 8 & 0xFF));
+ fourcc.append(static_cast<char>(m_fourcc & 0xFF));
+
+ return fourcc;
+}
+
+MP4::Fourcc::operator unsigned int() const
+{
+ return m_fourcc;
+}
+
+bool MP4::Fourcc::operator == (unsigned int fourccB ) const
+{
+ return (m_fourcc==fourccB);
+}
+
+bool MP4::Fourcc::operator != (unsigned int fourccB ) const
+{
+ return (m_fourcc!=fourccB);
+}
+
+MP4::Fourcc& MP4::Fourcc::operator = (unsigned int fourcc )
+{
+ m_fourcc = fourcc;
+ return *this;
+}
+
+MP4::Fourcc& MP4::Fourcc::operator = (char fourcc[4])
+{
+ m_fourcc = static_cast<unsigned char>(fourcc[0]) << 24 |
+ static_cast<unsigned char>(fourcc[1]) << 16 |
+ static_cast<unsigned char>(fourcc[2]) << 8 |
+ static_cast<unsigned char>(fourcc[3]);
+ return *this;
+}
diff --git a/src/metadata/m4a/mp4fourcc.h b/src/metadata/m4a/mp4fourcc.h
new file mode 100644
index 0000000..90e498a
--- /dev/null
+++ b/src/metadata/m4a/mp4fourcc.h
@@ -0,0 +1,63 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4FOURCC_H
+#define MP4FOURCC_H
+
+#include "tstring.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ /*! union for easy fourcc / type handling */
+ class Fourcc
+ {
+ public:
+ //! std constructor
+ Fourcc();
+ //! string constructor
+ Fourcc(TagLib::String fourcc);
+
+ //! destructor
+ ~Fourcc();
+
+ //! function to get a string version of the fourcc
+ TagLib::String toString() const;
+ //! cast operator to unsigned int
+ operator unsigned int() const;
+ //! comparison operator
+ bool operator == (unsigned int fourccB ) const;
+ //! comparison operator
+ bool operator != (unsigned int fourccB ) const;
+ //! assigment operator for unsigned int
+ Fourcc& operator = (unsigned int fourcc );
+ //! assigment operator for character string
+ Fourcc& operator = (char fourcc[4]);
+
+ private:
+ uint m_fourcc; /*!< integer code of the fourcc */
+ };
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4FOURCC_H
diff --git a/src/metadata/m4a/mp4hdlrbox.cpp b/src/metadata/m4a/mp4hdlrbox.cpp
new file mode 100644
index 0000000..a3ec5f7
--- /dev/null
+++ b/src/metadata/m4a/mp4hdlrbox.cpp
@@ -0,0 +1,75 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <deque>
+#include <iostream>
+#include "mp4hdlrbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4HdlrBox::Mp4HdlrBoxPrivate
+{
+public:
+ TagLib::uint pre_defined;
+ MP4::Fourcc handler_type;
+ TagLib::String hdlr_string;
+}; // class Mp4HdlrBoxPrivate
+
+MP4::Mp4HdlrBox::Mp4HdlrBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoFullBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4HdlrBox::Mp4HdlrBoxPrivate();
+}
+
+MP4::Mp4HdlrBox::~Mp4HdlrBox()
+{
+ delete d;
+}
+
+MP4::Fourcc MP4::Mp4HdlrBox::hdlr_type() const
+{
+ return d->handler_type;
+}
+
+TagLib::String MP4::Mp4HdlrBox::hdlr_string() const
+{
+ return d->hdlr_string;
+}
+
+void MP4::Mp4HdlrBox::parse()
+{
+ TagLib::uint totalread = 12+20;
+
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+ if( mp4file->readInt( d->pre_defined ) == false )
+ return;
+ if( mp4file->readFourcc( d->handler_type ) == false )
+ return;
+
+ // read reserved into trash
+ mp4file->seek( 3*4, TagLib::File::Current );
+
+ // check if there are bytes remaining - used for hdlr string
+ if( size() - totalread != 0 )
+ d->hdlr_string = mp4file->readBlock( size()-totalread );
+}
diff --git a/src/metadata/m4a/mp4hdlrbox.h b/src/metadata/m4a/mp4hdlrbox.h
new file mode 100644
index 0000000..0a6bf54
--- /dev/null
+++ b/src/metadata/m4a/mp4hdlrbox.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4HDLRBOX_H
+#define MP4HDLRBOX_H
+
+#include "mp4isofullbox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4HdlrBox: public Mp4IsoFullBox
+ {
+ public:
+ Mp4HdlrBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4HdlrBox();
+
+ //! parse hdlr contents
+ void parse();
+ //! get the handler type
+ MP4::Fourcc hdlr_type() const;
+ //! get the hdlr string
+ TagLib::String hdlr_string() const;
+
+ private:
+ class Mp4HdlrBoxPrivate;
+ Mp4HdlrBoxPrivate* d;
+ }; // Mp4HdlrBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4HDLRBOX_H
diff --git a/src/metadata/m4a/mp4ilstbox.cpp b/src/metadata/m4a/mp4ilstbox.cpp
new file mode 100644
index 0000000..1d5ae9a
--- /dev/null
+++ b/src/metadata/m4a/mp4ilstbox.cpp
@@ -0,0 +1,97 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4ilstbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4IlstBox::Mp4IlstBoxPrivate
+{
+public:
+ //! container for all boxes in ilst box
+ TagLib::List<Mp4IsoBox*> ilstBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+}; // class Mp4IlstBoxPrivate
+
+MP4::Mp4IlstBox::Mp4IlstBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4IlstBox::Mp4IlstBoxPrivate();
+}
+
+MP4::Mp4IlstBox::~Mp4IlstBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->ilstBoxes.begin();
+ delIter != d->ilstBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4IlstBox::parse()
+{
+#if 0
+ std::cout << " parsing ilst box" << std::endl;
+#endif
+
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+#if 0
+ std::cout << " ";
+#endif
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " ilst box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ curbox->parsebox();
+ d->ilstBoxes.append( curbox );
+
+ // check for end of ilst box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+
+#if 0
+ std::cout << " ";
+#endif
+ }
+}
diff --git a/src/metadata/m4a/mp4ilstbox.h b/src/metadata/m4a/mp4ilstbox.h
new file mode 100644
index 0000000..9e7ad1c
--- /dev/null
+++ b/src/metadata/m4a/mp4ilstbox.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4ILSTBOX_H
+#define MP4ILSTBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4IlstBox: public Mp4IsoBox
+ {
+ public:
+ Mp4IlstBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4IlstBox();
+
+ //! parse ilst contents
+ void parse();
+
+ private:
+ class Mp4IlstBoxPrivate;
+ Mp4IlstBoxPrivate* d;
+ }; // Mp4IlstBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4ILSTBOX_H
diff --git a/src/metadata/m4a/mp4isobox.cpp b/src/metadata/m4a/mp4isobox.cpp
new file mode 100644
index 0000000..7f08924
--- /dev/null
+++ b/src/metadata/m4a/mp4isobox.cpp
@@ -0,0 +1,76 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4isobox.h"
+#include "tfile.h"
+
+using namespace TagLib;
+
+class MP4::Mp4IsoBox::Mp4IsoBoxPrivate
+{
+public:
+ MP4::Fourcc fourcc;
+ TagLib::uint size;
+ long offset;
+ TagLib::File* file;
+};
+
+MP4::Mp4IsoBox::Mp4IsoBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+{
+ d = new MP4::Mp4IsoBox::Mp4IsoBoxPrivate();
+ d->file = file;
+ d->fourcc = fourcc;
+ d->size = size;
+ d->offset = offset;
+}
+
+MP4::Mp4IsoBox::~Mp4IsoBox()
+{
+ delete d;
+}
+
+void MP4::Mp4IsoBox::parsebox()
+{
+ // seek to offset
+ file()->seek( offset(), File::Beginning );
+ // simply call parse method of sub class
+ parse();
+}
+
+MP4::Fourcc MP4::Mp4IsoBox::fourcc() const
+{
+ return d->fourcc;
+}
+
+TagLib::uint MP4::Mp4IsoBox::size() const
+{
+ return d->size;
+}
+
+long MP4::Mp4IsoBox::offset() const
+{
+ return d->offset;
+}
+
+TagLib::File* MP4::Mp4IsoBox::file() const
+{
+ return d->file;
+}
diff --git a/src/metadata/m4a/mp4isobox.h b/src/metadata/m4a/mp4isobox.h
new file mode 100644
index 0000000..4d4edc9
--- /dev/null
+++ b/src/metadata/m4a/mp4isobox.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4ISOBOX_H
+#define MP4ISOBOX_H
+
+#include "taglib.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ class File;
+
+ namespace MP4
+ {
+ class Mp4IsoBox
+ {
+ public:
+ //! constructor for base class
+ Mp4IsoBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ //! destructor - simply freeing private ptr
+ virtual ~Mp4IsoBox();
+
+ //! function to get the fourcc code
+ MP4::Fourcc fourcc() const;
+ //! function to get the size of tha atom/box
+ uint size() const;
+ //! function to get the offset of the atom in the mp4 file
+ long offset() const;
+
+ //! parse wrapper to get common interface for both box and fullbox
+ virtual void parsebox();
+ //! pure virtual function for all subclasses to implement
+ virtual void parse() = 0;
+
+ protected:
+ //! function to get the file pointer
+ TagLib::File* file() const;
+
+ protected:
+ class Mp4IsoBoxPrivate;
+ Mp4IsoBoxPrivate* d;
+ };
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4ISOBOX_H
+
diff --git a/src/metadata/m4a/mp4isofullbox.cpp b/src/metadata/m4a/mp4isofullbox.cpp
new file mode 100644
index 0000000..f938ad4
--- /dev/null
+++ b/src/metadata/m4a/mp4isofullbox.cpp
@@ -0,0 +1,67 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4isofullbox.h"
+#include "tfile.h"
+
+using namespace TagLib;
+
+class MP4::Mp4IsoFullBox::Mp4IsoFullBoxPrivate
+{
+public:
+ uchar version;
+ uint flags;
+}; // Mp4IsoFullBoxPrivate
+
+
+MP4::Mp4IsoFullBox::Mp4IsoFullBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+: Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4IsoFullBox::Mp4IsoFullBoxPrivate();
+}
+
+MP4::Mp4IsoFullBox::~Mp4IsoFullBox()
+{
+ delete d;
+}
+
+void MP4::Mp4IsoFullBox::parsebox()
+{
+ // seek to offset
+ Mp4IsoBox::file()->seek(Mp4IsoBox::offset(), File::Beginning );
+ // parse version and flags
+ ByteVector version_flags = Mp4IsoBox::file()->readBlock(4);
+ d->version = version_flags[0];
+ d->flags = version_flags[1] << 16 || version_flags[2] << 8 || version_flags[3];
+ // call parse method of subclass
+ parse();
+}
+
+TagLib::uchar MP4::Mp4IsoFullBox::version()
+{
+ return d->version;
+}
+
+TagLib::uint MP4::Mp4IsoFullBox::flags()
+{
+ return d->flags;
+}
+
diff --git a/src/metadata/m4a/mp4isofullbox.h b/src/metadata/m4a/mp4isofullbox.h
new file mode 100644
index 0000000..bc01b61
--- /dev/null
+++ b/src/metadata/m4a/mp4isofullbox.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4ISOFULLBOX_H
+#define MP4ISOFULLBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4IsoFullBox : public Mp4IsoBox
+ {
+ public:
+ //! constructor for full box
+ Mp4IsoFullBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ //! destructor for mp4 iso full box
+ virtual ~Mp4IsoFullBox();
+
+ //! function to get the version of box
+ uchar version();
+ //! function to get the flag map
+ uint flags();
+
+ //! parse wrapper to get common interface for both box and fullbox
+ virtual void parsebox();
+
+ protected:
+ class Mp4IsoFullBoxPrivate;
+ Mp4IsoFullBoxPrivate* d;
+ };
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4ISOFULLBOX_H
+
diff --git a/src/metadata/m4a/mp4itunestag.cpp b/src/metadata/m4a/mp4itunestag.cpp
new file mode 100644
index 0000000..aabd644
--- /dev/null
+++ b/src/metadata/m4a/mp4itunestag.cpp
@@ -0,0 +1,197 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4itunestag.h"
+
+using namespace TagLib;
+
+class MP4::Tag::TagPrivate
+{
+public:
+ MP4::File* mp4file;
+ TagLib::String title;
+ TagLib::String artist;
+ TagLib::String album;
+ TagLib::String genre;
+ TagLib::uint year;
+ TagLib::uint track;
+ TagLib::String comment;
+ TagLib::String grouping;
+ TagLib::String composer;
+ TagLib::uint disk;
+ TagLib::uint bpm;
+ bool isEmpty;
+ TagLib::ByteVector cover;
+};
+
+
+MP4::Tag::Tag( )
+{
+ d = new TagPrivate();
+ d->year = 0;
+ d->track = 0;
+ d->disk = 0;
+ d->bpm = 0;
+ d->isEmpty = true;
+}
+
+MP4::Tag::~Tag()
+{
+ delete d;
+}
+
+String MP4::Tag::title() const
+{
+ return d->title;
+}
+
+String MP4::Tag::artist() const
+{
+ return d->artist;
+}
+
+String MP4::Tag::album() const
+{
+ return d->album;
+}
+
+String MP4::Tag::comment() const
+{
+ return d->comment;
+}
+
+String MP4::Tag::genre() const
+{
+ return d->genre;
+}
+
+TagLib::uint MP4::Tag::year() const
+{
+ return d->year;
+}
+
+TagLib::uint MP4::Tag::track() const
+{
+ return d->track;
+}
+
+String MP4::Tag::grouping() const
+{
+ return d->grouping;
+}
+
+String MP4::Tag::composer() const
+{
+ return d->composer;
+}
+
+TagLib::uint MP4::Tag::disk() const
+{
+ return d->disk;
+}
+
+TagLib::uint MP4::Tag::bpm() const
+{
+ return d->bpm;
+}
+
+TagLib::ByteVector MP4::Tag::cover() const
+{
+ return d->cover;
+}
+
+void MP4::Tag::setTitle(const String &s)
+{
+ d->title = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setArtist(const String &s)
+{
+ d->artist = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setAlbum(const String &s)
+{
+ d->album = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setComment(const String &s)
+{
+ d->comment = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setGenre(const String &s)
+{
+ d->genre = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setYear(const TagLib::uint i)
+{
+ d->year = i;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setTrack(const TagLib::uint i)
+{
+ d->track = i;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setGrouping(const String &s)
+{
+ d->grouping = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setComposer(const String &s)
+{
+ d->composer = s;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setDisk(const TagLib::uint i)
+{
+ d->disk = i;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setBpm(const TagLib::uint i)
+{
+ d->bpm = i;
+ d->isEmpty = false;
+}
+
+void MP4::Tag::setCover(const TagLib::ByteVector& c)
+{
+ d->cover = c;
+ d->isEmpty = false;
+}
+
+bool MP4::Tag::isEmpty() const
+{
+ return d->isEmpty;
+}
+
diff --git a/src/metadata/m4a/mp4itunestag.h b/src/metadata/m4a/mp4itunestag.h
new file mode 100644
index 0000000..9e572a7
--- /dev/null
+++ b/src/metadata/m4a/mp4itunestag.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4ITUNESTAG_H
+#define MP4ITUNESTAG_H
+
+#include "taglib.h"
+#include "tstring.h"
+#include "tag.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class File;
+
+ class Tag : public TagLib::Tag
+ {
+ public:
+ /*!
+ * Constructs an empty MP4 iTunes tag.
+ */
+ Tag( );
+
+ /*!
+ * Destroys this Tag instance.
+ */
+ virtual ~Tag();
+
+ // Reimplementations.
+
+ virtual String title() const;
+ virtual String artist() const;
+ virtual String album() const;
+ virtual String comment() const;
+ virtual String genre() const;
+ virtual uint year() const;
+ virtual uint track() const;
+
+ virtual void setTitle(const String &s);
+ virtual void setArtist(const String &s);
+ virtual void setAlbum(const String &s);
+ virtual void setComment(const String &s);
+ virtual void setGenre(const String &s);
+ virtual void setYear(const uint i);
+ virtual void setTrack(const uint i);
+
+ // MP4 specific fields
+
+ String grouping() const;
+ String composer() const;
+ uint disk() const;
+ uint bpm() const;
+ ByteVector cover() const;
+ int compilation() const { return -1; }
+
+ void setGrouping(const String &s);
+ void setComposer(const String &s);
+ void setDisk(const uint i);
+ void setBpm(const uint i);
+ void setCover( const ByteVector& cover );
+ void setCompilation( bool /*isCompilation*/ ) {}
+
+ virtual bool isEmpty() const;
+
+ private:
+ Tag(const Tag &);
+ Tag &operator=(const Tag &);
+
+ class TagPrivate;
+ TagPrivate *d;
+ };
+ } // namespace MP4
+
+} // namespace TagLib
+
+#endif // MP4ITUNESTAG_H
diff --git a/src/metadata/m4a/mp4mdiabox.cpp b/src/metadata/m4a/mp4mdiabox.cpp
new file mode 100644
index 0000000..c3b30ad
--- /dev/null
+++ b/src/metadata/m4a/mp4mdiabox.cpp
@@ -0,0 +1,111 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4mdiabox.h"
+#include "mp4hdlrbox.h"
+#include "mp4minfbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4MdiaBox::Mp4MdiaBoxPrivate
+{
+public:
+ //! container for all boxes in mdia box
+ TagLib::List<Mp4IsoBox*> mdiaBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+}; // class Mp4MdiaBoxPrivate
+
+MP4::Mp4MdiaBox::Mp4MdiaBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4MdiaBox::Mp4MdiaBoxPrivate();
+}
+
+MP4::Mp4MdiaBox::~Mp4MdiaBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->mdiaBoxes.begin();
+ delIter != d->mdiaBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4MdiaBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ // stores the current handler type
+ TagLib::MP4::Fourcc hdlrtype;
+
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " mdia box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ if( static_cast<TagLib::uint>( fourcc ) == 0x6d696e66 /*"minf"*/ )
+ {
+ // cast to minf
+ Mp4MinfBox* minfbox = dynamic_cast<Mp4MinfBox*>( curbox );
+ if(!minfbox)
+ return;
+ // set handler type
+ minfbox->setHandlerType( hdlrtype );
+ }
+
+ curbox->parsebox();
+ d->mdiaBoxes.append( curbox );
+
+ if(static_cast<TagLib::uint>( fourcc ) == 0x68646c72 /*"hdlr"*/ )
+ {
+ // cast to hdlr box
+ Mp4HdlrBox* hdlrbox = dynamic_cast<Mp4HdlrBox*>( curbox );
+ if(!hdlrbox)
+ return;
+ // get handler type
+ hdlrtype = hdlrbox->hdlr_type();
+ }
+ // check for end of mdia box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+
+ }
+}
diff --git a/src/metadata/m4a/mp4mdiabox.h b/src/metadata/m4a/mp4mdiabox.h
new file mode 100644
index 0000000..16503bd
--- /dev/null
+++ b/src/metadata/m4a/mp4mdiabox.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4MDIABOX_H
+#define MP4MDIABOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4MdiaBox: public Mp4IsoBox
+ {
+ public:
+ Mp4MdiaBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4MdiaBox();
+
+ //! parse mdia contents
+ void parse();
+
+ private:
+ class Mp4MdiaBoxPrivate;
+ Mp4MdiaBoxPrivate* d;
+ }; // Mp4MdiaBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4MDIABOX_H
diff --git a/src/metadata/m4a/mp4metabox.cpp b/src/metadata/m4a/mp4metabox.cpp
new file mode 100644
index 0000000..30eebf2
--- /dev/null
+++ b/src/metadata/m4a/mp4metabox.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tlist.h>
+#include <iostream>
+#include "mp4metabox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4MetaBox::Mp4MetaBoxPrivate
+{
+public:
+ //! container for all boxes in meta box
+ TagLib::List<Mp4IsoBox*> metaBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+}; // class Mp4MetaBoxPrivate
+
+MP4::Mp4MetaBox::Mp4MetaBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoFullBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4MetaBox::Mp4MetaBoxPrivate();
+}
+
+MP4::Mp4MetaBox::~Mp4MetaBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->metaBoxes.begin();
+ delIter != d->metaBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4MetaBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 12; // initial size of box
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " meta box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ curbox->parsebox();
+ d->metaBoxes.append( curbox );
+
+ // check for end of meta box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+ }
+}
diff --git a/src/metadata/m4a/mp4metabox.h b/src/metadata/m4a/mp4metabox.h
new file mode 100644
index 0000000..58d9667
--- /dev/null
+++ b/src/metadata/m4a/mp4metabox.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4METABOX_H
+#define MP4METABOX_H
+
+#include "mp4isofullbox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4MetaBox: public Mp4IsoFullBox
+ {
+ public:
+ Mp4MetaBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4MetaBox();
+
+ //! parse meta contents
+ void parse();
+
+ private:
+ class Mp4MetaBoxPrivate;
+ Mp4MetaBoxPrivate* d;
+ }; // Mp4MetaBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4METABOX_H
diff --git a/src/metadata/m4a/mp4minfbox.cpp b/src/metadata/m4a/mp4minfbox.cpp
new file mode 100644
index 0000000..0081dda
--- /dev/null
+++ b/src/metadata/m4a/mp4minfbox.cpp
@@ -0,0 +1,104 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4minfbox.h"
+#include "mp4stblbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4MinfBox::Mp4MinfBoxPrivate
+{
+public:
+ //! container for all boxes in minf box
+ TagLib::List<Mp4IsoBox*> minfBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+ //! stores the handler type of the current trak
+ MP4::Fourcc handler_type;
+}; // class Mp4MinfBoxPrivate
+
+MP4::Mp4MinfBox::Mp4MinfBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4MinfBox::Mp4MinfBoxPrivate();
+}
+
+MP4::Mp4MinfBox::~Mp4MinfBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->minfBoxes.begin();
+ delIter != d->minfBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4MinfBox::setHandlerType( MP4::Fourcc fourcc )
+{
+ d->handler_type = fourcc;
+}
+
+void MP4::Mp4MinfBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " minf box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ if(static_cast<TagLib::uint>( fourcc ) == 0x7374626c /*stbl*/ )
+ {
+ // cast to hdlr box
+ Mp4StblBox* stblbox = dynamic_cast<Mp4StblBox*>( curbox );
+ if(!stblbox)
+ return;
+ // set handler type
+ stblbox->setHandlerType( d->handler_type );
+ }
+
+ curbox->parsebox();
+ d->minfBoxes.append( curbox );
+
+ // check for end of minf box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+ }
+}
diff --git a/src/metadata/m4a/mp4minfbox.h b/src/metadata/m4a/mp4minfbox.h
new file mode 100644
index 0000000..9195d30
--- /dev/null
+++ b/src/metadata/m4a/mp4minfbox.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4MINFBOX_H
+#define MP4MINFBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4MinfBox: public Mp4IsoBox
+ {
+ public:
+ Mp4MinfBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4MinfBox();
+
+ //! parse minf contents
+ void parse();
+ //! set the handler type - needed for stsd
+ void setHandlerType( MP4::Fourcc fourcc );
+
+ private:
+ class Mp4MinfBoxPrivate;
+ Mp4MinfBoxPrivate* d;
+ }; // Mp4MinfBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4MINFBOX_H
diff --git a/src/metadata/m4a/mp4moovbox.cpp b/src/metadata/m4a/mp4moovbox.cpp
new file mode 100644
index 0000000..24826ec
--- /dev/null
+++ b/src/metadata/m4a/mp4moovbox.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4moovbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4MoovBox::Mp4MoovBoxPrivate
+{
+public:
+ //! container for all boxes in moov box
+ TagLib::List<Mp4IsoBox*> moovBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+}; // class Mp4MoovBoxPrivate
+
+MP4::Mp4MoovBox::Mp4MoovBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4MoovBox::Mp4MoovBoxPrivate();
+}
+
+MP4::Mp4MoovBox::~Mp4MoovBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->moovBoxes.begin();
+ delIter != d->moovBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4MoovBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " moov box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ curbox->parsebox();
+ d->moovBoxes.append( curbox );
+
+ // check for end of moov box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+ }
+}
diff --git a/src/metadata/m4a/mp4moovbox.h b/src/metadata/m4a/mp4moovbox.h
new file mode 100644
index 0000000..390953f
--- /dev/null
+++ b/src/metadata/m4a/mp4moovbox.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4MOOVBOX_H
+#define MP4MOOVBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4MoovBox: public Mp4IsoBox
+ {
+ public:
+ Mp4MoovBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4MoovBox();
+
+ //! parse moov contents
+ void parse();
+
+ private:
+ class Mp4MoovBoxPrivate;
+ Mp4MoovBoxPrivate* d;
+ }; // Mp4MoovBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4MOOVBOX_H
diff --git a/src/metadata/m4a/mp4mvhdbox.cpp b/src/metadata/m4a/mp4mvhdbox.cpp
new file mode 100644
index 0000000..36053e4
--- /dev/null
+++ b/src/metadata/m4a/mp4mvhdbox.cpp
@@ -0,0 +1,140 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <deque>
+#include <iostream>
+#include "mp4mvhdbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+#include "mp4propsproxy.h"
+
+using namespace TagLib;
+
+class MP4::Mp4MvhdBox::Mp4MvhdBoxPrivate
+{
+public:
+ //! creation time of the file
+ TagLib::ulonglong creationTime;
+ //! modification time of the file - since midnight, Jan. 1, 1904, UTC-time
+ TagLib::ulonglong modificationTime;
+ //! timescale for the file - referred by all time specifications in this box
+ TagLib::uint timescale;
+ //! duration of presentation
+ TagLib::ulonglong duration;
+ //! playout speed
+ TagLib::uint rate;
+ //! volume for entire presentation
+ TagLib::uint volume;
+ //! track ID for an additional track (next new track)
+ TagLib::uint nextTrackID;
+}; // class Mp4MvhdBoxPrivate
+
+MP4::Mp4MvhdBox::Mp4MvhdBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoFullBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4MvhdBox::Mp4MvhdBoxPrivate();
+}
+
+MP4::Mp4MvhdBox::~Mp4MvhdBox()
+{
+ delete d;
+}
+
+TagLib::ulonglong MP4::Mp4MvhdBox::creationTime() const
+{
+ return d->creationTime;
+}
+
+TagLib::ulonglong MP4::Mp4MvhdBox::modificationTime() const
+{
+ return d->modificationTime;
+}
+
+TagLib::uint MP4::Mp4MvhdBox::timescale() const
+{
+ return d->timescale;
+}
+
+TagLib::ulonglong MP4::Mp4MvhdBox::duration() const
+{
+ return d->duration;
+}
+
+TagLib::uint MP4::Mp4MvhdBox::rate() const
+{
+ return d->rate;
+}
+
+TagLib::uint MP4::Mp4MvhdBox::volume() const
+{
+ return d->volume;
+}
+
+TagLib::uint MP4::Mp4MvhdBox::nextTrackID() const
+{
+ return d->nextTrackID;
+}
+
+
+void MP4::Mp4MvhdBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ if( version() == 1 )
+ {
+ if( !mp4file->readLongLong( d->creationTime ) )
+ return;
+ if( !mp4file->readLongLong( d->modificationTime ) )
+ return;
+ if( !mp4file->readInt( d->timescale ) )
+ return;
+ if( !mp4file->readLongLong( d->duration ) )
+ return;
+ }
+ else
+ {
+ TagLib::uint creationTime_tmp, modificationTime_tmp, duration_tmp;
+
+ if( !mp4file->readInt( creationTime_tmp ) )
+ return;
+ if( !mp4file->readInt( modificationTime_tmp ) )
+ return;
+ if( !mp4file->readInt( d->timescale ) )
+ return;
+ if( !mp4file->readInt( duration_tmp ) )
+ return;
+
+ d->creationTime = creationTime_tmp;
+ d->modificationTime = modificationTime_tmp;
+ d->duration = duration_tmp;
+ }
+ if( !mp4file->readInt( d->rate ) )
+ return;
+ if( !mp4file->readInt( d->volume ) )
+ return;
+ // jump over unused fields
+ mp4file->seek( 68, File::Current );
+
+ if( !mp4file->readInt( d->nextTrackID ) )
+ return;
+ // register at proxy
+ mp4file->propProxy()->registerMvhd( this );
+}
diff --git a/src/metadata/m4a/mp4mvhdbox.h b/src/metadata/m4a/mp4mvhdbox.h
new file mode 100644
index 0000000..b133485
--- /dev/null
+++ b/src/metadata/m4a/mp4mvhdbox.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4MVHDBOX_H
+#define MP4MVHDBOX_H
+
+#include "mp4isofullbox.h"
+#include "mp4fourcc.h"
+#include "mp4file.h" // ulonglong
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4MvhdBox: public Mp4IsoFullBox
+ {
+ public:
+ Mp4MvhdBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4MvhdBox();
+
+ //! function to get the creation time of the mp4 file
+ ulonglong creationTime() const;
+ //! function to get the modification time of the mp4 file
+ ulonglong modificationTime() const;
+ //! function to get the timescale referenced by the above timestamps
+ uint timescale() const;
+ //! function to get the presentation duration in the mp4 file
+ ulonglong duration() const;
+ //! function to get the rate (playout speed) - typically 1.0;
+ uint rate() const;
+ //! function to get volume level for presentation - typically 1.0;
+ uint volume() const;
+ //! function to get the track ID for adding new tracks - useless for this lib
+ uint nextTrackID() const;
+
+ //! parse mvhd contents
+ void parse();
+
+ private:
+ class Mp4MvhdBoxPrivate;
+ Mp4MvhdBoxPrivate* d;
+ }; // Mp4MvhdBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4MVHDBOX_H
diff --git a/src/metadata/m4a/mp4propsproxy.cpp b/src/metadata/m4a/mp4propsproxy.cpp
new file mode 100644
index 0000000..ebee9c2
--- /dev/null
+++ b/src/metadata/m4a/mp4propsproxy.cpp
@@ -0,0 +1,89 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4propsproxy.h"
+
+using namespace TagLib;
+
+class MP4::Mp4PropsProxy::Mp4PropsProxyPrivate
+{
+public:
+ //! the movie header box
+ MP4::Mp4MvhdBox* mvhdbox;
+ //! the sample table box
+ MP4::Mp4AudioSampleEntry* audiosampleentry;
+};
+
+
+MP4::Mp4PropsProxy::Mp4PropsProxy()
+{
+ d = new MP4::Mp4PropsProxy::Mp4PropsProxyPrivate();
+ d->mvhdbox = 0;
+ d->audiosampleentry = 0;
+}
+
+MP4::Mp4PropsProxy::~Mp4PropsProxy()
+{
+ delete d;
+}
+
+TagLib::uint MP4::Mp4PropsProxy::seconds() const
+{
+ if( d->mvhdbox )
+ return static_cast<TagLib::uint>( d->mvhdbox->duration() / d->mvhdbox->timescale() );
+ else
+ return 0;
+}
+
+TagLib::uint MP4::Mp4PropsProxy::channels() const
+{
+ if( d->audiosampleentry )
+ return d->audiosampleentry->channels();
+ else
+ return 0;
+}
+
+TagLib::uint MP4::Mp4PropsProxy::sampleRate() const
+{
+ if( d->audiosampleentry )
+ return (d->audiosampleentry->samplerate()>>16);
+ else
+ return 0;
+}
+
+TagLib::uint MP4::Mp4PropsProxy::bitRate() const
+{
+ if( d->audiosampleentry )
+ return (d->audiosampleentry->bitrate());
+ else
+ return 0;
+}
+
+void MP4::Mp4PropsProxy::registerMvhd( MP4::Mp4MvhdBox* mvhdbox )
+{
+ d->mvhdbox = mvhdbox;
+}
+
+void MP4::Mp4PropsProxy::registerAudioSampleEntry( MP4::Mp4AudioSampleEntry* audioSampleEntry )
+{
+ d->audiosampleentry = audioSampleEntry;
+}
+
diff --git a/src/metadata/m4a/mp4propsproxy.h b/src/metadata/m4a/mp4propsproxy.h
new file mode 100644
index 0000000..0ea29e2
--- /dev/null
+++ b/src/metadata/m4a/mp4propsproxy.h
@@ -0,0 +1,65 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4PROPSPROXY_H
+#define MP4PROPSPROXY_H MP4PROPSPROXY_H
+#include "mp4mvhdbox.h"
+#include "mp4audiosampleentry.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ //! Mp4PropsProxy is used to access the stsd box and mvhd box directly
+ /*! this class works as a shortcut to avoid stepping through all parent boxes
+ * to access the boxes in question
+ */
+ class Mp4PropsProxy
+ {
+ public:
+ //! constructor for properties proxy
+ Mp4PropsProxy();
+ //! destructor
+ ~Mp4PropsProxy();
+
+ //! function to get length of media in seconds
+ TagLib::uint seconds() const;
+ //! function to get the nunmber of channels
+ TagLib::uint channels() const;
+ //! function to get the sample rate
+ TagLib::uint sampleRate() const;
+ //! function to get the bitrate rate
+ TagLib::uint bitRate() const;
+
+ //! function to register the movie header box - mvhd
+ void registerMvhd( MP4::Mp4MvhdBox* mvhdbox );
+ //! function to register the sample description box
+ void registerAudioSampleEntry( MP4::Mp4AudioSampleEntry* audiosampleentry );
+
+ private:
+ class Mp4PropsProxyPrivate;
+ Mp4PropsProxyPrivate* d;
+
+ }; // Mp4PropsProxy
+ } // MP4
+} // TagLib
+
+#endif // MP4PROPSPROXY_H
diff --git a/src/metadata/m4a/mp4sampleentry.cpp b/src/metadata/m4a/mp4sampleentry.cpp
new file mode 100644
index 0000000..e5619ea
--- /dev/null
+++ b/src/metadata/m4a/mp4sampleentry.cpp
@@ -0,0 +1,59 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4sampleentry.h"
+#include "mp4isobox.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4SampleEntry::Mp4SampleEntryPrivate
+{
+public:
+ TagLib::uint data_reference_index;
+};
+
+MP4::Mp4SampleEntry::Mp4SampleEntry( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::Mp4SampleEntry::Mp4SampleEntryPrivate();
+}
+
+MP4::Mp4SampleEntry::~Mp4SampleEntry()
+{
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::Mp4SampleEntry::parse()
+{
+ TagLib::MP4::File* mp4file = dynamic_cast<TagLib::MP4::File*>(file());
+ if(!mp4file)
+ return;
+
+ // skip the first 6 bytes
+ mp4file->seek( 6, TagLib::File::Current );
+ // read data reference index
+ if(!mp4file->readShort( d->data_reference_index))
+ return;
+ parseEntry();
+}
+
diff --git a/src/metadata/m4a/mp4sampleentry.h b/src/metadata/m4a/mp4sampleentry.h
new file mode 100644
index 0000000..39475f7
--- /dev/null
+++ b/src/metadata/m4a/mp4sampleentry.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4SAMPLEENTRY_H
+#define MP4SAMPLEENTRY_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4SampleEntry: public Mp4IsoBox
+ {
+ public:
+ Mp4SampleEntry( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~Mp4SampleEntry();
+
+ public:
+ //! parse the content of the box
+ virtual void parse();
+
+ private:
+ //! function to be implemented in subclass
+ virtual void parseEntry() = 0;
+
+ protected:
+ class Mp4SampleEntryPrivate;
+ Mp4SampleEntryPrivate* d;
+ }; // class Mp4SampleEntry
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4SAMPLEENTRY_H
diff --git a/src/metadata/m4a/mp4skipbox.cpp b/src/metadata/m4a/mp4skipbox.cpp
new file mode 100644
index 0000000..8cb5218
--- /dev/null
+++ b/src/metadata/m4a/mp4skipbox.cpp
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4skipbox.h"
+#include "mp4isobox.h"
+#include "tfile.h"
+
+using namespace TagLib;
+
+class MP4::Mp4SkipBox::Mp4SkipBoxPrivate
+{
+public:
+};
+
+MP4::Mp4SkipBox::Mp4SkipBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset )
+ :Mp4IsoBox(file, fourcc, size, offset)
+{
+ d = new MP4::Mp4SkipBox::Mp4SkipBoxPrivate();
+}
+
+MP4::Mp4SkipBox::~Mp4SkipBox()
+{
+ delete d;
+}
+
+//! parse the content of the box
+void MP4::Mp4SkipBox::parse()
+{
+ // skip contents
+ file()->seek( size() - 8, TagLib::File::Current );
+}
+
diff --git a/src/metadata/m4a/mp4skipbox.h b/src/metadata/m4a/mp4skipbox.h
new file mode 100644
index 0000000..896fcaa
--- /dev/null
+++ b/src/metadata/m4a/mp4skipbox.h
@@ -0,0 +1,50 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4SKIPBOX_H
+#define MP4SKIPBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4SkipBox: public Mp4IsoBox
+ {
+ public:
+ Mp4SkipBox( TagLib::File* file, MP4::Fourcc fourcc, uint size, long offset );
+ ~Mp4SkipBox();
+
+ private:
+ //! parse the content of the box
+ virtual void parse();
+
+ protected:
+ class Mp4SkipBoxPrivate;
+ Mp4SkipBoxPrivate* d;
+ }; // class Mp4SkipBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4SKIPBOX_H
diff --git a/src/metadata/m4a/mp4stblbox.cpp b/src/metadata/m4a/mp4stblbox.cpp
new file mode 100644
index 0000000..f67de59
--- /dev/null
+++ b/src/metadata/m4a/mp4stblbox.cpp
@@ -0,0 +1,105 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4stblbox.h"
+#include "mp4stsdbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4StblBox::Mp4StblBoxPrivate
+{
+public:
+ //! container for all boxes in stbl box
+ TagLib::List<Mp4IsoBox*> stblBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+ //! the handler type for the current trak
+ MP4::Fourcc handler_type;
+}; // class Mp4StblBoxPrivate
+
+MP4::Mp4StblBox::Mp4StblBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4StblBox::Mp4StblBoxPrivate();
+}
+
+MP4::Mp4StblBox::~Mp4StblBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->stblBoxes.begin();
+ delIter != d->stblBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4StblBox::setHandlerType( MP4::Fourcc fourcc )
+{
+ d->handler_type = fourcc;
+}
+
+void MP4::Mp4StblBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " stbl box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+
+ // check for stsd
+ if( static_cast<TagLib::uint>(fourcc) == 0x73747364 /*'stsd'*/ )
+ {
+ // cast to stsd box
+ MP4::Mp4StsdBox* stsdbox = dynamic_cast<MP4::Mp4StsdBox*>(curbox);
+ if(!stsdbox)
+ return;
+ // set the handler type
+ stsdbox->setHandlerType( d->handler_type );
+ }
+ curbox->parsebox();
+ d->stblBoxes.append( curbox );
+
+ // check for end of stbl box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+ }
+}
diff --git a/src/metadata/m4a/mp4stblbox.h b/src/metadata/m4a/mp4stblbox.h
new file mode 100644
index 0000000..39619a6
--- /dev/null
+++ b/src/metadata/m4a/mp4stblbox.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4STBLBOX_H
+#define MP4STBLBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4StblBox: public Mp4IsoBox
+ {
+ public:
+ Mp4StblBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4StblBox();
+
+ //! parse stbl contents
+ void parse();
+ //! set the handler type - needed for stsd
+ void setHandlerType( MP4::Fourcc fourcc );
+
+ private:
+ class Mp4StblBoxPrivate;
+ Mp4StblBoxPrivate* d;
+ }; // Mp4StblBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4STBLBOX_H
diff --git a/src/metadata/m4a/mp4stsdbox.cpp b/src/metadata/m4a/mp4stsdbox.cpp
new file mode 100644
index 0000000..6bb8cbe
--- /dev/null
+++ b/src/metadata/m4a/mp4stsdbox.cpp
@@ -0,0 +1,91 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4stsdbox.h"
+#include "mp4audiosampleentry.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4StsdBox::Mp4StsdBoxPrivate
+{
+public:
+ //! the handler type for the current trak
+ MP4::Fourcc handler_type;
+ //! the audio sample entry
+ MP4::Mp4AudioSampleEntry* audioSampleEntry;
+}; // class Mp4StsdBoxPrivate
+
+MP4::Mp4StsdBox::Mp4StsdBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoFullBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4StsdBox::Mp4StsdBoxPrivate();
+}
+
+MP4::Mp4StsdBox::~Mp4StsdBox()
+{
+ delete d;
+}
+
+void MP4::Mp4StsdBox::setHandlerType( MP4::Fourcc fourcc )
+{
+ d->handler_type = fourcc;
+}
+
+void MP4::Mp4StsdBox::parse()
+{
+ MP4::File* mp4file = dynamic_cast<MP4::File*>( file() );
+ if(!mp4file)
+ return;
+
+ TagLib::uint totalsize = 12; // initial size of box
+
+ // check for handler type - only parse if 'soun':
+ if( static_cast<TagLib::uint>(d->handler_type) == 0x736f756e )
+ {
+ // read entry count
+ TagLib::uint entry_count;
+ if(!mp4file->readInt( entry_count ))
+ return;
+
+ // simply read first entry and skip all following
+ // read size and type
+ TagLib::uint cursize;
+ MP4::Fourcc fourcc;
+ if( !mp4file->readSizeAndType( cursize, fourcc ))
+ return;
+
+ totalsize += 12;
+ // alocate an AudioSampleEntry
+ d->audioSampleEntry = new MP4::Mp4AudioSampleEntry( mp4file, fourcc, cursize, mp4file->tell() );
+ // parse the AudioSampleEntry
+ d->audioSampleEntry->parse();
+ totalsize += cursize-8;
+ // skip the remaining box contents
+ mp4file->seek( size()-totalsize, TagLib::File::Current );
+ }
+ else
+ {
+ mp4file->seek( size()-totalsize, TagLib::File::Current );
+ }
+}
diff --git a/src/metadata/m4a/mp4stsdbox.h b/src/metadata/m4a/mp4stsdbox.h
new file mode 100644
index 0000000..90bc014
--- /dev/null
+++ b/src/metadata/m4a/mp4stsdbox.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4STSDBOX_H
+#define MP4STSDBOX_H
+
+#include "mp4isofullbox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4StsdBox: public Mp4IsoFullBox
+ {
+ public:
+ Mp4StsdBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4StsdBox();
+
+ //! parse stsd contents
+ void parse();
+ //! set the handler type - needed for stsd
+ void setHandlerType( MP4::Fourcc fourcc );
+
+ private:
+ class Mp4StsdBoxPrivate;
+ Mp4StsdBoxPrivate* d;
+ }; // Mp4StsdBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4STSDBOX_H
diff --git a/src/metadata/m4a/mp4tagsproxy.cpp b/src/metadata/m4a/mp4tagsproxy.cpp
new file mode 100644
index 0000000..0a77427
--- /dev/null
+++ b/src/metadata/m4a/mp4tagsproxy.cpp
@@ -0,0 +1,168 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "mp4tagsproxy.h"
+#include "itunesdatabox.h"
+
+using namespace TagLib;
+
+class MP4::Mp4TagsProxy::Mp4TagsProxyPrivate
+{
+public:
+ ITunesDataBox* titleData;
+ ITunesDataBox* artistData;
+ ITunesDataBox* albumData;
+ ITunesDataBox* coverData;
+ ITunesDataBox* genreData;
+ ITunesDataBox* yearData;
+ ITunesDataBox* trknData;
+ ITunesDataBox* commentData;
+ ITunesDataBox* groupingData;
+ ITunesDataBox* composerData;
+ ITunesDataBox* diskData;
+ ITunesDataBox* bpmData;
+};
+
+MP4::Mp4TagsProxy::Mp4TagsProxy()
+{
+ d = new MP4::Mp4TagsProxy::Mp4TagsProxyPrivate();
+ d->titleData = 0;
+ d->artistData = 0;
+ d->albumData = 0;
+ d->coverData = 0;
+ d->genreData = 0;
+ d->yearData = 0;
+ d->trknData = 0;
+ d->commentData = 0;
+ d->groupingData = 0;
+ d->composerData = 0;
+ d->diskData = 0;
+ d->bpmData = 0;
+}
+
+MP4::Mp4TagsProxy::~Mp4TagsProxy()
+{
+ delete d;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::titleData() const
+{
+ return d->titleData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::artistData() const
+{
+ return d->artistData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::albumData() const
+{
+ return d->albumData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::genreData() const
+{
+ return d->genreData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::yearData() const
+{
+ return d->yearData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::trknData() const
+{
+ return d->trknData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::commentData() const
+{
+ return d->commentData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::groupingData() const
+{
+ return d->groupingData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::composerData() const
+{
+ return d->composerData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::diskData() const
+{
+ return d->diskData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::bpmData() const
+{
+ return d->bpmData;
+}
+
+MP4::ITunesDataBox* MP4::Mp4TagsProxy::coverData() const
+{
+ return d->coverData;
+}
+
+void MP4::Mp4TagsProxy::registerBox( EBoxType boxtype, ITunesDataBox* databox )
+{
+ switch( boxtype )
+ {
+ case title:
+ d->titleData = databox;
+ break;
+ case artist:
+ d->artistData = databox;
+ break;
+ case album:
+ d->albumData = databox;
+ break;
+ case cover:
+ d->coverData = databox;
+ break;
+ case genre:
+ d->genreData = databox;
+ break;
+ case year:
+ d->yearData = databox;
+ break;
+ case trackno:
+ d->trknData = databox;
+ break;
+ case comment:
+ d->commentData = databox;
+ break;
+ case grouping:
+ d->groupingData = databox;
+ break;
+ case composer:
+ d->composerData = databox;
+ break;
+ case disk:
+ d->diskData = databox;
+ break;
+ case bpm:
+ d->bpmData = databox;
+ break;
+ }
+}
+
diff --git a/src/metadata/m4a/mp4tagsproxy.h b/src/metadata/m4a/mp4tagsproxy.h
new file mode 100644
index 0000000..c8bcbf0
--- /dev/null
+++ b/src/metadata/m4a/mp4tagsproxy.h
@@ -0,0 +1,99 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4TAGSPROXY_H
+#define MP4TAGSPROXY_H
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ // forward declaration(s)
+ class ITunesDataBox;
+ /*! proxy for mp4 itunes tag relevant boxes
+ *
+ * this class works as a proxy for the specific tag boxes
+ * in an mp4 itunes file. the boxes are mired in
+ * the mp4 file structure and stepping through all box layers
+ * is avoided by registration at the proxy object.
+ */
+ class Mp4TagsProxy
+ {
+ public:
+ /*! enum for all supported box types */
+ typedef enum
+ {
+ title = 0,
+ artist,
+ album,
+ cover,
+ genre,
+ year,
+ trackno,
+ comment,
+ grouping,
+ composer,
+ disk,
+ bpm
+ } EBoxType;
+
+ //! constructor
+ Mp4TagsProxy();
+ //! destructor
+ ~Mp4TagsProxy();
+
+ //! function to get the data box for the title
+ ITunesDataBox* titleData() const;
+ //! function to get the data box for the artist
+ ITunesDataBox* artistData() const;
+ //! function to get the data box for the album
+ ITunesDataBox* albumData() const;
+ //! function to get the data box for the genre
+ ITunesDataBox* genreData() const;
+ //! function to get the data box for the year
+ ITunesDataBox* yearData() const;
+ //! function to get the data box for the track number
+ ITunesDataBox* trknData() const;
+ //! function to get the data box for the comment
+ ITunesDataBox* commentData() const;
+ //! function to get the data box for the grouping
+ ITunesDataBox* groupingData() const;
+ //! function to get the data box for the composer
+ ITunesDataBox* composerData() const;
+ //! function to get the data box for the disk number
+ ITunesDataBox* diskData() const;
+ //! function to get the data box for the bpm
+ ITunesDataBox* bpmData() const;
+ //! function to get the data box for the cover
+ ITunesDataBox* coverData() const;
+
+ //! function to register a data box for a certain box type
+ void registerBox( EBoxType boxtype, ITunesDataBox* databox );
+
+ private:
+ class Mp4TagsProxyPrivate;
+ //! private data of tags proxy
+ Mp4TagsProxyPrivate* d;
+ }; // class Mp4TagsProxy
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4TAGSPROXY_H
diff --git a/src/metadata/m4a/mp4trakbox.cpp b/src/metadata/m4a/mp4trakbox.cpp
new file mode 100644
index 0000000..c573ec7
--- /dev/null
+++ b/src/metadata/m4a/mp4trakbox.cpp
@@ -0,0 +1,86 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4trakbox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4TrakBox::Mp4TrakBoxPrivate
+{
+public:
+ //! container for all boxes in trak box
+ TagLib::List<Mp4IsoBox*> trakBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+}; // class Mp4TrakBoxPrivate
+
+MP4::Mp4TrakBox::Mp4TrakBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4TrakBox::Mp4TrakBoxPrivate();
+}
+
+MP4::Mp4TrakBox::~Mp4TrakBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->trakBoxes.begin();
+ delIter != d->trakBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4TrakBox::parse()
+{
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " trak box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ curbox->parsebox();
+ d->trakBoxes.append( curbox );
+
+ // check for end of trak box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+ }
+}
diff --git a/src/metadata/m4a/mp4trakbox.h b/src/metadata/m4a/mp4trakbox.h
new file mode 100644
index 0000000..b362fa3
--- /dev/null
+++ b/src/metadata/m4a/mp4trakbox.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4TRAKBOX_H
+#define MP4TRAKBOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4TrakBox: public Mp4IsoBox
+ {
+ public:
+ Mp4TrakBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4TrakBox();
+
+ //! parse trak contents
+ void parse();
+
+ private:
+ class Mp4TrakBoxPrivate;
+ Mp4TrakBoxPrivate* d;
+ }; // Mp4TrakBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4TRAKBOX_H
diff --git a/src/metadata/m4a/mp4udtabox.cpp b/src/metadata/m4a/mp4udtabox.cpp
new file mode 100644
index 0000000..268d1c1
--- /dev/null
+++ b/src/metadata/m4a/mp4udtabox.cpp
@@ -0,0 +1,95 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "tlist.h"
+#include <iostream>
+#include "mp4udtabox.h"
+#include "boxfactory.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::Mp4UdtaBox::Mp4UdtaBoxPrivate
+{
+public:
+ //! container for all boxes in udta box
+ TagLib::List<Mp4IsoBox*> udtaBoxes;
+ //! a box factory for creating the appropriate boxes
+ MP4::BoxFactory boxfactory;
+}; // class Mp4UdtaBoxPrivate
+
+MP4::Mp4UdtaBox::Mp4UdtaBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset )
+ : Mp4IsoBox( file, fourcc, size, offset )
+{
+ d = new MP4::Mp4UdtaBox::Mp4UdtaBoxPrivate();
+}
+
+MP4::Mp4UdtaBox::~Mp4UdtaBox()
+{
+ TagLib::List<Mp4IsoBox*>::Iterator delIter;
+ for( delIter = d->udtaBoxes.begin();
+ delIter != d->udtaBoxes.end();
+ delIter++ )
+ {
+ delete *delIter;
+ }
+ delete d;
+}
+
+void MP4::Mp4UdtaBox::parse()
+{
+#if 0
+ std::cout << " parsing udta box" << std::endl;
+#endif
+ TagLib::MP4::File* mp4file = static_cast<MP4::File*>( file() );
+
+ TagLib::uint totalsize = 8;
+ // parse all contained boxes
+ TagLib::uint size;
+ MP4::Fourcc fourcc;
+
+#if 0
+ std::cout << " ";
+#endif
+ while( (mp4file->readSizeAndType( size, fourcc ) == true) )
+ {
+ totalsize += size;
+
+ // check for errors
+ if( totalsize > MP4::Mp4IsoBox::size() )
+ {
+ std::cerr << "Error in mp4 file " << mp4file->name() << " udta box contains bad box with name: " << fourcc.toString() << std::endl;
+ return;
+ }
+
+ // create the appropriate subclass and parse it
+ MP4::Mp4IsoBox* curbox = d->boxfactory.createInstance( mp4file, fourcc, size, mp4file->tell() );
+ curbox->parsebox();
+ d->udtaBoxes.append( curbox );
+
+ // check for end of udta box
+ if( totalsize == MP4::Mp4IsoBox::size() )
+ break;
+#if 0
+ std::cout << " ";
+#endif
+ }
+}
diff --git a/src/metadata/m4a/mp4udtabox.h b/src/metadata/m4a/mp4udtabox.h
new file mode 100644
index 0000000..4873e32
--- /dev/null
+++ b/src/metadata/m4a/mp4udtabox.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ copyright : (C) 2002, 2003, 2006 by Jochen Issing
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 MP4UDTABOX_H
+#define MP4UDTABOX_H
+
+#include "mp4isobox.h"
+#include "mp4fourcc.h"
+
+namespace TagLib
+{
+ namespace MP4
+ {
+ class Mp4UdtaBox: public Mp4IsoBox
+ {
+ public:
+ Mp4UdtaBox( TagLib::File* file, MP4::Fourcc fourcc, TagLib::uint size, long offset );
+ ~Mp4UdtaBox();
+
+ //! parse moov contents
+ void parse();
+
+ private:
+ class Mp4UdtaBoxPrivate;
+ Mp4UdtaBoxPrivate* d;
+ }; // Mp4UdtaBox
+
+ } // namespace MP4
+} // namespace TagLib
+
+#endif // MP4UDTABOX_H
diff --git a/src/metadata/m4a/taglib_mp4filetyperesolver.cpp b/src/metadata/m4a/taglib_mp4filetyperesolver.cpp
new file mode 100644
index 0000000..9566a93
--- /dev/null
+++ b/src/metadata/m4a/taglib_mp4filetyperesolver.cpp
@@ -0,0 +1,42 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "taglib_mp4filetyperesolver.h"
+#include "mp4file.h"
+
+#include <string.h>
+
+TagLib::File *MP4FileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+// fprintf(stderr, "mp4?: %s\n", fileName);
+ const char *ext = strrchr(fileName, '.');
+ if(ext && (!strcasecmp(ext, ".m4a")
+ || !strcasecmp(ext, ".m4b") || !strcasecmp(ext, ".m4p")
+ || !strcasecmp(ext, ".mp4")
+ || !strcasecmp(ext, ".m4v") || !strcasecmp(ext, ".mp4v")))
+ {
+ return new TagLib::MP4::File(fileName, readProperties, propertiesStyle);
+ }
+
+ return 0;
+}
diff --git a/src/metadata/m4a/taglib_mp4filetyperesolver.h b/src/metadata/m4a/taglib_mp4filetyperesolver.h
new file mode 100644
index 0000000..fbc3dd4
--- /dev/null
+++ b/src/metadata/m4a/taglib_mp4filetyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_MP4FILETYPERESOLVER_H
+#define TAGLIB_MP4FILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class MP4FileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/mp4/Makefile.am b/src/metadata/mp4/Makefile.am
new file mode 100644
index 0000000..e0779c6
--- /dev/null
+++ b/src/metadata/mp4/Makefile.am
@@ -0,0 +1,18 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(MP4V2_INCLUDES) $(taglib_includes)
+METASOURCES = AUTO
+libtagmp4_la_LDFLAGS = $(all_libraries) $(MP4V2_LIBS)
+noinst_LTLIBRARIES = libtagmp4.la
+
+libtagmp4_la_SOURCES = \
+ mp4properties.cpp \
+ mp4tag.cpp \
+ mp4file.cpp \
+ taglib_mp4filetyperesolver.cpp
+
+noinst_HEADERS = \
+ mp4properties.h \
+ mp4tag.h \
+ mp4file.h \
+ taglib_mp4filetyperesolver.h
diff --git a/src/metadata/optimfrog/Makefile.am b/src/metadata/optimfrog/Makefile.am
new file mode 100755
index 0000000..f2b7e6b
--- /dev/null
+++ b/src/metadata/optimfrog/Makefile.am
@@ -0,0 +1,6 @@
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagoptimfrog_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagoptimfrog.la
+noinst_HEADERS = taglib_optimfrogfiletyperesolver.h
+libtagoptimfrog_la_SOURCES = taglib_optimfrogfiletyperesolver.cpp
diff --git a/src/metadata/optimfrog/taglib_optimfrogfiletyperesolver.cpp b/src/metadata/optimfrog/taglib_optimfrogfiletyperesolver.cpp
new file mode 100755
index 0000000..13cde6a
--- /dev/null
+++ b/src/metadata/optimfrog/taglib_optimfrogfiletyperesolver.cpp
@@ -0,0 +1,21 @@
+// (c) 2006 Martin Aumueller <[email protected]>
+// modified 2006 Daniel Faust <[email protected]>
+// See COPYING file for licensing information
+
+#include "taglib_optimfrogfiletyperesolver.h"
+#include <taglib/mpcfile.h>
+
+#include <string.h>
+
+TagLib::File *OptimFrogFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".ofr"))
+ {
+ return new TagLib::MPC::File(fileName, readProperties, propertiesStyle);
+ }
+
+ return 0;
+}
diff --git a/src/metadata/optimfrog/taglib_optimfrogfiletyperesolver.h b/src/metadata/optimfrog/taglib_optimfrogfiletyperesolver.h
new file mode 100755
index 0000000..b277ed5
--- /dev/null
+++ b/src/metadata/optimfrog/taglib_optimfrogfiletyperesolver.h
@@ -0,0 +1,19 @@
+// (c) 2006 Martin Aumueller <[email protected]>
+// modified 2006 Daniel Faust <[email protected]>
+// See COPYING file for licensing information
+
+#ifndef TAGLIB_OPTIMFROGFILETYPERESOLVER_H
+#define TAGLIB_OPTIMFROGFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class OptimFrogFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/rmff/Makefile.am b/src/metadata/rmff/Makefile.am
new file mode 100644
index 0000000..b493e66
--- /dev/null
+++ b/src/metadata/rmff/Makefile.am
@@ -0,0 +1,15 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagrealmedia_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagrealmedia.la
+
+libtagrealmedia_la_SOURCES = rmff.cpp \
+ taglib_realmediafile.cpp \
+ taglib_realmediafiletyperesolver.cpp
+
+noinst_HEADERS = rmff.h \
+ taglib_realmediafile.h \
+ taglib_realmediafiletyperesolver.h
+
diff --git a/src/metadata/rmff/rmff.cpp b/src/metadata/rmff/rmff.cpp
new file mode 100644
index 0000000..da0edd5
--- /dev/null
+++ b/src/metadata/rmff/rmff.cpp
@@ -0,0 +1,998 @@
+/***************************************************************************
+ copyright : (C) 2005 by Paul Cifarelli
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 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 St, 5th fl, Boston, MA 02110-1301, *
+ * USA, or check http://www.fsf.org/about/contact.html *
+ * *
+ * Note that no RealNetworks code appears or is duplicated, copied, or *
+ + used as a template in this code. The code was written from scratch *
+ * using the reference documentation found at: *
+ * *
+ * https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/helixsdk.htm *
+ * *
+ ***************************************************************************/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <taglib.h>
+#include <id3v1tag.h>
+#include <id3v2tag.h>
+#include <tfile.h>
+#include <fileref.h>
+#include <iostream>
+
+#include <string.h>
+
+#include "rmff.h"
+
+#define UNPACK4(a, buf, i) memcpy((void *)&a, (void *) &buf[i], 4),i+=4,a=ntohl(a)
+#define UNPACK2(a, buf, i) memcpy((void *)&a, (void *) &buf[i], 2),i+=2,a=ntohs(a)
+
+using namespace TagLib;
+using namespace TagLib::RealMedia;
+
+RMFFile::RMFFile(const char *filename) : File(filename), m_id3tag(0)
+{
+ if (isOpen())
+ m_id3tag = new ID3v1::Tag(this, length() - 128);
+}
+
+RMFFile::~RMFFile()
+{
+ delete m_id3tag;
+}
+
+bool RMFFile::save()
+{
+ ByteVector bv = m_id3tag->render(); //TODO finish this
+ return false;
+}
+
+
+String RealMediaFF::title () const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->title() : "";
+}
+
+String RealMediaFF::artist () const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->artist() : "";
+}
+
+String RealMediaFF::album () const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->album() : "";
+}
+
+String RealMediaFF::comment() const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->comment() : "";
+}
+
+String RealMediaFF::genre() const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->genre() : "";
+}
+
+TagLib::uint RealMediaFF::year() const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->year() : 0;
+}
+
+TagLib::uint RealMediaFF::track() const
+{
+ return !m_err && m_id3v1tag ? m_id3v1tag->tag()->track() : 0;
+}
+
+// properties
+int RealMediaFF::length () const
+{
+ return m_readProperties && !m_err && m_props ? m_props->duration : 0;
+}
+
+int RealMediaFF::bitrate () const
+{
+ return m_readProperties && !m_err && m_props ? m_props->avg_bit_rate : 0;
+}
+
+int RealMediaFF::sampleRate () const
+{
+ return 0;
+}
+
+int RealMediaFF::channels () const
+{
+ return 0;
+}
+
+
+RealMediaFF::RealMediaFF(const char *file, bool readProperties, AudioProperties::ReadStyle /*propertiesStyle*/)
+: m_filename(0)
+, m_head(0)
+, m_tail(0)
+, m_err(0)
+, m_hdr(0)
+, m_props(0)
+, media_hdrs(0)
+, m_contenthdr(0)
+, m_md(0)
+, m_title(0)
+, m_author(0)
+, m_copyright(0)
+, m_comment(0)
+, m_id3v1tag(0)
+, m_flipYearInMetadataSection(0)
+, m_readProperties(readProperties)
+{
+ m_filename = strdup(file);
+
+ m_fd = open(m_filename, O_RDONLY);
+ if (m_fd < 0)
+ {
+ m_err = -1;
+ return;
+ }
+
+ // ok, for RM files, the properties are embedded, so we ignore propertiesStyle
+ if (m_readProperties)
+ {
+ init();
+
+ // and now for the really complicated stuff...
+ if (initMetadataSection())
+ std::cerr << "ERROR reading Metadata\n";
+ }
+
+ // now get the ID3v1 tag at the end of this file
+ m_id3v1tag = new RMFFile(m_filename);
+}
+
+
+RealMediaFF::RealMediaFF(RealMediaFF &src)
+: m_filename(0)
+, m_head(0)
+, m_tail(0)
+, m_err(0)
+, m_hdr(0)
+, m_props(0)
+, media_hdrs(0)
+, m_contenthdr(0)
+, m_md(0)
+, m_title(0)
+, m_author(0)
+, m_copyright(0)
+, m_comment(0)
+, m_id3v1tag(0)
+, m_flipYearInMetadataSection(0)
+, m_readProperties(src.m_readProperties)
+{
+ m_filename=strdup(src.m_filename);
+
+ m_fd = open(m_filename, O_RDONLY);
+ if (m_fd < 0)
+ {
+ m_err = -1;
+ return;
+ }
+
+ // ok, for RM files, the properties are embedded, so we ignore propertiesStyle
+ if (m_readProperties)
+ {
+ init();
+
+ // and now for the really complicated stuff...
+ if (initMetadataSection())
+ std::cerr << "ERROR reading Metadata\n";
+ }
+
+ // now get the ID3v1 tag at the end of this file
+ m_id3v1tag = new RMFFile(m_filename);
+}
+
+RealMediaFF::~RealMediaFF()
+{
+ ::free(m_filename);
+
+ Collectable *hdr = m_head, *next;
+ while (hdr)
+ {
+ next = hdr->fwd;
+ delete hdr;
+ hdr = next;
+ }
+
+ delete m_id3v1tag;
+ delete m_md;
+
+ close(m_fd);
+}
+
+bool RealMediaFF::isEmpty() const
+{
+ return m_id3v1tag->tag()->isEmpty();
+}
+
+
+void RealMediaFF::saveHeader(Collectable *hdr)
+{
+ hdr->fwd = 0;
+ if (!m_head)
+ m_head = m_tail = hdr;
+ else
+ {
+ m_tail->fwd = hdr;
+ m_tail = hdr;
+ }
+}
+
+
+int RealMediaFF::init()
+{
+ int nbytes;
+ unsigned char buf[65536];
+ UINT32 object_id;
+ UINT32 sz;
+ UINT32 consumed = 0;
+
+ off_t s;
+ if ( (s = lseek(m_fd, 0, SEEK_SET)) )
+ {
+ m_err = -1;
+ return m_err;
+ }
+
+ m_hdr = new File_Header_v0_v1;
+ nbytes = getChunk(buf, 65536, m_hdr->s.object_id, m_hdr->s.size, consumed);
+ if (nbytes < 0 || m_hdr->s.size != consumed || memcmp((void *)&m_hdr->s.object_id, ".RMF", 4))
+ {
+ //std::cerr << "SERIOUS ERROR - not likely a RealMedia file\n";
+ m_err = -1;
+ return m_err;
+ }
+ if (!getRealFileHeader(m_hdr, buf, m_hdr->s.object_id, m_hdr->s.size))
+ {
+ saveHeader(m_hdr);
+ consumed = 0;
+ nbytes = getChunk(buf, 65536, object_id, sz, consumed);
+ if (nbytes < 0 || sz != consumed)
+ {
+ m_err = -1;
+ return m_err;
+ }
+
+ while (!m_err && memcmp((void *)&object_id, "DATA", 4))
+ {
+ char oid[5];
+ memcpy((void *)oid, (void *)&object_id, 4);
+ oid[4] = 0;
+ if (!memcmp((void *)&object_id, "PROP", 4))
+ {
+ m_props = new RMProperties;
+ getRealPropertyHeader(m_props, buf, object_id, sz);
+ saveHeader(m_props);
+ }
+
+ if (!memcmp((void *)&object_id, "MDPR", 4))
+ {
+ media_hdrs = new MediaProperties;
+ getMediaPropHeader(media_hdrs, buf, object_id, sz);
+ saveHeader(media_hdrs);
+ }
+
+ if (!memcmp((void *)&object_id, "CONT", 4))
+ {
+ m_contenthdr = new ContentDescription;
+ getContentDescription(m_contenthdr, buf, object_id, sz);
+ saveHeader(m_contenthdr);
+ }
+
+ consumed = 0;
+ do
+ {
+ nbytes = getChunk(buf, 65536, object_id, sz, consumed);
+ } while ( !m_err && memcmp((void *)&object_id, "DATA", 4) && (consumed < sz) );
+ }
+ }
+
+ return 0;
+}
+
+int RealMediaFF::getHdr(unsigned char *buf, size_t sz, UINT32 &fourcc, UINT32 &csz)
+{
+ int nbytes = 0, i = 0;
+
+ if (sz < (size_t)RMFF_HDR_SIZE)
+ return 0;
+
+ if ( (nbytes = read(m_fd, (void *) buf, RMFF_HDR_SIZE)) != RMFF_HDR_SIZE )
+ {
+ m_err = -1;
+
+ return (nbytes);
+ }
+
+ memcpy((void *)&fourcc, buf, 4); i+=4;
+ UNPACK4(csz,buf,i);
+
+ return nbytes;
+}
+
+int RealMediaFF::getChunk(unsigned char *buf, size_t sz, UINT32 &fourcc, UINT32 &csz, UINT32 &alreadyconsumed)
+{
+ int nbytes = 0, i = 0, readamount;
+ csz = 0;
+
+ if (!alreadyconsumed)
+ {
+ if ( (nbytes = getHdr(buf, sz, fourcc, csz)) != RMFF_HDR_SIZE )
+ {
+ m_err = -1;
+ alreadyconsumed += nbytes > 0 ? nbytes : 0;
+ return nbytes;
+ }
+ alreadyconsumed += RMFF_HDR_SIZE;
+ readamount = csz - RMFF_HDR_SIZE;
+ i = RMFF_HDR_SIZE;
+ }
+ else
+ readamount = csz - alreadyconsumed;
+
+ if ( (nbytes = read(m_fd, (void *) &buf[i], readamount > (int)sz - i ? (int)sz - i : readamount )) != readamount )
+ {
+ if (nbytes < 0)
+ {
+ m_err = -1;
+ }
+ else
+ alreadyconsumed += nbytes;
+
+ return (nbytes<0 ? i : i + nbytes);
+ }
+
+ alreadyconsumed += nbytes;
+ return (csz);
+}
+
+int RealMediaFF::getRealFileHeader(File_Header_v0_v1 *hdr, const unsigned char *buf, UINT32 object_id, int sz)
+{
+ int i = 0;
+
+ // RealMedia header
+ hdr->s.object_id = object_id;
+ hdr->s.size = sz;
+
+ i = RMFF_HDR_SIZE;
+ UNPACK2(hdr->object_version, buf, i);
+
+ if ( !strncmp((const char *) &hdr->s.object_id, ".RMF", 4) &&
+ (hdr->object_version == 0 || hdr->object_version == 1) )
+ {
+ UNPACK4(hdr->file_version, buf, i);
+ UNPACK4(hdr->num_headers, buf, i);
+ }
+ return 0;
+}
+
+int RealMediaFF::getRealPropertyHeader(RMProperties *props, const unsigned char *buf, UINT32 object_id, int sz)
+{
+ int i = 0;
+
+ // Properties
+ props->s.object_id = object_id;
+ props->s.size = sz;
+
+ i = RMFF_HDR_SIZE;
+ UNPACK2(props->object_version, buf, i);
+
+ if ( !strncmp((const char *)&props->s.object_id,"PROP",4) && (props->object_version == 0) )
+ {
+ UNPACK4(props->max_bit_rate, buf, i);
+ UNPACK4(props->avg_bit_rate, buf, i);
+ UNPACK4(props->max_packet_size, buf, i);
+ UNPACK4(props->avg_packet_size, buf, i);
+ UNPACK4(props->num_packets, buf, i);
+ UNPACK4(props->duration, buf, i);
+ UNPACK4(props->preroll, buf, i);
+ UNPACK4(props->index_offset, buf, i);
+ UNPACK4(props->data_offset, buf, i);
+ UNPACK2(props->num_streams, buf, i);
+ UNPACK2(props->flags, buf, i);
+ }
+ return 0;
+}
+
+
+int RealMediaFF::getMediaPropHeader(MediaProperties *media_hdr, const unsigned char *buf, UINT32 object_id, int sz)
+{
+ int i = 0;
+
+ // Properties
+ media_hdr->s.object_id = object_id;
+ media_hdr->s.size = sz;
+
+ i = RMFF_HDR_SIZE;
+ UNPACK2(media_hdr->object_version, buf, i);
+
+ if ( !strncmp((const char *)&media_hdr->s.object_id, "MDPR", 4) && media_hdr->object_version == 0)
+ {
+ UNPACK2(media_hdr->stream_number, buf, i);
+ UNPACK4(media_hdr->max_bit_rate, buf, i);
+ UNPACK4(media_hdr->avg_bit_rate, buf, i);
+ UNPACK4(media_hdr->max_packet_size, buf, i);
+ UNPACK4(media_hdr->avg_packet_size, buf, i);
+ UNPACK4(media_hdr->start_time, buf, i);
+ UNPACK4(media_hdr->preroll, buf, i);
+ UNPACK4(media_hdr->duration, buf, i);
+ media_hdr->stream_name_size = buf[i]; i++;
+ memcpy(media_hdr->stream_name, &buf[i], media_hdr->stream_name_size);
+ media_hdr->stream_name[media_hdr->stream_name_size] = 0;
+ i += media_hdr->stream_name_size;
+ media_hdr->mime_type_size = buf[i]; i++;
+ memcpy(media_hdr->mime_type, &buf[i], media_hdr->mime_type_size);
+ i += media_hdr->mime_type_size;
+ UNPACK4(media_hdr->type_specific_len, buf, i);
+ if (media_hdr->type_specific_len)
+ {
+ media_hdr->type_specific_data = new UINT8[media_hdr->type_specific_len];
+ memcpy(media_hdr->type_specific_data, &buf[i], media_hdr->type_specific_len);
+
+ if (!strncmp((const char *)media_hdr->mime_type, "logical-fileinfo", 16))
+ {
+ media_hdr->lstr = new LogicalStream;
+ UNPACK4(media_hdr->lstr->size, buf, i);
+ UNPACK2(media_hdr->lstr->object_version, buf, i);
+ if (media_hdr->lstr->object_version == 0)
+ {
+ UNPACK2(media_hdr->lstr->num_physical_streams, buf, i);
+ if (media_hdr->lstr->num_physical_streams > 0)
+ {
+ media_hdr->lstr->physical_stream_numbers = new UINT16[ media_hdr->lstr->num_physical_streams ];
+ media_hdr->lstr->data_offsets = new UINT32[ media_hdr->lstr->num_physical_streams ];
+ for (int j=0; j<media_hdr->lstr->num_physical_streams; j++)
+ {
+ UNPACK2(media_hdr->lstr->physical_stream_numbers[j], buf, i);
+ }
+ for (int j=0; j<media_hdr->lstr->num_physical_streams; j++)
+ {
+ UNPACK4(media_hdr->lstr->data_offsets[j], buf, i);
+ }
+ }
+
+ UNPACK2(media_hdr->lstr->num_rules, buf, i);
+ if (media_hdr->lstr->num_rules > 0)
+ {
+ media_hdr->lstr->rule_to_physical_stream_number_map = new UINT16[ media_hdr->lstr->num_rules ];
+ for (int j=0; j<media_hdr->lstr->num_rules; j++)
+ {
+ UNPACK2(media_hdr->lstr->rule_to_physical_stream_number_map[j], buf, i);
+ }
+ }
+ UNPACK2(media_hdr->lstr->num_properties, buf, i);
+ if (media_hdr->lstr->num_properties > 0)
+ {
+ media_hdr->lstr->properties = new NameValueProperty[ media_hdr->lstr->num_properties ];
+ for (int j=0; j<media_hdr->lstr->num_properties; j++)
+ {
+ UNPACK4(media_hdr->lstr->properties[j].size, buf, i);
+ UNPACK2(media_hdr->lstr->properties[j].object_version, buf, i);
+ if (media_hdr->lstr->properties[j].object_version == 0)
+ {
+ media_hdr->lstr->properties[j].name_length = buf[i]; i++;
+ if (media_hdr->lstr->properties[j].name_length)
+ {
+ media_hdr->lstr->properties[j].name = new UINT8[ media_hdr->lstr->properties[j].name_length + 1];
+ memcpy((void *)media_hdr->lstr->properties[j].name, (void *)&buf[i],
+ media_hdr->lstr->properties[j].name_length);
+ media_hdr->lstr->properties[j].name[ media_hdr->lstr->properties[j].name_length ] = 0;
+ i+=media_hdr->lstr->properties[j].name_length;
+ }
+
+ UNPACK4(media_hdr->lstr->properties[j].type, buf, i);
+ UNPACK2(media_hdr->lstr->properties[j].value_length, buf, i);
+ if (media_hdr->lstr->properties[j].value_length)
+ {
+ media_hdr->lstr->properties[j].value_data = new UINT8[ media_hdr->lstr->properties[j].value_length + 1];
+ memcpy((void *)media_hdr->lstr->properties[j].value_data, (void *)&buf[i],
+ media_hdr->lstr->properties[j].value_length);
+ media_hdr->lstr->properties[j].value_data[ media_hdr->lstr->properties[j].value_length ] = 0;
+ i+=media_hdr->lstr->properties[j].value_length;
+ }
+ }
+ }
+ }
+ }
+ else
+ media_hdr->lstr = 0;
+ }
+ }
+ else
+ media_hdr->type_specific_data = 0;
+ }
+ else
+ {
+ m_err = -1;
+ return m_err;
+ }
+
+ return 0;
+}
+
+
+int RealMediaFF::getContentDescription(ContentDescription *cont, const unsigned char *buf, UINT32 object_id, int sz)
+{
+ int i = 0;
+
+ // Properties
+ cont->s.object_id = object_id;
+ cont->s.size = sz;
+
+ i = RMFF_HDR_SIZE;
+ UNPACK2(cont->object_version, buf, i);
+
+ if ( !strncmp((const char *)&cont->s.object_id, "CONT", 4) && cont->object_version == 0)
+ {
+ UNPACK2(cont->title_len, buf, i);
+ cont->title = new UINT8[cont->title_len + 1];
+ memcpy((void *)cont->title, (void *)&buf[i], cont->title_len); i+=cont->title_len;
+ m_title = (char *)cont->title;
+ m_title[cont->title_len] = 0;
+
+ UNPACK2(cont->author_len, buf, i);
+ cont->author = new UINT8[cont->author_len + 1];
+ memcpy((void *)cont->author, (void *)&buf[i], cont->author_len); i+=cont->author_len;
+ m_author = (char *)cont->author;
+ m_author[cont->author_len] = 0;
+
+ UNPACK2(cont->copyright_len, buf, i);
+ cont->copyright = new UINT8[cont->copyright_len + 1];
+ memcpy((void *)cont->copyright, (void *)&buf[i], cont->copyright_len); i+=cont->copyright_len;
+ m_copyright = (char *)cont->copyright;
+ m_copyright[cont->copyright_len] = 0;
+
+ UNPACK2(cont->comment_len, buf, i);
+ cont->comment = new UINT8[cont->comment_len + 1];
+ memcpy((void *)cont->comment, (void *)&buf[i], cont->comment_len); i+=cont->comment_len;
+ m_comment = (char *)cont->comment;
+ m_comment[cont->comment_len] = 0;
+ }
+ else
+ {
+ m_err = -1;
+ return m_err;
+ }
+
+ return 0;
+}
+
+
+int RealMediaFF::seekChunk(UINT32 object_id)
+{
+ if (!m_err)
+ {
+ off_t s, tot;
+ UINT32 oid = 0, sz = 0;
+ unsigned char buf[255];
+ int nbytes = 0;
+
+ if ( (s = lseek(m_fd, 0, SEEK_SET)) != 0)
+ return -1;
+
+ tot = 0;
+ while( (nbytes = getHdr(buf, 255, oid, sz)) == RMFF_HDR_SIZE && memcmp((void *)&oid, (void *)&object_id, 4) )
+ {
+ tot += sz;
+ if (sz > (unsigned) RMFF_HDR_SIZE)
+ {
+ if ( (s = lseek(m_fd, sz - RMFF_HDR_SIZE, SEEK_CUR)) != tot )
+ return -1;
+ }
+ else
+ return -1; // bail in this case, since the chuck sz includes the header size
+ }
+ if ( (s = lseek(m_fd, -RMFF_HDR_SIZE, SEEK_CUR)) != tot )
+ return -1;
+
+ return s;
+ }
+ return -1;
+}
+
+int RealMediaFF::getMDProperties(MDProperties *props, const unsigned char *buf)
+{
+ int i = 0;
+
+ int start = i;
+
+ UNPACK4(props->size, buf, i);
+ UNPACK4(props->type, buf, i);
+ UNPACK4(props->flags, buf, i);
+ UNPACK4(props->value_offset, buf, i);
+ UNPACK4(props->subproperties_offset, buf, i);
+ UNPACK4(props->num_subproperties, buf, i);
+ UNPACK4(props->name_length, buf, i);
+ props->name = new UINT8[ props->name_length + 1 ];
+ memcpy((void *)props->name, (void *)&buf[i], props->name_length);
+ props->name[ props->name_length ] = 0;
+ i+=props->name_length;
+
+ i = start + props->value_offset;
+ UNPACK4(props->value_length, buf, i);
+ props->value = new UINT8[ props->value_length ];
+ memcpy( (void *) props->value, (void *)&buf[i], props->value_length );
+
+ if ( (props->type == MPT_ULONG) || (props->type == MPT_FLAG && props->value_length == 4) )
+ {
+ // wOOt! the Year is a ULONG, and its stored little endian?! my guess is this is a bug in
+ // RealPlayer 10 for Windows (where I created my test files)
+ // This hack is intended to ensure that we at least interpret the Year properly.
+ if (!strcmp((char *)props->name, "Year"))
+ {
+ if ( *(unsigned long *)props->value > 65536 )
+ {
+ *(unsigned long *)(props->value) = ntohl(*(unsigned long *)(props->value));
+ m_flipYearInMetadataSection = true;
+ }
+ else
+ m_flipYearInMetadataSection = false;
+ }
+ else
+ *(unsigned long *)(props->value) = ntohl(*(unsigned long *)(props->value));
+ }
+
+ i += props->value_length;
+
+ i = start + props->subproperties_offset;
+ props->subproperties_list = new PropListEntry[ props->num_subproperties ];
+ for (int j=0; j<(int)props->num_subproperties; j++)
+ {
+ UNPACK4(props->subproperties_list[j].offset, buf, i);
+ UNPACK4(props->subproperties_list[j].num_props_for_name, buf, i);
+ }
+
+ props->subproperties = new MDProperties[ props->num_subproperties ];
+ for (int j=0; j<(int)props->num_subproperties; j++)
+ {
+ i = start + props->subproperties_list[j].offset;
+ getMDProperties(&props->subproperties[j], &buf[i]);
+ }
+
+ return 0;
+}
+
+int RealMediaFF::initMetadataSection()
+{
+ UINT32 object_id;
+ off_t s;
+ int nbytes;
+ unsigned char buf[65536];
+ UINT32 consumed;
+
+ memcpy((void *)&object_id, "RMMD", 4);
+ if ( (s = seekChunk(object_id)) < 0 )
+ {
+ m_err = -1;
+ return m_err;
+ }
+
+ m_md = new MetadataSection;
+ consumed = 0;
+ nbytes = getChunk(buf, 65536, m_md->s.object_id, m_md->s.size, consumed);
+ if (nbytes < 0 || m_md->s.size != consumed || memcmp((void *)&m_md->s.object_id, "RMMD", 4))
+ {
+ //std::cerr << "SERIOUS ERROR - not able to find the chunk I just seek'd to!\n";
+ m_err = -1;
+ return m_err;
+ }
+ // Properties
+ int i = RMFF_HDR_SIZE;
+ memcpy((void *)&m_md->object_id, (void *)&buf[i], 4); i+=4;
+ UNPACK4(m_md->object_version, buf, i);
+ if ( !strncmp((const char *)&m_md->s.object_id, "RMMD", 4) )
+ {
+ if (!getMDProperties(&m_md->properties, &buf[i]))
+ saveHeader(m_md);
+ }
+ else
+ {
+ m_err = -1;
+ return m_err;
+ }
+
+ return 0;
+}
+
+#ifdef TESTING
+
+void RealMediaFF::printRealFileHeader(std::ostream &os)
+{
+ char object_id[5];
+
+ if (m_hdr)
+ {
+ strncpy(object_id, (const char *)&m_hdr->s.object_id, 4);
+ object_id[4]=0;
+
+ os << "HDR object_id: " << object_id << std::endl;
+ os << "HDR size: " << m_hdr->s.size << std::endl;
+ os << "HDR object version: " << m_hdr->object_version << std::endl;
+ os << "HDR file version: " << m_hdr->file_version << std::endl;
+ os << "HDR num headers: " << m_hdr->num_headers << std::endl;
+ }
+}
+
+
+void RealMediaFF::printRealPropHeader(std::ostream &os)
+{
+ char object_id[5];
+
+ if (m_props)
+ {
+ strncpy(object_id, (const char *)&m_props->s.object_id, 4);
+ object_id[4]=0;
+
+ os << "PROPS object_id: " << object_id << std::endl;
+ os << "PROPS size: " << m_props->s.size << std::endl;
+ os << "PROPS object_version: " << m_props->object_version << std::endl;
+
+ os << "PROPS max_bit_rate: " << m_props->max_bit_rate << std::endl;
+ os << "PROPS avg_bit_rate: " << m_props->avg_bit_rate << std::endl;
+ os << "PROPS max_packet_size: " << m_props->max_packet_size << std::endl;
+ os << "PROPS avg_packet_size: " << m_props->avg_packet_size << std::endl;
+ os << "PROPS num_packets: " << m_props->num_packets << std::endl;
+ os << "PROPS duration: " << m_props->duration << std::endl;
+ os << "PROPS preroll: " << m_props->preroll << std::endl;
+ os << "PROPS index_offset: " << m_props->index_offset << std::endl;
+ os << "PROPS data_offset: " << m_props->data_offset << std::endl;
+ os << "PROPS num_streams: " << m_props->num_streams << std::endl;
+ os << "PROPS flags: " << m_props->flags << std::endl;
+ }
+}
+
+
+void RealMediaFF::printMediaPropHeaders(std::ostream &os)
+{
+ int i = 0;
+ char object_id[5];
+ MediaProperties *media_hdr = (MediaProperties *)m_head;
+
+ while (media_hdr)
+ {
+ strncpy(object_id, (const char *)&media_hdr->s.object_id, 4);
+ object_id[4]=0;
+
+ if (!strncmp(object_id, "MDPR", 4))
+ {
+ os << "MEDIA HDR" << i << " object_id: " << object_id << std::endl;
+ os << "MEDIA HDR" << i << " size: " << media_hdr->s.size << std::endl;
+ os << "MEDIA HDR" << i << " max_bit_rate: " << media_hdr->max_bit_rate << std::endl;
+ os << "MEDIA HDR" << i << " avg_bit_rate: " << media_hdr->avg_bit_rate << std::endl;
+ os << "MEDIA HDR" << i << " max_packet_size: " << media_hdr->max_packet_size << std::endl;
+ os << "MEDIA HDR" << i << " avg_packet_size: " << media_hdr->avg_packet_size << std::endl;
+ os << "MEDIA HDR" << i << " start_time: " << media_hdr->start_time << std::endl;
+ os << "MEDIA HDR" << i << " preroll: " << media_hdr->preroll << std::endl;
+ os << "MEDIA HDR" << i << " duration: " << media_hdr->duration << std::endl;
+ os << "MEDIA HDR" << i << " stream_name: " << media_hdr->stream_name << std::endl;
+ os << "MEDIA HDR" << i << " mime type: " << media_hdr->mime_type << std::endl;
+
+
+ if (media_hdr->lstr)
+ {
+ os << "MEDIA HDR" << i << " LOGSTR info size: " << media_hdr->lstr->size << std::endl;
+ os << "MEDIA HDR" << i << " LOGSTR info num_physical_streams: " << media_hdr->lstr->num_physical_streams << std::endl;
+ os << "MEDIA HDR" << i << " LOGSTR info num_rules: " << media_hdr->lstr->num_rules << std::endl;
+ os << "MEDIA HDR" << i << " LOGSTR info num_properties: " << media_hdr->lstr->num_properties << std::endl;
+ for (int j=0; media_hdr->lstr->properties && j<media_hdr->lstr->num_properties; j++)
+ {
+ if (media_hdr->lstr->properties[j].name)
+ os << "MEDIA HDR" << i << " LOGSTR info prop name: " << media_hdr->lstr->properties[j].name << std::endl;
+ os << "MEDIA HDR" << i << " LOGSTR info prop type: " << media_hdr->lstr->properties[j].type << std::endl;
+ os << "MEDIA HDR" << i << " LOGSTR info prop value_length: " << media_hdr->lstr->properties[j].value_length << std::endl;
+ if (media_hdr->lstr->properties[j].value_data)
+ {
+ if (media_hdr->lstr->properties[j].type == 0)
+ os << "MEDIA HDR" << i << " LOGSTR info prop value: " <<
+ *(unsigned long *)media_hdr->lstr->properties[j].value_data << std::endl;
+ else if (media_hdr->lstr->properties[j].type == 2)
+ os << "MEDIA HDR" << i << " LOGSTR info prop value: " << media_hdr->lstr->properties[j].value_data << std::endl;
+ else
+ os << "MEDIA HDR" << i << " LOGSTR info prop value: <binary>\n";
+ }
+ }
+ }
+
+ i++;
+ }
+ media_hdr = (MediaProperties *)media_hdr->fwd;
+ }
+}
+
+
+void RealMediaFF::printContentDescription(std::ostream &os)
+{
+ char object_id[5];
+
+ if (m_contenthdr)
+ {
+ strncpy(object_id, (const char *)&m_contenthdr->s.object_id, 4);
+ object_id[4]=0;
+
+ os << "CONT object_id: " << object_id << std::endl;
+ os << "CONT title(" << m_contenthdr->title_len << "):\t\t<" << m_contenthdr->title << ">" << std::endl;
+ os << "CONT author(" << m_contenthdr->author_len << "):\t\t<" << m_contenthdr->author << ">" << std::endl;
+ os << "CONT copyright(" << m_contenthdr->copyright_len << "):\t\t<" << m_contenthdr->copyright << ">" << std::endl;
+ os << "CONT comment(" << m_contenthdr->comment_len << "):\t\t<" << m_contenthdr->comment << ">" << std::endl;
+ }
+}
+
+void RealMediaFF::printID3v1Tag(std::ostream &os)
+{
+ if (m_id3v1tag)
+ {
+ os << "ID3 tag : " << ID3v1::Tag::fileIdentifier() << std::endl;
+ os << "ID3 title : " << m_id3v1tag->tag()->title() << std::endl;
+ os << "ID3 artist : " << m_id3v1tag->tag()->artist() << std::endl;
+ os << "ID3 album : " << m_id3v1tag->tag()->album() << std::endl;
+ os << "ID3 year : " << m_id3v1tag->tag()->year() << std::endl;
+ os << "ID3 comment : " << m_id3v1tag->tag()->comment() << std::endl;
+ os << "ID3 track : " << m_id3v1tag->tag()->track() << std::endl;
+ os << "ID3 genre : " << m_id3v1tag->tag()->genre() << std::endl;
+ }
+}
+
+
+void RealMediaFF::printMDProperties(std::ostream &os, char *nam, MDProperties *props)
+{
+ char name[8192];
+
+ strcpy(name, nam);
+ os << "MDP subproperties for: " << name << std::endl;
+
+ os << "MD properties.size: " << props->size << std::endl;
+ os << "MD properties.type: " << props->type << std::endl;
+ os << "MD properties.flags: " << props->flags << std::endl;
+ os << "MD properties.value_offset: " << props->value_offset << std::endl;
+ os << "MD properties.subproperties_offset: " << props->subproperties_offset << std::endl;
+ os << "MD properties.num_subproperties: " << props->num_subproperties << std::endl;
+ os << "MD properties.name_length: " << props->name_length << std::endl;
+ os << "MD properties.name: " << (char *)props->name << std::endl;
+
+ os << "MD properties.value_length: " << props->value_length << std::endl;
+
+ switch (props->type)
+ {
+ case MPT_TEXT:
+ case MPT_TEXTLIST:
+ case MPT_URL:
+ case MPT_DATE:
+ case MPT_FILENAME:
+ os << "MD properties.value: " << (char *)props->value << std::endl;
+ break;
+ case MPT_FLAG:
+ if (props->value_length == 4)
+ os << "MD properties.value: " << *(unsigned long *)props->value << std::endl;
+ else
+ os << "MD properties.value: " << *props->value << std::endl;
+ break;
+ case MPT_ULONG:
+ os << "MD properties.value: " << *(unsigned long *)props->value << std::endl;
+ break;
+ case MPT_BINARY:
+ os << "MD properties.value: <binary>" << std::endl;
+ break;
+ case MPT_GROUPING:
+ os << "MD properties.value: <grouping>" << std::endl;
+ break;
+ case MPT_REFERENCE:
+ os << "MD properties.value: <reference>" << std::endl;
+ break;
+ }
+
+ if (props->num_subproperties)
+ {
+ strcat(name, (char *)props->name);
+ strcat(name, "/");
+ }
+ for (int j=0; j<props->num_subproperties; j++)
+ {
+ os << "MD properties.sub_properties_list[" << j << "].offset: " <<
+ props->subproperties_list[j].offset << std::endl;
+ os << "MD properties.sub_properties_list[" << j << "].num_props_for_name: " <<
+ props->subproperties_list[j].num_props_for_name << std::endl;
+
+ os << std::endl;
+
+ printMDProperties(os, name, &props->subproperties[j]);
+ }
+}
+
+
+void RealMediaFF::printMetadataSection(std::ostream &os)
+{
+ char name[8192];
+ char oid[5];
+
+ memcpy((void *)oid, (void *)&m_md->s.object_id, 4);
+ oid[4] = 0;
+
+ os << "MetadataSection: ";
+ os << "MS object_id: " << oid << std::endl;
+ os << "MS SIZE: " << m_md->s.size << std::endl;
+ os << "MD object_id: " << (char *)&m_md->object_id << std::endl;
+ os << "MD object_version: " << m_md->object_version << std::endl;
+ os << std::endl;
+
+ strcpy(name, "");
+ printMDProperties(os, name, &m_md->properties);
+}
+
+
+std::ostream &RealMediaFF::operator<<(std::ostream &os)
+{
+ if (m_readProperties)
+ {
+ printRealFileHeader(os);
+ printRealPropHeader(os);
+ printMediaPropHeaders(os);
+ printContentDescription(os);
+ printMetadataSection(os);
+ }
+ printID3v1Tag(os);
+
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, RealMediaFF &rmff)
+{
+ rmff.operator<<(os);
+
+ return os;
+}
+
+
+int main(int argc, char *argv[])
+{
+ char *m_filen;
+
+ if (argc > 1)
+ m_filen = argv[1];
+ else
+ m_filen = "./Drown.ra";
+
+ RealMediaFF rmff(m_filen);
+
+ if (!rmff.err())
+ std::cout << rmff;
+
+ /*
+ UINT32 oid = 0;
+ memcpy( (void *)&oid, (void *) ".RMF", 4);
+ off_t pos = rmff.seekChunk(oid);
+ std::cout << "POS=" << pos << std::endl;
+
+ memcpy( (void *)&oid, (void *) "MDPR", 4);
+ pos = rmff.seekChunk(oid);
+ std::cout << "POS=" << pos << std::endl;
+
+ memcpy( (void *)&oid, (void *) "RMMD", 4);
+ pos = rmff.seekChunk(oid);
+ std::cout << "POS=" << pos << std::endl;
+ */
+}
+#endif
diff --git a/src/metadata/rmff/rmff.h b/src/metadata/rmff/rmff.h
new file mode 100644
index 0000000..326b8ae
--- /dev/null
+++ b/src/metadata/rmff/rmff.h
@@ -0,0 +1,348 @@
+/***************************************************************************
+ copyright : (C) 2005 by Paul Cifarelli
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 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 St, 5th fl, Boston, MA 02110-1301, *
+ * USA, or check http://www.fsf.org/about/contact.html *
+ * *
+ * Note that no RealNetworks code appears or is duplicated, copied, or *
+ + used as a template in this code. The code was written from scratch *
+ * using the reference documentation found at: *
+ * *
+ * https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/helixsdk.htm *
+ * *
+ ***************************************************************************/
+#ifndef _RMFF_H_INCLUDED_
+#define _RMFF_H_INCLUDED_
+
+#include <config.h>
+
+namespace TagLib
+{
+ namespace RealMedia
+ {
+#if SIZEOF_LONG == 4
+ typedef unsigned long UINT32;
+#elif SIZEOF_INT == 4
+ typedef unsigned int UINT32;
+#else
+#error At least 1 builtin type needs to be 4 bytes!!
+#endif
+ typedef unsigned short UINT16;
+ typedef unsigned char UINT8;
+
+ static const int RMFF_HDR_SIZE = 8; // packed hdr size
+
+ // some assumptions on these 2 enum defs, based solely on the order they are listed on the website
+ enum PROPERTY_TYPES
+ {
+ MPT_TEXT = 1, // The value is string data.
+ MPT_TEXTLIST, // The value is a separated list of strings,
+ // separator specified as sub-property/type descriptor.
+ MPT_FLAG, // The value is a boolean flag-either 1 byte or 4 bytes, check size value.
+ MPT_ULONG, // The value is a four-byte integer.
+ MPT_BINARY, // The value is a byte stream.
+ MPT_URL, // The value is string data.
+ MPT_DATE, // The value is a string representation of the date in the form:
+ // YYYYmmDDHHMMSS (m = month, M = minutes).
+ MPT_FILENAME, // The value is string data.
+ MPT_GROUPING, // This property has subproperties, but its own value is empty.
+ MPT_REFERENCE // The value is a large buffer of data, use sub-properties/type
+ // descriptors to identify mime-type.
+ };
+
+ enum PROPERTY_FLAGS
+ {
+ MPT_READONLY = 1, // Read only, cannot be modified.
+ MPT_PRIVATE = 2, // Private, do not expose to users.
+ MPT_TYPE_DESCRIPTOR = 4 // Type descriptor used to further define type of value.
+ };
+
+ struct Collectable
+ {
+ Collectable() : fwd(0) {}
+ virtual ~Collectable() {}
+ Collectable *fwd;
+ };
+
+ struct File_Header_Start
+ {
+ UINT32 object_id;
+ UINT32 size;
+ };
+
+ struct File_Header_v0_v1 : public Collectable
+ {
+ File_Header_Start s;
+ UINT16 object_version;
+
+ UINT32 file_version;
+ UINT32 num_headers;
+ };
+
+ struct RMProperties : public Collectable
+ {
+ File_Header_Start s;
+ UINT16 object_version;
+
+ UINT32 max_bit_rate;
+ UINT32 avg_bit_rate;
+ UINT32 max_packet_size;
+ UINT32 avg_packet_size;
+ UINT32 num_packets;
+ UINT32 duration;
+ UINT32 preroll;
+ UINT32 index_offset;
+ UINT32 data_offset;
+ UINT16 num_streams;
+ UINT16 flags;
+ };
+
+
+ struct NameValueProperty
+ {
+ NameValueProperty() : name(0), value_data(0) {}
+ virtual ~NameValueProperty() { delete [] name; delete [] value_data; }
+
+ UINT32 size;
+ UINT16 object_version;
+
+ UINT8 name_length;
+ UINT8 *name;
+ UINT32 type;
+ UINT16 value_length;
+ UINT8 *value_data;
+ };
+
+
+ struct LogicalStream
+ {
+ LogicalStream() : physical_stream_numbers(0), data_offsets(0), rule_to_physical_stream_number_map(0), properties(0) {}
+ virtual ~LogicalStream()
+ { delete [] physical_stream_numbers; delete [] data_offsets;
+ delete [] rule_to_physical_stream_number_map; delete [] properties; }
+
+ UINT32 size;
+ UINT16 object_version;
+
+ UINT16 num_physical_streams;
+ UINT16 *physical_stream_numbers;
+ UINT32 *data_offsets;
+ UINT16 num_rules;
+ UINT16 *rule_to_physical_stream_number_map;
+ UINT16 num_properties;
+ NameValueProperty *properties;
+ };
+
+ struct MediaProperties : public Collectable
+ {
+ MediaProperties() : type_specific_data(0), lstr(0) {}
+ virtual ~MediaProperties() { delete lstr; delete [] type_specific_data; }
+
+ File_Header_Start s;
+ UINT16 object_version;
+
+ UINT16 stream_number;
+ UINT32 max_bit_rate;
+ UINT32 avg_bit_rate;
+ UINT32 max_packet_size;
+ UINT32 avg_packet_size;
+ UINT32 start_time;
+ UINT32 preroll;
+ UINT32 duration;
+ UINT8 stream_name_size;
+ UINT8 stream_name[256];
+ UINT8 mime_type_size;
+ UINT8 mime_type[256];
+ UINT32 type_specific_len;
+ UINT8 *type_specific_data;
+
+ LogicalStream *lstr; // only one of these
+ };
+
+
+ struct ContentDescription : public Collectable
+ {
+ ContentDescription() : title(0), author(0), copyright(0), comment(0) {}
+ virtual ~ContentDescription() { delete [] title; delete [] author; delete [] copyright; delete [] comment; }
+
+ File_Header_Start s;
+ UINT16 object_version;
+
+ UINT16 title_len;
+ UINT8 *title;
+ UINT16 author_len;
+ UINT8 *author;
+ UINT16 copyright_len;
+ UINT8 *copyright;
+ UINT16 comment_len;
+ UINT8 *comment;
+ };
+
+
+ struct PropListEntry
+ {
+ UINT32 offset;
+ UINT32 num_props_for_name;
+ };
+
+ struct MDProperties
+ {
+ MDProperties() : name(0), value(0), subproperties(0) {}
+ virtual ~MDProperties()
+ { delete [] name; delete [] value; delete [] subproperties_list; delete [] subproperties; }
+
+ UINT32 size;
+ UINT32 type;
+ UINT32 flags;
+ UINT32 value_offset;
+ UINT32 subproperties_offset;
+ UINT32 num_subproperties;
+ UINT32 name_length;
+ UINT8 *name;
+ UINT32 value_length;
+ UINT8 *value;
+ PropListEntry *subproperties_list; // num_subproperties
+ MDProperties *subproperties; // num_subproperties
+ };
+
+ struct MetadataSection : public Collectable
+ {
+ File_Header_Start s;
+
+ UINT32 object_id;
+ UINT32 object_version;
+
+ // this is the 1 "unnamed root property"
+ MDProperties properties;
+ };
+
+ class Tag;
+ class File;
+
+ // RealMedia File Format contains a normal ID3v1 Tag at the end of the file
+ // no sense reinventing the wheel, so this class is just so we can use TagLib
+ // to manage it
+ class RMFFile : public TagLib::File
+ {
+ public:
+ RMFFile(const char *filename);
+ virtual ~RMFFile();
+ bool save();
+ TagLib::Tag *tag() const { return m_id3tag; }
+ TagLib::AudioProperties *audioProperties() const { return 0; }
+
+ private:
+ TagLib::ID3v1::Tag *m_id3tag;
+ };
+
+ class TagLib::AudioProperties;
+
+ class RealMediaFF
+ {
+ public:
+ RealMediaFF(const char *file, bool readProperties = true,
+ TagLib::AudioProperties::ReadStyle propertiesStyle = TagLib::AudioProperties::Average);
+ RealMediaFF(RealMediaFF &src);
+ ~RealMediaFF();
+
+ int err() const { return m_err; }
+ bool isEmpty() const;
+
+ // tag
+ TagLib::String title () const;
+ TagLib::String artist () const;
+ TagLib::String album () const;
+ TagLib::String comment () const;
+ TagLib::String genre () const;
+ TagLib::uint year () const;
+ TagLib::uint track () const;
+ // TODO write support
+ //void setTitle (const String &s);
+ //void setArtist (const String &s);
+ //void setAlbum (const String &s);
+ //void setComment (const String &s);
+ //void setGenre (const String &s);
+ //void setYear (uint i);
+ //void setTrack (uint i);
+
+ // properties
+ int length () const;
+ int bitrate () const;
+ int sampleRate () const;
+ int channels () const;
+
+#ifdef TESTING
+ std::ostream &operator<<(std::ostream &os);
+#endif
+
+ private:
+ RealMediaFF();
+ char *m_filename;
+ Collectable *m_head;
+ Collectable *m_tail;
+ int m_fd;
+ int m_err;
+
+ File_Header_v0_v1 *m_hdr;
+ RMProperties *m_props;
+ MediaProperties *media_hdrs;
+ ContentDescription *m_contenthdr;
+ MetadataSection *m_md;
+
+ char *m_title;
+ char *m_author;
+ char *m_copyright;
+ char *m_comment;
+
+ RMFFile *m_id3v1tag;
+
+ bool m_flipYearInMetadataSection;
+ bool m_readProperties;
+
+ int init();
+ int initMetadataSection();
+ void saveHeader(Collectable *hdr);
+ int seekChunk(UINT32 object_id);
+
+ int getHdr(unsigned char *buf, size_t sz, UINT32 &fourcc, UINT32 &csz);
+ int getChunk(unsigned char *buf, size_t sz, UINT32 &fourcc, UINT32 &csz, UINT32 &consumed);
+ int getRealFileHeader(File_Header_v0_v1 *hdr, const unsigned char *buf, UINT32 object_id, int sz);
+ int getRealPropertyHeader(RMProperties *props, const unsigned char *buf, UINT32 object_id, int sz);
+ int getMediaPropHeader(MediaProperties *mh, const unsigned char *buf, UINT32 object_id, int sz);
+ int getContentDescription(ContentDescription *cont, const unsigned char *buf, UINT32 object_id, int sz);
+ int getMDProperties(MDProperties *md, const unsigned char *buf);
+
+#ifdef TESTING
+ void printRealFileHeader(std::ostream &os);
+ void printRealPropHeader(std::ostream &os);
+ void printMediaPropHeaders(std::ostream &os);
+ void printContentDescription(std::ostream &os);
+ void printID3v1Tag(std::ostream &os);
+ void printMetadataSection(std::ostream &os);
+ void printMDProperties(std::ostream &os, char *name, MDProperties *props);
+#endif
+ };
+
+ } // namespace RealMedia
+} // namespace TagLib
+
+#ifdef TESTING
+std::ostream &operator<<(std::ostream &os, TagLib::RealMedia::RealMediaFF &rmff);
+#endif
+
+#endif
diff --git a/src/metadata/rmff/taglib_realmediafile.cpp b/src/metadata/rmff/taglib_realmediafile.cpp
new file mode 100644
index 0000000..b28c846
--- /dev/null
+++ b/src/metadata/rmff/taglib_realmediafile.cpp
@@ -0,0 +1,213 @@
+/***************************************************************************
+ copyright : (C) 2005 by Paul Cifarelli
+
+ copyright : (C) 2005 by Lukas Lalinsky
+ (portions)
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2 or higher as published by the Free Software Foundation. *
+ * *
+ * 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 St, 5th fl, Boston, MA 02110-1301, *
+ * USA, or check http://www.fsf.org/about/contact.html *
+ ***************************************************************************/
+
+#include <tfile.h>
+#include <audioproperties.h>
+#include <tstring.h>
+#include <id3v1tag.h>
+#include "rmff.h"
+#include "taglib_realmediafile.h"
+
+using namespace TagLib;
+using namespace TagLib::RealMedia;
+
+
+RealMedia::Tag::Tag(RealMediaFF *rmff, bool allocnew) : m_rmff(rmff), m_owner(allocnew)
+{
+ if (m_owner)
+ m_rmff = new RealMediaFF(*rmff);
+}
+
+RealMedia::Tag::~Tag ()
+{
+ if (m_owner)
+ delete m_rmff;
+}
+
+String RealMedia::Tag::title () const
+{
+ return m_rmff->title();
+}
+
+String RealMedia::Tag::artist () const
+{
+ return m_rmff->artist();
+}
+
+String RealMedia::Tag::album () const
+{
+ return m_rmff->album();
+}
+
+String RealMedia::Tag::comment () const
+{
+ return m_rmff->comment();
+}
+
+String RealMedia::Tag::genre () const
+{
+ return m_rmff->genre();
+}
+
+TagLib::uint RealMedia::Tag::year () const
+{
+ return m_rmff->year();
+}
+
+TagLib::uint RealMedia::Tag::track () const
+{
+ return m_rmff->track();
+}
+
+void RealMedia::Tag::setTitle (const String &)
+{
+// TODO: write support
+}
+
+void RealMedia::Tag::setArtist (const String &)
+{
+// TODO: write support
+}
+
+void RealMedia::Tag::setAlbum (const String &)
+{
+// TODO: write support
+}
+
+void RealMedia::Tag::setComment (const String &)
+{
+// TODO: write support
+}
+
+void RealMedia::Tag::setGenre (const String &)
+{
+// TODO: write support
+}
+
+void RealMedia::Tag::setYear (uint)
+{
+// TODO: write support
+}
+
+void RealMedia::Tag::setTrack (uint)
+{
+// TODO: write support
+}
+
+bool RealMedia::Tag::isEmpty() const
+{
+ return TagLib::Tag::isEmpty() && m_rmff->isEmpty();
+}
+
+void RealMedia::Tag::duplicate(const Tag *source, Tag *target, bool overwrite)
+{
+ TagLib::Tag::duplicate(source, target, overwrite);
+ if (overwrite)
+ {
+ if (target->m_owner)
+ {
+ delete target->m_rmff;
+ target->m_rmff = new RealMediaFF(*source->m_rmff);
+ }
+ else
+ target->m_rmff = source->m_rmff;
+ }
+ else
+ {
+ if (target->isEmpty())
+ if (target->m_owner)
+ {
+ delete target->m_rmff;
+ target->m_rmff = new RealMediaFF(*source->m_rmff);
+ }
+ else
+ target->m_rmff = source->m_rmff;
+ }
+}
+
+
+
+int RealMedia::Properties::length () const
+{
+ return (m_rmff->length() / 1000);
+}
+
+int RealMedia::Properties::bitrate () const
+{
+ return (m_rmff->bitrate() / 1000);
+}
+
+int RealMedia::Properties::sampleRate () const
+{
+ return m_rmff->sampleRate();
+}
+
+int RealMedia::Properties::channels () const
+{
+ return m_rmff->channels();
+}
+
+
+RealMedia::File::File(const char *file, bool readProperties, Properties::ReadStyle propertiesStyle)
+ : TagLib::File(file), m_rmfile(0), m_tag(0), m_props(0)
+{
+ m_rmfile = new RealMediaFF(file, readProperties, propertiesStyle);
+ m_tag = new RealMedia::Tag(m_rmfile);
+ m_props = new RealMedia::Properties(m_rmfile);
+}
+
+RealMedia::File::~File()
+{
+ delete m_props;
+ delete m_tag;
+ delete m_rmfile;
+}
+
+TagLib::Tag *RealMedia::File::tag() const
+{
+ return m_tag;
+}
+
+RealMedia::Tag *RealMedia::File::RealMediaTag() const
+{
+ return m_tag;
+}
+
+RealMedia::Properties *RealMedia::File::audioProperties() const
+{
+ return m_props; // m_rmfile->properties;
+}
+
+
+
+
+
diff --git a/src/metadata/rmff/taglib_realmediafile.h b/src/metadata/rmff/taglib_realmediafile.h
new file mode 100644
index 0000000..0f0ca58
--- /dev/null
+++ b/src/metadata/rmff/taglib_realmediafile.h
@@ -0,0 +1,133 @@
+/***************************************************************************
+ copyright : (C) 2005 by Paul Cifarelli
+
+ copyright : (C) 2005 by Lukas Lalinsky
+ (portions)
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2 or higher as published by the Free Software Foundation. *
+ * *
+ * 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 St, 5th fl, Boston, MA 02110-1301, *
+ * USA, or check http://www.fsf.org/about/contact.html *
+ ***************************************************************************/
+#ifndef _TAGLIB_REALMEDIAFILE_H_
+#define _TAGLIB_REALMEDIAFILE_H_
+
+#include <tfile.h>
+#include <audioproperties.h>
+#include <tag.h>
+
+#include <iostream>
+
+class RealMediaFF;
+namespace TagLib {
+
+ namespace RealMedia {
+
+ class Tag : public TagLib::Tag
+ {
+ public:
+ Tag(RealMediaFF *rmff, bool allocnew = false);
+ virtual ~Tag ();
+ virtual String title () const;
+ virtual String artist () const;
+ virtual String album () const;
+ virtual String comment () const;
+ virtual String genre () const;
+ virtual uint year () const;
+ virtual uint track () const;
+ virtual void setTitle (const String &s);
+ virtual void setArtist (const String &s);
+ virtual void setAlbum (const String &s);
+ virtual void setComment (const String &s);
+ virtual void setGenre (const String &s);
+ virtual void setYear (uint i);
+ virtual void setTrack (uint i);
+
+ bool isEmpty() const;
+ void duplicate(const Tag *source, Tag *target, bool overwrite);
+
+ private:
+ Tag();
+ RealMediaFF *m_rmff;
+ bool m_owner;
+ };
+
+
+ class Properties : public TagLib::AudioProperties
+ {
+ public:
+ Properties(RealMediaFF *rmff) : TagLib::AudioProperties(Average), m_rmff(rmff) {}
+ virtual ~Properties() {} // you don't own rmff
+ virtual int length () const;
+ virtual int bitrate () const;
+ virtual int sampleRate () const;
+ virtual int channels () const;
+
+ private:
+ Properties();
+ RealMediaFF *m_rmff;
+ };
+
+ class File : public TagLib::File
+ {
+ public:
+
+ File(const char *file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ virtual ~File();
+
+ /*
+ * Returns the TagLib::Tag for this file.
+ */
+ virtual TagLib::Tag *tag() const;
+
+ /*
+ * Returns the RealMedia::RealMediaTag for this file.
+ */
+ virtual Tag *RealMediaTag() const;
+
+ /*
+ * Returns the RealMedia::Properties for this file.
+ */
+ virtual Properties *audioProperties() const;
+
+
+ /*
+ * Save the file.
+ *
+ * This returns true if the save was successful.
+ */
+ virtual bool save() { return false; } // for now
+
+ private:
+
+ RealMediaFF *m_rmfile;
+ Tag *m_tag;
+ Properties *m_props;
+ };
+
+ }
+
+}
+
+#endif
diff --git a/src/metadata/rmff/taglib_realmediafiletyperesolver.cpp b/src/metadata/rmff/taglib_realmediafiletyperesolver.cpp
new file mode 100644
index 0000000..a8660a6
--- /dev/null
+++ b/src/metadata/rmff/taglib_realmediafiletyperesolver.cpp
@@ -0,0 +1,53 @@
+/***************************************************************************
+ copyright : (C) 2005 by Paul Cifarelli
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 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 St, 5th fl, Boston, MA 02110-1301, *
+ * USA, or check http://www.fsf.org/about/contact.html *
+ * *
+ ***************************************************************************/
+
+#include <tfile.h>
+#include <audioproperties.h>
+#include <id3v1tag.h>
+#include "taglib_realmediafiletyperesolver.h"
+#include "taglib_realmediafile.h"
+#include "rmff.h"
+
+#include <string.h>
+
+TagLib::File *RealMediaFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && (!strcasecmp(ext, ".ra") || !strcasecmp(ext, ".rv") || !strcasecmp(ext, ".rm") ||
+ !strcasecmp(ext, ".rmj") || !strcasecmp(ext, ".rmvb") ))
+ {
+ TagLib::RealMedia::File *f = new TagLib::RealMedia::File(fileName, readProperties, propertiesStyle);
+ if(f->isValid())
+ return f;
+ else
+ {
+ delete f;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/metadata/rmff/taglib_realmediafiletyperesolver.h b/src/metadata/rmff/taglib_realmediafiletyperesolver.h
new file mode 100644
index 0000000..b292e10
--- /dev/null
+++ b/src/metadata/rmff/taglib_realmediafiletyperesolver.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ copyright : (C) 2005 by Paul Cifarelli
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 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 St, 5th fl, Boston, MA 02110-1301, *
+ * USA, or check http://www.fsf.org/about/contact.html *
+ * *
+ ***************************************************************************/
+#ifndef _TAGLIB_REALMEDIAFILETYPERESOLVER_H_
+#define _TAGLIB_REALMEDIAFILETYPERESOLVER_H_
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class RealMediaFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/speex/Makefile.am b/src/metadata/speex/Makefile.am
new file mode 100644
index 0000000..6bf01aa
--- /dev/null
+++ b/src/metadata/speex/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagspeex_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagspeex.la
+
+libtagspeex_la_SOURCES = \
+ speexfile.cpp \
+ speexproperties.cpp \
+ taglib_speexfiletyperesolver.cpp
+
+noinst_HEADERS = speexfile.h \
+ speexproperties.h \
+ taglib_speexfiletyperesolver.h
+
diff --git a/src/metadata/speex/speexfile.cpp b/src/metadata/speex/speexfile.cpp
new file mode 100644
index 0000000..eccaded
--- /dev/null
+++ b/src/metadata/speex/speexfile.cpp
@@ -0,0 +1,111 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2002 by Scott Wheeler
+ (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <bitset>
+
+#include <tstring.h>
+#if 0
+#include <tdebug.h>
+#endif
+
+#include "speexfile.h"
+
+using namespace TagLib;
+
+class Speex::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ comment(0),
+ properties(0) {}
+
+ ~FilePrivate()
+ {
+ delete comment;
+ delete properties;
+ }
+
+ Ogg::XiphComment *comment;
+ Properties *properties;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Speex::File::File(const char *file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+Speex::File::~File()
+{
+ delete d;
+}
+
+Ogg::XiphComment *Speex::File::tag() const
+{
+ return d->comment;
+}
+
+Speex::Properties *Speex::File::audioProperties() const
+{
+ return d->properties;
+}
+
+bool Speex::File::save()
+{
+ if(!d->comment)
+ d->comment = new Ogg::XiphComment;
+
+ setPacket(1, d->comment->render());
+
+ return Ogg::File::save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Speex::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ ByteVector speexHeaderData = packet(0);
+
+ if(!speexHeaderData.startsWith("Speex ")) {
+#if 0
+ debug("Speex::File::read() -- invalid Speex identification header");
+#endif
+ return;
+ }
+
+ ByteVector commentHeaderData = packet(1);
+
+ d->comment = new Ogg::XiphComment(commentHeaderData);
+
+ if(readProperties)
+ d->properties = new Properties(this, propertiesStyle);
+}
diff --git a/src/metadata/speex/speexfile.h b/src/metadata/speex/speexfile.h
new file mode 100644
index 0000000..d300ac3
--- /dev/null
+++ b/src/metadata/speex/speexfile.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2002 by Scott Wheeler
+ (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_SPEEXFILE_H
+#define TAGLIB_SPEEXFILE_H
+
+#include <oggfile.h>
+#include <xiphcomment.h>
+
+#include "speexproperties.h"
+
+namespace TagLib {
+
+ //! A namespace containing classes for Speex metadata
+
+ namespace Speex {
+
+ //! An implementation of Ogg::File with Speex specific methods
+
+ /*!
+ * This is the central class in the Ogg Speex metadata processing collection
+ * of classes. It's built upon Ogg::File which handles processing of the Ogg
+ * logical bitstream and breaking it down into pages which are handled by
+ * the codec implementations, in this case Speex specifically.
+ */
+
+ class File : public Ogg::File
+ {
+ public:
+ /*!
+ * Contructs a Speex file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(const char *file, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns the XiphComment for this file. XiphComment implements the tag
+ * interface, so this serves as the reimplementation of
+ * TagLib::File::tag().
+ */
+ virtual Ogg::XiphComment *tag() const;
+
+ /*!
+ * Returns the Speex::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ virtual bool save();
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+
+}
+
+#endif
diff --git a/src/metadata/speex/speexproperties.cpp b/src/metadata/speex/speexproperties.cpp
new file mode 100644
index 0000000..edd2b71
--- /dev/null
+++ b/src/metadata/speex/speexproperties.cpp
@@ -0,0 +1,171 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2002 by Scott Wheeler
+ (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tstring.h>
+#if 0
+#include <tdebug.h>
+#endif
+
+#include <oggpageheader.h>
+
+#include "speexproperties.h"
+#include "speexfile.h"
+
+using namespace TagLib;
+
+class Speex::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(File *f, ReadStyle s) :
+ file(f),
+ style(s),
+ length(0),
+ bitrate(0),
+ sampleRate(0),
+ channels(0),
+ speexVersion(0),
+ vbr(false),
+ mode(0) {}
+
+ File *file;
+ ReadStyle style;
+ int length;
+ int bitrate;
+ int sampleRate;
+ int channels;
+ int speexVersion;
+ bool vbr;
+ int mode;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Speex::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(file, style);
+ read();
+}
+
+Speex::Properties::~Properties()
+{
+ delete d;
+}
+
+int Speex::Properties::length() const
+{
+ return d->length;
+}
+
+int Speex::Properties::bitrate() const
+{
+ return int(float(d->bitrate) / float(1000) + 0.5);
+}
+
+int Speex::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int Speex::Properties::channels() const
+{
+ return d->channels;
+}
+
+int Speex::Properties::speexVersion() const
+{
+ return d->speexVersion;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Speex::Properties::read()
+{
+ // Get the identification header from the Ogg implementation.
+
+ ByteVector data = d->file->packet(0);
+
+ int pos = 28;
+
+ // speex_version_id; /**< Version for Speex (for checking compatibility) */
+ d->speexVersion = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // header_size; /**< Total size of the header ( sizeof(SpeexHeader) ) */
+ pos += 4;
+
+ // rate; /**< Sampling rate used */
+ d->sampleRate = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // mode; /**< Mode used (0 for narrowband, 1 for wideband) */
+ d->mode = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // mode_bitstream_version; /**< Version ID of the bit-stream */
+ pos += 4;
+
+ // nb_channels; /**< Number of channels encoded */
+ d->channels = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // bitrate; /**< Bit-rate used */
+ d->bitrate = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // frame_size; /**< Size of frames */
+ //unsigned int frameSize = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // vbr; /**< 1 for a VBR encoding, 0 otherwise */
+ d->vbr = data.mid(pos, 4).toUInt(false) == 1;
+ pos += 4;
+
+ // frames_per_packet; /**< Number of frames stored per Ogg packet */
+ //unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false);
+
+ const Ogg::PageHeader *first = d->file->firstPageHeader();
+ const Ogg::PageHeader *last = d->file->lastPageHeader();
+
+ if(first && last) {
+ long long start = first->absoluteGranularPosition();
+ long long end = last->absoluteGranularPosition();
+
+ if(start >= 0 && end >= 0 && d->sampleRate > 0)
+ d->length = (end - start) / (long long) d->sampleRate;
+#if 0
+ else
+ debug("Speex::Properties::read() -- Either the PCM values for the start or "
+ "end of this file was incorrect or the sample rate is zero.");
+#endif
+ }
+#if 0
+ else
+ debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages.");
+#endif
+}
diff --git a/src/metadata/speex/speexproperties.h b/src/metadata/speex/speexproperties.h
new file mode 100644
index 0000000..355ff3c
--- /dev/null
+++ b/src/metadata/speex/speexproperties.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2002 by Scott Wheeler
+ (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_SPEEXPROPERTIES_H
+#define TAGLIB_SPEEXPROPERTIES_H
+
+#include <audioproperties.h>
+
+namespace TagLib {
+
+ namespace Speex {
+
+ class File;
+
+ //! An implementation of audio property reading for Ogg Speex
+
+ /*!
+ * This reads the data from an Ogg Speex stream found in the AudioProperties
+ * API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of Vorbis::Properties with the data read from the
+ * Vorbis::File \a file.
+ */
+ Properties(File *file, ReadStyle style = Average);
+
+ /*!
+ * Destroys this VorbisProperties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * Returns the Vorbis version, currently "0" (as specified by the spec).
+ */
+ int speexVersion() const;
+
+ private:
+ Properties(const Properties &);
+ Properties &operator=(const Properties &);
+
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+
+}
+
+#endif
diff --git a/src/metadata/speex/taglib_speexfiletyperesolver.cpp b/src/metadata/speex/taglib_speexfiletyperesolver.cpp
new file mode 100644
index 0000000..9a5de0d
--- /dev/null
+++ b/src/metadata/speex/taglib_speexfiletyperesolver.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "taglib_speexfiletyperesolver.h"
+#include "speexfile.h"
+
+#include <string.h>
+
+TagLib::File *SpeexFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".spx"))
+ {
+ TagLib::Speex::File *f = new TagLib::Speex::File(fileName, readProperties, propertiesStyle);
+ if(f->isValid())
+ return f;
+ else
+ {
+ delete f;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/metadata/speex/taglib_speexfiletyperesolver.h b/src/metadata/speex/taglib_speexfiletyperesolver.h
new file mode 100644
index 0000000..b5c5c2c
--- /dev/null
+++ b/src/metadata/speex/taglib_speexfiletyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_SPEEXFILETYPERESOLVER_H
+#define TAGLIB_SPEEXFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class SpeexFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/tagengine.cpp b/src/metadata/tagengine.cpp
new file mode 100755
index 0000000..34abd23
--- /dev/null
+++ b/src/metadata/tagengine.cpp
@@ -0,0 +1,419 @@
+
+#include "tagengine.h"
+
+#include <qfile.h>
+
+// #include <taglib/attachedpictureframe.h>
+#include <taglib/fileref.h>
+#include <taglib/id3v1genres.h> //used to load genre list
+#include <taglib/mpegfile.h>
+#include <taglib/tag.h>
+#include <taglib/tstring.h>
+#include <taglib/tlist.h>
+#include <taglib/apetag.h>
+#include <taglib/id3v2tag.h>
+#include <taglib/id3v1tag.h>
+#include <taglib/mpcfile.h>
+#include <taglib/mpegfile.h>
+#include <taglib/oggfile.h>
+#include <taglib/oggflacfile.h>
+#include <taglib/vorbisfile.h>
+#include <taglib/flacfile.h>
+#include <taglib/textidentificationframe.h>
+#include <taglib/uniquefileidentifierframe.h>
+#include <taglib/xiphcomment.h>
+#include "wavpack/wvfile.h"
+#include "trueaudio/ttafile.h"
+
+#include <config.h>
+#ifdef HAVE_MP4V2
+#include "mp4/mp4file.h"
+#include "mp4/mp4tag.h"
+#else
+#include "m4a/mp4file.h"
+#include "m4a/mp4itunestag.h"
+#endif
+
+
+// TODO COMPILATION tag
+// FIXME BPM tag
+
+TagData::TagData( const QString& _artist, const QString& _composer,
+ const QString& _album, const QString& _title,
+ const QString& _genre, const QString& _comment,
+ int _track, int _disc, int _year,
+ int _length, int _fileSize, int _bitrate, int _samplingRate )
+{
+ artist = _artist;
+ composer = _composer;
+ album = _album;
+ title = _title;
+ genre = _genre;
+ comment = _comment;
+ track = _track;
+ disc = _disc;
+ year = _year;
+ length = _length;
+ fileSize = _fileSize;
+ bitrate = _bitrate;
+ samplingRate = _samplingRate;
+}
+
+TagData::~TagData()
+{}
+
+
+TagEngine::TagEngine()
+{
+ TagLib::StringList genres = TagLib::ID3v1::genreList();
+ for( TagLib::StringList::ConstIterator it = genres.begin(), end = genres.end(); it != end; ++it )
+ genreList += TStringToQString( (*it) );
+
+ genreList.sort();
+}
+
+TagEngine::~TagEngine()
+{}
+
+TagData* TagEngine::readTags( const QString& file )
+{
+ TagLib::FileRef fileref( QFile::encodeName(file) );
+ TagData* tagData = new TagData();
+
+ if( !fileref.isNull() )
+ {
+ TagLib::Tag *tag = fileref.tag();
+
+ tagData->track = 0;
+ tagData->year = 0;
+ tagData->disc = 0;
+ tagData->track_gain = 210588; // 0 is a valid value
+ tagData->album_gain = 210588;
+
+ if( tag )
+ {
+ tagData->title = TStringToQString( tag->title() ).stripWhiteSpace();
+ tagData->artist = TStringToQString( tag->artist() ).stripWhiteSpace();
+ tagData->album = TStringToQString( tag->album() ).stripWhiteSpace();
+ tagData->genre = TStringToQString( tag->genre() ).stripWhiteSpace();
+ tagData->comment = TStringToQString( tag->comment() ).stripWhiteSpace();
+ tagData->track = tag->track();
+ tagData->year = tag->year();
+ }
+
+ TagLib::AudioProperties *audioProperties = fileref.audioProperties();
+
+ if( audioProperties )
+ {
+ tagData->length = audioProperties->length();
+ // TODO read all information
+ //tagData->fileSize = ;
+ // = audioProperties->channels();
+ //tagData->bitrate = audioProperties->bitrate();
+ tagData->samplingRate = audioProperties->sampleRate();
+ }
+
+ QString disc;
+ QString track_gain;
+ QString album_gain;
+ if ( TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( fileref.file() ) )
+ {
+ if ( file->ID3v2Tag() )
+ {
+ if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
+ disc = TStringToQString( file->ID3v2Tag()->frameListMap()["TPOS"].front()->toString() ).stripWhiteSpace();
+
+ if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
+ tagData->composer = TStringToQString( file->ID3v2Tag()->frameListMap()["TCOM"].front()->toString() ).stripWhiteSpace();
+ }
+ if ( file->APETag() )
+ {
+ if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
+ track_gain = TStringToQString( file->APETag()->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString() ).stripWhiteSpace();
+
+ if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
+ album_gain = TStringToQString( file->APETag()->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString() ).stripWhiteSpace();
+ }
+ }
+ else if ( TagLib::Ogg::Vorbis::File *file = dynamic_cast<TagLib::Ogg::Vorbis::File *>( fileref.file() ) )
+ {
+ if ( file->tag() )
+ {
+ if ( !file->tag()->fieldListMap()[ "COMPOSER" ].isEmpty() )
+ tagData->composer = TStringToQString( file->tag()->fieldListMap()["COMPOSER"].front() ).stripWhiteSpace();
+
+ if ( !file->tag()->fieldListMap()[ "DISCNUMBER" ].isEmpty() )
+ disc = TStringToQString( file->tag()->fieldListMap()["DISCNUMBER"].front() ).stripWhiteSpace();
+
+ if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
+ track_gain = TStringToQString( file->tag()->fieldListMap()["REPLAYGAIN_TRACK_GAIN"].front() ).stripWhiteSpace();
+
+ if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
+ album_gain = TStringToQString( file->tag()->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"].front() ).stripWhiteSpace();
+ }
+ }
+ else if ( TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>( fileref.file() ) )
+ {
+ if ( file->xiphComment() )
+ {
+ if ( !file->xiphComment()->fieldListMap()[ "COMPOSER" ].isEmpty() )
+ tagData->composer = TStringToQString( file->xiphComment()->fieldListMap()["COMPOSER"].front() ).stripWhiteSpace();
+
+ if ( !file->xiphComment()->fieldListMap()[ "DISCNUMBER" ].isEmpty() )
+ disc = TStringToQString( file->xiphComment()->fieldListMap()["DISCNUMBER"].front() ).stripWhiteSpace();
+
+ if ( !file->xiphComment()->fieldListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
+ track_gain = TStringToQString( file->xiphComment()->fieldListMap()["REPLAYGAIN_TRACK_GAIN"].front() ).stripWhiteSpace();
+
+ if ( !file->xiphComment()->fieldListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
+ album_gain = TStringToQString( file->xiphComment()->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"].front() ).stripWhiteSpace();
+ }
+
+ /*if ( file->tag() )
+ {
+ if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
+ track_gain = TStringToQString( file->tag()->fieldListMap()["REPLAYGAIN_TRACK_GAIN"].front() ).stripWhiteSpace();
+
+ if ( !file->tag()->fieldListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
+ album_gain = TStringToQString( file->tag()->fieldListMap()["REPLAYGAIN_ALBUM_GAIN"].front() ).stripWhiteSpace();
+ }*/
+ }
+ else if ( TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File *>( fileref.file() ) )
+ {
+ TagLib::MP4::Tag *mp4tag = dynamic_cast<TagLib::MP4::Tag *>( file->tag() );
+ if( mp4tag )
+ {
+ tagData->composer = TStringToQString( mp4tag->composer() );
+
+ disc = QString::number( mp4tag->disk() );
+ }
+ }
+/* else if ( TagLib::MPC::File *file = dynamic_cast<TagLib::MPC::File *>( fileref.file() ) )
+ {
+ if ( file->APETag() )
+ {
+ if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
+ track_gain = TStringToQString( file->APETag()->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString() ).stripWhiteSpace();
+
+ if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
+ album_gain = TStringToQString( file->APETag()->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString() ).stripWhiteSpace();
+ }
+ }*/
+ else if ( TagLib::WavPack::File *file = dynamic_cast<TagLib::WavPack::File *>( fileref.file() ) )
+ {
+ if ( file->APETag() )
+ {
+ if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_TRACK_GAIN" ].isEmpty() )
+ track_gain = TStringToQString( file->APETag()->itemListMap()["REPLAYGAIN_TRACK_GAIN"].toString() ).stripWhiteSpace();
+
+ if ( !file->APETag()->itemListMap()[ "REPLAYGAIN_ALBUM_GAIN" ].isEmpty() )
+ album_gain = TStringToQString( file->APETag()->itemListMap()["REPLAYGAIN_ALBUM_GAIN"].toString() ).stripWhiteSpace();
+ }
+ }
+ else if ( TagLib::TTA::File *file = dynamic_cast<TagLib::TTA::File *>( fileref.file() ) )
+ {
+ if ( file->ID3v2Tag() )
+ {
+ if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
+ disc = TStringToQString( file->ID3v2Tag()->frameListMap()["TPOS"].front()->toString() ).stripWhiteSpace();
+
+ if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
+ tagData->composer = TStringToQString( file->ID3v2Tag()->frameListMap()["TCOM"].front()->toString() ).stripWhiteSpace();
+ }
+ }
+
+ if( !disc.isEmpty() )
+ {
+ int i = disc.find('/');
+ if( i != -1 )
+ // disc.right( i ).toInt() is total number of discs, we don't use this at the moment
+ tagData->disc = disc.left( i ).toInt();
+ else
+ tagData->disc = disc.toInt();
+ }
+
+ if( !track_gain.isEmpty() )
+ {
+ int i = track_gain.find(' ');
+ if( i != -1 )
+ tagData->track_gain = track_gain.left( i ).toFloat();
+ else
+ tagData->track_gain = track_gain.toFloat();
+ }
+
+ if( !album_gain.isEmpty() )
+ {
+ int i = album_gain.find(' ');
+ if( i != -1 )
+ tagData->album_gain = album_gain.left( i ).toFloat();
+ else
+ tagData->album_gain = album_gain.toFloat();
+ }
+
+ return tagData;
+ }
+
+ return 0;
+}
+
+bool TagEngine::writeTags( const QString& file, TagData* tagData )
+{
+ if( !tagData ) tagData = new TagData();
+
+ //Set default codec to UTF-8 (see bugs 111246 and 111232)
+ TagLib::ID3v2::FrameFactory::instance()->setDefaultTextEncoding( TagLib::String::UTF8 );
+
+ TagLib::FileRef fileref( QFile::encodeName(file), false );
+
+ if ( !fileref.isNull() )
+ {
+ TagLib::Tag* tag = fileref.tag();
+ if ( tag )
+ {
+ tag->setTitle( QStringToTString(tagData->title) );
+ tag->setArtist( QStringToTString(tagData->artist) );
+ tag->setAlbum( QStringToTString(tagData->album) );
+ tag->setTrack( tagData->track );
+ tag->setYear( tagData->year );
+ tag->setComment( QStringToTString(tagData->comment) );
+ tag->setGenre( QStringToTString(tagData->genre) );
+ }
+ else
+ {
+ return false;
+ }
+
+ if ( TagLib::MPEG::File *file = dynamic_cast<TagLib::MPEG::File *>( fileref.file() ) )
+ {
+ if ( file->ID3v2Tag() )
+ {
+ if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
+ {
+ file->ID3v2Tag()->frameListMap()[ "TPOS" ].front()->setText( QStringToTString( QString::number(tagData->disc) ) );
+ }
+ else
+ {
+ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TPOS", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
+ frame->setText( QStringToTString( QString::number(tagData->disc) ) );
+ file->ID3v2Tag()->addFrame( frame );
+ }
+
+ if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
+ {
+ file->ID3v2Tag()->frameListMap()[ "TCOM" ].front()->setText( QStringToTString( tagData->composer ) );
+ }
+ else
+ {
+ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TCOM", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
+ frame->setText( QStringToTString( tagData->composer ) );
+ file->ID3v2Tag()->addFrame( frame );
+ }
+
+ // HACK sets the id3v2 genre tag as string
+ if ( !file->ID3v2Tag()->frameListMap()[ "TCON" ].isEmpty() )
+ {
+ file->ID3v2Tag()->frameListMap()[ "TCON" ].front()->setText( QStringToTString( tagData->genre ) );
+ }
+ else
+ {
+ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TCON", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
+ frame->setText( QStringToTString( tagData->genre ) );
+ file->ID3v2Tag()->addFrame( frame );
+ }
+
+ // HACK sets the id3v2 year tag
+ if ( !file->ID3v2Tag()->frameListMap()[ "TYER" ].isEmpty() )
+ {
+ file->ID3v2Tag()->frameListMap()[ "TYER" ].front()->setText( QStringToTString( QString::number(tagData->year) ) );
+ }
+ else
+ {
+ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TYER", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
+ frame->setText( QStringToTString( QString::number(tagData->year) ) );
+ file->ID3v2Tag()->addFrame( frame );
+ }
+ }
+ }
+ else if ( TagLib::Ogg::Vorbis::File *file = dynamic_cast<TagLib::Ogg::Vorbis::File *>( fileref.file() ) )
+ {
+ if ( file->tag() )
+ {
+ file->tag()->addField( "COMPOSER", QStringToTString( tagData->composer ), true );
+
+ file->tag()->addField( "DISCNUMBER", QStringToTString( QString::number(tagData->disc) ), true );
+ }
+ }
+ else if ( TagLib::FLAC::File *file = dynamic_cast<TagLib::FLAC::File *>( fileref.file() ) )
+ {
+ if ( file->xiphComment() )
+ {
+ file->xiphComment()->addField( "COMPOSER", QStringToTString( tagData->composer ), true );
+
+ file->xiphComment()->addField( "DISCNUMBER", QStringToTString( QString::number(tagData->disc) ), true );
+ }
+ }
+ else if ( TagLib::MP4::File *file = dynamic_cast<TagLib::MP4::File *>( fileref.file() ) )
+ {
+ TagLib::MP4::Tag *mp4tag = dynamic_cast<TagLib::MP4::Tag *>( file->tag() );
+ if( mp4tag )
+ {
+ mp4tag->setComposer( QStringToTString( tagData->composer ) );
+
+ mp4tag->setDisk( tagData->disc );
+ }
+ }
+ if ( TagLib::TTA::File *file = dynamic_cast<TagLib::TTA::File *>( fileref.file() ) )
+ {
+ if ( file->ID3v2Tag() )
+ {
+ if ( !file->ID3v2Tag()->frameListMap()[ "TPOS" ].isEmpty() )
+ {
+ file->ID3v2Tag()->frameListMap()[ "TPOS" ].front()->setText( QStringToTString( QString::number(tagData->disc) ) );
+ }
+ else
+ {
+ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TPOS", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
+ frame->setText( QStringToTString( QString::number(tagData->disc) ) );
+ file->ID3v2Tag()->addFrame( frame );
+ }
+
+ if ( !file->ID3v2Tag()->frameListMap()[ "TCOM" ].isEmpty() )
+ {
+ file->ID3v2Tag()->frameListMap()[ "TCOM" ].front()->setText( QStringToTString( tagData->composer ) );
+ }
+ else
+ {
+ TagLib::ID3v2::TextIdentificationFrame *frame = new TagLib::ID3v2::TextIdentificationFrame( "TCOM", TagLib::ID3v2::FrameFactory::instance()->defaultTextEncoding() );
+ frame->setText( QStringToTString( tagData->composer ) );
+ file->ID3v2Tag()->addFrame( frame );
+ }
+ }
+ }
+
+ return fileref.save();
+ }
+ return false;
+}
+
+// bool TagEngine::canWrite( QString format )
+// {
+// format = format.lower();
+//
+// if( format == "ogg" ||
+// format == "flac" || format == "fla" ||
+// format == "mp3" || // TODO mp2 ?
+// format == "mpc" ||
+// format == "aac" ||
+// format == "ape" || format == "mac" ||
+// format == "aa" ||
+// format == "m4a" || format == "m4b" || format == "m4p" || format == "mp4" || format == "m4v" || format == "mp4v" ||
+// format == "ra" || format == "rv" || format == "rm" || format == "rmj" || format == "rmvb" ||
+// format == "wma" || format == "asf" )
+// {
+// return true;
+// }
+// else {
+// return false;
+// }
+// }
+
diff --git a/src/metadata/tagengine.h b/src/metadata/tagengine.h
new file mode 100755
index 0000000..e21a48c
--- /dev/null
+++ b/src/metadata/tagengine.h
@@ -0,0 +1,79 @@
+
+
+#ifndef TAGENGINE_H
+#define TAGENGINE_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+/**
+ * @short All metainformation can be stored in this class
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class TagData
+{
+public:
+ /**
+ * Constructor
+ */
+ TagData( const QString& _artist = QString::null, const QString& _composer = QString::null,
+ const QString& _album = QString::null, const QString& _title = QString::null,
+ const QString& _genre = QString::null, const QString& _comment = QString::null,
+ int _track = 0, int _disc = 0, int _year = 0,
+ int _length = 0, int _fileSize = 0, int _bitrate = 0, int _samplingRate = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~TagData();
+
+ /** The tags */
+ QString artist;
+ QString composer;
+ QString album;
+ QString title;
+ QString genre;
+ QString comment;
+ int track;
+ int disc;
+ int year;
+ float track_gain;
+ float album_gain;
+
+ /** The technical information */
+ int length;
+ int fileSize;
+ int bitrate;
+ int samplingRate;
+};
+
+
+/**
+ * @short Manages everything that has something to do with tags
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class TagEngine
+{
+public:
+ /**
+ * Constructor
+ */
+ TagEngine();
+
+ /**
+ * Destructor
+ */
+ virtual ~TagEngine();
+
+ /** A list of all genre */
+ QStringList genreList;
+
+ TagData* readTags( const QString& file );
+ bool writeTags( const QString& file, TagData* tagData );
+
+// bool canWrite( QString format ); // NOTE no const because this string is being modyfied
+};
+
+#endif // TAGENGINE_H
diff --git a/src/metadata/tplugins.cpp b/src/metadata/tplugins.cpp
new file mode 100755
index 0000000..b84d601
--- /dev/null
+++ b/src/metadata/tplugins.cpp
@@ -0,0 +1,151 @@
+/***************************************************************************
+ copyright : (C) 2005, 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <config.h>
+// #include <debug.h>
+
+#include <qfile.h>
+#include <kmimetype.h>
+
+#include <taglib/fileref.h>
+#include <taglib/tfile.h>
+
+#ifdef HAVE_MP4V2
+#include "mp4/taglib_mp4filetyperesolver.h"
+#include "mp4/mp4file.h"
+#else
+#include "m4a/taglib_mp4filetyperesolver.h"
+#include "m4a/mp4file.h"
+#endif
+
+#include "trueaudio/taglib_trueaudiofiletyperesolver.h"
+#include "trueaudio/ttafile.h"
+#include "wavpack/taglib_wavpackfiletyperesolver.h"
+#include "wavpack/wvfile.h"
+#include "speex/taglib_speexfiletyperesolver.h"
+#include "speex/speexfile.h"
+#include "asf/taglib_asffiletyperesolver.h"
+#include "asf/asffile.h"
+#include "rmff/taglib_realmediafiletyperesolver.h"
+#include "rmff/taglib_realmediafile.h"
+#include "audible/taglib_audiblefiletyperesolver.h"
+#include "audible/taglib_audiblefile.h"
+#include "wav/wavfiletyperesolver.h"
+#include "wav/wavfile.h"
+#include "aac/aacfiletyperesolver.h"
+#include "ape/taglib_monkeysaudiofiletyperesolver.h"
+#include "optimfrog/taglib_optimfrogfiletyperesolver.h"
+
+#include <taglib/mpegfile.h>
+#include <taglib/oggfile.h>
+#include <taglib/oggflacfile.h>
+#include <taglib/vorbisfile.h>
+#include <taglib/flacfile.h>
+#include <taglib/mpcfile.h>
+
+
+class MimeTypeFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+TagLib::File *MimeTypeFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ QString fn = QFile::decodeName( fileName );
+ int accuracy = 0;
+
+ KMimeType::Ptr mimetype = KMimeType::findByFileContent( fn, &accuracy );
+ if( accuracy <= 0 )
+ mimetype = KMimeType::findByPath( fn );
+
+ if( mimetype->is( "audio/aac" )
+ || mimetype->is( "audio/mpeg" )
+ || mimetype->is( "audio/mpegurl" )
+ || mimetype->is( "audio/x-mpegurl" )
+ || mimetype->is( "audio/x-mp3" ))
+ {
+ return new TagLib::MPEG::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/mp4" ) || mimetype->is( "video/mp4" ) )
+ {
+ return new TagLib::MP4::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/x-ms-wma" )
+ || mimetype->is( "video/x-ms-asf" )
+ || mimetype->is( "video/x-msvideo" )
+ || mimetype->is( "video/x-ms-wmv" ) )
+ {
+ return new TagLib::ASF::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/vnd.rn-realaudio" )
+ || mimetype->is( "audio/x-pn-realaudio" )
+ || mimetype->is( "audio/x-pn-realaudioplugin" )
+ || mimetype->is( "audio/vnd.rn-realvideo" ) )
+ {
+ return new TagLib::RealMedia::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/vorbis" ) )
+ {
+ return new TagLib::Ogg::Vorbis::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/x-oggflac" ) )
+ {
+ return new TagLib::Ogg::FLAC::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/x-flac" ) )
+ {
+ return new TagLib::FLAC::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/x-musepack" ) )
+ {
+ return new TagLib::MPC::File(fileName, readProperties, propertiesStyle);
+ }
+ else if( mimetype->is( "audio/x-musepack" )
+ || mimetype->is( "audio/x-ape" )
+ || mimetype->is( "application/x-ofr" ) )
+ {
+ return new TagLib::MPC::File(fileName, readProperties, propertiesStyle);
+ }
+
+// debug() << "kmimetype filetype guessing failed for" << fileName << endl;
+
+ return 0;
+}
+
+void registerTaglibPlugins()
+{
+ //TagLib::FileRef::addFileTypeResolver(new MimeTypeFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new MP4FileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new ASFFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new RealMediaFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new AudibleFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new AACFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new WavPackFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new SpeexFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new TTAFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new WavFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new MonkeysAudioFileTypeResolver);
+ TagLib::FileRef::addFileTypeResolver(new OptimFrogFileTypeResolver);
+}
diff --git a/src/metadata/tplugins.h b/src/metadata/tplugins.h
new file mode 100755
index 0000000..1ef356f
--- /dev/null
+++ b/src/metadata/tplugins.h
@@ -0,0 +1,27 @@
+/***************************************************************************
+ copyright : (C) 2005 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 METADATA_TPLUGIN_H
+#define METADATA_TPLUGIN_H
+
+void registerTaglibPlugins();
+
+#endif
diff --git a/src/metadata/trueaudio/Makefile.am b/src/metadata/trueaudio/Makefile.am
new file mode 100644
index 0000000..01af09f
--- /dev/null
+++ b/src/metadata/trueaudio/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagtrueaudio_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagtrueaudio.la
+
+libtagtrueaudio_la_SOURCES = \
+ ttafile.cpp \
+ ttaproperties.cpp \
+ taglib_trueaudiofiletyperesolver.cpp
+
+noinst_HEADERS = ttafile.h \
+ ttaproperties.h \
+ taglib_trueaudiofiletyperesolver.h
+
diff --git a/src/metadata/trueaudio/combinedtag.h b/src/metadata/trueaudio/combinedtag.h
new file mode 100644
index 0000000..7d530d2
--- /dev/null
+++ b/src/metadata/trueaudio/combinedtag.h
@@ -0,0 +1,171 @@
+/***************************************************************************
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 DO_NOT_DOCUMENT // Tell Doxygen not to document this header
+
+#ifndef TAGLIB_COMBINEDTAG_H
+#define TAGLIB_COMBINEDTAG_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Note that this header is not installed.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <tag.h>
+
+namespace TagLib {
+
+ /*!
+ * A union of two TagLib::Tags.
+ */
+ class CombinedTag : public TagLib::Tag
+ {
+ public:
+ CombinedTag(Tag *tag1 = 0, Tag *tag2 = 0)
+ : TagLib::Tag(),
+ tag1(tag1), tag2(tag2) {}
+
+ virtual String title() const {
+ if(tag1 && !tag1->title().isEmpty())
+ return tag1->title();
+
+ if(tag2)
+ return tag2->title();
+
+ return String::null;
+ }
+
+ virtual String artist() const {
+ if(tag1 && !tag1->artist().isEmpty())
+ return tag1->artist();
+
+ if(tag2)
+ return tag2->artist();
+
+ return String::null;
+ }
+
+ virtual String album() const {
+ if(tag1 && !tag1->album().isEmpty())
+ return tag1->album();
+
+ if(tag2)
+ return tag2->album();
+
+ return String::null;
+ }
+
+ virtual String comment() const {
+ if(tag1 && !tag1->comment().isEmpty())
+ return tag1->comment();
+
+ if(tag2)
+ return tag2->comment();
+
+ return String::null;
+ }
+
+ virtual String genre() const {
+ if(tag1 && !tag1->genre().isEmpty())
+ return tag1->genre();
+
+ if(tag2)
+ return tag2->genre();
+
+ return String::null;
+ }
+
+ virtual uint year() const {
+ if(tag1 && tag1->year() > 0)
+ return tag1->year();
+
+ if(tag2)
+ return tag2->year();
+
+ return 0;
+ }
+
+ virtual uint track() const {
+ if(tag1 && tag1->track() > 0)
+ return tag1->track();
+
+ if(tag2)
+ return tag2->track();
+
+ return 0;
+ }
+
+ virtual void setTitle(const String &s) {
+ if(tag1)
+ tag1->setTitle(s);
+ if(tag2)
+ tag2->setTitle(s);
+ }
+
+ virtual void setArtist(const String &s) {
+ if(tag1)
+ tag1->setArtist(s);
+ if(tag2)
+ tag2->setArtist(s);
+ }
+
+ virtual void setAlbum(const String &s) {
+ if(tag1)
+ tag1->setAlbum(s);
+ if(tag2)
+ tag2->setAlbum(s);
+ }
+
+ virtual void setComment(const String &s) {
+ if(tag1)
+ tag1->setComment(s);
+ if(tag2)
+ tag2->setComment(s);
+ }
+
+ virtual void setGenre(const String &s) {
+ if(tag1)
+ tag1->setGenre(s);
+ if(tag2)
+ tag2->setGenre(s);
+ }
+
+ virtual void setYear(uint i) {
+ if(tag1)
+ tag1->setYear(i);
+ if(tag2)
+ tag2->setYear(i);
+ }
+
+ virtual void setTrack(uint i) {
+ if(tag1)
+ tag1->setTrack(i);
+ if(tag2)
+ tag2->setTrack(i);
+ }
+
+ private:
+ Tag *tag1;
+ Tag *tag2;
+ };
+}
+
+#endif
+#endif
diff --git a/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.cpp b/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.cpp
new file mode 100644
index 0000000..e84e501
--- /dev/null
+++ b/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "taglib_trueaudiofiletyperesolver.h"
+#include "ttafile.h"
+
+#include <string.h>
+
+TagLib::File *TTAFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".tta"))
+ {
+ TagLib::TTA::File *f = new TagLib::TTA::File(fileName, readProperties, propertiesStyle);
+ if(f->isValid())
+ return f;
+ else
+ {
+ delete f;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.h b/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.h
new file mode 100644
index 0000000..e436e43
--- /dev/null
+++ b/src/metadata/trueaudio/taglib_trueaudiofiletyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_TRUEAUDIOFILETYPERESOLVER_H
+#define TAGLIB_TRUEAUDIOFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class TTAFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/trueaudio/ttafile.cpp b/src/metadata/trueaudio/ttafile.cpp
new file mode 100644
index 0000000..3a02e20
--- /dev/null
+++ b/src/metadata/trueaudio/ttafile.cpp
@@ -0,0 +1,307 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#if 0
+#include <tdebug.h>
+#endif
+
+#include "ttafile.h"
+#include "id3v1tag.h"
+#include "id3v2tag.h"
+#include "id3v2header.h"
+#include "combinedtag.h"
+
+using namespace TagLib;
+
+class TTA::File::FilePrivate
+{
+public:
+ FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
+ ID3v2FrameFactory(frameFactory),
+ ID3v2Tag(0),
+ ID3v2Location(-1),
+ ID3v2OriginalSize(0),
+ ID3v1Tag(0),
+ ID3v1Location(-1),
+ tag(0),
+ properties(0),
+ scanned(false),
+ hasID3v1(false),
+ hasID3v2(false) {}
+
+ ~FilePrivate()
+ {
+ if (tag != ID3v1Tag && tag != ID3v2Tag) delete tag;
+ delete ID3v1Tag;
+ delete ID3v2Tag;
+ delete properties;
+ }
+
+ const ID3v2::FrameFactory *ID3v2FrameFactory;
+ ID3v2::Tag *ID3v2Tag;
+ long ID3v2Location;
+ uint ID3v2OriginalSize;
+
+ ID3v1::Tag *ID3v1Tag;
+ long ID3v1Location;
+
+ Tag *tag;
+
+ Properties *properties;
+ bool scanned;
+
+ // These indicate whether the file *on disk* has these tags, not if
+ // this data structure does. This is used in computing offsets.
+
+ bool hasID3v1;
+ bool hasID3v2;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+TTA::File::File(const char *file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+ d = new FilePrivate;
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
+TTA::File::File(const char *file, ID3v2::FrameFactory *frameFactory,
+ bool readProperties, Properties::ReadStyle propertiesStyle) :
+ TagLib::File(file)
+{
+ d = new FilePrivate(frameFactory);
+ if(isOpen())
+ read(readProperties, propertiesStyle);
+}
+
+TTA::File::~File()
+{
+ delete d;
+}
+
+TagLib::Tag *TTA::File::tag() const
+{
+ return d->tag;
+}
+
+TTA::Properties *TTA::File::audioProperties() const
+{
+ return d->properties;
+}
+
+void TTA::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
+{
+ d->ID3v2FrameFactory = factory;
+}
+
+bool TTA::File::save()
+{
+ if(readOnly()) {
+#if 0
+ debug("TTA::File::save() -- File is read only.");
+#endif
+ return false;
+ }
+
+ // Update ID3v2 tag
+
+ if(d->ID3v2Tag) {
+ if(!d->hasID3v2) {
+ d->ID3v2Location = 0;
+ d->ID3v2OriginalSize = 0;
+ }
+ insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
+ d->hasID3v2 = true;
+ }
+ else if(d->hasID3v2) {
+ removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
+ d->hasID3v2 = false;
+ }
+
+ // Update ID3v1 tag
+
+ if(d->ID3v1Tag) {
+ if(!d->hasID3v1) {
+ seek(0, End);
+ d->ID3v1Location = tell();
+ }
+ else
+ seek(d->ID3v1Location);
+ writeBlock(d->ID3v1Tag->render());
+ d->hasID3v1 = true;
+ }
+ else if(d->hasID3v1) {
+ removeBlock(d->ID3v1Location, 128);
+ d->hasID3v1 = false;
+ }
+
+ return true;
+}
+
+ID3v1::Tag *TTA::File::ID3v1Tag(bool create)
+{
+ if(!create || d->ID3v1Tag)
+ return d->ID3v1Tag;
+
+ // no ID3v1 tag exists and we've been asked to create one
+
+ d->ID3v1Tag = new ID3v1::Tag;
+
+ if(d->ID3v2Tag)
+ d->tag = new CombinedTag(d->ID3v2Tag, d->ID3v1Tag);
+ else
+ d->tag = d->ID3v1Tag;
+
+ return d->ID3v1Tag;
+}
+
+ID3v2::Tag *TTA::File::ID3v2Tag(bool create)
+{
+ if(!create || d->ID3v2Tag)
+ return d->ID3v2Tag;
+
+ // no ID3v2 tag exists and we've been asked to create one
+
+ d->ID3v2Tag = new ID3v2::Tag;
+
+ if(d->ID3v1Tag)
+ d->tag = new CombinedTag(d->ID3v2Tag, d->ID3v1Tag);
+ else
+ d->tag = d->ID3v2Tag;
+
+ return d->ID3v2Tag;
+}
+
+void TTA::File::remove(int tags)
+{
+ if(tags & ID3v1) {
+ delete d->ID3v1Tag;
+ d->ID3v1Tag = 0;
+
+ if(d->ID3v2Tag)
+ d->tag = d->ID3v2Tag;
+ else
+ d->tag = d->ID3v2Tag = new ID3v2::Tag;
+ }
+
+ if(tags & ID3v2) {
+ delete d->ID3v2Tag;
+ d->ID3v2Tag = 0;
+
+ if(d->ID3v1Tag)
+ d->tag = d->ID3v1Tag;
+ else
+ d->tag = d->ID3v2Tag = new ID3v2::Tag;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void TTA::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
+{
+ // Look for an ID3v2 tag
+
+ d->ID3v2Location = findID3v2();
+
+ if(d->ID3v2Location >= 0) {
+
+ d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory);
+
+ d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize();
+
+ if(d->ID3v2Tag->header()->tagSize() <= 0) {
+ delete d->ID3v2Tag;
+ d->ID3v2Tag = 0;
+ }
+ else
+ d->hasID3v2 = true;
+ }
+
+ // Look for an ID3v1 tag
+
+ d->ID3v1Location = findID3v1();
+
+ if(d->ID3v1Location >= 0) {
+ d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
+ d->hasID3v1 = true;
+ }
+
+ if(d->hasID3v1 && d->hasID3v2)
+ d->tag = new CombinedTag(d->ID3v2Tag, d->ID3v1Tag);
+ else {
+ if(d->hasID3v1)
+ d->tag = d->ID3v1Tag;
+ else {
+ if(d->hasID3v2)
+ d->tag = d->ID3v2Tag;
+ else
+ d->tag = d->ID3v2Tag = new ID3v2::Tag;
+ }
+ }
+
+ // Look for TTA metadata
+
+ if(readProperties) {
+ seek(d->ID3v2Location + d->ID3v2OriginalSize);
+ d->properties = new Properties(readBlock(TTA::HeaderSize),
+ length() - d->ID3v2OriginalSize);
+ }
+}
+
+long TTA::File::findID3v1()
+{
+ if(!isValid())
+ return -1;
+
+ seek(-128, End);
+ long p = tell();
+
+ if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+ return p;
+
+ return -1;
+}
+
+long TTA::File::findID3v2()
+{
+ if(!isValid())
+ return -1;
+
+ seek(0);
+
+ if(readBlock(3) == ID3v2::Header::fileIdentifier())
+ return 0;
+
+ return -1;
+}
diff --git a/src/metadata/trueaudio/ttafile.h b/src/metadata/trueaudio/ttafile.h
new file mode 100644
index 0000000..2cb0785
--- /dev/null
+++ b/src/metadata/trueaudio/ttafile.h
@@ -0,0 +1,178 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_TTAFILE_H
+#define TAGLIB_TTAFILE_H
+
+#include "tfile.h"
+
+#include "ttaproperties.h"
+
+namespace TagLib {
+
+ class Tag;
+
+ namespace ID3v2 { class Tag; class FrameFactory; }
+ namespace ID3v1 { class Tag; }
+
+ //! An implementation of TTA metadata
+
+ /*!
+ * This is implementation of TTA metadata.
+ *
+ * This supports ID3v1 and ID3v2 tags as well as reading stream
+ * properties from the file.
+ */
+
+ namespace TTA {
+
+ //! An implementation of TagLib::File with TTA specific methods
+
+ /*!
+ * This implements and provides an interface for TTA files to the
+ * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+ * the abstract TagLib::File API as well as providing some additional
+ * information specific to TTA files.
+ */
+
+ class File : public TagLib::File
+ {
+ public:
+ /*!
+ * This set of flags is used for various operations and is suitable for
+ * being OR-ed together.
+ */
+ enum TagTypes {
+ //! Empty set. Matches no tag types.
+ NoTags = 0x0000,
+ //! Matches ID3v1 tags.
+ ID3v1 = 0x0001,
+ //! Matches ID3v2 tags.
+ ID3v2 = 0x0002,
+ //! Matches all tag types.
+ AllTags = 0xffff
+ };
+
+ /*!
+ * Contructs an MPC file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(const char *file, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Contructs an MPC file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored. The frames will be created using
+ * \a frameFactory.
+ */
+ File(const char *file, ID3v2::FrameFactory *frameFactory,
+ bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
+ * or a combination of the two.
+ */
+ virtual TagLib::Tag *tag() const;
+
+ /*!
+ * Returns the MPC::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ /*!
+ * Set the ID3v2::FrameFactory to something other than the default.
+ *
+ * \see ID3v2FrameFactory
+ */
+ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
+
+ /*!
+ * Saves the file.
+ */
+ virtual bool save();
+
+ /*!
+ * Returns a pointer to the ID3v2 tag of the file.
+ *
+ * If \a create is false (the default) this will return a null pointer
+ * if there is no valid ID3v2 tag. If \a create is true it will create
+ * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+ * new ID3v1 tag will be placed after it.
+ *
+ * \note The Tag <b>is still</b> owned by the TTA::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ ID3v1::Tag *ID3v1Tag(bool create = false);
+
+ /*!
+ * Returns a pointer to the ID3v1 tag of the file.
+ *
+ * If \a create is false (the default) this will return a null pointer
+ * if there is no valid ID3v1 tag. If \a create is true it will create
+ * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+ * new ID3v1 tag will be placed after it.
+ *
+ * \note The Tag <b>is still</b> owned by the TTA::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ ID3v2::Tag *ID3v2Tag(bool create = false);
+
+ /*!
+ * This will remove the tags that match the OR-ed together TagTypes from the
+ * file. By default it removes all tags.
+ *
+ * \note This will also invalidate pointers to the tags
+ * as their memory will be freed.
+ * \note In order to make the removal permanent save() still needs to be called
+ */
+ void remove(int tags = AllTags);
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+ void scan();
+ long findID3v1();
+ long findID3v2();
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/trueaudio/ttaproperties.cpp b/src/metadata/trueaudio/ttaproperties.cpp
new file mode 100644
index 0000000..c4f6a29
--- /dev/null
+++ b/src/metadata/trueaudio/ttaproperties.cpp
@@ -0,0 +1,134 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tstring.h>
+#if 0
+#include <tdebug.h>
+#endif
+#include <bitset>
+
+#include "ttaproperties.h"
+#include "ttafile.h"
+
+using namespace TagLib;
+
+class TTA::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
+ data(d),
+ streamLength(length),
+ style(s),
+ version(0),
+ length(0),
+ bitrate(0),
+ sampleRate(0),
+ channels(0),
+ bitsPerSample(0) {}
+
+ ByteVector data;
+ long streamLength;
+ ReadStyle style;
+ int version;
+ int length;
+ int bitrate;
+ int sampleRate;
+ int channels;
+ int bitsPerSample;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+TTA::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(data, streamLength, style);
+ read();
+}
+
+TTA::Properties::~Properties()
+{
+ delete d;
+}
+
+int TTA::Properties::length() const
+{
+ return d->length;
+}
+
+int TTA::Properties::bitrate() const
+{
+ return d->bitrate;
+}
+
+int TTA::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int TTA::Properties::bitsPerSample() const
+{
+ return d->bitsPerSample;
+}
+
+int TTA::Properties::channels() const
+{
+ return d->channels;
+}
+
+int TTA::Properties::ttaVersion() const
+{
+ return d->version;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void TTA::Properties::read()
+{
+ if(!d->data.startsWith("TTA"))
+ return;
+
+ int pos = 3;
+
+ d->version = d->data[pos] - '0';
+ pos += 1 + 2;
+
+ d->channels = d->data.mid(pos, 2).toShort(false);
+ pos += 2;
+
+ d->bitsPerSample = d->data.mid(pos, 2).toShort(false);
+ pos += 2;
+
+ d->sampleRate = d->data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ unsigned long samples = d->data.mid(pos, 4).toUInt(false);
+ d->length = samples / d->sampleRate;
+
+ d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
+}
diff --git a/src/metadata/trueaudio/ttaproperties.h b/src/metadata/trueaudio/ttaproperties.h
new file mode 100644
index 0000000..e694e3d
--- /dev/null
+++ b/src/metadata/trueaudio/ttaproperties.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_TTAPROPERTIES_H
+#define TAGLIB_TTAPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+ namespace TTA {
+
+ class File;
+
+ static const uint HeaderSize = 18;
+
+ //! An implementation of audio property reading for TTA
+
+ /*!
+ * This reads the data from an TTA stream found in the AudioProperties
+ * API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of TTA::Properties with the data read from the
+ * ByteVector \a data.
+ */
+ Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
+
+ /*!
+ * Destroys this TTA::Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * Returns number of bits per sample.
+ */
+ int bitsPerSample() const;
+
+ /*!
+ * Returns the major version number.
+ */
+ int ttaVersion() const;
+
+ private:
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/wav/Makefile.am b/src/metadata/wav/Makefile.am
new file mode 100644
index 0000000..add569f
--- /dev/null
+++ b/src/metadata/wav/Makefile.am
@@ -0,0 +1,15 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagwav_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagwav.la
+
+libtagwav_la_SOURCES = wavproperties.cpp \
+ wavfile.cpp \
+ wavfiletyperesolver.cpp
+
+noinst_HEADERS = wavproperties.h \
+ wavfile.h \
+ wavfiletyperesolver.h
+
diff --git a/src/metadata/wav/wavfile.cpp b/src/metadata/wav/wavfile.cpp
new file mode 100644
index 0000000..303c5b2
--- /dev/null
+++ b/src/metadata/wav/wavfile.cpp
@@ -0,0 +1,115 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "wavfile.h"
+
+#include <taglib/tfile.h>
+#include <taglib/audioproperties.h>
+#include <taglib/tag.h>
+
+namespace TagLib {
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Wav::File::File(const char *file,
+ bool readProperties,
+ Properties::ReadStyle propertiesStyle,
+ FILE *fp)
+ : TagLib::File(file)
+ , wavtag( NULL )
+ , properties( NULL )
+{
+
+ // debug ("Wav::File: create new file object.");
+ //debug ( file );
+
+ /**
+ * Create the Wav file.
+ */
+
+ if(fp)
+ wavfile = fp;
+ else
+ wavfile = fopen(file, "rb");
+
+ if( isOpen() )
+ {
+ read(readProperties, propertiesStyle );
+ }
+}
+
+Wav::File::~File()
+{
+ if(wavfile)
+ fclose(wavfile);
+ delete properties;
+}
+
+TagLib::Tag *Wav::File::tag() const
+{
+ return NULL;
+}
+
+TagLib::Tag *Wav::File::getWavTag() const
+{
+ return NULL;
+}
+
+Wav::Properties *Wav::File::audioProperties() const
+{
+ return properties;
+}
+
+bool Wav::File::save()
+{
+ return false;
+}
+
+bool Wav::File::isOpen()
+{
+ return wavfile != NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Wav::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ properties = new Wav::Properties(propertiesStyle);
+
+ if (wavfile != NULL) {
+ if(readProperties)
+ {
+ // Parse bitrate etc.
+ properties->readWavProperties( wavfile );
+ }
+ }
+}
+
+}
diff --git a/src/metadata/wav/wavfile.h b/src/metadata/wav/wavfile.h
new file mode 100644
index 0000000..e47fdfb
--- /dev/null
+++ b/src/metadata/wav/wavfile.h
@@ -0,0 +1,92 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_WAVFILE_H
+#define TAGLIB_WAVFILE_H
+
+#include <taglib/tfile.h>
+#include "wavproperties.h"
+
+namespace TagLib {
+
+ namespace Wav {
+
+ class Tag;
+
+ class File : public TagLib::File
+ {
+ public:
+ /*!
+ * Contructs a Wav file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(const char *file, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average,
+ FILE *fp=NULL);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+
+ virtual TagLib::Tag *tag() const;
+
+ /*!
+ * Returns the Wav::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Wav::Properties *audioProperties() const;
+
+ /*!
+ * Save the file.
+ * This is the same as calling save(AllTags);
+ *
+ * \note As of now, saving Wav tags is not supported.
+ */
+ virtual bool save();
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+ TagLib::Tag *getWavTag() const;
+
+ bool isWavFile() const;
+
+ protected:
+ File(const File &);
+ File &operator=(const File &);
+ bool isOpen();
+
+
+ TagLib::Tag *wavtag;
+ Wav::Properties *properties;
+
+ FILE *wavfile;
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/wav/wavfiletyperesolver.cpp b/src/metadata/wav/wavfiletyperesolver.cpp
new file mode 100644
index 0000000..830742f
--- /dev/null
+++ b/src/metadata/wav/wavfiletyperesolver.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "wavfiletyperesolver.h"
+#include "wavfile.h"
+
+#include <string.h>
+
+TagLib::File *WavFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".wav"))
+ {
+ FILE *fp = fopen(fileName, "rb");
+ if(!fp)
+ return 0;
+
+ return new TagLib::Wav::File(fileName, readProperties, propertiesStyle, fp);
+ }
+
+ return 0;
+}
diff --git a/src/metadata/wav/wavfiletyperesolver.h b/src/metadata/wav/wavfiletyperesolver.h
new file mode 100644
index 0000000..de818c9
--- /dev/null
+++ b/src/metadata/wav/wavfiletyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_WAVFILETYPERESOLVER_H
+#define TAGLIB_WAVFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class WavFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/wav/wavproperties.cpp b/src/metadata/wav/wavproperties.cpp
new file mode 100644
index 0000000..20ba1ab
--- /dev/null
+++ b/src/metadata/wav/wavproperties.cpp
@@ -0,0 +1,108 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <stdio.h>
+
+#include "wavproperties.h"
+
+#include <taglib/tstring.h>
+
+#include "wavfile.h"
+
+#include <netinet/in.h> // ntohl
+
+using namespace TagLib;
+
+struct WavHeader
+{
+ uint32_t riff_id;
+ uint32_t riff_size;
+ uint32_t wave_id;
+ uint32_t format_id;
+ uint32_t format_size;
+ uint16_t format_tag;
+ uint16_t num_channels;
+ uint32_t num_samples_per_sec;
+ uint32_t num_avg_bytes_per_sec;
+ uint16_t num_block_align;
+ uint16_t bits_per_sample;
+ uint32_t data_id;
+ uint32_t num_data_bytes;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Wav::Properties::Properties(Properties::ReadStyle style) : AudioProperties(style)
+{
+ m_length = 0;
+ m_bitrate = 0;
+ m_sampleRate = 0;
+ m_channels = 0;
+}
+
+Wav::Properties::~Properties()
+{
+}
+
+int Wav::Properties::length() const
+{
+ return m_length;
+}
+
+int Wav::Properties::bitrate() const
+{
+ return m_bitrate;
+}
+
+int Wav::Properties::sampleRate() const
+{
+ return m_sampleRate;
+}
+
+int Wav::Properties::channels() const
+{
+ return m_channels;
+}
+
+#define swap16(x) ((((x)&0xff00)>>8) | (((x)&0x00ff)<<8))
+#define swap32(x) ((swap16((x)&0x0000ffff)<<16) | swap16(((x)&0xffff0000)>>16))
+
+void Wav::Properties::readWavProperties( FILE *fp )
+{
+ fseek(fp, 0, SEEK_SET );
+ WavHeader header;
+ if( fread(&header, sizeof(header), 1, fp) != 1 )
+ {
+ return;
+ }
+
+ m_channels = ntohs(swap16(header.num_channels));
+ m_sampleRate = ntohl(swap32(header.num_samples_per_sec));
+ m_bitrate = ntohl(swap32(header.num_avg_bytes_per_sec)) * 8 / 1000;
+ m_length = ntohl(swap32(header.num_data_bytes))/ntohl(swap32(header.num_avg_bytes_per_sec));
+}
diff --git a/src/metadata/wav/wavproperties.h b/src/metadata/wav/wavproperties.h
new file mode 100644
index 0000000..a02e734
--- /dev/null
+++ b/src/metadata/wav/wavproperties.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+
+ copyright : (C) 2005 by Andy Leadbetter
+ (original mp4 implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_WAVPROPERTIES_H
+#define TAGLIB_WAVPROPERTIES_H
+
+#include <config.h>
+
+#include <taglib/audioproperties.h>
+#include <taglib/tstring.h>
+
+namespace TagLib {
+
+ namespace Wav {
+
+ class File;
+
+ /*!
+ * This reads the data from a Wav stream to support the
+ * AudioProperties API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Initialize this structure
+ */
+ Properties(Properties::ReadStyle style);
+
+ /*!
+ * Destroys this Wav Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ void readWavProperties(FILE *file);
+
+
+ private:
+ void readAudioTrackProperties(FILE *file);
+ friend class Wav::File;
+
+ int m_length;
+ int m_bitrate;
+ int m_sampleRate;
+ int m_channels;
+
+ Properties(const Properties &);
+ Properties &operator=(const Properties &);
+
+ void read();
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/wavpack/Makefile.am b/src/metadata/wavpack/Makefile.am
new file mode 100644
index 0000000..93a98c6
--- /dev/null
+++ b/src/metadata/wavpack/Makefile.am
@@ -0,0 +1,16 @@
+SUBDIRS =
+
+INCLUDES = $(all_includes) $(taglib_includes)
+METASOURCES = AUTO
+libtagwavpack_la_LDFLAGS = $(all_libraries)
+noinst_LTLIBRARIES = libtagwavpack.la
+
+libtagwavpack_la_SOURCES = \
+ wvfile.cpp \
+ wvproperties.cpp \
+ taglib_wavpackfiletyperesolver.cpp
+
+noinst_HEADERS = wvfile.h \
+ wvproperties.h \
+ taglib_wavpackfiletyperesolver.h
+
diff --git a/src/metadata/wavpack/combinedtag.h b/src/metadata/wavpack/combinedtag.h
new file mode 100644
index 0000000..7d530d2
--- /dev/null
+++ b/src/metadata/wavpack/combinedtag.h
@@ -0,0 +1,171 @@
+/***************************************************************************
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 DO_NOT_DOCUMENT // Tell Doxygen not to document this header
+
+#ifndef TAGLIB_COMBINEDTAG_H
+#define TAGLIB_COMBINEDTAG_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Note that this header is not installed.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <tag.h>
+
+namespace TagLib {
+
+ /*!
+ * A union of two TagLib::Tags.
+ */
+ class CombinedTag : public TagLib::Tag
+ {
+ public:
+ CombinedTag(Tag *tag1 = 0, Tag *tag2 = 0)
+ : TagLib::Tag(),
+ tag1(tag1), tag2(tag2) {}
+
+ virtual String title() const {
+ if(tag1 && !tag1->title().isEmpty())
+ return tag1->title();
+
+ if(tag2)
+ return tag2->title();
+
+ return String::null;
+ }
+
+ virtual String artist() const {
+ if(tag1 && !tag1->artist().isEmpty())
+ return tag1->artist();
+
+ if(tag2)
+ return tag2->artist();
+
+ return String::null;
+ }
+
+ virtual String album() const {
+ if(tag1 && !tag1->album().isEmpty())
+ return tag1->album();
+
+ if(tag2)
+ return tag2->album();
+
+ return String::null;
+ }
+
+ virtual String comment() const {
+ if(tag1 && !tag1->comment().isEmpty())
+ return tag1->comment();
+
+ if(tag2)
+ return tag2->comment();
+
+ return String::null;
+ }
+
+ virtual String genre() const {
+ if(tag1 && !tag1->genre().isEmpty())
+ return tag1->genre();
+
+ if(tag2)
+ return tag2->genre();
+
+ return String::null;
+ }
+
+ virtual uint year() const {
+ if(tag1 && tag1->year() > 0)
+ return tag1->year();
+
+ if(tag2)
+ return tag2->year();
+
+ return 0;
+ }
+
+ virtual uint track() const {
+ if(tag1 && tag1->track() > 0)
+ return tag1->track();
+
+ if(tag2)
+ return tag2->track();
+
+ return 0;
+ }
+
+ virtual void setTitle(const String &s) {
+ if(tag1)
+ tag1->setTitle(s);
+ if(tag2)
+ tag2->setTitle(s);
+ }
+
+ virtual void setArtist(const String &s) {
+ if(tag1)
+ tag1->setArtist(s);
+ if(tag2)
+ tag2->setArtist(s);
+ }
+
+ virtual void setAlbum(const String &s) {
+ if(tag1)
+ tag1->setAlbum(s);
+ if(tag2)
+ tag2->setAlbum(s);
+ }
+
+ virtual void setComment(const String &s) {
+ if(tag1)
+ tag1->setComment(s);
+ if(tag2)
+ tag2->setComment(s);
+ }
+
+ virtual void setGenre(const String &s) {
+ if(tag1)
+ tag1->setGenre(s);
+ if(tag2)
+ tag2->setGenre(s);
+ }
+
+ virtual void setYear(uint i) {
+ if(tag1)
+ tag1->setYear(i);
+ if(tag2)
+ tag2->setYear(i);
+ }
+
+ virtual void setTrack(uint i) {
+ if(tag1)
+ tag1->setTrack(i);
+ if(tag2)
+ tag2->setTrack(i);
+ }
+
+ private:
+ Tag *tag1;
+ Tag *tag2;
+ };
+}
+
+#endif
+#endif
diff --git a/src/metadata/wavpack/taglib_wavpackfiletyperesolver.cpp b/src/metadata/wavpack/taglib_wavpackfiletyperesolver.cpp
new file mode 100644
index 0000000..fe289a2
--- /dev/null
+++ b/src/metadata/wavpack/taglib_wavpackfiletyperesolver.cpp
@@ -0,0 +1,44 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include "taglib_wavpackfiletyperesolver.h"
+#include "wvfile.h"
+
+#include <string.h>
+
+TagLib::File *WavPackFileTypeResolver::createFile(const char *fileName,
+ bool readProperties,
+ TagLib::AudioProperties::ReadStyle propertiesStyle) const
+{
+ const char *ext = strrchr(fileName, '.');
+ if(ext && !strcasecmp(ext, ".wv"))
+ {
+ TagLib::WavPack::File *f = new TagLib::WavPack::File(fileName, readProperties, propertiesStyle);
+ if(f->isValid())
+ return f;
+ else
+ {
+ delete f;
+ }
+ }
+
+ return 0;
+}
diff --git a/src/metadata/wavpack/taglib_wavpackfiletyperesolver.h b/src/metadata/wavpack/taglib_wavpackfiletyperesolver.h
new file mode 100644
index 0000000..b9d4500
--- /dev/null
+++ b/src/metadata/wavpack/taglib_wavpackfiletyperesolver.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ copyright : (C) 2006 by Martin Aumueller
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_WAVPACKFILETYPERESOLVER_H
+#define TAGLIB_WAVPACKFILETYPERESOLVER_H
+
+#include <taglib/tfile.h>
+#include <taglib/fileref.h>
+
+
+class WavPackFileTypeResolver : public TagLib::FileRef::FileTypeResolver
+{
+ TagLib::File *createFile(const char *fileName,
+ bool readAudioProperties,
+ TagLib::AudioProperties::ReadStyle audioPropertiesStyle) const;
+};
+
+#endif
diff --git a/src/metadata/wavpack/wvfile.cpp b/src/metadata/wavpack/wvfile.cpp
new file mode 100644
index 0000000..3890bc9
--- /dev/null
+++ b/src/metadata/wavpack/wvfile.cpp
@@ -0,0 +1,311 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#if 0
+#include <tdebug.h>
+#endif
+
+#include "wvfile.h"
+#include "id3v1tag.h"
+#include "id3v2header.h"
+#include "apetag.h"
+#include "apefooter.h"
+#include "combinedtag.h"
+
+using namespace TagLib;
+
+class WavPack::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ APETag(0),
+ APELocation(-1),
+ APESize(0),
+ ID3v1Tag(0),
+ ID3v1Location(-1),
+ tag(0),
+ properties(0),
+ scanned(false),
+ hasAPE(false),
+ hasID3v1(false) {}
+
+ ~FilePrivate()
+ {
+ if (tag != ID3v1Tag && tag != APETag) delete tag;
+ delete ID3v1Tag;
+ delete APETag;
+ delete properties;
+ }
+
+ APE::Tag *APETag;
+ long APELocation;
+ uint APESize;
+
+ ID3v1::Tag *ID3v1Tag;
+ long ID3v1Location;
+
+ Tag *tag;
+
+ Properties *properties;
+ bool scanned;
+
+ // These indicate whether the file *on disk* has these tags, not if
+ // this data structure does. This is used in computing offsets.
+
+ bool hasAPE;
+ bool hasID3v1;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+WavPack::File::File(const char *file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+WavPack::File::~File()
+{
+ delete d;
+}
+
+TagLib::Tag *WavPack::File::tag() const
+{
+ return d->tag;
+}
+
+WavPack::Properties *WavPack::File::audioProperties() const
+{
+ return d->properties;
+}
+
+bool WavPack::File::save()
+{
+ if(readOnly()) {
+#if 0
+ debug("WavPack::File::save() -- File is read only.");
+#endif
+ return false;
+ }
+
+ // Update ID3v1 tag
+
+ if(d->ID3v1Tag) {
+ if(d->hasID3v1) {
+ seek(d->ID3v1Location);
+ writeBlock(d->ID3v1Tag->render());
+ }
+ else {
+ seek(0, End);
+ d->ID3v1Location = tell();
+ writeBlock(d->ID3v1Tag->render());
+ d->hasID3v1 = true;
+ }
+ } else
+ if(d->hasID3v1) {
+ removeBlock(d->ID3v1Location, 128);
+ d->hasID3v1 = false;
+ if(d->hasAPE) {
+ if(d->APELocation > d->ID3v1Location)
+ d->APELocation -= 128;
+ }
+ }
+
+ // Update APE tag
+
+ if(d->APETag) {
+ if(d->hasAPE)
+ insert(d->APETag->render(), d->APELocation, d->APESize);
+ else {
+ if(d->hasID3v1) {
+ insert(d->APETag->render(), d->ID3v1Location, 0);
+ d->APESize = d->APETag->footer()->completeTagSize();
+ d->hasAPE = true;
+ d->APELocation = d->ID3v1Location;
+ d->ID3v1Location += d->APESize;
+ }
+ else {
+ seek(0, End);
+ d->APELocation = tell();
+ writeBlock(d->APETag->render());
+ d->APESize = d->APETag->footer()->completeTagSize();
+ d->hasAPE = true;
+ }
+ }
+ }
+ else
+ if(d->hasAPE) {
+ removeBlock(d->APELocation, d->APESize);
+ d->hasAPE = false;
+ if(d->hasID3v1) {
+ if (d->ID3v1Location > d->APELocation)
+ d->ID3v1Location -= d->APESize;
+ }
+ }
+
+ return true;
+}
+
+ID3v1::Tag *WavPack::File::ID3v1Tag(bool create)
+{
+ if(!create || d->ID3v1Tag)
+ return d->ID3v1Tag;
+
+ // no ID3v1 tag exists and we've been asked to create one
+
+ d->ID3v1Tag = new ID3v1::Tag;
+
+ if(d->APETag)
+ d->tag = new CombinedTag(d->APETag, d->ID3v1Tag);
+ else
+ d->tag = d->ID3v1Tag;
+
+ return d->ID3v1Tag;
+}
+
+APE::Tag *WavPack::File::APETag(bool create)
+{
+ if(!create || d->APETag)
+ return d->APETag;
+
+ // no APE tag exists and we've been asked to create one
+
+ d->APETag = new APE::Tag;
+
+ if(d->ID3v1Tag)
+ d->tag = new CombinedTag(d->APETag, d->ID3v1Tag);
+ else
+ d->tag = d->APETag;
+
+ return d->APETag;
+}
+
+void WavPack::File::remove(int tags)
+{
+ if(tags & ID3v1) {
+ delete d->ID3v1Tag;
+ d->ID3v1Tag = 0;
+
+ if(d->APETag)
+ d->tag = d->APETag;
+ else
+ d->tag = d->APETag = new APE::Tag;
+ }
+
+ if(tags & APE) {
+ delete d->APETag;
+ d->APETag = 0;
+
+ if(d->ID3v1Tag)
+ d->tag = d->ID3v1Tag;
+ else
+ d->tag = d->APETag = new APE::Tag;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
+{
+ // Look for an ID3v1 tag
+
+ d->ID3v1Location = findID3v1();
+
+ if(d->ID3v1Location >= 0) {
+ d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
+ d->hasID3v1 = true;
+ }
+
+ // Look for an APE tag
+
+ d->APELocation = findAPE();
+
+ if(d->APELocation >= 0) {
+ d->APETag = new APE::Tag(this, d->APELocation);
+ d->APESize = d->APETag->footer()->completeTagSize();
+ d->APELocation = d->APELocation + d->APETag->footer()->size() - d->APESize;
+ d->hasAPE = true;
+ }
+
+ if(d->hasID3v1 && d->hasAPE)
+ d->tag = new CombinedTag(d->APETag, d->ID3v1Tag);
+ else {
+ if(d->hasID3v1)
+ d->tag = d->ID3v1Tag;
+ else {
+ if(d->hasAPE)
+ d->tag = d->APETag;
+ else
+ d->tag = d->APETag = new APE::Tag;
+ }
+ }
+
+ // Look for WavPack audio properties
+
+ if(readProperties) {
+ seek(0);
+ d->properties = new Properties(readBlock(WavPack::HeaderSize),
+ length() - d->APESize);
+ }
+}
+
+long WavPack::File::findAPE()
+{
+ if(!isValid())
+ return -1;
+
+ if(d->hasID3v1)
+ seek(-160, End);
+ else
+ seek(-32, End);
+
+ long p = tell();
+
+ if(readBlock(8) == APE::Tag::fileIdentifier())
+ return p;
+
+ return -1;
+}
+
+long WavPack::File::findID3v1()
+{
+ if(!isValid())
+ return -1;
+
+ seek(-128, End);
+ long p = tell();
+
+ if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+ return p;
+
+ return -1;
+}
diff --git a/src/metadata/wavpack/wvfile.h b/src/metadata/wavpack/wvfile.h
new file mode 100644
index 0000000..6c57f6d
--- /dev/null
+++ b/src/metadata/wavpack/wvfile.h
@@ -0,0 +1,160 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_WVFILE_H
+#define TAGLIB_WVFILE_H
+
+#include "tfile.h"
+
+#include "wvproperties.h"
+
+namespace TagLib {
+
+ class Tag;
+
+ namespace ID3v1 { class Tag; }
+ namespace APE { class Tag; }
+
+ //! An implementation of WavPack metadata
+
+ /*!
+ * This is implementation of WavPack metadata.
+ *
+ * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
+ * properties from the file.
+ */
+
+ namespace WavPack {
+
+ //! An implementation of TagLib::File with WavPack specific methods
+
+ /*!
+ * This implements and provides an interface for WavPack files to the
+ * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
+ * the abstract TagLib::File API as well as providing some additional
+ * information specific to WavPack files.
+ */
+
+ class File : public TagLib::File
+ {
+ public:
+ /*!
+ * This set of flags is used for various operations and is suitable for
+ * being OR-ed together.
+ */
+ enum TagTypes {
+ //! Empty set. Matches no tag types.
+ NoTags = 0x0000,
+ //! Matches ID3v1 tags.
+ ID3v1 = 0x0001,
+ //! Matches APE tags.
+ APE = 0x0002,
+ //! Matches all tag types.
+ AllTags = 0xffff
+ };
+
+ /*!
+ * Contructs an WavPack file from \a file. If \a readProperties is true the
+ * file's audio properties will also be read using \a propertiesStyle. If
+ * false, \a propertiesStyle is ignored.
+ */
+ File(const char *file, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
+ * or a combination of the two.
+ */
+ virtual TagLib::Tag *tag() const;
+
+ /*!
+ * Returns the MPC::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ /*!
+ * Saves the file.
+ */
+ virtual bool save();
+
+ /*!
+ * Returns a pointer to the ID3v1 tag of the file.
+ *
+ * If \a create is false (the default) this will return a null pointer
+ * if there is no valid ID3v1 tag. If \a create is true it will create
+ * an ID3v1 tag if one does not exist. If there is already an APE tag, the
+ * new ID3v1 tag will be placed after it.
+ *
+ * \note The Tag <b>is still</b> owned by the APE::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ ID3v1::Tag *ID3v1Tag(bool create = false);
+
+ /*!
+ * Returns a pointer to the APE tag of the file.
+ *
+ * If \a create is false (the default) this will return a null pointer
+ * if there is no valid APE tag. If \a create is true it will create
+ * a APE tag if one does not exist.
+ *
+ * \note The Tag <b>is still</b> owned by the APE::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ APE::Tag *APETag(bool create = false);
+
+ /*!
+ * This will remove the tags that match the OR-ed together TagTypes from the
+ * file. By default it removes all tags.
+ *
+ * \note This will also invalidate pointers to the tags
+ * as their memory will be freed.
+ * \note In order to make the removal permanent save() still needs to be called
+ */
+ void remove(int tags = AllTags);
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+ void scan();
+ long findID3v1();
+ long findAPE();
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+}
+
+#endif
diff --git a/src/metadata/wavpack/wvproperties.cpp b/src/metadata/wavpack/wvproperties.cpp
new file mode 100644
index 0000000..376824d
--- /dev/null
+++ b/src/metadata/wavpack/wvproperties.cpp
@@ -0,0 +1,141 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 *
+ ***************************************************************************/
+
+#include <tstring.h>
+#if 0
+#include <tdebug.h>
+#endif
+#include <bitset>
+
+#include "wvproperties.h"
+#include "wvfile.h"
+
+using namespace TagLib;
+
+class WavPack::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
+ data(d),
+ streamLength(length),
+ style(s),
+ length(0),
+ bitrate(0),
+ sampleRate(0),
+ channels(0),
+ version(0),
+ bitsPerSample(0) {}
+
+ ByteVector data;
+ long streamLength;
+ ReadStyle style;
+ int length;
+ int bitrate;
+ int sampleRate;
+ int channels;
+ int version;
+ int bitsPerSample;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(data, streamLength, style);
+ read();
+}
+
+WavPack::Properties::~Properties()
+{
+ delete d;
+}
+
+int WavPack::Properties::length() const
+{
+ return d->length;
+}
+
+int WavPack::Properties::bitrate() const
+{
+ return d->bitrate;
+}
+
+int WavPack::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int WavPack::Properties::channels() const
+{
+ return d->channels;
+}
+
+int WavPack::Properties::version() const
+{
+ return d->version;
+}
+
+int WavPack::Properties::bitsPerSample() const
+{
+ return d->bitsPerSample;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+static const unsigned int sample_rates[] = { 6000, 8000, 9600, 11025, 12000,
+ 16000, 22050, 24000, 32000, 44100, 48000, 64000, 88200, 96000, 192000 };
+
+#define BYTES_STORED 3
+#define MONO_FLAG 4
+
+#define SHIFT_LSB 13
+#define SHIFT_MASK (0x1fL << SHIFT_LSB)
+
+#define SRATE_LSB 23
+#define SRATE_MASK (0xfL << SRATE_LSB)
+
+void WavPack::Properties::read()
+{
+ if(!d->data.startsWith("wvpk"))
+ return;
+
+ d->version = d->data.mid(8, 2).toShort(false);
+
+ unsigned int flags = d->data.mid(24, 4).toUInt(false);
+ d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 -
+ ((flags & SHIFT_MASK) >> SHIFT_LSB);
+ d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB];
+ d->channels = (flags & MONO_FLAG) ? 1 : 2;
+
+ unsigned int samples = d->data.mid(12, 4).toUInt(false);
+ d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
+
+ d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
+}
+
diff --git a/src/metadata/wavpack/wvproperties.h b/src/metadata/wavpack/wvproperties.h
new file mode 100644
index 0000000..b615adf
--- /dev/null
+++ b/src/metadata/wavpack/wvproperties.h
@@ -0,0 +1,86 @@
+/***************************************************************************
+ copyright : (C) 2006 by Lukáš Lalinský
+
+ copyright : (C) 2004 by Allan Sandfeld Jensen
+ (original MPC implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * This library is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Lesser General Public License version *
+ * 2.1 as published by the Free Software Foundation. *
+ * *
+ * 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 TAGLIB_WVPROPERTIES_H
+#define TAGLIB_WVPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+ namespace WavPack {
+
+ class File;
+
+ static const uint HeaderSize = 32;
+
+ //! An implementation of audio property reading for WavPack
+
+ /*!
+ * This reads the data from an WavPack stream found in the AudioProperties
+ * API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of WavPack::Properties with the data read from the
+ * ByteVector \a data.
+ */
+ Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
+
+ /*!
+ * Destroys this WavPack::Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * Returns number of bits per sample.
+ */
+ int bitsPerSample() const;
+
+ /*!
+ * Returns WavPack version.
+ */
+ int version() const;
+
+ private:
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+}
+
+#endif
diff --git a/src/options.cpp b/src/options.cpp
new file mode 100755
index 0000000..fc1a5a8
--- /dev/null
+++ b/src/options.cpp
@@ -0,0 +1,273 @@
+
+#include "options.h"
+#include "optionssimple.h"
+#include "optionsdetailed.h"
+#include "config.h"
+
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qfile.h>
+#include <qcolor.h>
+
+#include <klocale.h>
+#include <ktabwidget.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+#include <kio/job.h>
+#include <kstandarddirs.h>
+
+// FIXME prevent converting wav files to wav
+
+Options::Options( Config* _config, const QString &text, QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ config = _config;
+ connect( config, SIGNAL(configChanged()),
+ this, SLOT(configChanged())
+ );
+
+ QGridLayout *gridLayout = new QGridLayout( this, 1, 1 );
+
+ tab = new KTabWidget( this, "tab" );
+
+ optionsDetailed = new OptionsDetailed( config, this, "optionsDetailed" );
+ optionsSimple = new OptionsSimple( config, optionsDetailed, text, this, "optionsSimple" );
+
+ tab->addTab( optionsSimple, i18n("Simple") );
+ connect( optionsSimple, SIGNAL(optionsChanged()),
+ this, SLOT(somethingChanged())
+ );
+
+ tab->addTab( optionsDetailed, i18n("Detailed") );
+ connect( optionsDetailed, SIGNAL(optionsChanged()),
+ this, SLOT(somethingChanged())
+ );
+
+// connect( optionsSimple, SIGNAL(setFormat(const QString&)),
+// optionsDetailed, SLOT(setFormat(const QString&))
+// );
+// connect( optionsSimple, SIGNAL(setQualityMode(const QString&)),
+// optionsDetailed, SLOT(setQualityMode(const QString&))
+// );
+// connect( optionsSimple, SIGNAL(setQuality(int)),
+// optionsDetailed, SLOT(setQuality(int))
+// );
+// connect( optionsSimple, SIGNAL(setBitrateMode(const QString&)),
+// optionsDetailed, SLOT(setBitrateMode(const QString&))
+// );
+// connect( optionsSimple, SIGNAL(setBitrateRangeEnabled(bool)),
+// optionsDetailed, SLOT(setBitrateRangeEnabled(bool))
+// );
+// connect( optionsSimple, SIGNAL(setMinBitrate(int)),
+// optionsDetailed, SLOT(setMinBitrate(int))
+// );
+// connect( optionsSimple, SIGNAL(setMaxBitrate(int)),
+// optionsDetailed, SLOT(setMaxBitrate(int))
+// );
+// connect( optionsSimple, SIGNAL(setSamplingrateEnabled(bool)),
+// optionsDetailed, SLOT(setSamplingrateEnabled(bool))
+// );
+// connect( optionsSimple, SIGNAL(setSamplingrate(int)),
+// optionsDetailed, SLOT(setSamplingrate(int))
+// );
+// connect( optionsSimple, SIGNAL(setSamplingrate(const QString&)),
+// optionsDetailed, SLOT(setSamplingrate(const QString&))
+// );
+// connect( optionsSimple, SIGNAL(setChannelsEnabled(bool)),
+// optionsDetailed, SLOT(setChannelsEnabled(bool))
+// );
+// connect( optionsSimple, SIGNAL(setChannels(const QString&)),
+// optionsDetailed, SLOT(setChannels(const QString&))
+// );
+// connect( optionsSimple, SIGNAL(setReplayGainEnabled(bool)),
+// optionsDetailed, SLOT(setReplayGainEnabled(bool))
+// );
+// connect( optionsSimple, SIGNAL(setOutputDirectoryMode(OutputDirectory::Mode)),
+// optionsDetailed, SLOT(setOutputDirectoryMode(OutputDirectory::Mode))
+// );
+// connect( optionsSimple, SIGNAL(setOutputDirectoryPath(const QString&)),
+// optionsDetailed, SLOT(setOutputDirectoryPath(const QString&))
+// );
+// connect( optionsSimple, SIGNAL(setOptions(const ConversionOptions&)),
+// optionsDetailed, SLOT(setCurrentOptions(const ConversionOptions&))
+// );
+// connect( optionsSimple, SIGNAL(setUserOptions(const QString&)),
+// optionsDetailed, SLOT(setUserOptions(const QString&))
+// );
+
+ if( config->data.general.startTab == 0 ) tab->setCurrentPage( config->data.general.lastTab );
+ else tab->setCurrentPage( config->data.general.startTab - 1 );
+ gridLayout->addWidget( tab, 0, 0 );
+ connect( tab, SIGNAL( currentChanged(QWidget*) ),
+ this, SLOT( tabChanged(QWidget*) )
+ );
+
+ // draw the toggle button
+ QVBoxLayout *optionsTopBox = new QVBoxLayout( -1, "optionsTopBox" );
+ gridLayout->addLayout( optionsTopBox, 0, 0 );
+
+ QHBoxLayout *optionsBox = new QHBoxLayout( 6, "optionsBox" );
+ optionsTopBox->addLayout( optionsBox );
+ optionsTopBox->addStretch();
+
+ optionsBox->addStretch();
+
+// pPluginsNotify = new KPushButton( "", this, "pPluginsNotify");
+// pPluginsNotify->setPixmap( KGlobal::iconLoader()->loadIcon("connect_creating",KIcon::Toolbar) );
+// QToolTip::add( pPluginsNotify, i18n("There are new plugin updates available.\nClick on this button in order to open the configuration dialog.") );
+// pPluginsNotify->hide();
+// pPluginsNotify->setPaletteBackgroundColor( QColor(255,220,247) );
+// optionsBox->addWidget( pPluginsNotify );
+// connect( pPluginsNotify, SIGNAL(clicked()),
+// this, SLOT(showConfigDialogPlugins())
+// );
+
+ pBackendsNotify = new KPushButton( "", this, "pBackendsNotify");
+ pBackendsNotify->setPixmap( KGlobal::iconLoader()->loadIcon("kcmsystem",KIcon::Toolbar) );
+ QToolTip::add( pBackendsNotify, i18n("soundKonverter either found new backends or misses some.\nClick on this button in order to open the configuration dialog.") );
+ pBackendsNotify->setShown( config->backendsChanged );
+ config->backendsChanged = false;
+ pBackendsNotify->setPaletteBackgroundColor( QColor(255,220,247) );
+ optionsBox->addWidget( pBackendsNotify );
+ connect( pBackendsNotify, SIGNAL(clicked()),
+ this, SLOT(showConfigDialogBackends())
+ );
+
+ pAdvancedOptionsToggle = new KPushButton( i18n("Advanced Options"), this, "pAdvancedOptionsToggle");
+ pAdvancedOptionsToggle->setToggleButton( true );
+ pAdvancedOptionsToggle->hide();
+ optionsBox->addWidget( pAdvancedOptionsToggle );
+ connect( pAdvancedOptionsToggle, SIGNAL(clicked()),
+ optionsDetailed, SLOT(toggleAdvancedOptions())
+ );
+/* NOTE kaligames.de is down
+ if( config->data.plugins.checkForUpdates ) {
+ config->onlinePluginsChanged = false;
+ getPluginListJob = KIO::file_copy("http://kaligames.de/downloads/soundkonverter/plugins/download.php?version=" + QString::number(config->data.app.configVersion),
+ locateLocal("data","soundkonverter/pluginlist_new.txt"), -1, true, false, false );
+ connect( getPluginListJob, SIGNAL(result(KIO::Job*)),
+ this, SLOT(getPluginListFinished(KIO::Job*))
+ );
+ }
+*/
+ if( config->data.general.defaultProfile == i18n("Last used") || config->getAllProfiles().findIndex(config->data.general.defaultProfile) != -1 ) {
+ setCurrentOptions( config->getProfile(config->data.general.defaultProfile) );
+ }
+ else {
+ setProfile( config->data.general.defaultProfile );
+ setFormat( config->data.general.defaultFormat );
+ }
+}
+
+Options::~Options()
+{}
+
+// void Options::getPluginListFinished( KIO::Job* job )
+// {
+// if( job->error() == 0 ) {
+// QFile file( locateLocal("data","soundkonverter/pluginlist.txt") );
+// QFile newFile( locateLocal("data","soundkonverter/pluginlist_new.txt") );
+//
+// if( !file.exists() ) { // TODO check against the installed plugins
+// pPluginsNotify->show();
+// KIO::file_move( locateLocal("data","soundkonverter/pluginlist_new.txt"), locateLocal("data","soundkonverter/pluginlist.txt"), -1, true, false, false );
+// config->onlinePluginsChanged = true;
+// return;
+// }
+//
+// if( file.open(IO_ReadOnly) && newFile.open(IO_ReadOnly) ) {
+// QTextStream stream( &file );
+// QTextStream newStream( &newFile );
+// while( !stream.atEnd() && !newStream.atEnd() ) {
+// if( stream.readLine() != newStream.readLine() ) {
+// file.close();
+// newFile.close();
+// pPluginsNotify->show();
+// KIO::file_move( locateLocal("data","soundkonverter/pluginlist_new.txt"), locateLocal("data","soundkonverter/pluginlist.txt"), -1, true, false, false );
+// config->onlinePluginsChanged = true;
+// return;
+// }
+// }
+// if( stream.atEnd() != newStream.atEnd() ) {
+// file.close();
+// newFile.close();
+// pPluginsNotify->show();
+// KIO::file_move( locateLocal("data","soundkonverter/pluginlist_new.txt"), locateLocal("data","soundkonverter/pluginlist.txt"), -1, true, false, false );
+// config->onlinePluginsChanged = true;
+// return;
+// }
+// file.close();
+// newFile.close();
+// }
+// newFile.remove();
+// }
+// }
+
+// void Options::showConfigDialogPlugins()
+// {
+// pPluginsNotify->hide();
+// emit showConfigPluginsPage();
+// }
+
+void Options::showConfigDialogBackends()
+{
+ pBackendsNotify->hide();
+ emit showConfigEnvironmentPage();
+}
+
+ConversionOptions Options::getCurrentOptions()
+{
+ return optionsDetailed->getCurrentOptions();
+}
+
+void Options::setCurrentOptions( const ConversionOptions& options )
+{
+ optionsDetailed->setCurrentOptions( options );
+ /*if( tab->page(tab->currentPageIndex()) == optionsSimple ) {
+ optionsSimple->refill();
+ }*/
+ optionsSimple->refill();
+}
+
+void Options::tabChanged( QWidget* widget )
+{
+ if( widget == optionsSimple ) {
+ pAdvancedOptionsToggle->hide();
+ optionsSimple->refill();
+ }
+ else {
+ pAdvancedOptionsToggle->show();
+ }
+ config->data.general.lastTab = tab->currentPageIndex();
+}
+
+void Options::setProfile( const QString& profile )
+{
+ optionsSimple->setCurrentProfile( profile );
+}
+
+void Options::setFormat( const QString& format )
+{
+ optionsSimple->setCurrentFormat( format );
+}
+
+void Options::setOutputDirectory( const QString& directory )
+{
+ optionsSimple->setCurrentOutputDirectory( directory );
+}
+
+void Options::somethingChanged()
+{
+ emit optionsChanged();
+}
+
+// TODO right this way? - seems to work
+void Options::configChanged()
+{
+ optionsDetailed->refill();
+ /*if( tab->page(tab->currentPageIndex()) == optionsSimple ) {
+ optionsSimple->refill();
+ }*/
+ optionsSimple->refill();
+}
+
diff --git a/src/options.h b/src/options.h
new file mode 100755
index 0000000..f675aa1
--- /dev/null
+++ b/src/options.h
@@ -0,0 +1,123 @@
+
+
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <qwidget.h>
+
+#include "conversionoptions.h"
+
+#include <kio/jobclasses.h>
+
+class OptionsSimple;
+class OptionsDetailed;
+class Config;
+
+class QStringList;
+class KTabWidget;
+class KPushButton;
+
+
+/**
+ * All we need to know about a profile
+ */
+struct ProfileData
+{
+ QString name;
+ ConversionOptions options;
+};
+
+
+/**
+ * The data pool
+ */
+// struct OptionsData
+// {
+// QString name;
+// ConversionOptions options;
+// };
+
+
+/**
+ * @short The widget, where we can set our output options
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class Options : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ Options( Config*, const QString &text, QWidget* parent = 0, const char* name = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~Options();
+
+ /**
+ * Return the current options
+ */
+ ConversionOptions getCurrentOptions();
+
+public slots:
+ /**
+ * Set the current options
+ */
+ void setCurrentOptions( const ConversionOptions& );
+
+ /**
+ * Set the current profile
+ */
+ void setProfile( const QString& );
+
+ /**
+ * Set the current format
+ */
+ void setFormat( const QString& );
+
+ /**
+ * Set the current output directory
+ */
+ void setOutputDirectory( const QString& );
+
+private:
+ /** Toggle between normal and advanced options in the detailed tab */
+ KPushButton* pAdvancedOptionsToggle;
+
+ //** Notify the user about new plugins */
+// KPushButton* pPluginsNotify;
+ /** Notify the user about new / lost backends */
+ KPushButton* pBackendsNotify;
+
+ KTabWidget* tab;
+
+ OutputDirectory* outputDirectory;
+
+ OptionsSimple* optionsSimple;
+ OptionsDetailed* optionsDetailed;
+
+ Config *config;
+
+ KIO::FileCopyJob* getPluginListJob;
+
+ // /** Copy all options from the detailed tab to the simple tab / find the matching profile */
+ //void updateSimpleTab();
+
+private slots:
+ void tabChanged( QWidget* );
+ void somethingChanged();
+ void configChanged();
+// void getPluginListFinished( KIO::Job* job );
+// void showConfigDialogPlugins();
+ void showConfigDialogBackends();
+
+signals:
+ void optionsChanged();
+ void showConfigPluginsPage();
+ void showConfigEnvironmentPage();
+};
+
+#endif // OPTIONS_H
diff --git a/src/optionsdetailed.cpp b/src/optionsdetailed.cpp
new file mode 100755
index 0000000..320f3bb
--- /dev/null
+++ b/src/optionsdetailed.cpp
@@ -0,0 +1,911 @@
+
+#include "optionsdetailed.h"
+#include "convertpluginloader.h"
+#include "config.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qtooltip.h>
+#include <qwhatsthis.h>
+
+#include <knuminput.h>
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <ktoolbarbutton.h>
+#include <klineedit.h>
+#include <kinputdialog.h>
+//#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include <math.h>
+
+
+// TODO hide save button, if shown in the edit options window?
+
+// FIXME refill the formats box, when the configuration changed
+
+OptionsDetailed::OptionsDetailed( Config* _config, QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ config = _config;
+
+ QGridLayout *gridLayout = new QGridLayout( this );
+
+ // normal options
+ normalOptions = new QWidget( this, "normalOptions" );
+ gridLayout->addWidget( normalOptions, 0, 0 );
+
+ QGridLayout *normalGrid = new QGridLayout( normalOptions, 2, 1, 6, 3 );
+
+ QHBoxLayout *normalTopBox = new QHBoxLayout();
+ normalGrid->addLayout( normalTopBox, 0, 0 );
+
+ QLabel* lConvert = new QLabel( i18n("Convert")+":", normalOptions, "lConvert" );
+ normalTopBox->addWidget( lConvert, 0, Qt::AlignVCenter );
+ cFormat = new KComboBox( normalOptions, "cFormat" );
+ normalTopBox->addWidget( cFormat, 0, Qt::AlignVCenter );
+ connect( cFormat, SIGNAL(activated(int)),
+ this, SLOT(formatChanged())
+ );
+ connect( cFormat, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ cQualityMode = new KComboBox( normalOptions, "cQualityMode" );
+ cQualityMode->setFixedSize( cQualityMode->sizeHint() );
+ normalTopBox->addWidget( cQualityMode, 0, Qt::AlignVCenter );
+ connect( cQualityMode, SIGNAL(activated(int)),
+ this, SLOT(qualityModeChanged())
+ );
+ connect( cQualityMode, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ iQuality = new KIntSpinBox( normalOptions, "iQuality" );
+ normalTopBox->addWidget( iQuality, 0, Qt::AlignVCenter );
+ connect( iQuality, SIGNAL(valueChanged(int)),
+ this, SLOT(qualityChanged())
+ );
+ connect( iQuality, SIGNAL(valueChanged(int)),
+ this, SLOT(somethingChanged())
+ );
+ cBitrateMode = new KComboBox( normalOptions, "cBitrateMode" );
+ QToolTip::add( cBitrateMode, i18n("vbr - variable bitrate\nabr - average bitrate\ncbr - constant bitrate") );
+ normalTopBox->addWidget( cBitrateMode, 0, Qt::AlignVCenter );
+ connect( cBitrateMode, SIGNAL(activated(int)),
+ this, SLOT(bitrateModeChanged())
+ );
+ connect( cBitrateMode, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ normalTopBox->addSpacing( 18 );
+
+ cBitrateRangeSwitch = new QCheckBox( i18n("Bitrate range")+":", normalOptions, "cBitrateRangeSwitch" );
+ QToolTip::add( cBitrateRangeSwitch, i18n("Use it only if, you know what you are doing, you could reduce the quality.") );
+ normalTopBox->addWidget( cBitrateRangeSwitch, 0, Qt::AlignVCenter );
+ connect( cBitrateRangeSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(bitrateRangeToggled())
+ );
+ connect( cBitrateRangeSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(somethingChanged())
+ );
+ iMinBitrate = new KIntSpinBox( normalOptions, "iMinBitrate" );
+ iMinBitrate->setMinValue( 32 );
+ iMinBitrate->setMaxValue( 320 );
+ iMinBitrate->setLineStep( 8 );
+ iMinBitrate->setValue( 64 );
+ normalTopBox->addWidget( iMinBitrate, 0, Qt::AlignVCenter );
+ connect( iMinBitrate, SIGNAL(valueChanged(int)),
+ this, SLOT(somethingChanged())
+ );
+ lBitrateRangeTo = new QLabel( "-", normalOptions, "lBitrateRangeTo" );
+ normalTopBox->addWidget( lBitrateRangeTo, 0, Qt::AlignVCenter );
+ iMaxBitrate = new KIntSpinBox( normalOptions, "iMaxBitrate" );
+ iMaxBitrate->setMinValue( 32 );
+ iMaxBitrate->setMaxValue( 320 );
+ iMaxBitrate->setLineStep( 8 );
+ iMaxBitrate->setValue( 192 );
+ normalTopBox->addWidget( iMaxBitrate, 0, Qt::AlignVCenter );
+ connect( iMaxBitrate, SIGNAL(valueChanged(int)),
+ this, SLOT(somethingChanged())
+ );
+ lBitrateRangeUnit = new QLabel( "kbps", normalOptions, "lBitrateRangeUnit" );
+ normalTopBox->addWidget( lBitrateRangeUnit, 0, Qt::AlignVCenter );
+ normalTopBox->addStretch();
+
+ QHBoxLayout *normalMiddleBox = new QHBoxLayout();
+ normalGrid->addLayout( normalMiddleBox, 1, 0 );
+
+ cSamplingrateSwitch = new QCheckBox( i18n("Resample")+":", normalOptions, "cSamplingrateSwitch" );
+ normalMiddleBox->addWidget( cSamplingrateSwitch, 0, Qt::AlignVCenter );
+ connect( cSamplingrateSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(samplingrateToggled())
+ );
+ connect( cSamplingrateSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(somethingChanged())
+ );
+ cSamplingrate = new KComboBox( normalOptions, "cSamplingrate" );
+ cSamplingrate->setEditable(true);
+ cSamplingrate->insertItem("48000");
+ cSamplingrate->insertItem("44100");
+ cSamplingrate->insertItem("32000");
+ cSamplingrate->insertItem("24000");
+ cSamplingrate->insertItem("22050");
+ cSamplingrate->insertItem("16000");
+ cSamplingrate->insertItem("12000");
+ cSamplingrate->insertItem("11025");
+ cSamplingrate->insertItem("8000");
+ cSamplingrate->setCurrentText("44100");
+ normalMiddleBox->addWidget( cSamplingrate, 0, Qt::AlignVCenter );
+ connect( cSamplingrate, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ lSamplingrateUnit = new QLabel( "Hz", normalOptions, "lSamplingrateUnit" );
+ normalMiddleBox->addWidget( lSamplingrateUnit, 0, Qt::AlignVCenter );
+ normalMiddleBox->addSpacing( 18 );
+
+ cChannelsSwitch = new QCheckBox( i18n("Channels")+":", normalOptions, "cChannelsSwitch" );
+ normalMiddleBox->addWidget( cChannelsSwitch, 0, Qt::AlignVCenter );
+ connect( cChannelsSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(channelsToggled())
+ );
+ connect( cChannelsSwitch, SIGNAL(toggled(bool)),
+ this, SLOT(somethingChanged())
+ );
+ cChannels = new KComboBox( normalOptions, "cChannels" );
+/* sChannels.append( i18n("Mono") );
+ sChannels.append( i18n("Stereo") );
+ sChannels.append( i18n("Joint-Stereo") );
+ sChannels.append( i18n("Forced Joint-Stereo") );
+ sChannels.append( i18n("Dual Channels") );
+ cChannels->insertStringList( sChannels );*/
+ normalMiddleBox->addWidget( cChannels, 0, Qt::AlignVCenter );
+ connect( cChannels, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ normalMiddleBox->addSpacing( 18 );
+
+ cReplayGain = new QCheckBox( i18n("Replay Gain"), normalOptions, "cReplayGain" );
+ cReplayGain->setChecked(true);
+ QToolTip::add( cReplayGain, i18n("Add a Replay Gain tag to the converted file.") );
+ QWhatsThis::add( cReplayGain, i18n("Replay Gain is a volume correction technique. A volume difference is calculated and stored in a tag. This way audio players can automatically adjust the volume and the original music data is not modified (like at normalization).") );
+ normalMiddleBox->addWidget( cReplayGain, 0, Qt::AlignVCenter );
+ connect( cReplayGain, SIGNAL(toggled(bool)),
+ this, SLOT(somethingChanged())
+ );
+ normalMiddleBox->addStretch();
+
+ QHBoxLayout *normalBottomBox = new QHBoxLayout( );
+ normalGrid->addLayout( normalBottomBox, 2, 0 );
+
+ outputDirectory = new OutputDirectory( config, normalOptions, "outputDirectory" );
+ normalBottomBox->addWidget( outputDirectory, 0, Qt::AlignVCenter );
+ connect( outputDirectory, SIGNAL(modeChanged(OutputDirectory::Mode)),
+ this, SLOT(somethingChanged())
+ );
+ connect( outputDirectory, SIGNAL(directoryChanged(const QString&)),
+ this, SLOT(somethingChanged())
+ );
+ normalBottomBox->addSpacing( 18 );
+
+ pProfileSave = new KToolBarButton( "filesave", 1003, normalOptions, "pProfileSave" );
+ QToolTip::add( pProfileSave, i18n("Save current options as a profile") );
+ normalBottomBox->addWidget( pProfileSave, 0, Qt::AlignVCenter );
+ connect( pProfileSave, SIGNAL(clicked()),
+ this, SLOT(saveProfile())
+ );
+
+ // advanced options
+ advancedOptions = new QWidget( this, "advancedOptions" );
+ advancedOptions->hide();
+ gridLayout->addWidget( advancedOptions, 0, 0 );
+
+ QGridLayout *advancedGrid = new QGridLayout( advancedOptions, 2, 1, 6, 3 );
+
+ QHBoxLayout *advancedTopBox = new QHBoxLayout();
+ advancedGrid->addLayout( advancedTopBox, 0, 0 );
+
+ QLabel* lUserOptionsLabel = new QLabel( i18n("Command")+":", advancedOptions, "lUserOptionsLabel" );
+ advancedTopBox->addWidget( lUserOptionsLabel, 0, Qt::AlignVCenter );
+ lUserOptions = new KLineEdit( advancedOptions, "lUserOptions" );
+ advancedTopBox->addWidget( lUserOptions, 0, Qt::AlignVCenter );
+ connect( lUserOptions, SIGNAL(textChanged(const QString&)),
+ this, SLOT(somethingChanged())
+ );
+
+ QHBoxLayout *advancedMiddleBox = new QHBoxLayout();
+ advancedGrid->addLayout( advancedMiddleBox, 1, 0 );
+
+ QLabel* lInfoParams = new QLabel( i18n("%p: The parameters generated by soundKonverter"), advancedOptions, "lInfoParams" );
+ advancedMiddleBox->addWidget( lInfoParams, 0, Qt::AlignVCenter );
+
+ QHBoxLayout *advancedBottomBox = new QHBoxLayout();
+ advancedGrid->addLayout( advancedBottomBox, 2, 0 );
+
+ QLabel* lInfoFiles = new QLabel( i18n("%i: The input file ; %o: The output file"), advancedOptions, "lInfoFiles" );
+ advancedBottomBox->addWidget( lInfoFiles, 0, Qt::AlignVCenter );
+
+ refill();
+}
+
+OptionsDetailed::~OptionsDetailed()
+{}
+
+ConversionOptions OptionsDetailed::getCurrentOptions()
+{
+ ConversionOptions options;
+
+ options.encodingOptions.sFormat = cFormat->currentText();
+ options.encodingOptions.sQualityMode = cQualityMode->currentText();
+ if( iQuality->isEnabled() ) {
+ options.encodingOptions.iQuality = iQuality->value();
+ }
+ else {
+ options.encodingOptions.iQuality = 0;
+ }
+ if( cQualityMode->currentText() == i18n("Bitrate") || cQualityMode->currentText() == i18n("Quality") ) {
+ options.encodingOptions.sBitrateMode = cBitrateMode->currentText();
+ }
+ else {
+ options.encodingOptions.sBitrateMode = "";
+ }
+
+ if( cBitrateRangeSwitch->isEnabled() && cBitrateRangeSwitch->isChecked() ) {
+ options.encodingOptions.bBitrateRange = true;
+ options.encodingOptions.iMinBitrate = iMinBitrate->value();
+ options.encodingOptions.iMaxBitrate = iMaxBitrate->value();
+ }
+ else {
+ options.encodingOptions.bBitrateRange = false;
+ options.encodingOptions.iMinBitrate = 0;
+ options.encodingOptions.iMaxBitrate = 0;
+ }
+
+ if( cSamplingrateSwitch->isEnabled() && cSamplingrateSwitch->isChecked() ) {
+ options.encodingOptions.samplingRate.bEnabled = true;
+ options.encodingOptions.samplingRate.iSamplingRate = cSamplingrate->currentText().toInt();
+ }
+ else {
+ options.encodingOptions.samplingRate.bEnabled = false;
+ options.encodingOptions.samplingRate.iSamplingRate = 0;
+ }
+
+ if( cChannelsSwitch->isEnabled() && cChannelsSwitch->isChecked() ) {
+ options.encodingOptions.channels.bEnabled = true;
+ options.encodingOptions.channels.sChannels = cChannels->currentText();
+ }
+ else {
+ options.encodingOptions.channels.bEnabled = false;
+ options.encodingOptions.channels.sChannels = "";
+ }
+
+ if( cReplayGain->isEnabled() && cReplayGain->isChecked() ) {
+ options.encodingOptions.replaygain.bEnabled = true;
+ }
+ else {
+ options.encodingOptions.replaygain.bEnabled = false;
+ }
+
+ options.outputOptions.mode = outputDirectory->mode();
+ options.outputOptions.directory = outputDirectory->directory();
+
+ options.encodingOptions.sInOutFiles = lUserOptions->text();
+
+ return options;
+}
+
+void OptionsDetailed::setCurrentOptions( const ConversionOptions& options )
+{
+ // NOTE the values are not verified because they HAVE to be correct
+
+ cFormat->setCurrentItem( formatIndex(options.encodingOptions.sFormat) );
+ formatChanged();
+ cQualityMode->setCurrentItem( qualityModeIndex(options.encodingOptions.sQualityMode) );
+ qualityModeChanged();
+ if( options.encodingOptions.iQuality != 0 ) {
+ iQuality->setValue( options.encodingOptions.iQuality );
+ }
+ if( options.encodingOptions.sBitrateMode != "" ) {
+ cBitrateMode->setCurrentItem( bitrateModeIndex(options.encodingOptions.sBitrateMode) );
+ }
+
+ if( options.encodingOptions.bBitrateRange == true ) {
+ cBitrateRangeSwitch->setChecked( true );
+ iMinBitrate->setValue( options.encodingOptions.iMinBitrate );
+ iMaxBitrate->setValue( options.encodingOptions.iMaxBitrate );
+ }
+ else {
+ cBitrateRangeSwitch->setChecked( false );
+ }
+
+ if( options.encodingOptions.samplingRate.bEnabled == true ) {
+ cSamplingrateSwitch->setChecked( true );
+ cSamplingrate->setCurrentText( QString::number(options.encodingOptions.samplingRate.iSamplingRate) );
+ }
+ else {
+ cSamplingrateSwitch->setChecked( false );
+ }
+
+ if( options.encodingOptions.channels.bEnabled == true ) {
+ cChannelsSwitch->setChecked( true );
+ cChannels->setCurrentItem( channelsIndex(options.encodingOptions.channels.sChannels) );
+ }
+ else {
+ cChannelsSwitch->setChecked( false );
+ }
+
+ if( options.encodingOptions.replaygain.bEnabled == true ) {
+ cReplayGain->setChecked( true );
+ }
+ else {
+ cReplayGain->setChecked( false );
+ }
+
+ outputDirectory->setMode( options.outputOptions.mode );
+ outputDirectory->setDirectory( options.outputOptions.directory );
+
+ lUserOptions->setText( options.encodingOptions.sInOutFiles );
+}
+
+void OptionsDetailed::saveProfile()
+{
+ bool ok;
+ QString name = KInputDialog::getText( i18n("New profile"), i18n("Enter a name for the new profile:"), "", &ok );
+ if( ok ) {
+ QStringList profiles;
+ profiles += i18n("Very low");
+ profiles += i18n("Low");
+ profiles += i18n("Medium");
+ profiles += i18n("High");
+ profiles += i18n("Very high");
+ profiles += i18n("Lossless");
+ profiles += i18n("Hybrid");
+ profiles += i18n("Last used");
+ profiles += "Last used";
+ profiles += i18n("User defined");
+ if( profiles.findIndex(name) != -1 ) {
+ KMessageBox::error( this,
+ i18n("You cannot overwrite the built-in profiles."),
+ i18n("Profile already exists") );
+ return;
+ }
+ profiles = config->getAllProfiles();
+ if( profiles.findIndex(name) == -1 ) {
+ config->addProfile( name, getCurrentOptions() );
+ }
+ else {
+ int ret = KMessageBox::questionYesNo( this,
+ i18n("A profile with this name already exists.\n\nDo you want to overwrite the existing one?"),
+ i18n("Profile already exists") );
+ if( ret == KMessageBox::Yes ) {
+ config->removeProfile( name );
+ config->addProfile( name, getCurrentOptions() );
+ }
+ }
+ }
+}
+
+int OptionsDetailed::formatIndex( const QString &string )
+{
+ return sFormat.findIndex( string );
+}
+
+int OptionsDetailed::qualityModeIndex( const QString &string )
+{
+ return sQualityMode.findIndex( string );
+}
+
+int OptionsDetailed::bitrateModeIndex( const QString &string )
+{
+ return sBitrateMode.findIndex( string );
+}
+
+int OptionsDetailed::channelsIndex( const QString &string )
+{
+ return sChannels.findIndex( string );
+}
+
+void OptionsDetailed::refill()
+{
+ QString format = cFormat->currentText();
+
+ sFormat = config->allEncodableFormats();
+ sFormat.append( "wav" );
+ cFormat->clear();
+ cFormat->insertStringList( sFormat );
+
+ cFormat->setCurrentItem( formatIndex(format) );
+
+ formatChanged();
+}
+
+void OptionsDetailed::formatChanged()
+{
+ if( cFormat->currentText() == "wav" ) {
+ cReplayGain->setEnabled( false );
+ sQualityMode.clear();
+ sQualityMode.append( i18n("Lossless") );
+ cQualityMode->clear();
+ cQualityMode->insertStringList( sQualityMode );
+ qualityModeChanged();
+ lUserOptions->setEnabled( false );
+ return;
+ }
+
+ FormatItem* formatItem = config->getFormatItem( cFormat->currentText() );
+ if( formatItem == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "Convert::formatChanged() / formatItem" << "'" << endl;
+ return;
+ }
+ ConvertPlugin* plugin = formatItem->encoder;
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsDetailed::formatChanged() / plugin" << "'" << endl;
+ return;
+ }
+
+ lUserOptions->setEnabled( true );
+ QString lastString = lUserOptions->text();
+ QString bin = config->binaries[plugin->enc.bin];
+ if( lastString.left(bin.length()) != bin ) {
+ lUserOptions->setText( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+
+ QString lastQualityMode = cQualityMode->currentText();
+ sQualityMode.clear();
+
+ if( formatItem->replaygain || ( plugin->enc.replaygain.enabled && formatItem->internalReplayGain ) ) {
+ cReplayGain->setEnabled( true );
+ }
+ else {
+ cReplayGain->setEnabled( false );
+ }
+
+ if( plugin->enc.lossy.enabled ) {
+ if( plugin->enc.lossy.quality.enabled ) {
+ sQualityMode.append( i18n("Quality") );
+ }
+ if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled ) {
+ sQualityMode.append( i18n("Bitrate") );
+ }
+ if( !plugin->enc.lossy.quality.enabled && !plugin->enc.lossy.bitrate.abr.enabled && !plugin->enc.lossy.bitrate.cbr.enabled ) {
+ sQualityMode.append( i18n("Unchanged") );
+ }
+ }
+ if( plugin->enc.lossless.enabled ) {
+ sQualityMode.append( i18n("Lossless") );
+ }
+ if( plugin->enc.hybrid.enabled ) {
+ sQualityMode.append( i18n("Hybrid") );
+ }
+ if( !plugin->enc.lossy.enabled && !plugin->enc.lossless.enabled && !plugin->enc.hybrid.enabled ) {
+ sQualityMode.append( i18n("Undefined") );
+ }
+
+ cQualityMode->clear();
+ cQualityMode->insertStringList( sQualityMode );
+
+ cQualityMode->setCurrentItem( qualityModeIndex(lastQualityMode) );
+ qualityModeChanged();
+}
+
+void OptionsDetailed::qualityModeChanged()
+{
+ QWhatsThis::remove( iQuality );
+
+ if( cFormat->currentText() == "wav" ) {
+ iQuality->setEnabled( false );
+ cBitrateMode->setEnabled( false );
+ cBitrateRangeSwitch->setEnabled( false );
+ iMinBitrate->setEnabled( false );
+ lBitrateRangeTo->setEnabled( false );
+ iMaxBitrate->setEnabled( false );
+ lBitrateRangeUnit->setEnabled( false );
+ cSamplingrateSwitch->setEnabled( false );
+ cSamplingrate->setEnabled( false );
+ lSamplingrateUnit->setEnabled( false );
+ cChannelsSwitch->setEnabled( false );
+ cChannels->setEnabled( false );
+ return;
+ }
+
+ QString qualityModeString = cQualityMode->currentText();
+ QString lastString;
+ int lastValue;
+
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+
+ if( lastQualityMode != qualityModeString ) {
+ lastValue = 0;
+ lastQualityMode = qualityModeString;
+ }
+ else {
+ lastValue = iQuality->value();
+ }
+
+ if( qualityModeString != i18n("Lossless") ) {
+// bitrateRangeToggled(); NOTE seems to be unnecessary
+ if( plugin->enc.lossy.samplingrate.enabled ) cSamplingrateSwitch->setEnabled( true );
+ else cSamplingrateSwitch->setEnabled( false );
+ samplingrateToggled();
+ if( plugin->enc.lossy.channels.mono_enabled || plugin->enc.lossy.channels.stereo_enabled || plugin->enc.lossy.channels.joint_stereo_enabled ||
+ plugin->enc.lossy.channels.forced_joint_stereo_enabled || plugin->enc.lossy.channels.dual_channels_enabled ) {
+ cChannelsSwitch->setEnabled( true );
+ lastString = cChannels->currentText();
+ sChannels.clear();
+ if( plugin->enc.lossy.channels.mono_enabled ) {
+ sChannels.append( i18n("Mono") );
+ }
+ if( plugin->enc.lossy.channels.stereo_enabled ) {
+ sChannels.append( i18n("Stereo") );
+ }
+ if( plugin->enc.lossy.channels.joint_stereo_enabled ) {
+ sChannels.append( i18n("Joint-Stereo") );
+ }
+ if( plugin->enc.lossy.channels.forced_joint_stereo_enabled ) {
+ sChannels.append( i18n("Forced Joint-Stereo") );
+ }
+ if( plugin->enc.lossy.channels.dual_channels_enabled ) {
+ sChannels.append( i18n("Dual Channels") );
+ }
+ cChannels->clear();
+ cChannels->insertStringList( sChannels );
+ cChannels->setCurrentItem( channelsIndex(lastString) );
+ }
+ else cChannelsSwitch->setEnabled( false );
+ channelsToggled();
+ }
+
+ if( qualityModeString == i18n("Bitrate") ) {
+ QToolTip::add( iQuality, i18n("Kilobit per second") );
+ iQuality->setEnabled( true );
+ iQuality->setMinValue( 32 );
+ iQuality->setMaxValue( 320 );
+ iQuality->setLineStep( 8 );
+ if( lastValue != 0 ) iQuality->setValue( lastValue );
+ else iQuality->setValue( 192 );
+ cBitrateMode->setEnabled( true );
+ lastString = cBitrateMode->currentText();
+ sBitrateMode.clear();
+ if( plugin->enc.lossy.bitrate.abr.enabled ) {
+ sBitrateMode.append( "abr" );
+ }
+ if( plugin->enc.lossy.bitrate.cbr.enabled ) {
+ sBitrateMode.append( "cbr" );
+ }
+ cBitrateMode->clear();
+ cBitrateMode->insertStringList( sBitrateMode );
+ cBitrateMode->setCurrentItem( bitrateModeIndex(lastString) );
+ bitrateModeChanged();
+ }
+ else if( qualityModeString == i18n("Quality") ) {
+// QToolTip::add( iQuality, i18n("This is a relative quality between 0 and 100.\nThe higher this number the higher is the quality.\nsoundKonverter will convert it into the file format's quality format.\nSee the \"What's this?\" for more informations.") );
+ iQuality->setEnabled( true );
+ iQuality->setMinValue( 0 );
+ iQuality->setMaxValue( 100 );
+ iQuality->setLineStep( 5 );
+ if( lastValue != 0 ) iQuality->setValue( lastValue );
+ else iQuality->setValue( 40 );
+ qualityChanged();
+ cBitrateMode->setEnabled( true );
+ sBitrateMode.clear();
+ sBitrateMode.append( "vbr" );
+ cBitrateMode->clear();
+ cBitrateMode->insertStringList( sBitrateMode );
+ bitrateModeChanged();
+
+ if( plugin->enc.lossy.quality.help ) {
+ QString str = plugin->enc.lossy.quality.help;
+ str.replace("\\n","<br>");
+ QWhatsThis::add( iQuality, "<p>"+str+"</p>" );
+ }
+ }
+ else if( qualityModeString == i18n("Lossless") || qualityModeString == i18n("Undefined") ) {
+ iQuality->setEnabled( false );
+ cBitrateMode->setEnabled( false );
+ cBitrateRangeSwitch->setEnabled( false );
+ iMinBitrate->setEnabled( false );
+ lBitrateRangeTo->setEnabled( false );
+ iMaxBitrate->setEnabled( false );
+ lBitrateRangeUnit->setEnabled( false );
+ cSamplingrateSwitch->setEnabled( false );
+ cSamplingrate->setEnabled( false );
+ lSamplingrateUnit->setEnabled( false );
+ cChannelsSwitch->setEnabled( false );
+ cChannels->setEnabled( false );
+ }
+ else if( qualityModeString == i18n("Hybrid") ) {
+ QToolTip::add( iQuality, i18n("Kilobit per second") );
+ iQuality->setEnabled( true );
+ iQuality->setMinValue( 32 );
+ iQuality->setMaxValue( 320 );
+ iQuality->setLineStep( 8 );
+ if( lastValue != 0 ) iQuality->setValue( lastValue );
+ else iQuality->setValue( 192 );
+ cBitrateMode->setEnabled( false );
+ }
+ else if( qualityModeString == i18n("Unchanged") ) {
+ iQuality->setEnabled( false );
+ cBitrateMode->setEnabled( false );
+ cBitrateRangeSwitch->setEnabled( false );
+ iMinBitrate->setEnabled( false );
+ lBitrateRangeTo->setEnabled( false );
+ iMaxBitrate->setEnabled( false );
+ lBitrateRangeUnit->setEnabled( false );
+ }
+}
+
+void OptionsDetailed::qualityChanged()
+{
+ if( cQualityMode->currentText() == i18n("Quality") ) {
+ QToolTip::remove( iQuality );
+
+ FormatItem* formatItem = config->getFormatItem( cFormat->currentText() );
+ if( formatItem == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "Convert::formatChanged() / formatItem" << "'" << endl;
+ return;
+ }
+ ConvertPlugin* plugin = formatItem->encoder;
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsDetailed::formatChanged() / plugin" << "'" << endl;
+ return;
+ }
+
+ QString quality;
+ if( plugin->enc.lossy.quality.enabled ) {
+ quality = plugin->enc.lossy.quality.param;
+ int qualityLevel = iQuality->value();
+ if( plugin->enc.lossy.quality.profiles.empty() ) {
+ if( plugin->enc.lossy.quality.step < 1 ) {
+ if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
+// t_float = ( (float)item->fileListItem->options.encodingOptions.iQuality * ( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100 ) + plugin->enc.lossy.quality.range_min;
+ quality.replace( "%q", QString::number( ( (float)qualityLevel * ( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100 ) + plugin->enc.lossy.quality.range_min ) );
+ else
+// t_float = ( (100.0f - (float)item->fileListItem->options.encodingOptions.iQuality) * ( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100 ) + plugin->enc.lossy.quality.range_max;
+ quality.replace( "%q", QString::number( ( (100.0f - qualityLevel) * ( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100 ) + plugin->enc.lossy.quality.range_max ) );
+ }
+ else {
+ if( plugin->enc.lossy.quality.range_max >= plugin->enc.lossy.quality.range_min)
+// t_int = ( item->fileListItem->options.encodingOptions.iQuality * (int)( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100) + (int)plugin->enc.lossy.quality.range_min;
+ quality.replace( "%q", QString::number( (qualityLevel * (int)( plugin->enc.lossy.quality.range_max - plugin->enc.lossy.quality.range_min ) / 100) + (int)plugin->enc.lossy.quality.range_min ) );
+ else
+// t_int = ( (100 - item->fileListItem->options.encodingOptions.iQuality) * (int)( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100) + (int)plugin->enc.lossy.quality.range_max;
+ quality.replace( "%q", QString::number( ( (100 - qualityLevel) * (int)( plugin->enc.lossy.quality.range_min - plugin->enc.lossy.quality.range_max ) / 100) + (int)plugin->enc.lossy.quality.range_max ) );
+ }
+ if( plugin->enc.bin == "oggenc" ) quality.replace(QChar('.'),KGlobal::locale()->decimalSymbol()); // HACK make oggenc usable with all langauges
+ else if( plugin->enc.lossy.quality.separator != '.' ) {
+ quality.replace( QChar('.'), plugin->enc.lossy.quality.separator );
+ }
+ }
+ else {
+ QStringList::Iterator it = plugin->enc.lossy.quality.profiles.at( rint(qualityLevel*plugin->enc.lossy.quality.range_max/100) );
+ quality.replace( "%q", *it );
+ }
+ }
+
+ QToolTip::add( iQuality, i18n("This is a relative quality between 0 and 100.\nThe higher this number the higher is the quality.\nsoundKonverter will convert it into the file format's quality format.\nSee the \"What's this?\" for more informations.\n\nCurrent parameter: \"%1\"").arg(quality) );
+// QToolTip toolTip( iQuality );
+// toolTip.tip( i18n("Current parameter: \"%1\"").arg(quality) );
+ }
+}
+
+void OptionsDetailed::bitrateModeChanged()
+{
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+
+ if( cBitrateMode->currentText() == "abr" && plugin->enc.lossy.bitrate.abr.bitrate_range.enabled ) {
+ cBitrateRangeSwitch->setEnabled( true );
+ bitrateRangeToggled();
+ }
+ else {
+ cBitrateRangeSwitch->setEnabled( false );
+ iMinBitrate->setEnabled( false );
+ lBitrateRangeTo->setEnabled( false );
+ iMaxBitrate->setEnabled( false );
+ lBitrateRangeUnit->setEnabled( false );
+ }
+}
+
+void OptionsDetailed::bitrateRangeToggled()
+{
+ if( cBitrateRangeSwitch->isChecked() && cBitrateRangeSwitch->isEnabled() ) {
+ iMinBitrate->setEnabled( true );
+ lBitrateRangeTo->setEnabled( true );
+ iMaxBitrate->setEnabled( true );
+ lBitrateRangeUnit->setEnabled( true );
+ }
+ else {
+ iMinBitrate->setEnabled( false );
+ lBitrateRangeTo->setEnabled( false );
+ iMaxBitrate->setEnabled( false );
+ lBitrateRangeUnit->setEnabled( false );
+ }
+}
+
+void OptionsDetailed::samplingrateToggled() {
+ if( cSamplingrateSwitch->isChecked() && cSamplingrateSwitch->isEnabled() ) {
+ cSamplingrate->setEnabled( true );
+ lSamplingrateUnit->setEnabled( true );
+ }
+ else {
+ cSamplingrate->setEnabled( false );
+ lSamplingrateUnit->setEnabled( false );
+ }
+}
+
+void OptionsDetailed::channelsToggled() {
+ if( cChannelsSwitch->isChecked() && cChannelsSwitch->isEnabled() ) {
+ cChannels->setEnabled( true );
+ }
+ else {
+ cChannels->setEnabled( false);
+ }
+}
+
+void OptionsDetailed::somethingChanged()
+{
+ emit optionsChanged();
+}
+
+////////////////////////////////////////////////////////////
+// access the options data - BEGIN
+////////////////////////////////////////////////////////////
+
+QString OptionsDetailed::getFormat()
+{
+ return cFormat->currentText();
+}
+
+// QString OptionsDetailed::getQualityMode()
+// {
+// return cQualityMode->currentText();
+// }
+
+OutputDirectory::Mode OptionsDetailed::getOutputDirectoryMode()
+{
+ return outputDirectory->mode();
+}
+
+QString OptionsDetailed::getOutputDirectoryPath()
+{
+ return outputDirectory->directory();
+}
+
+
+void OptionsDetailed::setFormat( const QString &format )
+{
+ cFormat->setCurrentItem( formatIndex(format) );
+
+ formatChanged();
+}
+
+void OptionsDetailed::setQualityMode( const QString &qualityMode )
+{
+ cQualityMode->setCurrentItem( qualityModeIndex(qualityMode) );
+ qualityModeChanged();
+}
+
+void OptionsDetailed::setQuality( int quality )
+{
+ iQuality->setValue( quality );
+}
+
+void OptionsDetailed::setBitrateMode( const QString &bitrateMode )
+{
+ cBitrateMode->setCurrentItem( bitrateModeIndex(bitrateMode) );
+ bitrateModeChanged();
+}
+
+void OptionsDetailed::setBitrateRangeEnabled( bool enabled )
+{
+ cBitrateRangeSwitch->setChecked( enabled );
+}
+
+void OptionsDetailed::setMinBitrate( int bitrate )
+{
+ iMinBitrate->setValue( bitrate );
+}
+
+void OptionsDetailed::setMaxBitrate( int bitrate )
+{
+ iMaxBitrate->setValue( bitrate );
+}
+
+void OptionsDetailed::setSamplingrateEnabled( bool enabled )
+{
+ cSamplingrateSwitch->setChecked( enabled );
+}
+
+void OptionsDetailed::setSamplingrate( int samplingrate )
+{
+ char text[8];
+
+ sprintf( text, "%i", samplingrate );
+
+ setSamplingrate( text );
+}
+
+void OptionsDetailed::setSamplingrate( const QString &samplingrate )
+{
+ cSamplingrate->setCurrentText( samplingrate );
+}
+
+void OptionsDetailed::setChannelsEnabled( bool enabled )
+{
+ cChannelsSwitch->setChecked( enabled );
+}
+
+void OptionsDetailed::setChannels( const QString &channels )
+{
+ cChannels->setCurrentItem( channelsIndex(channels) );
+}
+
+void OptionsDetailed::setReplayGainEnabled( bool enabled )
+{
+ cReplayGain->setChecked( enabled );
+}
+
+void OptionsDetailed::setOutputDirectoryMode( OutputDirectory::Mode mode )
+{
+ outputDirectory->setMode( mode );
+}
+
+void OptionsDetailed::setOutputDirectoryPath( const QString &path )
+{
+ outputDirectory->setDirectory( path );
+}
+
+void OptionsDetailed::setUserOptions( const QString &options )
+{
+ lUserOptions->setText( options );
+}
+
+int OptionsDetailed::getQuality()
+{
+ return iQuality->value();
+}
+
+bool OptionsDetailed::getBitrateRangeEnabled()
+{
+ return cBitrateRangeSwitch->isChecked();
+}
+
+bool OptionsDetailed::getSamplingrateEnabled()
+{
+ return cSamplingrateSwitch->isChecked();
+}
+
+int OptionsDetailed::getSamplingrate()
+{
+ return cSamplingrate->currentText().toInt();
+}
+
+bool OptionsDetailed::getChannelsEnabled()
+{
+ return cChannelsSwitch->isChecked();
+}
+
+QString OptionsDetailed::getChannels()
+{
+ return cChannels->currentText();
+}
+
+////////////////////////////////////////////////////////////
+// access the options data - END
+////////////////////////////////////////////////////////////
+
+void OptionsDetailed::toggleAdvancedOptions()
+{
+ if( normalOptions->isVisible() ) {
+ normalOptions->hide();
+ advancedOptions->show();
+ }
+ else {
+ advancedOptions->hide();
+ normalOptions->show();
+ }
+}
+
diff --git a/src/optionsdetailed.h b/src/optionsdetailed.h
new file mode 100755
index 0000000..e743907
--- /dev/null
+++ b/src/optionsdetailed.h
@@ -0,0 +1,145 @@
+
+
+#ifndef OPTIONSDETAILED_H
+#define OPTIONSDETAILED_H
+
+#include "outputdirectory.h"
+#include "conversionoptions.h"
+
+#include <qwidget.h>
+
+class Config;
+//class OutputDirectory;
+class ConversionOptions;
+
+class QLabel;
+class KIntSpinBox;
+class QCheckBox;
+class KComboBox;
+class KLineEdit;
+class KToolBarButton;
+
+/**
+ * @short The options widget for setting up the output options more detailed
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class OptionsDetailed : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ OptionsDetailed( Config*, QWidget* parent=0, const char* name=0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~OptionsDetailed();
+
+ /**
+ * Return the current options
+ */
+ ConversionOptions getCurrentOptions();
+
+ /**
+ * Set the current options
+ */
+ void setCurrentOptions( const ConversionOptions& );
+
+ /**
+ * Refills the whole form (e.g. after a config change)
+ */
+ void refill(); // TODO syncronize with optionsSimple
+
+private:
+ KComboBox* cFormat;
+ KComboBox* cQualityMode;
+ KIntSpinBox* iQuality;
+ KComboBox* cBitrateMode;
+
+ QCheckBox* cBitrateRangeSwitch;
+ KIntSpinBox* iMinBitrate;
+ QLabel* lBitrateRangeTo;
+ KIntSpinBox* iMaxBitrate;
+ QLabel* lBitrateRangeUnit;
+
+ QCheckBox* cSamplingrateSwitch;
+ KComboBox* cSamplingrate;
+ QLabel* lSamplingrateUnit;
+ QCheckBox* cChannelsSwitch;
+ KComboBox* cChannels;
+ QCheckBox* cReplayGain;
+
+ KToolBarButton* pProfileSave;
+
+ KLineEdit* lUserOptions;
+
+ QWidget* normalOptions;
+ QWidget* advancedOptions;
+
+ OutputDirectory* outputDirectory;
+
+ Config* config;
+// QString getQualityMode();
+
+ QString lastQualityMode;
+
+ /** because we can't search within combo boxes, we need a seperate string lists, that we can search */
+ QStringList sFormat;
+ QStringList sQualityMode;
+ QStringList sBitrateMode;
+ QStringList sChannels;
+ int formatIndex( const QString &string );
+ int qualityModeIndex( const QString &string );
+ int bitrateModeIndex( const QString &string );
+ int channelsIndex( const QString &string );
+
+ int getQuality();
+ bool getBitrateRangeEnabled();
+ bool getSamplingrateEnabled();
+ int getSamplingrate();
+ bool getChannelsEnabled();
+ QString getChannels();
+
+private slots:
+ void formatChanged();
+ void qualityModeChanged();
+ void qualityChanged();
+ void bitrateModeChanged();
+ void bitrateRangeToggled();
+ void samplingrateToggled();
+ void channelsToggled();
+ void saveProfile();
+ void somethingChanged();
+
+public:
+ QString getFormat();
+ OutputDirectory::Mode getOutputDirectoryMode();
+ QString getOutputDirectoryPath();
+ void setFormat( const QString &format );
+ void setQualityMode( const QString &qualityMode );
+ void setQuality( int quality );
+ void setBitrateMode( const QString &bitrateMode );
+ void setBitrateRangeEnabled( bool enabled );
+ void setMinBitrate( int bitrate );
+ void setMaxBitrate( int bitrate );
+ void setSamplingrateEnabled( bool enabled );
+ void setSamplingrate( int sampleRate );
+ void setSamplingrate( const QString &sampleRate );
+ void setChannelsEnabled( bool enabled );
+ void setChannels( const QString &channels );
+ void setReplayGainEnabled( bool enabled );
+ void setOutputDirectoryMode( OutputDirectory::Mode mode );
+ void setOutputDirectoryPath( const QString &path );
+ void setUserOptions( const QString &options );
+
+public slots:
+ void toggleAdvancedOptions();
+
+signals:
+ void optionsChanged();
+};
+
+#endif // OPTIONSDETAILED_H
diff --git a/src/optionseditor.cpp b/src/optionseditor.cpp
new file mode 100755
index 0000000..efd9b1e
--- /dev/null
+++ b/src/optionseditor.cpp
@@ -0,0 +1,729 @@
+
+#include "optionseditor.h"
+#include "options.h"
+#include "tagengine.h"
+#include "filelist.h"
+#include "conversionoptions.h"
+#include "outputdirectory.h"
+
+#include <qlayout.h>
+#include <qstring.h>
+#include <qlabel.h>
+#include <qdatetime.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kpushbutton.h>
+//#include <kmessagebox.h>
+#include <klineedit.h>
+#include <kcombobox.h>
+#include <knuminput.h>
+#include <ktextedit.h>
+
+// TODO add warning, when editing tags and converting to an unsupported file format
+
+OptionsEditor::OptionsEditor( TagEngine* _tagEngine, Config* _config, FileList* _fileList, QWidget* parent, const char* name, Page startPage )
+ : KDialogBase(
+ IconList,
+ i18n("Options Editor"),
+ /*User2 | User1 |*/ Ok,
+ Ok, // default button
+ parent,
+ name,
+ false, // modal
+ true/*, // separator
+ i18n("Next"),
+ i18n("Previous")*/
+ )
+{
+ fileList = _fileList;
+ config = _config;
+ tagEngine = _tagEngine;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ resize( 400, 350 );
+ // TODO move the dialog beside the main window
+ setIcon( iconLoader->loadIcon("view_text",KIcon::Small) );
+
+// setButtonGuiItem( User2, KGuiItem(i18n("Previous"),iconLoader->loadIconSet("previous",KIcon::Small,16,true/*KDE3.5,false*/)) );
+// setButtonGuiItem( User1, KGuiItem(i18n("Next"),iconLoader->loadIconSet("next",KIcon::Small,16,true/*KDE3.5,false*/)) );
+ setButtonGuiItem( Ok, KGuiItem(i18n("Close"),iconLoader->loadIconSet("exit",KIcon::Small,16,true/*KDE3.5,false*/)) );
+
+ //// options page ////
+ conversionOptions = addPage( i18n("Conversion"), i18n("Conversion options"), iconLoader->loadIcon("soundkonverter",KIcon::Desktop) );
+ // the grid for all widgets in the main window
+ QGridLayout* conversionOptionsGridLayout = new QGridLayout( conversionOptions, 1, 1, 0, 6, "conversionOptionsGridLayout" );
+ // generate the options input area
+ options = new Options( config, i18n("Choose your prefered output options and click on \"Close\"!"), conversionOptions, "options" );
+ conversionOptionsGridLayout->addWidget( options, 0, 0 );
+ connect( options, SIGNAL(optionsChanged()),
+ this, SLOT(optionsChanged())
+ );
+ conversionOptionsGridLayout->setRowStretch( 1, 1 );
+
+ lEditOptions = new QLabel( "", conversionOptions, "lEditOptions" );
+ conversionOptionsGridLayout->addWidget( lEditOptions, 2, 0 );
+ lEditOptions->setAlignment( Qt::AlignHCenter );
+ lEditOptions->hide();
+ pEditOptions = new KPushButton( i18n("Edit conversion options"), conversionOptions, "pEditOptions" );
+ pEditOptions->setFixedWidth( pEditOptions->sizeHint().width() );
+ conversionOptionsGridLayout->addWidget( pEditOptions, 3, 0, Qt::AlignHCenter );
+ pEditOptions->hide();
+ connect( pEditOptions, SIGNAL(clicked()),
+ this, SLOT(editOptionsClicked())
+ );
+
+ //// tags page ////
+ tags = addPage( i18n("Tags"), i18n("Tags"), iconLoader->loadIcon("view_text",KIcon::Desktop) );
+ // the grid for all widgets in the main window
+ QGridLayout* tagsGridLayout = new QGridLayout( tags, 1, 1, 0, 6, "tagsGridLayout" );
+
+ // add the inputs
+ // add a horizontal box layout for the title and track number
+ QHBoxLayout *titleBox = new QHBoxLayout( -1, "titleBox" );
+ tagsGridLayout->addLayout( titleBox, 0, 1 );
+ // and fill it up
+ lTitleLabel = new QLabel( i18n("Title:"), tags, "lTitleLabel" );
+ tagsGridLayout->addWidget( lTitleLabel, 0, 0 );
+ lTitle = new KLineEdit( tags, "lTitle" );
+ titleBox->addWidget( lTitle );
+ connect( lTitle, SIGNAL(textChanged(const QString&)),
+ this, SLOT(titleChanged(const QString&))
+ );
+ pTitleEdit = new KPushButton( " ", tags, "pTitleEdit" );
+ pTitleEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pTitleEdit->setFixedSize( lTitle->sizeHint().height(), lTitle->sizeHint().height() );
+ pTitleEdit->hide();
+ titleBox->addWidget( pTitleEdit );
+ connect( pTitleEdit, SIGNAL(clicked()),
+ this, SLOT(editTitleClicked())
+ );
+ lNumberLabel = new QLabel( i18n("Track No.:"), tags, "lNumberLabel" );
+ titleBox->addWidget( lNumberLabel );
+ iNumber = new KIntSpinBox( 0, 999, 1, 1, 10, tags, "iNumber" );
+ titleBox->addWidget( iNumber );
+ connect( iNumber, SIGNAL(valueChanged(int)),
+ this, SLOT(numberChanged(int))
+ );
+ pNumberEdit = new KPushButton( " ", tags, "pNumberEdit" );
+ pNumberEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pNumberEdit->setFixedSize( iNumber->sizeHint().height(), iNumber->sizeHint().height() );
+ pNumberEdit->hide();
+ titleBox->addWidget( pNumberEdit );
+ connect( pNumberEdit, SIGNAL(clicked()),
+ this, SLOT(editNumberClicked())
+ );
+
+ // add a horizontal box layout for the artist and the composer
+ QHBoxLayout *artistBox = new QHBoxLayout( -1, "artistBox" );
+ tagsGridLayout->addLayout( artistBox, 1, 1 );
+ // and fill it up
+ lArtistLabel = new QLabel( i18n("Artist:"), tags, "lArtistLabel" );
+ tagsGridLayout->addWidget( lArtistLabel, 1, 0 );
+ lArtist = new KLineEdit( tags, "lArtist" );
+ artistBox->addWidget( lArtist );
+ connect( lArtist, SIGNAL(textChanged(const QString&)),
+ this, SLOT(artistChanged(const QString&))
+ );
+ pArtistEdit = new KPushButton( " ", tags, "pArtistEdit" );
+ pArtistEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pArtistEdit->setFixedSize( lArtist->sizeHint().height(), lArtist->sizeHint().height() );
+ pArtistEdit->hide();
+ artistBox->addWidget( pArtistEdit );
+ connect( pArtistEdit, SIGNAL(clicked()),
+ this, SLOT(editArtistClicked())
+ );
+ lComposerLabel = new QLabel( i18n("Composer:"), tags, "lComposerLabel" );
+ artistBox->addWidget( lComposerLabel );
+ lComposer = new KLineEdit( tags, "lComposer" );
+ artistBox->addWidget( lComposer );
+ connect( lComposer, SIGNAL(textChanged(const QString&)),
+ this, SLOT(composerChanged(const QString&))
+ );
+ pComposerEdit = new KPushButton( " ", tags, "pComposerEdit" );
+ pComposerEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pComposerEdit->setFixedSize( lComposer->sizeHint().height(), lComposer->sizeHint().height() );
+ pComposerEdit->hide();
+ artistBox->addWidget( pComposerEdit );
+ connect( pComposerEdit, SIGNAL(clicked()),
+ this, SLOT(editComposerClicked())
+ );
+
+ // add a horizontal box layout for the album
+ QHBoxLayout *albumBox = new QHBoxLayout( -1, "albumBox" );
+ tagsGridLayout->addLayout( albumBox, 2, 1 );
+ // and fill it up
+ lAlbumLabel = new QLabel( i18n("Album:"), tags, "lAlbumLabel" );
+ tagsGridLayout->addWidget( lAlbumLabel, 2, 0 );
+ lAlbum = new KLineEdit( tags, "lAlbum" );
+ albumBox->addWidget( lAlbum );
+ connect( lAlbum, SIGNAL(textChanged(const QString&)),
+ this, SLOT(albumChanged(const QString&))
+ );
+ pAlbumEdit = new KPushButton( " ", tags, "pAlbumEdit" );
+ pAlbumEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pAlbumEdit->setFixedSize( lAlbum->sizeHint().height(), lAlbum->sizeHint().height() );
+ pAlbumEdit->hide();
+ albumBox->addWidget( pAlbumEdit );
+ connect( pAlbumEdit, SIGNAL(clicked()),
+ this, SLOT(editAlbumClicked())
+ );
+
+ // add a horizontal box layout for the disc number, year and genre
+ QHBoxLayout *albumdataBox = new QHBoxLayout( -1, "albumdataBox" );
+ tagsGridLayout->addLayout( albumdataBox, 3, 1 );
+ // and fill it up
+ lDiscLabel = new QLabel( i18n("Disc No.:"), tags, "lDiscLabel" );
+ tagsGridLayout->addWidget( lDiscLabel, 3, 0 );
+ iDisc = new KIntSpinBox( 0, 99, 1, 1, 10, tags, "iDisc" );
+ albumdataBox->addWidget( iDisc );
+ connect( iDisc, SIGNAL(valueChanged(int)),
+ this, SLOT(discChanged(int))
+ );
+ pDiscEdit = new KPushButton( " ", tags, "pDiscEdit" );
+ pDiscEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pDiscEdit->setFixedSize( iDisc->sizeHint().height(), iDisc->sizeHint().height() );
+ pDiscEdit->hide();
+ albumdataBox->addWidget( pDiscEdit );
+ connect( pDiscEdit, SIGNAL(clicked()),
+ this, SLOT(editDiscClicked())
+ );
+ albumdataBox->addStretch();
+ lYearLabel = new QLabel( i18n("Year:"), tags, "lYearLabel" );
+ albumdataBox->addWidget( lYearLabel );
+ iYear = new KIntSpinBox( 0, 99999, 1, QDate::currentDate().year(), 10, tags, "iYear" );
+ albumdataBox->addWidget( iYear );
+ connect( iYear, SIGNAL(valueChanged(int)),
+ this, SLOT(yearChanged(int))
+ );
+ pYearEdit = new KPushButton( " ", tags, "pYearEdit" );
+ pYearEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pYearEdit->setFixedSize( iYear->sizeHint().height(), iYear->sizeHint().height() );
+ pYearEdit->hide();
+ albumdataBox->addWidget( pYearEdit );
+ connect( pYearEdit, SIGNAL(clicked()),
+ this, SLOT(editYearClicked())
+ );
+ albumdataBox->addStretch();
+ lGenreLabel = new QLabel( i18n("Genre:"), tags, "lGenreLabel" );
+ albumdataBox->addWidget( lGenreLabel );
+ cGenre = new KComboBox( true, tags, "cGenre" );
+ cGenre->insertStringList( tagEngine->genreList );
+ cGenre->setCurrentText( "" );
+ KCompletion *cGenreCompletion = cGenre->completionObject();
+ cGenreCompletion->insertItems( tagEngine->genreList );
+ cGenreCompletion->setIgnoreCase( tags );
+ albumdataBox->addWidget( cGenre );
+ connect( cGenre, SIGNAL(textChanged(const QString&)),
+ this, SLOT(genreChanged(const QString&))
+ );
+ pGenreEdit = new KPushButton( " ", tags, "pGenreEdit" );
+ pGenreEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pGenreEdit->setFixedSize( cGenre->sizeHint().height(), cGenre->sizeHint().height() );
+ pGenreEdit->hide();
+ albumdataBox->addWidget( pGenreEdit );
+ connect( pGenreEdit, SIGNAL(clicked()),
+ this, SLOT(editGenreClicked())
+ );
+
+ // add a horizontal box layout for the comment
+ QHBoxLayout *commentBox = new QHBoxLayout( -1, "commentBox" );
+ tagsGridLayout->addLayout( commentBox, 4, 1 );
+ // and fill it up
+ lCommentLabel = new QLabel( i18n("Comment:"), tags, "lCommentLabel" );
+ tagsGridLayout->addWidget( lCommentLabel, 4, 0 );
+ tComment = new KTextEdit( tags, "tComment" );
+ commentBox->addWidget( tComment );
+ connect( tComment, SIGNAL(textChanged()),
+ this, SLOT(commentChanged())
+ );
+ pCommentEdit = new KPushButton( " ", tags, "pCommentEdit" );
+ pCommentEdit->setPixmap( iconLoader->loadIcon("kwrite",KIcon::Small) );
+ pCommentEdit->setFixedSize( lTitle->sizeHint().height(), lTitle->sizeHint().height() );
+ pCommentEdit->hide();
+ commentBox->addWidget( pCommentEdit );
+ connect( pCommentEdit, SIGNAL(clicked()),
+ this, SLOT(editCommentClicked())
+ );
+ tagsGridLayout->setRowStretch( 4, 1 );
+
+ lEditTags = new QLabel( "", tags, "lEditTags" );
+ tagsGridLayout->addWidget( lEditTags, 5, 1 );
+ lEditTags->setAlignment( Qt::AlignHCenter );
+ lEditTags->hide();
+ pEditTags = new KPushButton( i18n("Edit tags"), tags, "pEditTags" );
+ pEditTags->setFixedWidth( pEditTags->sizeHint().width() );
+ tagsGridLayout->addWidget( pEditTags, 6, 1, Qt::AlignHCenter );
+ pEditTags->hide();
+ connect( pEditTags, SIGNAL(clicked()),
+ this, SLOT(editTagsClicked())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+OptionsEditor::~OptionsEditor()
+{}
+
+/*void OptionsEditor::moveWindow( int x, int y )
+{
+ move( x, y );
+}*/
+
+void OptionsEditor::setTagInputEnabled( bool enabled )
+{
+ lTitleLabel->setEnabled( enabled );
+ lTitle->setEnabled( enabled );
+ pTitleEdit->hide();
+ lNumberLabel->setEnabled( enabled );
+ iNumber->setEnabled( enabled );
+ pNumberEdit->hide();
+ lArtistLabel->setEnabled( enabled );
+ lArtist->setEnabled( enabled );
+ pArtistEdit->hide();
+ lComposerLabel->setEnabled( enabled );
+ lComposer->setEnabled( enabled );
+ pComposerEdit->hide();
+ lAlbumLabel->setEnabled( enabled );
+ lAlbum->setEnabled( enabled );
+ pAlbumEdit->hide();
+ lDiscLabel->setEnabled( enabled );
+ iDisc->setEnabled( enabled );
+ pDiscEdit->hide();
+ lYearLabel->setEnabled( enabled );
+ iYear->setEnabled( enabled );
+ pYearEdit->hide();
+ lGenreLabel->setEnabled( enabled );
+ cGenre->setEnabled( enabled );
+ pGenreEdit->hide();
+ lCommentLabel->setEnabled( enabled );
+ tComment->setEnabled( enabled );
+ tComment->setReadOnly( !enabled );
+ pCommentEdit->hide();
+
+ if( !enabled ) {
+ lTitle->setText( "" );
+ iNumber->setValue( 0 );
+ lArtist->setText( "" );
+ lComposer->setText( "" );
+ lAlbum->setText( "" );
+ iDisc->setValue( 0 );
+ iYear->setValue( 0 );
+ cGenre->setCurrentText( "" );
+ tComment->setText( "" );
+ }
+}
+
+void OptionsEditor::itemsSelected( QValueList<FileListItem*> items )
+{
+ for( QValueList<FileListItem*>::Iterator it = items.begin(); it != items.end(); ) {
+ if( (*it)->converting || (*it) == 0 ) it = items.remove( it );
+ else it++;
+ }
+
+ selectedItems = items;
+
+ if( items.count() == 0 ) {
+ setCaption( i18n("No file selected") );
+ options->setEnabled( false );
+ lEditOptions->hide();
+ pEditOptions->hide();
+ setTagInputEnabled( false );
+ lEditTags->hide();
+ pEditTags->hide();
+ return;
+ }
+
+ options->setEnabled( true );
+ lEditOptions->hide();
+ pEditOptions->hide();
+ setTagInputEnabled( true );
+ lEditTags->hide();
+ pEditTags->hide();
+
+ if( items.count() == 1 ) {
+ setCaption( KURL::decode_string(items.first()->fileName).replace("%2f","/").replace("%%","%") );
+ // HACK ...but seems to work...
+ // FIXME directory does get set properly
+ disconnect( options, SIGNAL(optionsChanged()), 0, 0 );
+ options->setCurrentOptions( items.first()->options );
+ connect( options, SIGNAL(optionsChanged()),
+ this, SLOT(optionsChanged())
+ );
+ if( items.first()->tags == 0 && !items.first()->local ) {
+ setTagInputEnabled( false );
+ lEditTags->setText( i18n("The tags could not be read, because this file isn't a local one.\n"
+ "soundKonverter will try to read the tags, when it is about to convert the file.\n"
+ "If you want to edit the tags, you can hit the button below but then soundKonverter will not try\n"
+ "to read the tags.") );
+ lEditTags->show();
+ pEditTags->show();
+ }
+ else if( items.first()->tags == 0 ) {
+ setTagInputEnabled( false );
+ lEditTags->setText( i18n("Reading the tags of this file failed.\n"
+ "soundKonverter will try to read the tags a second time, when it is about to convert the file.\n"
+ "If you want to edit the tags, you can hit the button below but then soundKonverter will not try\n"
+ "to read the tags a second time.") );
+ lEditTags->show();
+ pEditTags->show();
+ }
+ else {
+ lTitle->setText( items.first()->tags->title );
+ iNumber->setValue( items.first()->tags->track );
+ lArtist->setText( items.first()->tags->artist );
+ lComposer->setText( items.first()->tags->composer );
+ lAlbum->setText( items.first()->tags->album );
+ iDisc->setValue( items.first()->tags->disc );
+ iYear->setValue( items.first()->tags->year );
+ cGenre->setCurrentText( items.first()->tags->genre );
+ tComment->setText( items.first()->tags->comment );
+ }
+ }
+ else {
+ setCaption( i18n("%1 Files").arg(items.count()) );
+ QValueList<FileListItem*>::Iterator it = items.begin();
+ ConversionOptions cOptions = (*it)->options;
+ QString title = ( (*it)->tags == 0 ) ? "" : (*it)->tags->title;
+ int number = ( (*it)->tags == 0 ) ? 0 : (*it)->tags->track;
+ QString artist = ( (*it)->tags == 0 ) ? "" : (*it)->tags->artist;
+ QString composer = ( (*it)->tags == 0 ) ? "" : (*it)->tags->composer;
+ QString album = ( (*it)->tags == 0 ) ? "" : (*it)->tags->album;
+ int disc = ( (*it)->tags == 0 ) ? 0 : (*it)->tags->disc;
+ int year = ( (*it)->tags == 0 ) ? 0 : (*it)->tags->year;
+ QString genre = ( (*it)->tags == 0 ) ? "" : (*it)->tags->genre;
+ QString comment = ( (*it)->tags == 0 ) ? "" : (*it)->tags->comment;
+ while( it != items.end() ) {
+ if( !cOptions.nearlyEqual((*it)->options) ) {
+ options->setEnabled( false );
+ lEditOptions->setText( i18n("You have selected multiple files with different conversion options.\nYou can change the options of all files by hitting the button below.") );
+ lEditOptions->show();
+ pEditOptions->show();
+ }
+ if( (*it)->tags == 0 ) {
+ setTagInputEnabled( false );
+ lEditTags->setText( i18n("Reading the tags of one or more files failed.\n"
+ "soundKonverter will try to read the tags a second time, when it is about to convert the files.\n"
+ "If you want to edit the tags, you can hit the button below but then soundKonverter will not try\n"
+ "to read the tags a second time.") );
+ lEditTags->show();
+ pEditTags->show();
+ it++;
+ continue;
+ }
+ if( title != (*it)->tags->title && lTitle->isEnabled() ) {
+ lTitle->setEnabled( false );
+ lTitle->setText( "" );
+ pTitleEdit->show();
+ }
+ if( number != (*it)->tags->track && iNumber->isEnabled() ) {
+ iNumber->setEnabled( false );
+ iNumber->setValue( 1 );
+ pNumberEdit->show();
+ }
+ if( artist != (*it)->tags->artist && lArtist->isEnabled() ) {
+ lArtist->setEnabled( false );
+ lArtist->setText( "" );
+ pArtistEdit->show();
+ }
+ if( composer != (*it)->tags->composer && lComposer->isEnabled() ) {
+ lComposer->setEnabled( false );
+ lComposer->setText( "" );
+ pComposerEdit->show();
+ }
+ if( album != (*it)->tags->album && lAlbum->isEnabled() ) {
+ lAlbum->setEnabled( false );
+ lAlbum->setText( "" );
+ pAlbumEdit->show();
+ }
+ if( disc != (*it)->tags->disc && iDisc->isEnabled() ) {
+ iDisc->setEnabled( false );
+ iDisc->setValue( 1 );
+ pDiscEdit->show();
+ }
+ if( year != (*it)->tags->year && iYear->isEnabled() ) {
+ iYear->setEnabled( false );
+ iYear->setValue( QDate::currentDate().year() );
+ pYearEdit->show();
+ }
+ if( genre != (*it)->tags->genre && cGenre->isEnabled() ) {
+ cGenre->setEnabled( false );
+ cGenre->setCurrentText( "" );
+ pGenreEdit->show();
+ }
+ if( comment != (*it)->tags->comment && tComment->isEnabled() ) {
+ tComment->setEnabled( false );
+ tComment->setReadOnly( true );
+ tComment->setText( "" );
+ pCommentEdit->show();
+ }
+ it++;
+ }
+ if( options->isEnabled() ) {
+ // HACK ...but seems to work...
+ // FIXME directory does get set properly
+ disconnect( options, SIGNAL(optionsChanged()), 0, 0 );
+ options->setCurrentOptions( items.first()->options );
+ connect( options, SIGNAL(optionsChanged()),
+ this, SLOT(optionsChanged())
+ );
+ }
+ if( lTitle->isEnabled() ) {
+ lTitle->setText( title );
+ }
+ if( iNumber->isEnabled() ) {
+ iNumber->setValue( number );
+ }
+ if( lArtist->isEnabled() ) {
+ lArtist->setText( artist );
+ }
+ if( lComposer->isEnabled() ) {
+ lComposer->setText( composer );
+ }
+ if( lAlbum->isEnabled() ) {
+ lAlbum->setText( album );
+ }
+ if( iDisc->isEnabled() ) {
+ iDisc->setValue( disc );
+ }
+ if( iYear->isEnabled() ) {
+ iYear->setValue( year );
+ }
+ if( cGenre->isEnabled() ) {
+ cGenre->setCurrentText( genre );
+ }
+ if( tComment->isEnabled() ) {
+ tComment->setText( comment );
+ }
+ }
+}
+
+void OptionsEditor::setPreviousEnabled( bool enabled )
+{
+ enableButton( User2, enabled );
+}
+
+void OptionsEditor::setNextEnabled( bool enabled )
+{
+ enableButton( User1, enabled );
+}
+
+
+void OptionsEditor::optionsChanged()
+{
+ if( !options->isEnabled() ) return;
+
+ QString filePathName;
+// QString outputFilePathName;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ filePathName = (*it)->options.filePathName;
+// outputFilePathName = (*it)->options.outputFilePathName;
+ (*it)->options = options->getCurrentOptions();
+ (*it)->options.filePathName = filePathName;
+// (*it)->options.outputFilePathName = outputFilePathName;
+ //(*it)->updateOptionsCell();
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::titleChanged( const QString& text )
+{
+ if( !lTitle->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->title = text;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::numberChanged( int value )
+{
+ if( !iNumber->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->track = value;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::artistChanged( const QString& text )
+{
+ if( !lArtist->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->artist = text;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::composerChanged( const QString& text )
+{
+ if( !lComposer->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->composer = text;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::albumChanged( const QString& text )
+{
+ if( !lAlbum->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->album = text;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::discChanged( int value )
+{
+ if( !iDisc->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->disc = value;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::yearChanged( int value )
+{
+ if( !iYear->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->year = value;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::genreChanged( const QString& text )
+{
+ if( !cGenre->isEnabled() ) return;
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->genre = text;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+void OptionsEditor::commentChanged()
+{
+ if( !tComment->isEnabled() ) return;
+
+ QString text = tComment->text();
+
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags->comment = text;
+ //(*it)->updateOutputCell();
+ fileList->updateItem( *it );
+ }
+}
+
+
+void OptionsEditor::editTitleClicked()
+{
+ lTitle->setEnabled( true );
+ lTitle->setFocus();
+ pTitleEdit->hide();
+ titleChanged( lTitle->text() );
+}
+
+void OptionsEditor::editNumberClicked()
+{
+ iNumber->setEnabled( true );
+ iNumber->setFocus();
+ pNumberEdit->hide();
+ numberChanged( iNumber->value() );
+}
+
+void OptionsEditor::editArtistClicked()
+{
+ lArtist->setEnabled( true );
+ lArtist->setFocus();
+ pArtistEdit->hide();
+ artistChanged( lArtist->text() );
+}
+
+void OptionsEditor::editComposerClicked()
+{
+ lComposer->setEnabled( true );
+ lComposer->setFocus();
+ pComposerEdit->hide();
+ composerChanged( lComposer->text() );
+}
+
+void OptionsEditor::editAlbumClicked()
+{
+ lAlbum->setEnabled( true );
+ lAlbum->setFocus();
+ pAlbumEdit->hide();
+ albumChanged( lAlbum->text() );
+}
+
+void OptionsEditor::editDiscClicked()
+{
+ iDisc->setEnabled( true );
+ iDisc->setFocus();
+ pDiscEdit->hide();
+ discChanged( iDisc->value() );
+}
+
+void OptionsEditor::editYearClicked()
+{
+ iYear->setEnabled( true );
+ iYear->setFocus();
+ pYearEdit->hide();
+ yearChanged( iYear->value() );
+}
+
+void OptionsEditor::editGenreClicked()
+{
+ cGenre->setEnabled( true );
+ cGenre->setFocus();
+ pGenreEdit->hide();
+ genreChanged( cGenre->currentText() );
+}
+
+void OptionsEditor::editCommentClicked()
+{
+ tComment->setEnabled( true );
+ tComment->setReadOnly( false );
+ tComment->setFocus();
+ pCommentEdit->hide();
+ commentChanged();
+}
+
+
+void OptionsEditor::editOptionsClicked()
+{
+ // TODO set default / first profile (use config values)
+ options->setProfile( i18n("Medium") );
+ options->setFormat( "ogg" );
+ options->setEnabled( true );
+ lEditOptions->hide();
+ pEditOptions->hide();
+ optionsChanged();
+}
+
+void OptionsEditor::editTagsClicked()
+{
+ for( QValueList<FileListItem*>::Iterator it = selectedItems.begin(); it != selectedItems.end(); ++it ) {
+ (*it)->tags = new TagData();
+ }
+
+ itemsSelected( selectedItems );
+}
+
diff --git a/src/optionseditor.h b/src/optionseditor.h
new file mode 100755
index 0000000..d509774
--- /dev/null
+++ b/src/optionseditor.h
@@ -0,0 +1,146 @@
+
+
+#ifndef OPTIONSEDITOR_H
+#define OPTIONSEDITOR_H
+
+#include <kdialogbase.h>
+//#include <kpushbutton.h>
+
+class Options;
+class Config;
+class TagEngine;
+class FileList;
+class FileListItem;
+
+class KLineEdit;
+class KComboBox;
+class KIntSpinBox;
+class KTextEdit;
+class KPushButton;
+class QLabel;
+
+/**
+ * @short The options edit dialog that can be opened through the file list's context menu
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class OptionsEditor : public KDialogBase
+{
+ Q_OBJECT
+public:
+ enum Page {
+ OptionsPage,
+ TagsPage
+ };
+
+ /**
+ * Constructor
+ */
+ OptionsEditor( TagEngine*, Config*, FileList* _fileList, QWidget* parent = 0, const char* name=0, Page startPage = OptionsPage );
+
+ /**
+ * Destructor
+ */
+ virtual ~OptionsEditor();
+
+ FileListItem* selectedItem();
+
+private:
+ FileList* fileList;
+ Config* config;
+ TagEngine* tagEngine;
+
+ QFrame* conversionOptions;
+ QFrame* tags;
+
+ /** The widget, where we can set our output options */
+ Options* options;
+
+ /** A lineedit for entering the title of track */
+ QLabel* lTitleLabel;
+ KLineEdit* lTitle;
+ KPushButton* pTitleEdit;
+ /** A spinbox for entering or selecting the track number */
+ QLabel* lNumberLabel;
+ KIntSpinBox* iNumber;
+ KPushButton* pNumberEdit;
+ /** A lineedit for entering the artist of a track */
+ QLabel* lArtistLabel;
+ KLineEdit* lArtist;
+ KPushButton* pArtistEdit;
+ /** A lineedit for entering the composer of a track */
+ QLabel* lComposerLabel;
+ KLineEdit* lComposer;
+ KPushButton* pComposerEdit;
+ /** A lineedit for entering the album name */
+ QLabel* lAlbumLabel;
+ KLineEdit* lAlbum;
+ KPushButton* pAlbumEdit;
+ /** A spinbox for entering or selecting the disc number */
+ QLabel* lDiscLabel;
+ KIntSpinBox* iDisc;
+ KPushButton* pDiscEdit;
+ /** A spinbox for entering or selecting the year of the album */
+ QLabel* lYearLabel;
+ KIntSpinBox* iYear;
+ KPushButton* pYearEdit;
+ /** A combobox for entering or selecting the genre of the album */
+ QLabel* lGenreLabel;
+ KComboBox* cGenre;
+ KPushButton* pGenreEdit;
+ /** A textedit for entering a comment for a track */
+ QLabel* lCommentLabel;
+ KTextEdit* tComment;
+ KPushButton* pCommentEdit;
+
+ /** When hitting this button, the options lock (when multiple files are selected) will be deactivated */
+ QLabel* lEditOptions;
+ KPushButton* pEditOptions;
+
+ /** When hitting this button, the tag lock (when reading tags failed) will be deactivated */
+ QLabel* lEditTags;
+ KPushButton* pEditTags;
+
+ //FileListItem* currentItem;
+ QValueList<FileListItem*> selectedItems;
+
+ void setTagInputEnabled( bool enabled );
+
+private slots:
+ void optionsChanged();
+
+ void editTitleClicked();
+ void editNumberClicked();
+ void editArtistClicked();
+ void editComposerClicked();
+ void editAlbumClicked();
+ void editDiscClicked();
+ void editYearClicked();
+ void editGenreClicked();
+ void editCommentClicked();
+
+ void titleChanged( const QString& text );
+ void numberChanged( int value );
+ void artistChanged( const QString& text );
+ void composerChanged( const QString& text );
+ void albumChanged( const QString& text );
+ void discChanged( int value );
+ void yearChanged( int value );
+ void genreChanged( const QString& text );
+ void commentChanged();
+
+ void editOptionsClicked();
+ void editTagsClicked();
+
+public slots:
+ void itemsSelected( QValueList<FileListItem*> );
+ void setPreviousEnabled( bool );
+ void setNextEnabled( bool );
+ //void moveWindow( int x, int y );
+
+signals:
+ void previousItem();
+ void nextItem();
+};
+
+#endif // OPTIONSEDITOR_H
diff --git a/src/optionsrequester.cpp b/src/optionsrequester.cpp
new file mode 100755
index 0000000..3fc90b4
--- /dev/null
+++ b/src/optionsrequester.cpp
@@ -0,0 +1,98 @@
+
+#include "optionsrequester.h"
+#include "options.h"
+#include "config.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qstringlist.h>
+
+#include <klocale.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+
+
+OptionsRequester::OptionsRequester( Config* config, QStringList list, QWidget *parent, const char *name, bool modal, WFlags f )
+ : KDialog( parent, name, modal, f )
+{
+ setCaption( i18n("Choose output options") );
+
+ files = list;
+ int row = 1;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ QGridLayout *grid = new QGridLayout( this, 2, 1, 11, 6 );
+
+ options = new Options( config, i18n("Click on \"Ok\" to add the files to the list in the main window!"), this, "options" );
+ grid->addWidget( options, 0, 0 );
+
+ QStringList::Iterator it = files.begin();
+ while( it != files.end() )
+ {
+ QString sFormat = *it;
+ int i = sFormat.findRev( '.' );
+ sFormat.remove( 0, i + 1 );
+ sFormat.lower();
+
+ if( sFormat == "wav" ) // FIXME use mime types
+ {
+ QHBoxLayout *warningBox = new QHBoxLayout();
+ grid->addLayout( warningBox, 1, 0 );
+ QLabel* lWarning = new QLabel( i18n("Warning: If you select \"wav\" as output format, your wav files will not be added to the list."), this, "lWarning" );
+ warningBox->addWidget( lWarning );
+ row++;
+ break;
+ }
+ it++;
+ }
+
+ QHBoxLayout* buttonBox = new QHBoxLayout();
+ grid->addLayout( buttonBox, row, 0 );
+
+ buttonBox->addStretch();
+
+ pCancel = new KPushButton( iconLoader->loadIcon("exit",KIcon::Small), i18n("Cancel"), this, "pCancel" );
+ buttonBox->addWidget( pCancel );
+
+ pOk = new KPushButton( iconLoader->loadIcon("apply",KIcon::Small), i18n("Ok"), this, "pOk" );
+ buttonBox->addWidget( pOk );
+
+ connect( pCancel, SIGNAL(clicked()),
+ this, SLOT(reject())
+ );
+ connect( pOk, SIGNAL(clicked()),
+ this, SLOT(okClicked())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+OptionsRequester::~OptionsRequester()
+{}
+
+void OptionsRequester::okClicked()
+{
+ emit setCurrentOptions( options->getCurrentOptions() );
+ emit addFiles( files );
+ accept();
+}
+
+void OptionsRequester::setProfile( const QString& profile )
+{
+ options->setProfile( profile );
+}
+
+void OptionsRequester::setFormat( const QString& format )
+{
+ options->setFormat( format );
+}
+
+void OptionsRequester::setOutputDirectory( const QString& directory )
+{
+ options->setOutputDirectory( directory );
+}
+
+
diff --git a/src/optionsrequester.h b/src/optionsrequester.h
new file mode 100755
index 0000000..594e955
--- /dev/null
+++ b/src/optionsrequester.h
@@ -0,0 +1,54 @@
+
+
+#ifndef OPTIONSREQUESTER_H
+#define OPTIONSREQUESTER_H
+
+#include <kdialog.h>
+
+#include "conversionoptions.h"
+
+class Options;
+class Config;
+class QStringList;
+class KPushButton;
+
+/**
+ * @short Shows an input dialog for selecting conversion options
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class OptionsRequester : public KDialog
+{
+ Q_OBJECT
+public:
+ /**
+ * Default Constructor
+ */
+ OptionsRequester( Config*, QStringList list, QWidget *parent=0, const char *name=0, bool modal=true, WFlags f=0 );
+
+ /**
+ * Default Destructor
+ */
+ virtual ~OptionsRequester();
+
+ void setProfile( const QString& profile );
+ void setFormat( const QString& format );
+ void setOutputDirectory( const QString& directory );
+
+private slots:
+ void okClicked();
+
+signals:
+ void setCurrentOptions( const ConversionOptions& );
+ void addFiles( QStringList );
+
+private:
+ KPushButton* pOk;
+ KPushButton* pCancel;
+ QStringList files;
+
+ Options* options;
+};
+
+#endif // OPTIONSREQUESTER_H
+
diff --git a/src/optionssimple.cpp b/src/optionssimple.cpp
new file mode 100755
index 0000000..47b431f
--- /dev/null
+++ b/src/optionssimple.cpp
@@ -0,0 +1,574 @@
+
+#include "optionssimple.h"
+#include "config.h"
+#include "convertpluginloader.h"
+#include "optionsdetailed.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qstring.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <ktoolbarbutton.h>
+//#include <kdebug.h>
+#include <kpushbutton.h>
+
+
+// FIXME when changing the output directory, check if the profile is a user defined and set it to 'User defined', if it is
+
+// TODO hide lossless/hybrid/etc. when not available
+OptionsSimple::OptionsSimple( Config* _config, OptionsDetailed* _optionsDetailed, const QString &text, QWidget *parent, const char *name )
+ : QWidget( parent, name )
+{
+ config = _config;
+ optionsDetailed = _optionsDetailed;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ QGridLayout *grid = new QGridLayout( this, 3, 1, 6, 3 );
+
+ QHBoxLayout *topBox = new QHBoxLayout( );
+ grid->addLayout( topBox, 0, 0 );
+
+ QLabel *lQuality = new QLabel( i18n("Quality")+":", this, "lQuality" );
+ topBox->addWidget( lQuality, 0, Qt::AlignVCenter );
+ cProfile = new KComboBox( this, "cProfile" );
+ sProfile += i18n("Very low");
+ sProfile += i18n("Low");
+ sProfile += i18n("Medium");
+ sProfile += i18n("High");
+ sProfile += i18n("Very high");
+ sProfile += i18n("Lossless");
+ sProfile += i18n("Hybrid");
+ sProfile += config->getAllProfiles();
+ sProfile.remove( i18n("Last used") );
+ sProfile.remove( "Last used" );
+ sProfile += i18n("User defined");
+ cProfile->insertStringList( sProfile );
+ topBox->addWidget( cProfile, 0, Qt::AlignVCenter );
+ connect( cProfile, SIGNAL(activated(int)),
+ this, SLOT(profileChanged())
+ );
+ connect( cProfile, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ topBox->addSpacing( 3 );
+ //pProfileRemove = new KToolBarButton( "editdelete", 1002, this, "pProfileRemove" );
+ pProfileRemove = new KPushButton( iconLoader->loadIcon("editdelete",KIcon::Small), i18n("Remove"), this, "pProfileRemove" );
+ pProfileRemove->hide();
+ QToolTip::add( pProfileRemove, i18n("Remove the selected profile") );
+ topBox->addWidget( pProfileRemove, 0, Qt::AlignVCenter );
+ connect( pProfileRemove, SIGNAL(clicked()),
+ this, SLOT(profileRemove())
+ );
+ //pProfileInfo = new KToolBarButton( "messagebox_info", 1110, this, "pProfileInfo" );
+ pProfileInfo = new KPushButton( iconLoader->loadIcon("messagebox_info",KIcon::Small), i18n("Info"), this, "pProfileInfo" );
+ QToolTip::add( pProfileInfo, i18n("Information about the selected profile") );
+ topBox->addWidget( pProfileInfo, 0, Qt::AlignVCenter );
+ connect( pProfileInfo, SIGNAL(clicked()),
+ this, SLOT(profileInfo())
+ );
+ topBox->addSpacing( 18 );
+
+ QLabel *lFormat = new QLabel( i18n("Output format")+":", this, "lFormat" );
+ topBox->addWidget( lFormat, 0, Qt::AlignVCenter );
+ cFormat = new KComboBox( this, "cFormat" );
+ topBox->addWidget( cFormat, 0, Qt::AlignVCenter );
+ connect( cFormat, SIGNAL(activated(int)),
+ this, SLOT(formatChanged())
+ );
+ connect( cFormat, SIGNAL(activated(int)),
+ this, SLOT(somethingChanged())
+ );
+ topBox->addSpacing( 3 );
+ //pFormatInfo = new KToolBarButton( "messagebox_info", 1111, this, "pFormatInfo" );
+ pFormatInfo = new KPushButton( iconLoader->loadIcon("messagebox_info",KIcon::Small), i18n("Info"), this, "pFormatInfo" );
+ QToolTip::add( pFormatInfo, i18n("Information about the selected file format") );
+ topBox->addWidget( pFormatInfo, 0, Qt::AlignVCenter );
+ connect( pFormatInfo, SIGNAL(clicked()),
+ this, SLOT(formatInfo())
+ );
+ topBox->addStretch( );
+
+ QHBoxLayout *middleBox = new QHBoxLayout( );
+ grid->addLayout( middleBox, 1, 0 );
+
+ outputDirectory = new OutputDirectory( config, this, "outputDirectory" );
+ middleBox->addWidget( outputDirectory, 0, Qt::AlignVCenter );
+ connect( outputDirectory, SIGNAL(modeChanged(OutputDirectory::Mode)),
+ this, SLOT(outputDirectoryModeChanged(OutputDirectory::Mode))
+ );
+ connect( outputDirectory, SIGNAL(directoryChanged(const QString&)),
+ this, SLOT(outputDirectoryPathChanged(const QString&))
+ );
+ connect( outputDirectory, SIGNAL(modeChanged(OutputDirectory::Mode)),
+ this, SLOT(somethingChanged())
+ );
+ connect( outputDirectory, SIGNAL(directoryChanged(const QString&)),
+ this, SLOT(somethingChanged())
+ );
+
+ QHBoxLayout *bottomBox = new QHBoxLayout( );
+ grid->addLayout( bottomBox, 2, 0 );
+
+ QLabel *lInfo = new QLabel( text, this, "lInfo" );
+ lInfo->setFixedHeight( cProfile->height() );
+ bottomBox->addWidget( lInfo, 0, Qt::AlignVCenter | Qt::AlignLeft );
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+
+OptionsSimple::~OptionsSimple()
+{
+}
+
+int OptionsSimple::profileIndex( const QString &string )
+{
+ QString profile = string;
+ if( profile == "Very low" ) profile = i18n("Very low");
+ else if( profile == "Low" ) profile = i18n("Low");
+ else if( profile == "Medium" ) profile = i18n("Medium");
+ else if( profile == "High" ) profile = i18n("High");
+ else if( profile == "Very high" ) profile = i18n("Very high");
+ else if( profile == "Lossless" ) profile = i18n("Lossless");
+ else if( profile == "Hybrid" ) profile = i18n("Hybrid");
+ else if( profile == "User defined" ) profile = i18n("User defined");
+ return sProfile.findIndex( profile );
+}
+
+int OptionsSimple::formatIndex( const QString &string )
+{
+ return sFormat.findIndex( string );
+}
+
+void OptionsSimple::profileInfo()
+{
+ QString sProfileString = cProfile->currentText();
+
+ if( sProfileString == i18n("Very low") ) {
+ KMessageBox::information( this,
+ i18n("This produces sound files of a very low quality.\nThat can be useful, if you have a mobile device, where your memory cell is limited. It is not recommended to save your music in this quality without a copy with higher quality.\nIt can also be used to save audio files with voices."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("Low") ) {
+ KMessageBox::information( this,
+ i18n("This produces sound files of a low quality.\nThat can be useful if you habe a mobile device where your memory cell is limited. It is not recommended to save your music in this quality without a copy with higher quality."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("Medium") ) {
+ KMessageBox::information( this,
+ i18n("This produces sound files of a medium quality.\nIf your disc space is limited, you can use this to save your music."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("High") ) {
+ KMessageBox::information( this,
+ i18n("This produces sound files of a high quality.\nIf you have enough disc space available, you can use this to save your music."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("Very high") ) {
+ KMessageBox::information( this,
+ i18n("This produces sound files of a very high quality.\nYou should only use this, if you are a quality freak and have enough disc space available."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("Lossless") ) {
+ KMessageBox::information( this,
+ i18n("This produces files, that have exact the same quality as the input files.\nThis files are very big and definitely only for quality freaks."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("Hybrid") ) {
+ KMessageBox::information( this,
+ i18n("This produces two files. One lossy compressed playable file and one correction file.\nBoth files together result in a file that is equivalent to the input file."),
+ i18n("Profile")+": "+sProfileString );
+ }
+ else if( sProfileString == i18n("User defined") ) {
+ KMessageBox::information( this,
+ i18n("You can define your own profile in the \"detailed\" tab."),
+ i18n("Profile")+": "+sProfileString );
+ }
+// else { // the info button is hidden when showing user defined profiles
+// KMessageBox::error( this,
+// i18n("This is a user defined profile."),
+// i18n("Profile")+": "+sProfileString );
+// }
+}
+
+void OptionsSimple::profileRemove()
+{
+ int ret = KMessageBox::questionYesNo( this,
+ i18n("Do you really want to remove the profile: %1").arg(cProfile->currentText()),
+ i18n("Remove profile?") );
+ if( ret != KMessageBox::Yes ) return;
+
+ config->removeProfile( cProfile->currentText() );
+
+ sProfile.clear();
+ cProfile->clear();
+ sProfile += i18n("Very low");
+ sProfile += i18n("Low");
+ sProfile += i18n("Medium");
+ sProfile += i18n("High");
+ sProfile += i18n("Very high");
+ sProfile += i18n("Lossless");
+ sProfile += i18n("Hybrid");
+ sProfile += config->getAllProfiles();
+ sProfile.remove( i18n("Last used") );
+ sProfile.remove( "Last used" );
+ sProfile += i18n("User defined");
+ cProfile->insertStringList( sProfile );
+ profileChanged();
+}
+
+void OptionsSimple::formatInfo()
+{
+ QString format = cFormat->currentText();
+
+ if( format == "wav" ) {
+ KMessageBox::information( this,
+ i18n("<p>Wave is a file format, that doesn't compress it's audio data.</p>\n<p>So the quality is very high, but the file size is enormous. It is widely spread and should work with every audio player.</p>\n<a href=\"http://en.wikipedia.org/wiki/Wav\">http://en.wikipedia.org/wiki/Wav</a>"),
+ i18n("File format")+": " + format,
+ QString::null, KMessageBox::Notify | KMessageBox::AllowLink );
+ return;
+ }
+ else {
+ KMessageBox::information( this,
+ config->getFormatDescription(format),
+ i18n("File format")+": " + format,
+ QString::null, KMessageBox::Notify | KMessageBox::AllowLink );
+ }
+}
+
+void OptionsSimple::profileChanged()
+{
+ QString last;
+
+ ConversionOptions options = config->getProfile( cProfile->currentText() );
+ if( !options.encodingOptions.sFormat.isEmpty() ) {
+ pProfileRemove->show();
+ pProfileInfo->hide();
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = options.encodingOptions.sFormat;
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ outputDirectory->setMode( options.outputOptions.mode );
+ outputDirectory->setDirectory( options.outputOptions.directory );
+ optionsDetailed->setCurrentOptions( options );
+ return;
+ }
+
+ pProfileRemove->hide();
+ pProfileInfo->show();
+
+ if( cProfile->currentText() == i18n("Very low") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allLossyEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsSimple::profileChanged() / Very low" << "'" << endl;
+ return;
+ }
+ if( plugin->enc.lossy.quality.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Quality") );
+ optionsDetailed->setQuality( 20 );
+ }
+ else if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled) {
+ optionsDetailed->setQualityMode( i18n("Bitrate") );
+ optionsDetailed->setQuality( 64 );
+ }
+ optionsDetailed->setBitrateRangeEnabled( false );
+ optionsDetailed->setSamplingrateEnabled( true );
+ optionsDetailed->setSamplingrate( 22050 );
+ optionsDetailed->setChannelsEnabled( true );
+ optionsDetailed->setChannels( i18n("Mono") );
+ optionsDetailed->setReplayGainEnabled( true );
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ else if( cProfile->currentText() == i18n("Low") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allLossyEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsSimple::profileChanged() / Low" << "'" << endl;
+ return;
+ }
+ if( plugin->enc.lossy.quality.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Quality") );
+ optionsDetailed->setQuality( 30 );
+ }
+ else if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Bitrate") );
+ optionsDetailed->setQuality( 96 );
+ }
+ optionsDetailed->setBitrateRangeEnabled( false );
+ optionsDetailed->setSamplingrateEnabled( true );
+ optionsDetailed->setSamplingrate( 22050 );
+ optionsDetailed->setChannelsEnabled( false );
+ optionsDetailed->setReplayGainEnabled( true );
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ else if( cProfile->currentText() == i18n("Medium") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allLossyEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsSimple::profileChanged() / Medium" << "'" << endl;
+ return;
+ }
+ if( plugin->enc.lossy.quality.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Quality") );
+ optionsDetailed->setQuality( 40 );
+ }
+ else if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Bitrate") );
+ optionsDetailed->setQuality( 192 );
+ }
+ optionsDetailed->setBitrateRangeEnabled( false );
+ optionsDetailed->setSamplingrateEnabled( false );
+ optionsDetailed->setChannelsEnabled( false );
+ optionsDetailed->setReplayGainEnabled( true );
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ else if( cProfile->currentText() == i18n("High") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allLossyEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsSimple::profileChanged() / High" << "'" << endl;
+ return;
+ }
+ if( plugin->enc.lossy.quality.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Quality") );
+ optionsDetailed->setQuality( 50 );
+ }
+ else if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Bitrate") );
+ optionsDetailed->setQuality( 240 );
+ }
+ optionsDetailed->setBitrateRangeEnabled( false );
+ optionsDetailed->setSamplingrateEnabled( false );
+ optionsDetailed->setChannelsEnabled( false );
+ optionsDetailed->setReplayGainEnabled( true );
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ else if( cProfile->currentText() == i18n("Very high") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allLossyEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ if( plugin == 0 ) {
+ // FIXME error handling
+ //kdDebug() << "NULL POINTER: `" << "OptionsSimple::profileChanged() / Very high" << "'" << endl;
+ return;
+ }
+ if( plugin->enc.lossy.quality.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Quality") );
+ optionsDetailed->setQuality( 60 );
+ }
+ else if( plugin->enc.lossy.bitrate.abr.enabled || plugin->enc.lossy.bitrate.cbr.enabled ) {
+ optionsDetailed->setQualityMode( i18n("Bitrate") );
+ optionsDetailed->setQuality( 320 );
+ }
+ optionsDetailed->setBitrateRangeEnabled( false );
+ optionsDetailed->setSamplingrateEnabled( false );
+ optionsDetailed->setChannelsEnabled( false );
+ optionsDetailed->setReplayGainEnabled( true );
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ else if( cProfile->currentText() == i18n("Lossless") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allLosslessEncodableFormats();
+ sFormat += "wav";
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ optionsDetailed->setReplayGainEnabled( true );
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ if( plugin != 0 ) {
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ }
+ else if( cProfile->currentText() == i18n("Hybrid")/* || cProfile->currentText() == "Hybrid"*/ ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allHybridEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ optionsDetailed->setReplayGainEnabled( true );
+ ConvertPlugin* plugin = config->encoderForFormat( cFormat->currentText() );
+ optionsDetailed->setQualityMode( i18n("Hybrid") );
+ if( plugin != 0 ) {
+ optionsDetailed->setUserOptions( config->binaries[plugin->enc.bin] + " " + plugin->enc.in_out_files );
+ }
+ }
+ else if( cProfile->currentText() == i18n("User defined") ) {
+ last = cFormat->currentText();
+ cFormat->clear();
+ sFormat = config->allEncodableFormats();
+ sFormat += "wav";
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(last) );
+ formatChanged();
+ }
+}
+
+void OptionsSimple::formatChanged()
+{
+ optionsDetailed->setFormat( cFormat->currentText() );
+}
+
+void OptionsSimple::outputDirectoryModeChanged( OutputDirectory::Mode mode )
+{
+ optionsDetailed->setOutputDirectoryMode( mode );
+ if( cProfile->currentText() != i18n("User defined") && config->getAllProfiles().findIndex(cProfile->currentText()) != -1 ) {
+ ConversionOptions options = config->getProfile( cProfile->currentText() );
+// if( options.encodingOptions.sFormat.isEmpty() ) return;
+ if( outputDirectory->mode() != options.outputOptions.mode || outputDirectory->directory() != options.outputOptions.directory ) {
+ cProfile->setCurrentItem( profileIndex(i18n("User defined")) ); // NOTE not refill() ?
+ profileChanged();
+ }
+// refill();
+ }
+}
+
+void OptionsSimple::outputDirectoryPathChanged( const QString& path )
+{
+ optionsDetailed->setOutputDirectoryPath( path );
+ if( cProfile->currentText() != i18n("User defined") && config->getAllProfiles().findIndex(cProfile->currentText()) != -1 ) {
+ ConversionOptions options = config->getProfile( cProfile->currentText() );
+// if( options.encodingOptions.sFormat.isEmpty() ) return;
+ if( outputDirectory->mode() != options.outputOptions.mode || outputDirectory->directory() != options.outputOptions.directory ) {
+ cProfile->setCurrentItem( profileIndex(i18n("User defined")) ); // NOTE not refill() ?
+ profileChanged();
+ }
+// refill();
+ }
+}
+
+void OptionsSimple::setCurrentProfile( const QString& profile )
+{
+ // TODO check profile (and don't change, if not available)
+ cProfile->setCurrentItem( profileIndex(profile) );
+ profileChanged();
+}
+
+void OptionsSimple::setCurrentFormat( const QString& format )
+{
+ cFormat->setCurrentItem( formatIndex(format) );
+ formatChanged();
+}
+
+// TODO check for errors
+void OptionsSimple::setCurrentOutputDirectory( const QString& directory )
+{
+ outputDirectory->setMode( OutputDirectory::Specify );
+ outputDirectory->setDirectory( directory );
+ optionsDetailed->setOutputDirectoryMode( OutputDirectory::Specify );
+ optionsDetailed->setOutputDirectoryPath( directory );
+}
+
+void OptionsSimple::somethingChanged()
+{
+ emit optionsChanged();
+}
+
+void OptionsSimple::refill()
+{
+ sProfile.clear();
+ cProfile->clear();
+ sProfile += i18n("Very low");
+ sProfile += i18n("Low");
+ sProfile += i18n("Medium");
+ sProfile += i18n("High");
+ sProfile += i18n("Very high");
+ sProfile += i18n("Lossless");
+ sProfile += i18n("Hybrid");
+ sProfile += config->getAllProfiles();
+ sProfile.remove( i18n("Last used") );
+ sProfile.remove( "Last used" );
+ sProfile += i18n("User defined");
+ cProfile->insertStringList( sProfile );
+
+ cProfile->setCurrentItem( profileIndex(config->getProfileName(optionsDetailed->getCurrentOptions())) );
+
+ if( cProfile->currentText() == i18n("Very low") || cProfile->currentText() == i18n("Low") ||
+ cProfile->currentText() == i18n("Medium") || cProfile->currentText() == i18n("High") ||
+ cProfile->currentText() == i18n("Very high") ) {
+ cFormat->clear();
+ sFormat = config->allLossyEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(optionsDetailed->getFormat()) );
+ pProfileInfo->show();
+ pProfileRemove->hide();
+ }
+ else if( cProfile->currentText() == i18n("Lossless") ) {
+ cFormat->clear();
+ sFormat = config->allLosslessEncodableFormats();
+ sFormat += "wav";
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(optionsDetailed->getFormat()) );
+ pProfileInfo->show();
+ pProfileRemove->hide();
+ }
+ else if( cProfile->currentText() == i18n("Hybrid") ) {
+ cFormat->clear();
+ sFormat = config->allHybridEncodableFormats();
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(optionsDetailed->getFormat()) );
+ pProfileInfo->show();
+ pProfileRemove->hide();
+ }
+ else if( cProfile->currentText() == i18n("User defined") ) {
+ cFormat->clear();
+ sFormat = config->allEncodableFormats();
+ sFormat += "wav";
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(optionsDetailed->getFormat()) );
+ pProfileInfo->show();
+ pProfileRemove->hide();
+ }
+ else {
+ ConversionOptions options = config->getProfile( cProfile->currentText() );
+ cFormat->clear();
+ sFormat = options.encodingOptions.sFormat;
+ cFormat->insertStringList( sFormat );
+ cFormat->setCurrentItem( formatIndex(optionsDetailed->getFormat()) );
+ pProfileInfo->hide();
+ pProfileRemove->show();
+ }
+
+ outputDirectory->setMode( optionsDetailed->getOutputDirectoryMode() );
+ outputDirectory->setDirectory( optionsDetailed->getOutputDirectoryPath() );
+}
+
diff --git a/src/optionssimple.h b/src/optionssimple.h
new file mode 100755
index 0000000..bb88d22
--- /dev/null
+++ b/src/optionssimple.h
@@ -0,0 +1,107 @@
+
+
+#ifndef OPTIONSSIMPLE_H
+#define OPTIONSSIMPLE_H
+
+#include "outputdirectory.h"
+#include "conversionoptions.h"
+
+#include <qwidget.h>
+
+class Config;
+class ConversionOptions;
+class OptionsDetailed;
+
+class KComboBox;
+class KToolBarButton;
+class KPushButton;
+
+/**
+ * @short The options widget for setting up the output options very easy
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class OptionsSimple : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ OptionsSimple( Config*, OptionsDetailed*, const QString &text, QWidget* parent=0, const char* name=0 );
+
+ /**
+ * Detructor
+ */
+ virtual ~OptionsSimple();
+
+ /**
+ * Set the current options
+ */
+// void setCurrentOptions( const ConversionOptions& );
+
+ /**
+ * Refills the whole form (e.g. after a config change)
+ */
+ void refill(); // TODO syncronize with optionsDetailed
+
+ void setCurrentProfile( const QString& profile );
+ void setCurrentFormat( const QString& format );
+ void setCurrentOutputDirectory( const QString& directory );
+
+private:
+ KComboBox* cProfile;
+ KPushButton* pProfileRemove;
+ KPushButton* pProfileInfo;
+ KComboBox* cFormat;
+ KPushButton* pFormatInfo;
+
+ OutputDirectory* outputDirectory;
+
+ Config* config;
+ OptionsDetailed* optionsDetailed;
+
+ QStringList sProfile;
+ QStringList sFormat;
+
+ int profileIndex( const QString& string );
+ int formatIndex( const QString& string );
+
+//public slots:
+// void setProfile( const QString &profile );
+// void setFormat( const QString &format );
+
+private slots:
+ void profileInfo();
+ void profileRemove();
+ void formatInfo();
+ void profileChanged();
+ void formatChanged();
+ void outputDirectoryModeChanged( OutputDirectory::Mode );
+ void outputDirectoryPathChanged( const QString& );
+
+ void somethingChanged();
+
+signals:
+// void setFormat( const QString& format );
+// void setQualityMode( const QString& qualityMode );
+// void setQuality( int quality );
+// void setBitrateMode( const QString& bitrateMode );
+// void setBitrateRangeEnabled( bool enabled );
+// void setMinBitrate( int bitrate );
+// void setMaxBitrate( int bitrate );
+// void setSamplingrateEnabled( bool enabled );
+// void setSamplingrate( int sampleRate );
+// void setSamplingrate( const QString& sampleRate );
+// void setChannelsEnabled( bool enabled );
+// void setChannels( const QString& channels );
+// void setReplayGainEnabled( bool enabled );
+// void setOutputDirectoryMode( OutputDirectory::Mode );
+// void setOutputDirectoryPath( const QString& directory );
+// void setOptions( const ConversionOptions& options );
+// void setUserOptions( const QString& options );
+
+ void optionsChanged();
+};
+
+#endif
diff --git a/src/outputdirectory.cpp b/src/outputdirectory.cpp
new file mode 100755
index 0000000..88a5466
--- /dev/null
+++ b/src/outputdirectory.cpp
@@ -0,0 +1,571 @@
+
+#include "outputdirectory.h"
+#include "filelist.h"
+#include "conversionoptions.h"
+#include "tagengine.h"
+#include "config.h"
+
+#include <qlayout.h>
+#include <qtooltip.h>
+#include <qdir.h>
+#include <qfileinfo.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qlabel.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmessagebox.h>
+#include <kfiledialog.h>
+#include <ktoolbarbutton.h>
+#include <kcombobox.h>
+#include <klineedit.h>
+#include <kuser.h>
+#include <kmountpoint.h>
+
+
+OutputDirectory::OutputDirectory( Config* _config, QWidget* parent, const char* name )
+ : QWidget( parent, name )
+{
+ config = _config;
+
+ // create an icon loader object for loading icons
+ KIconLoader *iconLoader = new KIconLoader();
+
+ QGridLayout *grid = new QGridLayout( this, 1, 1, 0, 3, "grid" );
+
+ QHBoxLayout *box = new QHBoxLayout( );
+ grid->addLayout( box, 0, 0 );
+
+ QLabel *lOutput = new QLabel( i18n("Output")+":", this, "lOutput" );
+ box->addWidget(lOutput);
+
+ cMode = new KComboBox( this );
+// cMode->insertItem( i18n("Default output directory") );
+ cMode->insertItem( i18n("By meta data") );
+ cMode->insertItem( i18n("Source directory") );
+ cMode->insertItem( i18n("Specify output directory") );
+ cMode->insertItem( i18n("Copy directory structure") );
+ //QToolTip::add( cMode, i18n("Output all converted files into...") );
+ box->addWidget( cMode );
+ connect( cMode, SIGNAL(activated(int)),
+ this, SLOT(modeChangedSlot(int))
+ );
+ modeJustChanged = false;
+ /*pModeInfo = new KToolBarButton( "messagebox_info", 1010, this, "pModeInfo" );
+ QToolTip::add( pModeInfo, i18n("Information about the output mode") );
+ box->addWidget( pModeInfo );
+ connect( pModeInfo, SIGNAL(clicked()),
+ this, SLOT(modeInfo())
+ );*/
+ pClear = new KToolBarButton( "locationbar_erase", 1001, this, "pClear" );
+ QToolTip::add( pClear, i18n("Clear the directory input field") );
+ box->addWidget( pClear );
+ lDir = new KLineEdit( this, "lDir" );
+ box->addWidget( lDir );
+ connect( lDir, SIGNAL(textChanged(const QString&)),
+ this, SLOT(directoryChangedSlot(const QString&))
+ );
+ connect( pClear, SIGNAL(clicked()),
+ lDir, SLOT(setFocus())
+ );
+ connect( pClear, SIGNAL(clicked()),
+ lDir, SLOT(clear())
+ );
+ /*pDirInfo = new KToolBarButton( "messagebox_info", 1011, this, "pDirInfo" );
+ QToolTip::add( pDirInfo, i18n("Information about the wildcards") );
+ box->addWidget( pDirInfo );
+ connect( pDirInfo, SIGNAL(clicked()),
+ this, SLOT(dirInfo())
+ );*/
+ pDirSelect = new KToolBarButton( "folder", 1012, this, "pDirSelect" );
+ QToolTip::add( pDirSelect, i18n("Choose an output directory") );
+ box->addWidget( pDirSelect );
+ connect( pDirSelect, SIGNAL(clicked()),
+ this, SLOT(selectDir())
+ );
+ pDirGoto = new KToolBarButton( "konqueror", 1013, this, "pDirGoto" );
+ QToolTip::add( pDirGoto, i18n("Open Konqueror with the output directory") );
+ box->addWidget( pDirGoto );
+ connect( pDirGoto, SIGNAL(clicked()),
+ this, SLOT(gotoDir())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+
+ modeChangedSlot( MetaData ); // TODO implement proper
+ // save the current directory always on text change
+}
+
+OutputDirectory::~OutputDirectory()
+{}
+
+void OutputDirectory::disable()
+{
+ cMode->setEnabled( false );
+ lDir->setEnabled( false );
+ pClear->setEnabled( false );
+ pDirSelect->setEnabled( false );
+}
+
+void OutputDirectory::enable()
+{
+ cMode->setEnabled( true );
+ modeChangedSlot( cMode->currentItem() );
+}
+
+OutputDirectory::Mode OutputDirectory::mode()
+{
+ return (Mode)cMode->currentItem();
+}
+
+void OutputDirectory::setMode( OutputDirectory::Mode mode )
+{
+ cMode->setCurrentItem( (int)mode );
+ modeChangedSlot( (int)mode );
+}
+
+QString OutputDirectory::directory()
+{
+ if( /*(Mode)cMode->currentItem() != Default && */(Mode)cMode->currentItem() != Source ) return lDir->text();
+ else return "";
+}
+
+void OutputDirectory::setDirectory( const QString& directory )
+{
+ if( /*(Mode)cMode->currentItem() != Default && */(Mode)cMode->currentItem() != Source ) lDir->setText( directory );
+}
+
+QString OutputDirectory::calcPath( FileListItem* fileListItem, Config* config, QString extension )
+{
+ // TODO replace '//' by '/' ???
+ // FIXME test fvat names
+ QString path;
+ if( extension.isEmpty() ) extension = fileListItem->options.encodingOptions.sFormat;
+
+ // FIXME file name
+ QString fileName;
+ if( fileListItem->track == -1 ) fileName = fileListItem->fileName;
+ else if( fileListItem->tags != 0 ) fileName = QString().sprintf("%02i",fileListItem->tags->track) + " - " + fileListItem->tags->title + "." + extension;
+ else fileName = "track" + QString::number(fileListItem->track) + "." + extension; // NOTE shouldn't be possible
+
+ // if the user wants to change the output directory/file name per file!
+ if( !fileListItem->options.outputFilePathName.isEmpty() ) {
+// path = uniqueFileName( changeExtension(fileListItem->options.outputFilePathName,extension) );
+ path = changeExtension(fileListItem->options.outputFilePathName,extension);
+ if( config->data.general.useVFATNames ) path = vfatPath( path );
+ return path;
+ }
+
+ if( fileListItem->options.outputOptions.mode == Specify ) {
+// path = uniqueFileName( changeExtension(fileListItem->options.outputOptions.directory+"/"+fileName,extension) );
+ path = changeExtension(fileListItem->options.outputOptions.directory+"/"+fileName,extension);
+ if( config->data.general.useVFATNames ) path = vfatPath( path );
+ return path;
+ }
+ else if( /*fileListItem->options.outputOptions.mode == Default || */fileListItem->options.outputOptions.mode == MetaData ) {
+ /*if( fileListItem->options.outputOptions.mode == Default ) path = config->data.general.defaultOutputDirectory;
+ else */path = fileListItem->options.outputOptions.directory;
+
+ if( path.right(1) == "/" ) path += "%f";
+ else if( path.findRev(QRegExp("%[aAbBcCdDfFgGnNpPtTyY]{1,1}")) < path.findRev("/") ) path += "/%f";
+
+ path.replace( "%a", "$replace_by_artist$" );
+ path.replace( "%b", "$replace_by_album$" );
+ path.replace( "%c", "$replace_by_comment$" );
+ path.replace( "%d", "$replace_by_disc$" );
+ path.replace( "%g", "$replace_by_genre$" );
+ path.replace( "%n", "$replace_by_track$" );
+ path.replace( "%p", "$replace_by_composer$" );
+ path.replace( "%t", "$replace_by_title$" );
+ path.replace( "%y", "$replace_by_year$" );
+ path.replace( "%f", "$replace_by_filename$" );
+
+ QString artist = ( fileListItem->tags == 0 || fileListItem->tags->artist.isEmpty() ) ? i18n("Unknown Artist") : fileListItem->tags->artist;
+/// artist.replace("/","\\");
+ path.replace( "$replace_by_artist$", KURL::encode_string(artist).replace("/","%2f") );
+
+ QString album = ( fileListItem->tags == 0 || fileListItem->tags->album.isEmpty() ) ? i18n("Unknown Album") : fileListItem->tags->album;
+/// album.replace("/","\\");
+ path.replace( "$replace_by_album$", KURL::encode_string(album).replace("/","%2f") );
+
+ QString comment = ( fileListItem->tags == 0 || fileListItem->tags->comment.isEmpty() ) ? i18n("No Comment") : fileListItem->tags->comment;
+/// comment.replace("/","\\");
+ path.replace( "$replace_by_comment$", KURL::encode_string(comment).replace("/","%2f") );
+
+ QString disc = ( fileListItem->tags == 0 ) ? "0" : QString().sprintf("%i",fileListItem->tags->disc);
+ path.replace( "$replace_by_disc$", disc );
+
+ QString genre = ( fileListItem->tags == 0 || fileListItem->tags->genre.isEmpty() ) ? i18n("Unknown Genre") : fileListItem->tags->genre;
+/// genre.replace("/","\\");
+ path.replace( "$replace_by_genre$", KURL::encode_string(genre).replace("/","%2f") );
+
+ QString track = ( fileListItem->tags == 0 ) ? "00" : QString().sprintf("%02i",fileListItem->tags->track);
+ path.replace( "$replace_by_track$", track );
+
+ QString composer = ( fileListItem->tags == 0 || fileListItem->tags->composer.isEmpty() ) ? i18n("Unknown Composer") : fileListItem->tags->composer;
+/// composer.replace("/","\\");
+ path.replace( "$replace_by_composer$", KURL::encode_string(composer).replace("/","%2f") );
+
+ QString title = ( fileListItem->tags == 0 || fileListItem->tags->title.isEmpty() ) ? i18n("Unknown Title") : fileListItem->tags->title;
+/// title.replace("/","\\");
+ path.replace( "$replace_by_title$", KURL::encode_string(title).replace("/","%2f") );
+
+ QString year = ( fileListItem->tags == 0 ) ? "0000" : QString().sprintf("%04i",fileListItem->tags->year);
+ path.replace( "$replace_by_year$", year );
+
+ QString filename = fileName.left( fileName.findRev(".") );
+/// filename.replace("/","\\");
+ path.replace( "$replace_by_filename$", filename );
+
+// path = uniqueFileName( path + "." + extension );
+ path = path + "." + extension;
+ if( config->data.general.useVFATNames ) path = vfatPath( path );
+ return path;
+ }
+ else if( fileListItem->options.outputOptions.mode == CopyStructure ) { // TODO is that correct ???
+ QString basePath = fileListItem->options.outputOptions.directory;
+ QString originalPath = fileListItem->options.filePathName;
+ QString cutted;
+ while( basePath.length() > 0 ) {
+ if( fileListItem->options.filePathName.find(basePath) == 0 ) {
+ originalPath.replace( basePath, "" );
+ return uniqueFileName( changeExtension(basePath+cutted+originalPath,extension) );
+ }
+ else {
+ cutted = basePath.right( basePath.length() - basePath.findRev("/") ) + cutted;
+ basePath = basePath.left( basePath.findRev("/") );
+ }
+ }
+// path = uniqueFileName( changeExtension(fileListItem->options.outputOptions.directory+"/"+fileListItem->options.filePathName,extension) );
+ path = changeExtension(fileListItem->options.outputOptions.directory+"/"+fileListItem->options.filePathName,extension);
+ if( config->data.general.useVFATNames ) path = vfatPath( path );
+ return path;
+ }
+ else {
+// path = uniqueFileName( changeExtension(fileListItem->options.filePathName,extension) );
+ path = changeExtension(fileListItem->options.filePathName,extension);
+ if( config->data.general.useVFATNames ) path = vfatPath( path );
+ return path;
+ }
+}
+
+QString OutputDirectory::changeExtension( const QString& filename, const QString& extension )
+{
+ return filename.left( filename.findRev(".") + 1 ) + extension;
+}
+
+QString OutputDirectory::uniqueFileName( const QString& filename )
+{
+ QString filePathName;
+
+ if( filename.left( 1 ) == "/" ) {
+ filePathName = filename;
+ }
+ else if( filename.left( 7 ) == "file://" ) {
+ filePathName = filename;
+ filePathName.remove( 0, 7 );
+ }
+ else if( filename.left( 13 ) == "system:/home/" ) {
+ filePathName = filename;
+ filePathName.remove( 0, 13 );
+ filePathName = QDir::homeDirPath() + "/" + filePathName;
+ }
+ else if( filename.left( 14 ) == "system:/users/" || filename.left( 6 ) == "home:/" ) {
+ int length = ( filename.left(6) == "home:/" ) ? 6 : 14;
+ QString username = filename;
+ username.remove( 0, length );
+ username = username.left( username.find("/") );
+ filePathName = filename;
+ filePathName.remove( 0, length + username.length() );
+ KUser user( username );
+ filePathName = user.homeDir() + filePathName;
+ }
+ else if( filename.left( 14 ) == "system:/media/" || filename.left( 7 ) == "media:/" ) {
+ int length = ( filename.left(7) == "media:/" ) ? 7 : 14;
+ QString device = filename;
+ device.remove( 0, length );
+ device = "/dev/" + device.left( device.find( "/" ) );
+
+ KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
+
+ for( KMountPoint::List::ConstIterator jt = mountPoints.begin(); jt != mountPoints.end(); ++jt )
+ {
+ const KSharedPtr<KMountPoint> mp = *jt;
+ if( mp->mountedFrom() == device )
+ {
+ filePathName = ( mp->mountPoint() == "/" ) ? mp->mountPoint() : mp->mountPoint() + "/";
+ filePathName += filename.right( filename.length() - device.length() - length + 4 );
+ }
+ }
+ }
+ else {
+ filePathName = filename;
+ }
+
+ QFileInfo fileInfo( KURL::decode_string(filePathName) );
+
+ // generate a unique file name
+ while( fileInfo.exists() ) {
+ fileInfo.setFile( fileInfo.filePath().left( fileInfo.filePath().findRev(".")+1 ) + i18n("new") + fileInfo.filePath().right( fileInfo.filePath().length() - fileInfo.filePath().findRev(".") ) );
+ }
+
+ return KURL::encode_string( fileInfo.filePath() ); // was: .replace( "//", "/" )
+}
+
+QString OutputDirectory::makePath( const QString& path )
+{
+ QFileInfo fileInfo( path );
+
+ QStringList dirs = QStringList::split( "/", fileInfo.dirPath() );
+ QString mkDir;
+ QDir dir;
+ for( QStringList::Iterator it = dirs.begin(); it != dirs.end(); ++it ) {
+ mkDir += "/" + KURL::decode_string(*it).replace("/","%2f");
+ dir.setPath( mkDir );
+ if( !dir.exists() ) dir.mkdir( mkDir );
+ }
+
+ return path;
+}
+
+// copyright : (C) 2002 by Mark Kretschmann
+// email : [email protected]
+// modified : 2008 Daniel Faust <[email protected]>
+QString OutputDirectory::vfatPath( const QString& path )
+{
+ QString s = KURL::decode_string(path.right( path.length() - path.findRev("/") - 1 ));
+ QString p = KURL::decode_string(path.left( path.findRev("/") + 1 ));
+
+ for( uint i = 0; i < s.length(); i++ )
+ {
+ QChar c = s.ref( i );
+ if( c < QChar(0x20)
+ || c=='*' || c=='?' || c=='<' || c=='>'
+ || c=='|' || c=='"' || c==':' || c=='/'
+ || c=='\\' )
+ c = '_';
+ s.ref( i ) = c;
+ }
+
+ uint len = s.length();
+ if( len == 3 || (len > 3 && s[3] == '.') )
+ {
+ QString l = s.left(3).lower();
+ if( l=="aux" || l=="con" || l=="nul" || l=="prn" )
+ s = "_" + s;
+ }
+ else if( len == 4 || (len > 4 && s[4] == '.') )
+ {
+ QString l = s.left(3).lower();
+ QString d = s.mid(3,1);
+ if( (l=="com" || l=="lpt") &&
+ (d=="0" || d=="1" || d=="2" || d=="3" || d=="4" ||
+ d=="5" || d=="6" || d=="7" || d=="8" || d=="9") )
+ s = "_" + s;
+ }
+
+ while( s.startsWith( "." ) )
+ s = s.mid(1);
+
+ while( s.endsWith( "." ) )
+ s = s.left( s.length()-1 );
+
+ s = s.left(255);
+ len = s.length();
+ if( s[len-1] == ' ' )
+ s[len-1] = '_';
+
+ return QString( p + s ).replace("%2f","_");
+// return p + s;
+}
+
+void OutputDirectory::selectDir()
+{
+ QString startDir = lDir->text();
+ int i = startDir.find( QRegExp("%[aAbBcCdDfFgGnNpPtTyY]{1,1}") );
+ if( i != -1 ) {
+ i = startDir.findRev( "/", i );
+ startDir = startDir.left( i );
+ }
+
+ QString directory = KFileDialog::getExistingDirectory( startDir, this, i18n("Choose an output directory") );
+ if( !directory.isEmpty() ) {
+ QString dir = lDir->text();
+ i = dir.find( QRegExp("%[aAbBcCdDfFgGnNpPtTyY]{1,1}") );
+ if( i != -1 && (Mode)cMode->currentItem() == MetaData ) {
+ i = dir.findRev( "/", i );
+ lDir->setText( directory + dir.mid(i) );
+ }
+ else {
+ lDir->setText( directory );
+ }
+ emit directoryChanged( directory );
+ }
+}
+
+void OutputDirectory::gotoDir()
+{
+ QString startDir = lDir->originalText();
+ int i = startDir.find( QRegExp("%[aAbBcCdDfFgGnNpPtTyY]{1,1}") );
+ if( i != -1 ) {
+ i = startDir.findRev( "/", i );
+ startDir = startDir.left( i );
+ }
+
+ kfm.clearArguments();
+ kfm << "kfmclient";
+ kfm << "openURL";
+ kfm << startDir;
+ kfm.start( KProcess::DontCare );
+}
+
+void OutputDirectory::modeChangedSlot( int mode )
+{
+ modeJustChanged = true;
+
+ /*if( (Mode)mode == Default ) {
+ pClear->setEnabled( false );
+ lDir->setText( config->data.general.defaultOutputDirectory );
+ lDir->setEnabled( true );
+ lDir->setReadOnly( true );
+ lDir->setEnableSqueezedText( true );
+ pDirSelect->setEnabled( false );
+ // TODO hide pDirSelect and show pDirEdit
+ pDirGoto->setEnabled( true );
+ //pDirInfo->hide();
+ QToolTip::remove( cMode );
+ QToolTip::add( cMode, i18n("Output all converted files into the soundKonverter default output directory") );
+ QToolTip::remove( lDir );
+ }
+ else */
+ if( (Mode)mode == MetaData ) {
+ pClear->setEnabled( true );
+ if( config->data.general.metaDataOutputDirectory.isEmpty() ) config->data.general.metaDataOutputDirectory = QDir::homeDirPath() + "/soundKonverter/%b/%d - %n - %a - %t";
+ lDir->setText( config->data.general.metaDataOutputDirectory );
+ lDir->setEnabled( true );
+ lDir->setReadOnly( false );
+// lDir->setEnableSqueezedText( false );
+ pDirSelect->setEnabled( true );
+ pDirGoto->setEnabled( true );
+ //pDirInfo->show();
+ QToolTip::remove( cMode );
+ QToolTip::add( cMode, i18n("Name all converted files according to the specified pattern") );
+ QToolTip::remove( lDir );
+ QToolTip::add( lDir, i18n("<p>The following strings are wildcards, that will be replaced by the information in the meta data:</p><p>%a - Artist<br>%b - Album<br>%c - Comment<br>%d - Disc number<br>%g - Genre<br>%n - Track number<br>%p - Composer<br>%t - Title<br>%y - Year<br>%f - Original file name<p>") );
+ }
+ else if( (Mode)mode == Source ) {
+ pClear->setEnabled( false );
+ lDir->setText( "" );
+ lDir->setEnabled( false );
+ lDir->setReadOnly( false );
+// lDir->setEnableSqueezedText( false );
+ pDirSelect->setEnabled( false );
+ pDirGoto->setEnabled( false );
+ //pDirInfo->hide();
+ QToolTip::remove( cMode );
+ QToolTip::add( cMode, i18n("Output all converted files into the same directory as the original files") );
+ QToolTip::remove( lDir );
+ }
+ else if( (Mode)mode == Specify ) {
+ pClear->setEnabled( true );
+ if( config->data.general.specifyOutputDirectory.isEmpty() ) config->data.general.specifyOutputDirectory = QDir::homeDirPath() + "/soundKonverter";
+ lDir->setText( config->data.general.specifyOutputDirectory );
+ lDir->setEnabled( true );
+ lDir->setReadOnly( false );
+// lDir->setEnableSqueezedText( false );
+ pDirSelect->setEnabled( true );
+ pDirGoto->setEnabled( true );
+ //pDirInfo->hide();
+ QToolTip::remove( cMode );
+ QToolTip::add( cMode, i18n("Output all converted files into the specified output directory") );
+ QToolTip::remove( lDir );
+ }
+ else if( (Mode)mode == CopyStructure ) {
+ pClear->setEnabled( true );
+ if( config->data.general.copyStructureOutputDirectory.isEmpty() ) config->data.general.copyStructureOutputDirectory = QDir::homeDirPath() + "/soundKonverter";
+ lDir->setText( config->data.general.copyStructureOutputDirectory );
+ lDir->setEnabled( true );
+ lDir->setReadOnly( false );
+// lDir->setEnableSqueezedText( false );
+ pDirSelect->setEnabled( true );
+ pDirGoto->setEnabled( true );
+ //pDirInfo->hide();
+ QToolTip::remove( cMode );
+ QToolTip::add( cMode, i18n("Copy the whole directory structure for all converted files") );
+ QToolTip::remove( lDir );
+ }
+
+ emit modeChanged( (Mode)mode );
+
+ modeJustChanged = false;
+}
+
+void OutputDirectory::directoryChangedSlot( const QString& directory )
+{
+ if( modeJustChanged ) {
+ modeJustChanged = false;
+ return;
+ }
+
+ Mode mode = (Mode)cMode->currentItem();
+
+ if( mode == MetaData ) {
+ config->data.general.metaDataOutputDirectory = directory;
+ }
+ else if( mode == Specify ) {
+ config->data.general.specifyOutputDirectory = directory;
+ }
+ else if( mode == CopyStructure ) {
+ config->data.general.copyStructureOutputDirectory = directory;
+ }
+
+ emit directoryChanged( directory );
+}
+
+/*void OutputDirectory::modeInfo()
+{
+ int mode = cMode->currentItem();
+ QString sModeString = cMode->currentText();
+
+ if( (Mode)mode == Default ) {
+ KMessageBox::information( this,
+ i18n("This will output each file into the soundKonverter default directory."),
+ QString(i18n("Mode")+": ").append(sModeString) );
+ }
+ else if( (Mode)mode == Source ) {
+ KMessageBox::information( this,
+ i18n("This will output each file into the same directory as the original file."),
+ QString(i18n("Mode")+": ").append(sModeString) );
+ }
+ else if( (Mode)mode == Specify ) {
+ KMessageBox::information( this,
+ i18n("This will output each file into the directory specified in the editbox behind."),
+ QString(i18n("Mode")+": ").append(sModeString) );
+ }
+ else if( (Mode)mode == MetaData ) {
+ KMessageBox::information( this,
+ i18n("This will output each file into a directory, which is created based on the metadata in the audio files. Select a directory, where the new directories should be created."),
+ QString(i18n("Mode")+": ").append(sModeString) );
+ }
+ else if( (Mode)mode == CopyStructure ) {
+ KMessageBox::information( this,
+ i18n("This will output each file into a directory, which is created based on the name of the original directory. So you can copy a whole directory structure, in one you have the original files, in the other the converted."),
+ QString(i18n("Mode")+": ").append(sModeString) );
+ }
+ else {
+ KMessageBox::error( this,
+ i18n("This mode (%s) doesn't exist.", sModeString),
+ QString(i18n("Mode")+": ").append(sModeString) );
+ }
+}*/
+
+/*void OutputDirectory::dirInfo()
+{
+ KMessageBox::information( this,
+ i18n("<p>The following strings are space holders, that will be replaced by the information in the metatags.</p><p>%a - Artist<br>%b - Album<br>%c - Comment<br>%d - Disc number<br>%g - Genre<br>%n - Track number<br>%p - Composer<br>%t - Title<br>%y - Year<br>%f - Original file name<p>"),
+ QString(i18n("Legend")) );
+}*/
+
diff --git a/src/outputdirectory.h b/src/outputdirectory.h
new file mode 100755
index 0000000..85b5236
--- /dev/null
+++ b/src/outputdirectory.h
@@ -0,0 +1,92 @@
+
+
+#ifndef OUTPUTDIRECTORY_H
+#define OUTPUTDIRECTORY_H
+
+#include <qwidget.h>
+#include <kprocess.h>
+
+class FileListItem;
+
+class Config;
+class KComboBox;
+class KLineEdit;
+class KToolBarButton;
+
+/**
+ * @short The input area for the output directory
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class OutputDirectory : public QWidget
+{
+ Q_OBJECT
+public:
+ enum Mode {
+// Default,
+ MetaData,
+ Source,
+ Specify,
+ CopyStructure
+ };
+
+ /**
+ * Constructor
+ */
+ OutputDirectory( Config*, QWidget* parent = 0, const char* name = 0 );
+
+ Mode mode();
+ void setMode( Mode );
+ QString directory();
+ void setDirectory( const QString& );
+
+ static QString calcPath( FileListItem* fileListItem, Config* config, QString extension = "" );
+ static QString changeExtension( const QString& filename, const QString& extension );
+ static QString uniqueFileName( const QString& filename );
+ static QString makePath( const QString& path );
+ static QString vfatPath( const QString& path );
+
+ /**
+ * Destructor
+ */
+ virtual ~OutputDirectory();
+
+public slots:
+ //void setActive( bool );
+ void enable();
+ void disable();
+
+private slots:
+ void modeChangedSlot( int );
+ void directoryChangedSlot( const QString& );
+ void selectDir();
+ void gotoDir();
+ //void modeInfo();
+ //void dirInfo();
+ //void savePaths();
+
+private:
+ KComboBox* cMode;
+ //KToolBarButton* pModeInfo;
+ KLineEdit* lDir;
+ KToolBarButton* pDirSelect;
+ KToolBarButton* pDirGoto;
+ //KToolBarButton* pDirInfo;
+ KToolBarButton* pClear;
+
+ bool modeJustChanged;
+
+ KProcess kfm;
+
+ Config* config;
+
+/* QString sharedDirPath;
+ QString metadataPath;
+ QString copyStructurePath;
+*/
+signals:
+ void modeChanged( OutputDirectory::Mode );
+ void directoryChanged( const QString& );
+};
+
+#endif // OUTPUTDIRECTORY_H
diff --git a/src/paranoia.cpp b/src/paranoia.cpp
new file mode 100755
index 0000000..ef1048c
--- /dev/null
+++ b/src/paranoia.cpp
@@ -0,0 +1,257 @@
+/***************************************************************************
+ paranoia.cpp - description
+ -------------------
+ copyright : (C) 2002-2006 by Christophe Thommeret
+ modified : 2006 Daniel Faust <[email protected]>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#include <unistd.h>
+#include <math.h>
+
+#include <qfile.h>
+#include <qslider.h>
+#include <qlcdnumber.h>
+#include <qdir.h>
+#include <qlineedit.h>
+#include <qbuttongroup.h>
+#include <qtoolbutton.h>
+#include <qcheckbox.h>
+
+#include <qcombobox.h>
+
+#include <kmessagebox.h>
+#include <klocale.h>
+//#include <kdebug.h>
+#include <ktrader.h>
+#include <kpushbutton.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kparts/componentfactory.h>
+
+
+#include "paranoia.h"
+
+#define DEFAULT_DRIVE "/dev/cdrom"
+
+
+
+
+void paranoiaCallback( long, int )
+{
+}
+
+
+
+Paranoia::Paranoia()
+{
+ d = 0;
+ p = 0;
+}
+
+
+
+bool Paranoia::init( QString dev )
+{
+ QString s;
+ QFile f;
+
+ if ( p!=0 ) paranoia_free( p );
+ if ( d!=0 ) cdda_close( d );
+ nTracks = 0;
+
+ dev = dev.stripWhiteSpace();
+ f.setName( dev );
+ if ( !f.exists() ) {
+ /*if ( !findCdrom() ) {
+ d = cdda_find_a_cdrom( CDDA_MESSAGE_PRINTIT, 0 );
+ if ( cdda_open( d )!=0 )
+ return false;
+ }*/
+ return false;
+ }
+ else {
+ d = cdda_identify( dev.ascii(), CDDA_MESSAGE_PRINTIT, 0 );
+ if ( d==0 )
+ return false;
+ if ( cdda_open( d )!=0 )
+ return false;
+ }
+ p = paranoia_init( d );
+ nTracks = cdda_tracks( d );
+ return true;
+}
+
+
+
+bool Paranoia::findCdrom()
+{
+ QFile *f;
+ QString c;
+ QString s="";
+ int pos, i;
+ bool stop=false;
+ char dev[4][4]={"","","",""};
+
+ f = new QFile( "/proc/sys/dev/cdrom/info" );
+ if ( !f->open(IO_ReadOnly) )
+ return false;
+
+ QTextStream t( f );
+ while ( !t.eof() && !stop ) {
+ s = t.readLine();
+ if ( s.contains("drive name:") )
+ stop = true;
+ }
+ if ( !stop )
+ return false;
+
+ pos = s.find(":");
+ c = s.right( s.length()-pos-1 );
+ sscanf( c.latin1(), "%s %s %s %s", dev[0], dev[1], dev[2], dev[3] );
+
+ for ( i=0; i<4; i++ )
+ if ( procCdrom( dev[i] ) )
+ return true;
+
+ f->close();
+ return false;
+}
+
+
+
+bool Paranoia::procCdrom( QString name )
+{
+ int pos;
+
+ if ( name.contains("sr") ) {
+ pos = name.find("r");
+ name = name.right( name.length()-pos-1 );
+ name = "/dev/scd"+name;
+ d = cdda_identify( name.ascii(), CDDA_MESSAGE_PRINTIT, 0 );
+ if ( cdda_open( d )==0 )
+ return true;
+ }
+ else if ( name.contains("hd") ) {
+ name = "/dev/"+name;
+ d = cdda_identify( name.ascii(), CDDA_MESSAGE_PRINTIT, 0 );
+ if ( cdda_open( d )==0 )
+ return true;
+ }
+ return false;
+}
+
+
+
+void Paranoia::setMode( int mode )
+{
+ switch ( mode ) {
+ case 0 : mode = PARANOIA_MODE_DISABLE;
+ break;
+ case 1 : mode = PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP;
+ break;
+ case 2 : mode = PARANOIA_MODE_FULL;
+ }
+ paranoia_modeset( p, mode );
+}
+
+
+bool Paranoia::initTrack( int t )
+{
+ currentSector = cdda_track_firstsector( d, t );
+ endOfTrack = cdda_track_lastsector( d, t );
+ paranoia_seek( p, currentSector, SEEK_SET );
+ return true;
+}
+
+
+
+
+int Paranoia::trackFirstSector( int t )
+{
+ return cdda_track_firstsector( d, t );
+}
+
+
+
+int Paranoia::discFirstSector()
+{
+ return cdda_disc_firstsector( d );
+}
+
+
+
+int Paranoia::discLastSector()
+{
+ return cdda_disc_lastsector( d );
+}
+
+
+
+bool Paranoia::isAudio( int t )
+{
+ if ( cdda_track_audiop( d, t+1 ) ) return true;
+ else return false;
+}
+
+
+
+QString Paranoia::trackSize( int t )
+{
+ QString s, c;
+ long total;
+
+ total = CD_FRAMESIZE_RAW * (cdda_track_lastsector( d, t+1 )-cdda_track_firstsector( d, t+1 ) );
+ if ( total>(1048576 ) ) s = c.setNum(total/1048576.0, 'f', 2)+" "+i18n("MB");
+ else if ( total>1024 ) s = c.setNum(total/1024.0, 'f', 2)+" "+i18n("KB");
+ else s = c.setNum(total*1.0, 'f', 2)+" "+i18n("Bytes");
+ return s;
+}
+
+
+
+long Paranoia::trackSectorSize( int t )
+{
+ return cdda_track_lastsector( d, t )-cdda_track_firstsector( d, t );
+}
+
+
+
+long Paranoia::trackTime( int t )
+{
+ QString c;
+ long total, time;
+// int m, s;
+
+ if ( t<0 ) total = CD_FRAMESIZE_RAW * (cdda_disc_lastsector( d )-cdda_disc_firstsector( d ) );
+ else total = CD_FRAMESIZE_RAW * (cdda_track_lastsector( d, t+1 )-cdda_track_firstsector( d, t+1 ) );
+ time = (8 * total) / (44100 * 2 * 16);
+// m = time/60;
+// s = time%60;
+// c.sprintf( "%.2i:%.2i", m, s );
+ return time;
+}
+
+
+
+Paranoia::~Paranoia()
+{
+ if ( p!=0 ) paranoia_free( p );
+ if (d!=0 ) cdda_close( d );
+}
+
+
+
+long Paranoia::getTracks()
+{
+ return nTracks;
+}
diff --git a/src/paranoia.h b/src/paranoia.h
new file mode 100755
index 0000000..f1a5ae8
--- /dev/null
+++ b/src/paranoia.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ paranoia.h - description
+ -------------------
+ copyright : (C) 2002-2006 by Christophe Thommeret
+ modified : 2006 Daniel Faust <[email protected]>
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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. *
+ * *
+ ***************************************************************************/
+
+#ifndef PARANOIA_H
+#define PARANOIA_H
+
+#include <qstringlist.h>
+
+extern "C"
+{
+#include <cdda_interface.h>
+#include <cdda_paranoia.h>
+}
+
+
+
+class Paranoia
+{
+public:
+ Paranoia();
+ bool init( QString dev );
+ ~Paranoia();
+ long getTracks();
+ long trackTime( int t );
+ int trackFirstSector( int t );
+ int discFirstSector();
+ int discLastSector();
+
+private:
+ bool findCdrom();
+ bool procCdrom( QString name );
+ bool initTrack( int t );
+ void setMode( int mode );
+ bool isAudio( int t );
+ QString trackSize( int t );
+ long trackSectorSize( int t );
+
+ long nTracks;
+ cdrom_drive *d;
+ cdrom_paranoia *p;
+ long currentSector, endOfTrack;
+ int paraMode;
+ int progress;
+};
+#endif
diff --git a/src/pics/Makefile.am b/src/pics/Makefile.am
new file mode 100755
index 0000000..4120423
--- /dev/null
+++ b/src/pics/Makefile.am
@@ -0,0 +1,7 @@
+picsdir = $(kde_datadir)/soundkonverter/pics
+pics_DATA = ledgreen_legend.png \
+ ledgreen.png \
+ ledred_legend.png \
+ ledred.png \
+ ledyellow_legend.png \
+ ledyellow.png
diff --git a/src/pics/ledgreen.png b/src/pics/ledgreen.png
new file mode 100755
index 0000000..f593745
--- /dev/null
+++ b/src/pics/ledgreen.png
Binary files differ
diff --git a/src/pics/ledgreen_legend.png b/src/pics/ledgreen_legend.png
new file mode 100755
index 0000000..f21384e
--- /dev/null
+++ b/src/pics/ledgreen_legend.png
Binary files differ
diff --git a/src/pics/ledred.png b/src/pics/ledred.png
new file mode 100755
index 0000000..62a84cd
--- /dev/null
+++ b/src/pics/ledred.png
Binary files differ
diff --git a/src/pics/ledred_legend.png b/src/pics/ledred_legend.png
new file mode 100755
index 0000000..9ae9c68
--- /dev/null
+++ b/src/pics/ledred_legend.png
Binary files differ
diff --git a/src/pics/ledyellow.png b/src/pics/ledyellow.png
new file mode 100755
index 0000000..725dc73
--- /dev/null
+++ b/src/pics/ledyellow.png
Binary files differ
diff --git a/src/pics/ledyellow_legend.png b/src/pics/ledyellow_legend.png
new file mode 100755
index 0000000..599499e
--- /dev/null
+++ b/src/pics/ledyellow_legend.png
Binary files differ
diff --git a/src/pluginloader/Makefile.am b/src/pluginloader/Makefile.am
new file mode 100755
index 0000000..64ca193
--- /dev/null
+++ b/src/pluginloader/Makefile.am
@@ -0,0 +1,9 @@
+INCLUDES = -I$(top_srcdir)/src/metadata $(all_includes) -I$(top_srcdir)/src/
+METASOURCES = AUTO
+noinst_HEADERS = pluginloaderbase.h convertpluginloader.h \
+ replaygainpluginloader.h formatinfoloader.h ripperpluginloader.h
+
+noinst_LTLIBRARIES = libpluginloader.la
+libpluginloader_la_SOURCES = pluginloaderbase.cpp \
+ convertpluginloader.cpp replaygainpluginloader.cpp formatinfoloader.cpp \
+ ripperpluginloader.cpp
diff --git a/src/pluginloader/convertpluginloader.cpp b/src/pluginloader/convertpluginloader.cpp
new file mode 100755
index 0000000..89996ba
--- /dev/null
+++ b/src/pluginloader/convertpluginloader.cpp
@@ -0,0 +1,265 @@
+
+#include "convertpluginloader.h"
+
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+
+// TODO check whether it is possible to implement presets in the lame plugin
+
+ConvertPlugin::ConvertPlugin()
+{}
+
+ConvertPlugin::~ConvertPlugin()
+{}
+
+
+ConvertPluginLoader::ConvertPluginLoader()
+{}
+
+ConvertPluginLoader::~ConvertPluginLoader()
+{}
+
+int ConvertPluginLoader::verifyFile( QString fileName )
+{
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return -1;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return -1;
+ }
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "converter" ) return -1;
+ int version;
+ QDomNode node;
+ node = root.firstChild();
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+ version = node.toElement().attribute("version","0").toInt();
+ break;
+ }
+ }
+
+ return version;
+}
+
+ConvertPlugin* ConvertPluginLoader::loadFile( QString fileName )
+{
+ int t_int;
+ float t_float;
+ QString t_str;
+
+ ConvertPlugin* plugin = new ConvertPlugin();
+ plugin->info.version = -1; // if something goes wrong, we can see that by looking at plugin->info.version
+ plugin->filePathName = fileName;
+
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return plugin;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return plugin;
+ }
+ opmlFile.close();
+
+ QString language = KGlobal::locale()->languagesTwoAlpha().first();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "converter" ) return plugin;
+ QDomNode node, sub1Node, sub2Node, sub3Node, sub4Node;
+ node = root.firstChild();
+
+ plugin->enc.enabled = false;
+ plugin->enc.strength.enabled = false;
+ plugin->enc.lossy.enabled = false;
+ plugin->enc.lossy.quality.enabled = false;
+ plugin->enc.lossy.bitrate.abr.enabled = false;
+ plugin->enc.lossy.bitrate.abr.bitrate_range.enabled = false;
+ plugin->enc.lossy.bitrate.cbr.enabled = false;
+ plugin->enc.lossy.samplingrate.enabled = false;
+ plugin->enc.lossy.channels.stereo_enabled = false;
+ plugin->enc.lossy.channels.joint_stereo_enabled = false;
+ plugin->enc.lossy.channels.forced_joint_stereo_enabled = false;
+ plugin->enc.lossy.channels.dual_channels_enabled = false;
+ plugin->enc.lossy.channels.mono_enabled = false;
+ plugin->enc.lossless.enabled = false;
+ plugin->enc.hybrid.enabled = false;
+ plugin->enc.replaygain.enabled = false;
+ plugin->enc.tag.enabled = false;
+ plugin->dec.enabled = false;
+
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+
+ plugin->info.name = node.toElement().attribute("name",i18n("Unknown Name"));
+ plugin->info.about = node.toElement().attribute("about_"+language);
+ if( plugin->info.about.isEmpty() ) plugin->info.about = node.toElement().attribute("about");
+ if( plugin->info.about.isEmpty() ) plugin->info.about = i18n("Sorry, no information available!");
+ plugin->info.author = node.toElement().attribute("author",i18n("Unknown Author"));
+ plugin->info.version = node.toElement().attribute("version","0").toInt();
+
+ }
+ else if( node.isElement() && node.nodeName() == "enc" ) {
+
+ if(node.toElement().attribute("enabled") == "true") plugin->enc.enabled = true;
+ plugin->enc.rank = node.toElement().attribute("rank","10").toInt();
+ plugin->enc.bin = node.toElement().attribute( "bin" );
+ plugin->enc.param = node.toElement().attribute("param");
+ plugin->enc.silent_param = node.toElement().attribute("silent_param");
+ plugin->enc.mime_types = QStringList::split( ',', node.toElement().attribute("mime_types","application/octet-stream") );
+ plugin->enc.in_out_files = node.toElement().attribute("in_out_files");
+ plugin->enc.overwrite = node.toElement().attribute("overwrite");
+
+ sub1Node = node.toElement().firstChild();
+ while( !sub1Node.isNull() ) {
+ if( sub1Node.isElement() && sub1Node.nodeName() == "strength" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->enc.strength.enabled = true;
+ plugin->enc.strength.range_min = sub1Node.toElement().attribute("range_min").toFloat();
+ plugin->enc.strength.range_max = sub1Node.toElement().attribute("range_max").toFloat();
+ plugin->enc.strength.separator = sub1Node.toElement().attribute("separator").at(0);
+ plugin->enc.strength.param = sub1Node.toElement().attribute("param");
+ plugin->enc.strength.step = sub1Node.toElement().attribute("step").toFloat();
+ plugin->enc.strength.profiles = QStringList::split( ',', sub1Node.toElement().attribute("profiles") );
+ plugin->enc.strength.default_value = sub1Node.toElement().attribute("default_value").toFloat();
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "lossy" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossy.enabled = true;
+
+ sub2Node = sub1Node.toElement().firstChild();
+ while( !sub2Node.isNull() ) {
+ if( sub2Node.isElement() && sub2Node.nodeName() == "quality" ) {
+
+ if( sub2Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossy.quality.enabled = true;
+ plugin->enc.lossy.quality.range_min = sub2Node.toElement().attribute("range_min").toFloat();
+ plugin->enc.lossy.quality.range_max = sub2Node.toElement().attribute("range_max").toFloat();
+ plugin->enc.lossy.quality.separator = sub2Node.toElement().attribute("separator").at(0);
+ plugin->enc.lossy.quality.help = sub2Node.toElement().attribute("help");
+ plugin->enc.lossy.quality.output = sub2Node.toElement().attribute("output");
+ plugin->enc.lossy.quality.param = sub2Node.toElement().attribute("param");
+ plugin->enc.lossy.quality.step = sub2Node.toElement().attribute("step").toFloat();
+ plugin->enc.lossy.quality.profiles = QStringList::split( ',', sub2Node.toElement().attribute("profiles") );
+
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "bitrate" ) {
+ sub3Node = sub2Node.toElement().firstChild();
+ while( !sub3Node.isNull() ) {
+ if( sub3Node.isElement() && sub3Node.nodeName() == "abr" ) {
+
+ if( sub3Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossy.bitrate.abr.enabled = true;
+ plugin->enc.lossy.bitrate.abr.output = sub3Node.toElement().attribute("output");
+ plugin->enc.lossy.bitrate.abr.param = sub3Node.toElement().attribute("param");
+
+ sub4Node = sub3Node.toElement().firstChild();
+ while( !sub4Node.isNull() ) {
+ if( sub4Node.isElement() && sub4Node.nodeName() == "bitrate_range" ) {
+
+ if( sub4Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossy.bitrate.abr.bitrate_range.enabled = true;
+ plugin->enc.lossy.bitrate.abr.bitrate_range.param_min = sub4Node.toElement().attribute("min_param");
+ plugin->enc.lossy.bitrate.abr.bitrate_range.param_max = sub4Node.toElement().attribute("max_param");
+
+ }
+ sub4Node = sub4Node.nextSibling();
+ }
+ }
+ else if( sub3Node.isElement() && sub3Node.nodeName() == "cbr" ) {
+
+ if( sub3Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossy.bitrate.cbr.enabled = true;
+ plugin->enc.lossy.bitrate.cbr.output = sub3Node.toElement().attribute("output");
+ plugin->enc.lossy.bitrate.cbr.param = sub3Node.toElement().attribute("param");
+
+ }
+ sub3Node = sub3Node.nextSibling();
+ }
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "samplingrate" ) {
+
+ if( sub2Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossy.samplingrate.enabled = true;
+ if( sub2Node.toElement().attribute("unit") == "Hz" ) plugin->enc.lossy.samplingrate.unit = PluginLoaderBase::Hz;
+ else plugin->enc.lossy.samplingrate.unit = PluginLoaderBase::KHz;
+ plugin->enc.lossy.samplingrate.param = sub2Node.toElement().attribute("param");
+
+ }
+ else if( sub2Node.isElement() && sub2Node.nodeName() == "channels" ) {
+
+ if( sub2Node.toElement().attribute("stereo_enabled") == "true" ) plugin->enc.lossy.channels.stereo_enabled = true;
+ plugin->enc.lossy.channels.stereo_param = sub2Node.toElement().attribute("stereo_param");
+ if( sub2Node.toElement().attribute("joint_stereo_enabled") == "true" ) plugin->enc.lossy.channels.joint_stereo_enabled = true;
+ plugin->enc.lossy.channels.joint_stereo_param = sub2Node.toElement().attribute("joint_stereo_param");
+ if( sub2Node.toElement().attribute("forced_joint_stereo_enabled") == "true" ) plugin->enc.lossy.channels.forced_joint_stereo_enabled = true;
+ plugin->enc.lossy.channels.forced_joint_stereo_param = sub2Node.toElement().attribute("forced_joint_stereo_param");
+ if( sub2Node.toElement().attribute("dual_channels_enabled") == "true" ) plugin->enc.lossy.channels.dual_channels_enabled = true;
+ plugin->enc.lossy.channels.dual_channels_param=sub2Node.toElement().attribute("dual_channels_param");
+ if( sub2Node.toElement().attribute("mono_enabled") == "true" ) plugin->enc.lossy.channels.mono_enabled = true;
+ plugin->enc.lossy.channels.mono_param=sub2Node.toElement().attribute("mono_param");
+
+ }
+ sub2Node = sub2Node.nextSibling();
+ }
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "lossless" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->enc.lossless.enabled = true;
+ plugin->enc.lossless.output = sub1Node.toElement().attribute("output");
+ plugin->enc.lossless.param = sub1Node.toElement().attribute("param");
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "hybrid" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->enc.hybrid.enabled = true;
+ plugin->enc.hybrid.output = sub1Node.toElement().attribute("output");
+ plugin->enc.hybrid.param = sub1Node.toElement().attribute("param");
+ plugin->enc.hybrid.correction_file_mime_type = sub1Node.toElement().attribute("correction_file_mime_type","application/octet-stream");
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "replay_gain" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->enc.replaygain.enabled = true;
+ plugin->enc.replaygain.use = sub1Node.toElement().attribute("use");
+ plugin->enc.replaygain.avoid = sub1Node.toElement().attribute("avoid");
+ plugin->enc.replaygain.rank = sub1Node.toElement().attribute("rank","10").toInt();
+
+ }
+ else if( sub1Node.isElement() && sub1Node.nodeName() == "tag" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->enc.tag.enabled = true;
+ plugin->enc.tag.param = sub1Node.toElement().attribute("param");
+ plugin->enc.tag.artist = sub1Node.toElement().attribute("artist");
+ plugin->enc.tag.composer = sub1Node.toElement().attribute("composer");
+ plugin->enc.tag.album = sub1Node.toElement().attribute("album");
+ plugin->enc.tag.disc = sub1Node.toElement().attribute("disc");
+ plugin->enc.tag.title = sub1Node.toElement().attribute("title");
+ plugin->enc.tag.genre = sub1Node.toElement().attribute("genre");
+ plugin->enc.tag.comment = sub1Node.toElement().attribute("comment");
+ plugin->enc.tag.track = sub1Node.toElement().attribute("track");
+ plugin->enc.tag.year = sub1Node.toElement().attribute("year");
+
+ }
+ sub1Node = sub1Node.nextSibling();
+ }
+ }
+ else if ( node.isElement() && node.nodeName() == "dec" ) {
+
+ if(node.toElement().attribute("enabled") == "true") plugin->dec.enabled = true;
+ plugin->dec.rank = node.toElement().attribute("rank","10").toInt();
+ plugin->dec.bin = node.toElement().attribute("bin");
+ plugin->dec.output = node.toElement().attribute("output");
+ plugin->dec.param = node.toElement().attribute("param");
+ plugin->dec.overwrite = node.toElement().attribute("overwrite");
+ plugin->dec.mime_types = QStringList::split( ',', node.toElement().attribute("mime_types","application/octet-stream") );
+ plugin->dec.in_out_files = node.toElement().attribute("in_out_files");
+ plugin->dec.silent_param = node.toElement().attribute("silent_param");
+
+ }
+ node = node.nextSibling();
+ }
+
+ return plugin;
+}
+
diff --git a/src/pluginloader/convertpluginloader.h b/src/pluginloader/convertpluginloader.h
new file mode 100755
index 0000000..ad09e12
--- /dev/null
+++ b/src/pluginloader/convertpluginloader.h
@@ -0,0 +1,190 @@
+
+
+#ifndef CONVERTPLUGINLOADER_H
+#define CONVERTPLUGINLOADER_H
+
+#include "pluginloaderbase.h"
+
+
+/**
+ * @short The complete data, how to use the backend
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConvertPlugin
+{
+public:
+ /**
+ * Constructor
+ */
+ ConvertPlugin();
+
+ /**
+ * Destructor
+ */
+ virtual ~ConvertPlugin();
+
+ QString filePathName; // the file name of th plugin (needed to detect write permissions)
+
+ struct Info
+ {
+ int version; // the version of our plugin (v0.2.1 = 201, v11.3 = 110300)
+ QString name; // the name of our plugin
+ QString author; // the author of the plugin
+ QString about; // a short information aboue the plugin
+ } info;
+
+ struct Enc
+ {
+ bool enabled;
+ int rank;
+ QString bin;
+ QString param;
+ QString silent_param;
+ QStringList mime_types;
+ QString in_out_files;
+ QString overwrite;
+
+ struct Strength {
+ bool enabled;
+ QString param;
+ float range_min;
+ float range_max;
+ float step;
+ QChar separator;
+ QStringList profiles;
+ float default_value;
+ } strength;
+
+ struct Lossy {
+ bool enabled;
+
+ struct Quality {
+ bool enabled;
+ QString param;
+ float range_min;
+ float range_max;
+ float step;
+ QChar separator;
+ QString help;
+ QString output;
+ QStringList profiles; // NOTE when using profiles, step must be 1 and range_min 0
+ } quality;
+
+ struct Bitrate {
+ struct Abr
+ {
+ bool enabled;
+ QString param;
+ QString output;
+
+ struct BitrateRange {
+ bool enabled;
+ QString param_min;
+ QString param_max;
+ } bitrate_range;
+
+ } abr;
+
+ struct Cbr {
+ bool enabled;
+ QString param;
+ QString output;
+ } cbr;
+
+ } bitrate;
+
+ struct Samplingrate {
+ bool enabled;
+ QString param;
+ PluginLoaderBase::Unit unit;
+ } samplingrate;
+
+ struct Channels {
+ bool stereo_enabled;
+ QString stereo_param;
+ bool joint_stereo_enabled;
+ QString joint_stereo_param;
+ bool forced_joint_stereo_enabled;
+ QString forced_joint_stereo_param;
+ bool dual_channels_enabled;
+ QString dual_channels_param;
+ bool mono_enabled;
+ QString mono_param;
+ } channels;
+ } lossy;
+
+ struct Lossless {
+ bool enabled;
+ QString param;
+ QString output;
+ } lossless;
+
+ struct Hybrid {
+ bool enabled;
+ QString param;
+ QString output;
+ QString correction_file_mime_type;
+ } hybrid;
+
+ struct ReplayGain {
+ bool enabled;
+ QString use;
+ QString avoid;
+ int rank;
+ } replaygain;
+
+ struct Tag {
+ bool enabled;
+ QString param;
+ QString artist;
+ QString composer;
+ QString album;
+ QString disc;
+ QString title;
+ QString genre;
+ QString comment;
+ QString track;
+ QString year;
+ } tag;
+ } enc;
+
+ struct Dec {
+ bool enabled;
+ int rank;
+ QString bin;
+ QString param;
+ QString silent_param;
+ QStringList mime_types;
+ QString output;
+ QString in_out_files;
+ QString overwrite;
+ } dec;
+};
+
+/**
+ * @short The plugin loader for the converter plugins
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ConvertPluginLoader : public PluginLoaderBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ ConvertPluginLoader();
+
+ /**
+ * Destructor
+ */
+ virtual ~ConvertPluginLoader();
+
+ /** is this file a converter plugin and loadable? */
+ int verifyFile( QString );
+ /** load a given file */
+ ConvertPlugin* loadFile( QString );
+};
+
+#endif // CONVERTPLUGINLOADER_H
diff --git a/src/pluginloader/formatinfoloader.cpp b/src/pluginloader/formatinfoloader.cpp
new file mode 100755
index 0000000..c585ed6
--- /dev/null
+++ b/src/pluginloader/formatinfoloader.cpp
@@ -0,0 +1,88 @@
+
+#include "formatinfoloader.h"
+
+#include <qfile.h>
+
+#include <klocale.h>
+#include <kglobal.h>
+
+FormatInfo::FormatInfo()
+{}
+
+FormatInfo::~FormatInfo()
+{}
+
+
+FormatInfoLoader::FormatInfoLoader()
+{}
+
+FormatInfoLoader::~FormatInfoLoader()
+{}
+
+bool FormatInfoLoader::verifyFile( QString fileName )
+{
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return false;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return false;
+ }
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") == "format_info" ) return true;
+
+ return false;
+}
+
+FormatInfo* FormatInfoLoader::loadFile( QString fileName )
+{
+ int t_int;
+ float t_float;
+ QString t_str;
+
+ FormatInfo* plugin = new FormatInfo();
+ plugin->mime_types = "application/octet-stream"; // if something goes wrong, we can see that by looking at plugin->mimetype
+
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return plugin;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return plugin;
+ }
+ opmlFile.close();
+
+ QString language = KGlobal::locale()->languagesTwoAlpha().first();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "format_info" ) return plugin;
+ QDomNode node;
+ node = root.firstChild();
+
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "data" ) {
+
+ plugin->mime_types = QStringList::split( ',',node.toElement().attribute("mime_types","application/octet-stream") );
+ plugin->extensions = QStringList::split( ',',node.toElement().attribute("extensions") );
+ plugin->description = node.toElement().attribute("description_"+language);
+ if( plugin->description.isEmpty() ) plugin->description = node.toElement().attribute("description");
+ plugin->urls = QStringList::split( ',',node.toElement().attribute("urls_"+language) );
+ if( plugin->urls.isEmpty() ) plugin->urls = QStringList::split( ',',node.toElement().attribute("urls") );
+ QStringList compressionTypeList = QStringList::split( ',',node.toElement().attribute("compression_type") );
+ plugin->compressionType = (FormatInfo::CompressionType)0x0000;
+ for( QStringList::Iterator it = compressionTypeList.begin(); it != compressionTypeList.end(); ++it ) {
+ if( *it == "lossy" ) plugin->compressionType = FormatInfo::CompressionType( plugin->compressionType | FormatInfo::lossy );
+ else if( *it == "lossless" ) plugin->compressionType = FormatInfo::CompressionType( plugin->compressionType | FormatInfo::lossless );
+ else if( *it == "hybrid" ) plugin->compressionType = FormatInfo::CompressionType( plugin->compressionType | FormatInfo::hybrid );
+ }
+ plugin->size = node.toElement().attribute("size","15000").toInt();
+
+ }
+ node = node.nextSibling();
+ }
+
+ return plugin;
+}
+
diff --git a/src/pluginloader/formatinfoloader.h b/src/pluginloader/formatinfoloader.h
new file mode 100755
index 0000000..f36f7fe
--- /dev/null
+++ b/src/pluginloader/formatinfoloader.h
@@ -0,0 +1,67 @@
+
+
+#ifndef FORMATINFOLOADER_H
+#define FORMATINFOLOADER_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qdom.h>
+
+/**
+ * @short The complete information about that format
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class FormatInfo
+{
+public:
+ /**
+ * Constructor
+ */
+ FormatInfo();
+
+ /**
+ * Destructor
+ */
+ virtual ~FormatInfo();
+
+ QStringList mime_types;
+ QStringList extensions;
+ QString description;
+ QStringList urls;
+ enum CompressionType {
+ lossy = 0x0001, // encode with loss
+ lossless = 0x0002, // encode without loss
+ hybrid = 0x0004 // encode a file with loss and a correction file
+ } compressionType;
+ int size;
+};
+
+/**
+ * @short The format info loader that provides information about the compression type and a format description
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class FormatInfoLoader : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ FormatInfoLoader();
+
+ /**
+ * Destructor
+ */
+ virtual ~FormatInfoLoader();
+
+ /** is this file a converter plugin and loadable? */
+ bool verifyFile( QString );
+ /** load a given file */
+ FormatInfo* loadFile( QString );
+ /** the dom tree for loading the xml file */
+ QDomDocument domTree;
+};
+
+#endif // FORMATINFOLOADER_H
diff --git a/src/pluginloader/pluginloaderbase.cpp b/src/pluginloader/pluginloaderbase.cpp
new file mode 100755
index 0000000..d9a8413
--- /dev/null
+++ b/src/pluginloader/pluginloaderbase.cpp
@@ -0,0 +1,17 @@
+
+#include "pluginloaderbase.h"
+
+PluginLoaderBase::PluginLoaderBase()
+{}
+
+PluginLoaderBase::~PluginLoaderBase()
+{}
+
+void PluginLoaderBase::unloadAll()
+{}
+
+void PluginLoaderBase::unload( QString pluginName )
+{}
+
+void PluginLoaderBase::remove( QString pluginName )
+{}
diff --git a/src/pluginloader/pluginloaderbase.h b/src/pluginloader/pluginloaderbase.h
new file mode 100755
index 0000000..c35bebe
--- /dev/null
+++ b/src/pluginloader/pluginloaderbase.h
@@ -0,0 +1,86 @@
+
+
+#ifndef PLUGINLOADERBASE_H
+#define PLUGINLOADERBASE_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qdom.h>
+
+#include "config.h"
+
+// TODO implement rank everywhere
+
+/**
+ * @short The base for all pluginloaders
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class PluginLoaderBase : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Do we recommend this backend to the user
+ */
+ enum FeatureLevel
+ {
+ Full, // the backend provides full support
+ Most, // most features are supported
+ Basic // we'll have just basic support
+ };
+
+ /**
+ * Just a small enum for our sampling rate units
+ */
+ enum Unit
+ {
+ Hz,
+ KHz
+ };
+
+ /**
+ * Constructor
+ */
+ PluginLoaderBase();
+
+ /**
+ * Destructor
+ */
+ virtual ~PluginLoaderBase();
+
+ /** a list of all plugin files that are loaded at the moment */
+// QStringList openPluginFiles;
+
+ /** the dom tree for loading the xml file */
+ QDomDocument domTree;
+
+public slots:
+ /**
+ * Unload all plugins
+ */
+ void unloadAll();
+
+ /**
+ * Unoad a specific plugin with name @p pluginName
+ */
+ void unload( QString pluginName );
+
+ /**
+ * Unoad a specific plugin with name @p pluginName and delete the plugin file
+ */
+ void remove( QString pluginName );
+};
+
+#endif // PLUGINLOADERBASE_H
+
+
+/**
+
+Each plugin package contains 3 files:
+ - The plugin file ( $KDE_DIR/share/apps/soundkonverter/plugins/'filename' )
+ - The format info file ( $KDE_DIR/share/apps/soundkonverter/format_infos/'lang'/'plugin'_'filename' )
+ - The service menu ( $KDE_DIR/share/apps/konqueror/servicemenus/'filename' )
+
+*/
+
diff --git a/src/pluginloader/replaygainpluginloader.cpp b/src/pluginloader/replaygainpluginloader.cpp
new file mode 100755
index 0000000..7ac11f6
--- /dev/null
+++ b/src/pluginloader/replaygainpluginloader.cpp
@@ -0,0 +1,116 @@
+
+#include "replaygainpluginloader.h"
+
+#include <qfile.h>
+
+#include <klocale.h>
+
+
+ReplayGainPlugin::ReplayGainPlugin()
+{}
+
+ReplayGainPlugin::~ReplayGainPlugin()
+{}
+
+
+ReplayGainPluginLoader::ReplayGainPluginLoader()
+{}
+
+ReplayGainPluginLoader::~ReplayGainPluginLoader()
+{}
+
+int ReplayGainPluginLoader::verifyFile( QString fileName )
+{
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return -1;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return -1;
+ }
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "replaygain" ) return -1;
+ int version;
+ QDomNode node;
+ node = root.firstChild();
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+ version = node.toElement().attribute("version","0").toInt();
+ break;
+ }
+ }
+
+ return version;
+}
+
+ReplayGainPlugin* ReplayGainPluginLoader::loadFile( QString fileName )
+{
+ //int t_int;
+ //float t_float;
+ //QString t_str;
+
+ ReplayGainPlugin* plugin = new ReplayGainPlugin();
+ plugin->info.version = -1; // if something goes wrong, we can see that by looking at plugin->info.version
+ plugin->filePathName = fileName;
+
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return plugin;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return plugin;
+ }
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "replaygain" ) return plugin;
+ QDomNode node;//, sub1Node;
+ node = root.firstChild();
+
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+
+ plugin->info.name = node.toElement().attribute("name",i18n("Unknown Name"));
+ plugin->info.about = node.toElement().attribute("about",i18n("Sorry, no information available!"));
+ plugin->info.author = node.toElement().attribute("author",i18n("Unknown Author"));
+ plugin->info.version = node.toElement().attribute("version","0").toInt();
+
+ }
+ else if( node.isElement() && node.nodeName() == "replaygain" ) {
+
+ plugin->replaygain.rank = node.toElement().attribute("rank","10").toInt();
+ plugin->replaygain.bin = node.toElement().attribute( "bin" );
+ plugin->replaygain.param = node.toElement().attribute("param");
+ plugin->replaygain.silent_param = node.toElement().attribute("silent_param");
+ plugin->replaygain.in_files = node.toElement().attribute("in_files");
+ plugin->replaygain.output_single = node.toElement().attribute("output_single");
+ plugin->replaygain.output_multiple = node.toElement().attribute("output_multiple");
+ plugin->replaygain.mime_types = QStringList::split( ',', node.toElement().attribute("mime_types","application/octet-stream") );
+ plugin->replaygain.force = node.toElement().attribute("force");
+ plugin->replaygain.skip = node.toElement().attribute("skip");
+ plugin->replaygain.track = node.toElement().attribute("track");
+ plugin->replaygain.album = node.toElement().attribute("album");
+ plugin->replaygain.remove = node.toElement().attribute("remove");
+
+ /*sub1Node = node.toElement().firstChild(); // obsolete
+ while( !sub1Node.isNull() ) {
+ if( sub1Node.isElement() && sub1Node.nodeName() == "test" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->replaygain.test.enabled = true;
+ else plugin->replaygain.test.enabled = false;
+ plugin->replaygain.test.param = sub1Node.toElement().attribute("param");
+ plugin->replaygain.test.output_track = sub1Node.toElement().attribute("output_track");
+ plugin->replaygain.test.output_album = sub1Node.toElement().attribute("output_album");
+
+ }
+ sub1Node = sub1Node.nextSibling();
+ }*/
+ }
+ node = node.nextSibling();
+ }
+
+ return plugin;
+}
+
diff --git a/src/pluginloader/replaygainpluginloader.h b/src/pluginloader/replaygainpluginloader.h
new file mode 100755
index 0000000..d1c1237
--- /dev/null
+++ b/src/pluginloader/replaygainpluginloader.h
@@ -0,0 +1,89 @@
+
+
+#ifndef REPLAYGAINPLUGINLOADER_H
+#define REPLAYGAINPLUGINLOADER_H
+
+#include "pluginloaderbase.h"
+
+
+/**
+ * @short The complete data, how to use the backend
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ReplayGainPlugin
+{
+public:
+ /**
+ * Constructor
+ */
+ ReplayGainPlugin();
+
+ /**
+ * Destructor
+ */
+ virtual ~ReplayGainPlugin();
+
+ QString filePathName; // the file name of th plugin (needed to detect write permissions)
+
+ struct Info
+ {
+ int version; // the version of our plugin (v0.2.1 = 201, v11.3 = 110300)
+ QString name; // the name of our plugin
+ QString author; // the author of the plugin
+ QString about; // a short information aboue the plugin
+ } info;
+
+ struct ReplayGain
+ {
+ //PluginLoaderBase::FeatureLevel level;
+ int rank;
+ QString bin;
+ QString param;
+ QString silent_param;
+ QStringList mime_types;
+ QString in_files;
+ QString output_single;
+ QString output_multiple;
+ QString force;
+ QString skip;
+ QString track; // TODO remove track and album (put them into param)
+ QString album;
+ QString remove;
+
+ /*struct Test // obsolete
+ {
+ bool enabled;
+ QString param;
+ QString output_track;
+ QString output_album;
+ } test;*/
+ } replaygain;
+};
+
+/**
+ * @short The plugin loader for the replaygain plugins
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ReplayGainPluginLoader : public PluginLoaderBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ ReplayGainPluginLoader();
+
+ /**
+ * Destructor
+ */
+ virtual ~ReplayGainPluginLoader();
+
+ /** is this file a replaygain plugin and loadable? */
+ int verifyFile( QString );
+ /** load a given file */
+ ReplayGainPlugin* loadFile( QString );
+};
+
+#endif // REPLAYGAINPLUGINLOADER_H
diff --git a/src/pluginloader/ripperpluginloader.cpp b/src/pluginloader/ripperpluginloader.cpp
new file mode 100755
index 0000000..9c1f9d4
--- /dev/null
+++ b/src/pluginloader/ripperpluginloader.cpp
@@ -0,0 +1,111 @@
+
+#include "ripperpluginloader.h"
+
+#include <qfile.h>
+
+#include <klocale.h>
+
+
+RipperPlugin::RipperPlugin()
+{}
+
+RipperPlugin::~RipperPlugin()
+{}
+
+
+RipperPluginLoader::RipperPluginLoader()
+{}
+
+RipperPluginLoader::~RipperPluginLoader()
+{}
+
+int RipperPluginLoader::verifyFile( QString fileName )
+{
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return -1;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return -1;
+ }
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "ripper" ) return -1;
+ int version;
+ QDomNode node;
+ node = root.firstChild();
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+ version = node.toElement().attribute("version","0").toInt();
+ break;
+ }
+ }
+
+ return version;
+}
+
+RipperPlugin* RipperPluginLoader::loadFile( QString fileName )
+{
+ int t_int;
+ float t_float;
+ QString t_str;
+
+ RipperPlugin* plugin = new RipperPlugin();
+ plugin->info.version = -1; // if something goes wrong, we can see that by looking at plugin->info.version
+ plugin->filePathName = fileName;
+
+ QFile opmlFile( fileName );
+ if( !opmlFile.open( IO_ReadOnly ) ) {
+ return plugin;
+ }
+ if( !domTree.setContent( &opmlFile ) ) {
+ return plugin;
+ }
+ opmlFile.close();
+
+ QDomElement root = domTree.documentElement();
+ if( root.attribute("type") != "ripper" ) return plugin;
+ QDomNode node, sub1Node;
+ node = root.firstChild();
+
+ while( !node.isNull() ) {
+ if( node.isElement() && node.nodeName() == "info" ) {
+
+ plugin->info.name = node.toElement().attribute("name",i18n("Unknown Name"));
+ plugin->info.about = node.toElement().attribute("about",i18n("Sorry, no information available!"));
+ plugin->info.author = node.toElement().attribute("author",i18n("Unknown Author"));
+ plugin->info.version = node.toElement().attribute("version","0").toInt();
+
+ }
+ else if( node.isElement() && node.nodeName() == "rip" ) {
+
+ plugin->rip.rank = node.toElement().attribute("rank","10").toInt();
+ plugin->rip.bin = node.toElement().attribute( "bin" );
+ plugin->rip.param = node.toElement().attribute("param");
+ plugin->rip.silent_param = node.toElement().attribute("silent_param");
+ plugin->rip.out_file = node.toElement().attribute("out_file");
+ plugin->rip.track = node.toElement().attribute("track");
+ plugin->rip.device = node.toElement().attribute("device");
+ plugin->rip.overwrite = node.toElement().attribute("overwrite");
+ plugin->rip.output = node.toElement().attribute("output");
+
+ sub1Node = node.toElement().firstChild();
+ while( !sub1Node.isNull() ) {
+ if( sub1Node.isElement() && sub1Node.nodeName() == "full_disc" ) {
+
+ if( sub1Node.toElement().attribute("enabled") == "true" ) plugin->rip.full_disc.enabled = true;
+ else plugin->rip.full_disc.enabled = false;
+ plugin->rip.full_disc.param = sub1Node.toElement().attribute("param");
+ plugin->rip.full_disc.output = sub1Node.toElement().attribute("output");
+
+ }
+ sub1Node = sub1Node.nextSibling();
+ }
+ }
+ node = node.nextSibling();
+ }
+
+ return plugin;
+}
+
diff --git a/src/pluginloader/ripperpluginloader.h b/src/pluginloader/ripperpluginloader.h
new file mode 100755
index 0000000..6cf965e
--- /dev/null
+++ b/src/pluginloader/ripperpluginloader.h
@@ -0,0 +1,84 @@
+
+
+#ifndef RIPPERPLUGINLOADER_H
+#define RIPPERPLUGINLOADER_H
+
+#include "pluginloaderbase.h"
+
+
+/**
+ * @short The complete data, how to use the backend
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class RipperPlugin
+{
+public:
+ /**
+ * Constructor
+ */
+ RipperPlugin();
+
+ /**
+ * Destructor
+ */
+ virtual ~RipperPlugin();
+
+ QString filePathName; // the file name of th plugin (needed to detect write permissions)
+
+ struct Info
+ {
+ int version; // the version of our plugin (v0.2.1 = 201, v11.3 = 110300)
+ QString name; // the name of our plugin
+ QString author; // the author of the plugin
+ QString about; // a short information aboue the plugin
+ } info;
+
+ struct Rip
+ {
+ //PluginLoaderBase::FeatureLevel level;
+ int rank;
+ QString bin;
+ QString param;
+ QString silent_param;
+ QString out_file;
+ QString track;
+ QString device;
+ QString overwrite;
+ QString output;
+
+ struct FullDisc
+ {
+ bool enabled;
+ QString param;
+ QString output;
+ } full_disc;
+ } rip;
+};
+
+/**
+ * @short The plugin loader for the ripper plugins
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class RipperPluginLoader : public PluginLoaderBase
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ RipperPluginLoader();
+
+ /**
+ * Destructor
+ */
+ virtual ~RipperPluginLoader();
+
+ /** is this file a ripper plugin and loadable? */
+ int verifyFile( QString );
+ /** load a given file */
+ RipperPlugin* loadFile( QString );
+};
+
+#endif // RIPPERPLUGINLOADER_H
diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am
new file mode 100755
index 0000000..f3dea7a
--- /dev/null
+++ b/src/plugins/Makefile.am
@@ -0,0 +1,5 @@
+
+pluginsdir = $(kde_datadir)/soundkonverter/plugins
+format_infos_endir = $(kde_datadir)/soundkonverter/format_infos/en
+
+SUBDIRS = format_infos mime_types plugins
diff --git a/src/plugins/format_infos/3gp.xml b/src/plugins/format_infos/3gp.xml
new file mode 100755
index 0000000..ff6a621
--- /dev/null
+++ b/src/plugins/format_infos/3gp.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="3gp,3GP" compression_type="lossy" size="20000" mime_types="video/3gpp" description="3gp video" />
+</soundkonverter>
diff --git a/src/plugins/format_infos/Makefile.am b/src/plugins/format_infos/Makefile.am
new file mode 100644
index 0000000..3f45bea
--- /dev/null
+++ b/src/plugins/format_infos/Makefile.am
@@ -0,0 +1,5 @@
+format_infosdir = $(kde_datadir)/soundkonverter/format_infos
+format_infos_DATA = 3gp.xml aac.xml ac3.xml aiff.xml amr.xml ape.xml au.xml \
+ avi.xml bonk.xml flac.xml la.xml m4a.xml mid.xml mod.xml mp2.xml mp3.xml \
+ mpc.xml ofc.xml ofr.xml ofs.xml ogg.xml pac.xml qt.xml ra.xml shn.xml spx.xml \
+ tta.xml wma.xml wv.xml wvc.xml
diff --git a/src/plugins/format_infos/aac.xml b/src/plugins/format_infos/aac.xml
new file mode 100755
index 0000000..ad24c96
--- /dev/null
+++ b/src/plugins/format_infos/aac.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="aac,AAC,Aac" compression_type="lossy" size="20000" urls="http://en.wikipedia.org/wiki/Advanced_Audio_Coding" description_de="AAC kodiert Audiodateien verlustbehaftet. Es ist sehr populär, da es das Standartformat von Apple ist und daher kompatibel mit iTunes und iPods." mime_types="audio/aac" urls_de="http://de.wikipedia.org/wiki/Advanced_Audio_Coding" description="Advanced Audio Coding compresses audio files with loss. It's very popular since it is Apple's default format and hence compatible with iTunes and iPods." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ac3.xml b/src/plugins/format_infos/ac3.xml
new file mode 100755
index 0000000..d4249b7
--- /dev/null
+++ b/src/plugins/format_infos/ac3.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ac3,AC3,Ac3" compression_type="lossy" size="20000" urls="http://en.wikipedia.org/wiki/Ac3" description_de="AC-3 ist ein verlustbehafteter Codec von Dolby Digital (von Dolby Laboratories). Er kann mehrere Kanäle speichern (Mono, Stereo, Surround)." mime_types="audio/ac3" urls_de="http://de.wikipedia.org/wiki/Ac3" description="AC-3 is a lossy audio codec of Dolby Digital (by Dolby Laboratories). It can hold multiple channels (Mono, Stereo, Surround)." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/aiff.xml b/src/plugins/format_infos/aiff.xml
new file mode 100644
index 0000000..ca572d6
--- /dev/null
+++ b/src/plugins/format_infos/aiff.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="aiff,AIFF,Aiff,aif,AIF,Aif" compression_type="lossless" size="89000" urls="http://en.wikipedia.org/wiki/Audio_Interchange_File_Format" description_de="Aiff ist ein Format, das die Audiodaten nicht komprimiert. (Ähnlich wie das Wave Format.)\nEs wurde von Apple Computer mit entwickelt." mime_types="audio/x-aiff" urls_de="http://de.wikipedia.org/wiki/Audio_Interchange_File_Format" description="Aiff is a file format that doesn't compress it's data. (Similar to the wave format.)\nIt was co-developed by Apple Computer." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/amr.xml b/src/plugins/format_infos/amr.xml
new file mode 100755
index 0000000..2ce353e
--- /dev/null
+++ b/src/plugins/format_infos/amr.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="amr,AMR,Amr" compression_type="lossy" size="14000" urls="http://en.wikipedia.org/wiki/Adaptive_multi-rate_compression" description_de="AMR ist ein Audiocodec für menschliche Sprache.\nBenutzen Sie es, wenn sie z.B. Audiobücher komprimieren wollen." mime_types="audio/amr" urls_de="http://de.wikipedia.org/wiki/Adaptive_Multi-Rate##Adaptive_Multirate_Codec_.28AMR.29" description="AMR is an audio codec for human voice.\nUse it if you want to encode speech, an audio book for example." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ape.xml b/src/plugins/format_infos/ape.xml
new file mode 100755
index 0000000..de0d7da
--- /dev/null
+++ b/src/plugins/format_infos/ape.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ape,APE,Ape,mac,MAC,Mac" compression_type="lossless" size="20000" urls="http://en.wikipedia.org/wiki/Monkey's_Audio" description_de="Ape (Monkey's Audio) ist ein Format, welches seine Daten verlustfrei komprimiert.\nDadurch wird eine sehr Hohe Qualität erreicht, die Datei ist jedoch sehr groß. Ape ist Freeware." mime_types="audio/x-ape" urls_de="http://de.wikipedia.org/wiki/Monkey's_Audio" description="Ape (Monkey's Audio) is a file format that compresses the audio data without loss.\nThe result is a very high quality but very big file. Ape is freeware." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/au.xml b/src/plugins/format_infos/au.xml
new file mode 100755
index 0000000..cf98e76
--- /dev/null
+++ b/src/plugins/format_infos/au.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="au,AU,snd,SND" compression_type="lossless" size="89000" urls="http://en.wikipedia.org/wiki/Au_file_format" description_de="Ein sehr einfaches Audioformat von Sun Microsystems." mime_types="audio/basic" urls_de="http://de.wikipedia.org/wiki/Au_(Dateiformat)" description="A simple lossless audio format by Sun Microsystems." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/avi.xml b/src/plugins/format_infos/avi.xml
new file mode 100755
index 0000000..24836e6
--- /dev/null
+++ b/src/plugins/format_infos/avi.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="avi,AVI" compression_type="lossy" size="14000" mime_types="video/x-msvideo" description="avi video" />
+</soundkonverter>
diff --git a/src/plugins/format_infos/bonk.xml b/src/plugins/format_infos/bonk.xml
new file mode 100755
index 0000000..e2f18f2
--- /dev/null
+++ b/src/plugins/format_infos/bonk.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="bonk,BONK,Bonk" compression_type="lossless,lossy" size="89000" urls="http://www.logarithmic.net/pfh/bonk" description_de="Bonk kann Audiodateien sowohl verlustfrei, als auch verlustbehaftet komprimieren." mime_types="audio/x-bonk" description="Bonk can compress it's data with and without loss." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/flac.xml b/src/plugins/format_infos/flac.xml
new file mode 100755
index 0000000..e03e0ce
--- /dev/null
+++ b/src/plugins/format_infos/flac.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="flac,FLAC,Flac" compression_type="lossless" size="89000" urls="http://en.wikipedia.org/wiki/Flac,http://wiki.hydrogenaudio.org/index.php?title=FLAC" description_de="FLAC (Free Lossless Audio Compressor) ist ein freies, verlustfrei komprimierendes Format.\nDas Ergebnis ist eine sehr große Datei mit einer sehr hohen Qualität." mime_types="audio/x-flac" urls_de="http://de.wikipedia.org/wiki/Flac,http://www.audiohq.de/index.php?showtopic=53" description="FLAC (Free Lossless Audio Compressor) is a free file format that compresses the audio data without loss.\nThe result is a very high quality but very big file." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/la.xml b/src/plugins/format_infos/la.xml
new file mode 100755
index 0000000..eb2759b
--- /dev/null
+++ b/src/plugins/format_infos/la.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="la,LA,La" compression_type="lossless" size="20000" description_de="LA komprimiert Audiodateien verlustfrei.\nDabei kann man Kompressionsraten von ca. 50% erwarten." mime_types="application/x-la" description="Lossless Audio compresses it's data without loss.\nYou can expect a compression ratio at about 50%." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/m4a.xml b/src/plugins/format_infos/m4a.xml
new file mode 100755
index 0000000..ec437f3
--- /dev/null
+++ b/src/plugins/format_infos/m4a.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="m4a,M4A,M4a" compression_type="lossy" size="20000" urls="http://en.wikipedia.org/wiki/M4a" description_de="MP4 ist ein Containerformat für den AAC Codec.\nAAC kodiert Audiodateien verlustbehaftet. Es ist sehr populär, da es das Standartformat von Apple ist und daher kompatibel mit iTunes und iPods." mime_types="audio/mp4,video/mp4,audio/x-m4a" urls_de="http://de.wikipedia.org/wiki/M4a" description="MP4 is a container format for the aac codec.\nAdvanced Audio Coding compresses audio files with loss. It's very popular since it is Apple's default format and hence compatible with iTunes and iPods." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/mid.xml b/src/plugins/format_infos/mid.xml
new file mode 100755
index 0000000..bf1b613
--- /dev/null
+++ b/src/plugins/format_infos/mid.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="midi,MIDI,Midi,mid,MID,Mid" compression_type="lossy" size="9" urls="http://en.wikipedia.org/wiki/Musical_Instrument_Digital_Interface" mime_types="audio/x-midi" description="Electronic music without audio data but with events." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/mod.xml b/src/plugins/format_infos/mod.xml
new file mode 100755
index 0000000..cfc3443
--- /dev/null
+++ b/src/plugins/format_infos/mod.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="mod,MOD,Mod,s3m,S3M,S3m,stm,STM,Stm,ult,ULT,Ult,UNI,uni,Uni,xm,XM,Xm,m15,M15,mtm,MTM,Mtm,669,it,IT,It" compression_type="lossy" size="9" urls="http://en.wikipedia.org/wiki/MOD_(file_format)" mime_types="audio/x-mod" description="Electronic music without audio data but with events." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/mp2.xml b/src/plugins/format_infos/mp2.xml
new file mode 100755
index 0000000..bfa4740
--- /dev/null
+++ b/src/plugins/format_infos/mp2.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="mp2,MP2,Mp2" compression_type="lossy" size="25000" urls="http://en.wikipedia.org/wiki/Mp2" description_de="MP2 ist der Vorgänger von MP3.\nEs könnte für ältere Abspieler nützlich sein." mime_types="audio/x-mp2" urls_de="http://de.wikipedia.org/wiki/MPEG-1_Audio_Layer_2" description="MP2 is the predecessor of MP3.\nYou may need this for old audio players." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/mp3.xml b/src/plugins/format_infos/mp3.xml
new file mode 100755
index 0000000..f57d0c6
--- /dev/null
+++ b/src/plugins/format_infos/mp3.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="mp3,MP3,Mp3" compression_type="lossy" size="20000" urls="http://en.wikipedia.org/wiki/Mp3" description_de="MP3 ist das am weitesten verbreitete Format.\nWenn Sie nicht wissen, was Sie wählen sollen, nehmen Sie dieses." mime_types="audio/x-mp3,video/mpeg,audio/mpeg" urls_de="http://de.wikipedia.org/wiki/Mp3" description="MP3 is the most popular file format.\nIf you don't know what to choose, this should work with every audio player." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/mpc.xml b/src/plugins/format_infos/mpc.xml
new file mode 100755
index 0000000..2d8dda1
--- /dev/null
+++ b/src/plugins/format_infos/mpc.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="mpc,MPC,Mpc,mp+,MP+,Mp+,mpp,MPP,Mpp" compression_type="lossy" size="20000" urls="http://en.wikipedia.org/wiki/Musepack" description_de="Musepack ist ein MPEG 2 Dateiformat.\nEs wurde für den Gebrauch von hohen Bitraten entwickelt, unterstützt jedoch auch niedrige. Musepack ist Open Source." mime_types="audio/x-musepack" urls_de="http://de.wikipedia.org/wiki/Musepack,http://www.audiohq.de/index.php?showtopic=51" description="Musepack is an MPEG 2 file format.\nIt is designed for high bitrates but supportes low bitrates, too. Musepack is Open Source." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ofc.xml b/src/plugins/format_infos/ofc.xml
new file mode 100755
index 0000000..edeee6f
--- /dev/null
+++ b/src/plugins/format_infos/ofc.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ofc,OFC,Ofc" compression_type="hybrid" size="89000" mime_types="application/x-ofc" description="OptimFROG correction file" />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ofr.xml b/src/plugins/format_infos/ofr.xml
new file mode 100755
index 0000000..f48581d
--- /dev/null
+++ b/src/plugins/format_infos/ofr.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ofr,OFR,Ofr" compression_type="lossless" size="14000" urls="http://losslessaudio.org" description_de="OptimFROG ist ein verlustfrei komprimierendes Dateiformat." mime_types="application/x-ofr" description="OptimFROG is a file format compressing it't data without loss." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ofs.xml b/src/plugins/format_infos/ofs.xml
new file mode 100755
index 0000000..b533c44
--- /dev/null
+++ b/src/plugins/format_infos/ofs.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ofs,OFS,Ofs" compression_type="lossy,hybrid" size="89000" urls="http://losslessaudio.org" description_de="OptimFROG Dualstream kann seine Daten sowohl verlustbehaftet, als auch hybrid komprimieren." mime_types="application/x-ofs" description="OptimFROG Dualstream can compress it's data lossy and hybrid." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ogg.xml b/src/plugins/format_infos/ogg.xml
new file mode 100755
index 0000000..4bacbc9
--- /dev/null
+++ b/src/plugins/format_infos/ogg.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ogg,Ogg,OGG" compression_type="lossy" size="14000" urls="http://en.wikipedia.org/wiki/Vorbis" description_de="Ogg Vorbis ist eine freie Alternative zu MP3.\nEs produziert in den meißten Fällen Dateien mit einer höheren Qualität als MP3, Sie sollten es verwenden, wenn Sie können." mime_types="application/ogg,audio/vorbis" urls_de="http://de.wikipedia.org/wiki/Vorbis" description="Ogg Vorbis is a free alternative to MP3.\nIn most cases it produces a file of better quality than MP3, you should use it, if you can." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/pac.xml b/src/plugins/format_infos/pac.xml
new file mode 100755
index 0000000..a1b863c
--- /dev/null
+++ b/src/plugins/format_infos/pac.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="pac,PAC,Pac,lpac,LPAC,Lpac" compression_type="lossless" size="20000" description_de="LPAC ist ein verlustfreier audio codec." mime_types="audio/x-pac" description="Lossless Predictive Audio Compression compresses without loss." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/qt.xml b/src/plugins/format_infos/qt.xml
new file mode 100755
index 0000000..19a2ae8
--- /dev/null
+++ b/src/plugins/format_infos/qt.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="qt,mov,moov,qtvr,QT,MOV,MOOV,MooV,QTVR,Qt,Mov,Qtvr,Moov" compression_type="lossy" size="14000" mime_types="video/quicktime" description="quicktime video" />
+</soundkonverter>
diff --git a/src/plugins/format_infos/ra.xml b/src/plugins/format_infos/ra.xml
new file mode 100755
index 0000000..4c7a822
--- /dev/null
+++ b/src/plugins/format_infos/ra.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="ra,RA,Ra,ram,RAM,Ram,rmm,RMM,Rmm" compression_type="lossy" size="20000" urls="http://en.wikipedia.org/wiki/RealAudio" description_de="Das RealMedia Format komprimiert verlustbehaftet.\nDer RealMedia Player ist weit verbreitet und verfügbar für Windows und Linux." mime_types="audio/vnd.rn-realaudio,application/vnd.rn-realmedia,video/vnd.rn-realvideo" urls_de="http://de.wikipedia.org/wiki/RealAudio" description="The RealMedia format comresses it's audio data with loss.\nThe RealMedia Player is widespreaded and you can obtain it for Windows and Linux." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/shn.xml b/src/plugins/format_infos/shn.xml
new file mode 100755
index 0000000..db33e81
--- /dev/null
+++ b/src/plugins/format_infos/shn.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="shn,SHN,Shn" compression_type="lossless" size="20000" urls="http://en.wikipedia.org/wiki/Shorten" description_de="Shorten komprimiert seine Daten ohne Verlust." mime_types="application/x-shorten" urls_de="http://de.wikipedia.org/wiki/Shorten" description="Shorten is a file format the comresses it's data without loss." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/spx.xml b/src/plugins/format_infos/spx.xml
new file mode 100755
index 0000000..7f97476
--- /dev/null
+++ b/src/plugins/format_infos/spx.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="spx,SPX,Spx" compression_type="lossy" size="14000" urls="http://en.wikipedia.org/wiki/Speex" description_de="Speex ist ein Audiocodec für menschliche Sprache.\nBenutzen Sie es, wenn sie z.B. Audiobücher komprimieren wollen.\nSpeex ist Open Source." mime_types="audio/x-speex" urls_de="http://de.wikipedia.org/wiki/Speex" description="Speex is an audio codec for human voice.\nUse it if you want to encode speech, an audio book for example.\nSpeex is Open Source." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/tta.xml b/src/plugins/format_infos/tta.xml
new file mode 100755
index 0000000..0d9bb7c
--- /dev/null
+++ b/src/plugins/format_infos/tta.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="tta,TTA,Tta" compression_type="lossless" size="89000" urls="http://en.wikipedia.org/wiki/TTA_(codec),http://tta.sourceforge.net" description_de="TTA kann Audiodateien verlustfrei komprimieren." mime_types="audio/x-tta" urls_de="http://de.wikipedia.org/wiki/True_Audio" description="TTA can compress it's data without loss." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/wma.xml b/src/plugins/format_infos/wma.xml
new file mode 100755
index 0000000..53ccbb8
--- /dev/null
+++ b/src/plugins/format_infos/wma.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="wma,WMA,Wma" compression_type="lossy" size="14000" urls="http://en.wikipedia.org/wiki/Windows_Media_Audio" description_de="Das Windows Media Audio Format it propritär. In den meißten Fällen werden Dateien mit geringerer Qualität als mp3 und natürlich ogg erstellt.\nWma ist aus Vollständigkeitsgründen verfügbar aber es wird nicht empfohlen es zu benutzen." mime_types="audio/x-ms-wma,video/x-ms-asf,video/x-ms-wmv" urls_de="http://de.wikipedia.org/wiki/Windows_Media_Audio" description="The Windows Media Audio format is propriatry. In most cases the quality of the output files is worse than mp3 and of cause ogg.\nWma is available for completeness and it is not recommended to use it." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/wv.xml b/src/plugins/format_infos/wv.xml
new file mode 100755
index 0000000..d4fab5a
--- /dev/null
+++ b/src/plugins/format_infos/wv.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="wv,WV,Wv" compression_type="lossless,hybrid" size="89000" urls="http://en.wikipedia.org/wiki/WavPack,http://www.wavpack.com" description_de="WavPack kann Audiodateien entweder verlustfrei oder hybrid komprimieren." mime_types="audio/x-wavpack" urls_de="http://de.wikipedia.org/wiki/WavPack" description="WavPack can compress it's data without loss or hybrid." />
+</soundkonverter>
diff --git a/src/plugins/format_infos/wvc.xml b/src/plugins/format_infos/wvc.xml
new file mode 100755
index 0000000..33698b7
--- /dev/null
+++ b/src/plugins/format_infos/wvc.xml
@@ -0,0 +1,4 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="format_info" >
+ <data extensions="wvc,WVC,Wvc" compression_type="hybrid" size="89000" mime_types="audio/x-wavpack-correction" description="WavPack correction file" />
+</soundkonverter>
diff --git a/src/plugins/mime_types/Makefile.am b/src/plugins/mime_types/Makefile.am
new file mode 100755
index 0000000..cedeba7
--- /dev/null
+++ b/src/plugins/mime_types/Makefile.am
@@ -0,0 +1,11 @@
+audio_mimedir = $(kde_mimedir)/audio
+application_mimedir = $(kde_mimedir)/application
+video_mimedir = $(kde_mimedir)/video
+audio_mime_DATA = amr.soundkonverter.desktop x-ape.soundkonverter.desktop x-bonk.soundkonverter.desktop x-pac.soundkonverter.desktop \
+ x-tta.soundkonverter.desktop x-wavpack-correction.soundkonverter.desktop x-wavpack.soundkonverter.desktop
+application_mime_DATA = x-la.soundkonverter.desktop \
+ x-ofc.soundkonverter.desktop \
+ x-ofr.soundkonverter.desktop \
+ x-ofs.soundkonverter.desktop \
+ x-shorten.soundkonverter.desktop
+video_mime_DATA = x-flv.soundkonverter.desktop
diff --git a/src/plugins/mime_types/amr.soundkonverter.desktop b/src/plugins/mime_types/amr.soundkonverter.desktop
new file mode 100755
index 0000000..fc572cf
--- /dev/null
+++ b/src/plugins/mime_types/amr.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=Adaptive Multirate audio file
+Hidden=false
+Icon=audio
+MimeType=audio/amr
+Patterns=*.amr;*.AMR;*.Amr
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-ape.soundkonverter.desktop b/src/plugins/mime_types/x-ape.soundkonverter.desktop
new file mode 100755
index 0000000..cda24b6
--- /dev/null
+++ b/src/plugins/mime_types/x-ape.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=Monkey's Audio file
+Hidden=false
+Icon=audio
+MimeType=audio/x-ape
+Patterns=*.ape;*.APE;*.mac;*.MAC;*.ApE;*.aPE;*.APe;*.apE;*.APe;*.aPe
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-bonk.soundkonverter.desktop b/src/plugins/mime_types/x-bonk.soundkonverter.desktop
new file mode 100755
index 0000000..7e2fc78
--- /dev/null
+++ b/src/plugins/mime_types/x-bonk.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=BONK
+Hidden=false
+Icon=audio
+MimeType=audio/x-bonk
+Patterns=*.bonk;*.BONK;*.Bonk;*.BOnk
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-flv.soundkonverter.desktop b/src/plugins/mime_types/x-flv.soundkonverter.desktop
new file mode 100755
index 0000000..d220911
--- /dev/null
+++ b/src/plugins/mime_types/x-flv.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=Flash Video
+Hidden=false
+Icon=video
+MimeType=video/x-flv
+Patterns=*.flv;*.FLV
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-la.soundkonverter.desktop b/src/plugins/mime_types/x-la.soundkonverter.desktop
new file mode 100755
index 0000000..27edf9f
--- /dev/null
+++ b/src/plugins/mime_types/x-la.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=Lossless Audio file
+Hidden=false
+Icon=audio
+MimeType=application/x-la
+Patterns=*.la;*.LA;*.La;*.lA
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-ofc.soundkonverter.desktop b/src/plugins/mime_types/x-ofc.soundkonverter.desktop
new file mode 100755
index 0000000..7f62e25
--- /dev/null
+++ b/src/plugins/mime_types/x-ofc.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=OptimFROG DualStream correction file
+Hidden=false
+Icon=audio
+MimeType=application/x-ofc
+Patterns=*.ofc;*.OFC
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-ofr.soundkonverter.desktop b/src/plugins/mime_types/x-ofr.soundkonverter.desktop
new file mode 100755
index 0000000..f757caa
--- /dev/null
+++ b/src/plugins/mime_types/x-ofr.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=OptimFROG file
+Hidden=false
+Icon=audio
+MimeType=application/x-ofr
+Patterns=*.ofr;*.OFR;*.OfR;*.OFr;*.oFR;*.ofR
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-ofs.soundkonverter.desktop b/src/plugins/mime_types/x-ofs.soundkonverter.desktop
new file mode 100755
index 0000000..cecf374
--- /dev/null
+++ b/src/plugins/mime_types/x-ofs.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=OptimFROG DualStream file
+Hidden=false
+Icon=audio
+MimeType=application/x-ofs
+Patterns=*.ofs;*.OFS;*.OfS;*.OFs;*.oFS;*.ofS
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-pac.soundkonverter.desktop b/src/plugins/mime_types/x-pac.soundkonverter.desktop
new file mode 100755
index 0000000..7cef430
--- /dev/null
+++ b/src/plugins/mime_types/x-pac.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=Pac file
+Hidden=false
+Icon=audio
+MimeType=audio/x-pac
+Patterns=*.pac;*.PAC;*.lpac;*.LPAC;*.PaC;*.PAc;*.pAC;*.paC;*.LPaC;*.LPAc
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-shorten.soundkonverter.desktop b/src/plugins/mime_types/x-shorten.soundkonverter.desktop
new file mode 100755
index 0000000..01e5d47
--- /dev/null
+++ b/src/plugins/mime_types/x-shorten.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=Shorten file
+Hidden=false
+Icon=audio
+MimeType=application/x-shorten
+Patterns=*.shn;*.SHN;*.ShN;*.sHN;*.SHn;*.shN;*.sHn
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-tta.soundkonverter.desktop b/src/plugins/mime_types/x-tta.soundkonverter.desktop
new file mode 100755
index 0000000..65050a5
--- /dev/null
+++ b/src/plugins/mime_types/x-tta.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=The True Audio Codec
+Hidden=false
+Icon=audio
+MimeType=audio/x-tta
+Patterns=*.tta;*.TTA
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-wavpack-correction.soundkonverter.desktop b/src/plugins/mime_types/x-wavpack-correction.soundkonverter.desktop
new file mode 100755
index 0000000..a032b0b
--- /dev/null
+++ b/src/plugins/mime_types/x-wavpack-correction.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=WavPack file
+Hidden=false
+Icon=audio
+MimeType=audio/x-wavpack-correction
+Patterns=*.wvc;*.WVC
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/mime_types/x-wavpack.soundkonverter.desktop b/src/plugins/mime_types/x-wavpack.soundkonverter.desktop
new file mode 100755
index 0000000..14ae9b0
--- /dev/null
+++ b/src/plugins/mime_types/x-wavpack.soundkonverter.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Comment=WavPack file
+Hidden=false
+Icon=audio
+MimeType=audio/x-wavpack
+Patterns=*.wv;*.WV;*.wV;*.Wv
+Type=MimeType
+X-KDE-AutoEmbed=false
diff --git a/src/plugins/plugins/110.oggvorbis.soundkonverter.xml b/src/plugins/plugins/110.oggvorbis.soundkonverter.xml
new file mode 100755
index 0000000..592fa5c
--- /dev/null
+++ b/src/plugins/plugins/110.oggvorbis.soundkonverter.xml
@@ -0,0 +1,18 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about_de="Plugin fuer Ogg Vorbis" about="Plugin for en-/decoding Ogg Vorbis files with oggenc and oggdec. oggenc and oggdec should be shipped with your distribution. Nothing else is to install." version="304" author="Daniel Faust" name="Ogg Vorbis (oggenc &amp; oggdec)" />
+ <enc bin="oggenc" param="" overwrite="" mime_types="application/ogg,audio/vorbis" silent_param="--quiet" rank="100" in_out_files="%p -o %o %i" enabled="true" >
+ <lossy enabled="true" >
+ <channels stereo_param="" stereo_enabled="" mono_param="--downmix" mono_enabled="true" />
+ <samplingrate unit="Hz" param="--resample %s" enabled="true" />
+ <bitrate>
+ <abr output=" [ %p,%*s%%]" param="-b %b" enabled="true" >
+ <bitrate_range min_param="-m %m" max_param="-M %M" enabled="true" />
+ </abr>
+ </bitrate>
+ <quality range_min="-1" range_max="10" help="0 ~ -1.0\n10 ~ 0.1\n20 ~ 1.2\n30 ~ 2.3\n40 ~ 3.4\n50 ~ 4.5\n60 ~ 5.6\n70 ~ 6.7\n80 ~ 7.8\n90 ~ 8.9\n100 ~ 10.0" output=" [ %p,%*s%%]" separator="." param="-q %q" step="0.01" profiles="" enabled="true" />
+ </lossy>
+ <tag comment="--comment %tc" title="--title %tt" track="--tracknum %tn" param="" artist="--artist %ta" album="--album %tb" year="--date %ty" genre="--genre %tg" enabled="true" />
+ </enc>
+ <dec bin="oggdec" output=" [ %p,%*s%%]" param="" overwrite="" mime_types="application/ogg,audio/vorbis" silent_param="--quiet" rank="100" in_out_files="%p -o %o %i" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/120.lame.soundkonverter.xml b/src/plugins/plugins/120.lame.soundkonverter.xml
new file mode 100755
index 0000000..eb2adba
--- /dev/null
+++ b/src/plugins/plugins/120.lame.soundkonverter.xml
@@ -0,0 +1,22 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for lame. Get lame at http://lame.sf.net . With lame you can encode and decode mp3 files." author="Daniel Faust" version="304" name="Mp3 (lame)" />
+ <enc bin="lame" param="" overwrite="" mime_types="audio/x-mp3,audio/mpeg" silent_param="--quiet" rank="100" in_out_files="%p %i %o" enabled="true" >
+ <strength range_min="9" range_max="0" separator="" param="-q %c" step="1" profiles="" default_value="2" enabled="true" />
+ <lossless output="" param="" enabled="" />
+ <lossy enabled="true" >
+ <quality range_min="9" output=" %*s (%p%%)" help="0 ~ 9\n10 ~ 8\n20 ~ 7\n30 ~ 6\n40 ~ 5\n50 ~ 5\n60 ~ 4\n70 ~ 3\n80 ~ 2\n90 ~ 1\n100 ~ 0" range_max="0" separator="" param="-V %q --vbr-new --nohist" step="1" profiles="" enabled="true" />
+ <bitrate>
+ <abr output="%*s (%p%%)| %*s %*s %*s %*s %*s %*s\n" param="-h --abr %b --nohist" enabled="true" >
+ <bitrate_range min_param="-b %m" max_param="-B %M" enabled="true" />
+ </abr>
+ <cbr output="%*s %*s (%p%%)| %*s %*s %*s %*s %*s %*s\n" param="--cbr -b %b" enabled="true" />
+ </bitrate>
+ <samplingrate unit="KHz" param="--resample %s" enabled="true" />
+ <channels joint_stereo_enabled="true" stereo_param="-m s" dual_channels_enabled="false" forced_joint_stereo_enabled="true" dual_channels_param="-m d" stereo_enabled="true" joint_stereo_param="-m j" mono_param="-m m" mono_enabled="true" forced_joint_stereo_param="-m f" />
+ </lossy>
+ <replay_gain avoid="--noreplaygain" use="--replaygain-accurate" rank="30" enabled="true" />
+ <tag comment="--tc %tc" title="--tt %tt" track="--tn %tn" param="--ignore-tag-errors --add-id3v2 --pad-id3v2" artist="--ta %ta" album="--tl %tb" year="--ty %ty" genre="--tg %tg" enabled="true" />
+ </enc>
+ <dec output=" Frame# %0/%1 %*s kbps" bin="lame" param="--decode" overwrite="" mime_types="audio/x-mp3,audio/x-mp2,audio/mpeg" silent_param="--quiet" rank="100" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/122.twolame.soundkonverter.xml b/src/plugins/plugins/122.twolame.soundkonverter.xml
new file mode 100755
index 0000000..da081c7
--- /dev/null
+++ b/src/plugins/plugins/122.twolame.soundkonverter.xml
@@ -0,0 +1,15 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for TwoLAME. Get TwoLAME at http://www.twolame.org . With twolame you can encode and decode mp2 files." author="Daniel Faust" version="300" name="Mp2 (TwoLAME)" />
+ <enc bin="twolame" param="" overwrite="" mime_types="audio/x-mp2" silent_param="--quiet" rank="100" in_out_files="%p %i %o" enabled="true" >
+ <lossy enabled="true" >
+ <quality range_min="-10" output=" %*s (%p%%)" help="" range_max="10" separator="" param="-V %q" step="1" profiles="" enabled="false" />
+ <bitrate>
+ <abr output=" %*s (%p%%)" param="-v -b %b" enabled="false" />
+ <cbr output=" %*s (%p%%)" param="-b %b" enabled="true" />
+ </bitrate>
+ <channels joint_stereo_enabled="true" stereo_param="-m s" dual_channels_enabled="false" dual_channels_param="-m d" stereo_enabled="true" joint_stereo_param="-m j" mono_param="-m m" mono_enabled="true" />
+ </lossy>
+ </enc>
+ <dec output=" Frame# %0/%1 %*s kbps" bin="twolame" param="" overwrite="" mime_types="audio/x-mp2" silent_param="--quiet" rank="100" in_out_files="%p %i %o" enabled="false" />
+</soundkonverter>
diff --git a/src/plugins/plugins/123.toolame.soundkonverter.xml b/src/plugins/plugins/123.toolame.soundkonverter.xml
new file mode 100755
index 0000000..c4a7e2a
--- /dev/null
+++ b/src/plugins/plugins/123.toolame.soundkonverter.xml
@@ -0,0 +1,15 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for TooLAME. Get TooLAME at http://toolame.sourceforge.net . With toolame you can encode and decode mp2 files." author="Daniel Faust" version="300" name="Mp2 (TooLAME)" />
+ <enc bin="toolame" param="" overwrite="" mime_types="audio/x-mp2" silent_param="--quiet" rank="100" in_out_files="%p %i %o" enabled="true" >
+ <lossy enabled="true" >
+ <quality range_min="-10" output=" %*s (%p%%)" help="" range_max="10" separator="" param="-v %q" step="1" profiles="" enabled="false" />
+ <bitrate>
+ <abr output=" %*s (%p%%)" param="-v -b %b" enabled="false" />
+ <cbr output=" %*s (%p%%)" param="-b %b" enabled="true" />
+ </bitrate>
+ <channels joint_stereo_enabled="true" stereo_param="-m s" dual_channels_enabled="false" dual_channels_param="-m d" stereo_enabled="true" joint_stereo_param="-m j" mono_param="-m m" mono_enabled="true" />
+ </lossy>
+ </enc>
+ <dec output=" Frame# %0/%1 %*s kbps" bin="toolame" param="" overwrite="" mime_types="audio/x-mp2" silent_param="--quiet" rank="100" in_out_files="%p %i %o" enabled="false" />
+</soundkonverter>
diff --git a/src/plugins/plugins/125.gogo.soundkonverter.xml b/src/plugins/plugins/125.gogo.soundkonverter.xml
new file mode 100755
index 0000000..ebe351c
--- /dev/null
+++ b/src/plugins/plugins/125.gogo.soundkonverter.xml
@@ -0,0 +1,18 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="gogo is a lame based high speed mp3 encoder. If you have a x86 cpu gogo speeds up the conversion progress a lot. Especially on smp machines in cbr mode." author="Daniel Faust" version="301" name="Mp3 (gogo)" />
+ <enc bin="gogo" param="" overwrite="" max_version="" mime_types="audio/x-mp3,audio/mpeg" min_version="" rank="60" silent_param="-silent" in_out_files="%p %i %o" enabled="true" >
+ <strength range_min="9" range_max="0" separator="" param="-q %c" step="1" profiles="" default_value="2" enabled="true" />
+ <lossy enabled="true" >
+ <quality range_min="9" output="{ %*s %*s %p.%*s%%" help="" range_max="0" separator="" param="-v %q" step="1" profiles="" enabled="false" />
+ <bitrate>
+ <abr output="{ %*s %*s %p.%*s%%" param="-a -b %b" enabled="false" >
+ <bitrate_range min_param="-vb %m" max_param="%M" enabled="false" />
+ </abr>
+ <cbr output="{ %*s %*s %p.%*s%%" param="-b %b" enabled="true" />
+ </bitrate>
+ <samplingrate unit="KHz" param="-d %s" enabled="true" />
+ <channels stereo_param="-m j" joint_stereo_enabled="true" stereo_enabled="true" joint_stereo_param="-m j" mono_param="-m m" mono_enabled="true" />
+ </lossy>
+ </enc>
+</soundkonverter>
diff --git a/src/plugins/plugins/127.faac.soundkonverter.xml b/src/plugins/plugins/127.faac.soundkonverter.xml
new file mode 100755
index 0000000..1018d60
--- /dev/null
+++ b/src/plugins/plugins/127.faac.soundkonverter.xml
@@ -0,0 +1,15 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Adds support for m4a/mp4/aac files. faac/faad is required." version="301" author="Daniel Faust" name="Freeware Advanced Audio Coder (faac &amp; faad)" />
+ <enc bin="faac" param="" overwrite="" mime_types="audio/mp4,audio/aac,audio/x-m4a" rank="100" silent_param="" in_out_files="%p %i -o %o" enabled="true" >
+ <resample unit="Hz" param="-c %s" enabled="true" />
+ <lossy enabled="true" >
+ <quality range_min="10" range_max="500" help="0 ~ 10\n10 ~ 59\n20 ~ 108\n30 ~ 157\n40 ~ 206\n50 ~ 255\n60 ~ 304\n70 ~ 353\n80 ~ 402\n90 ~ 451\n100 ~ 500" output=" %*s ( %p%%)" separator="" param="-q %q" step="1" profiles="" enabled="true" />
+ <bitrate>
+ <abr output=" %*s ( %p%%)" param="-b %b" enabled="true" />
+ </bitrate>
+ </lossy>
+ <tag title="--title %tt" comment="--comment %tc" param="" track="--track %tn" disc="--disc %td" artist="--artist %ta" album="--album %tb" composer="--writer %tp" genre="--genre %tg" year="--year %ty" enabled="true" />
+ </enc>
+ <dec bin="faad" output="%p%% decoding" param="" overwrite="" mime_types="audio/mp4,audio/aac,video/mp4,video/3gpp,audio/x-m4a" rank="100" silent_param="" in_out_files="%p %i -o %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/130.musepack.soundkonverter.xml b/src/plugins/plugins/130.musepack.soundkonverter.xml
new file mode 100755
index 0000000..6fd61e1
--- /dev/null
+++ b/src/plugins/plugins/130.musepack.soundkonverter.xml
@@ -0,0 +1,11 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Encode and decode Muspack files with mppenc and mppdec. You can get mppenc &amp; mppdec at http://www.musepack.net ." author="Daniel Faust" version="301" name="MusePack (mppenc &amp; mppdec)" />
+ <enc bin="mppenc" param="" overwrite="--overwrite" mime_types="audio/x-musepack" silent_param="--silent" rank="100" in_out_files="%p %i %o" enabled="true" >
+ <lossy enabled="true" >
+ <quality range_min="0" output=" %p.%*s %*s kbps" help="0 ~ 0.0 ~ below telephone\n10 ~ 1.0 ~ below telephone\n20 ~ 2.0 ~ telephone\n30 ~ 3.0 ~ thumb\n40 ~ 4.0 ~ radio\n50 ~ 5.0 ~ standard\n60 ~ 6.0 ~ xtreme\n70 ~ 7.0 ~ insane\n80 ~ 8.0 ~ braindead\n90 ~ 9.0 ~ above braindead\n100 ~ 10.0 ~ above braindead" range_max="10" separator="." param="--quality %q" step="0.1" profiles="" enabled="true" />
+ </lossy>
+ <tag comment="--comment %tc" title="--title %tt" track="--track %tn" param="" artist="--artist %ta" album="--album %tb" year="--year %ty" genre="--genre %tg" enabled="true" />
+ </enc>
+ <dec output=" decoded (%p.%*s%%)" bin="mppdec" param="" overwrite="" mime_types="audio/x-musepack" silent_param="--silent" rank="100" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/132.aften.soundkonverter.xml b/src/plugins/plugins/132.aften.soundkonverter.xml
new file mode 100755
index 0000000..1561006
--- /dev/null
+++ b/src/plugins/plugins/132.aften.soundkonverter.xml
@@ -0,0 +1,13 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Aften (A Fifty-Two ENcoder) is an A/52 encoder. Get it at http://aften.sourceforge.net" version="301" author="Daniel Faust" name="Aften (aften)" />
+ <enc bin="aften" param="" overwrite="" mime_types="audio/ac3" rank="100" silent_param="-v 0" in_out_files="%p %i %o" enabled="true" >
+ <lossy enabled="true" >
+ <quality range_min="0" range_max="600" help="10 ~ 60\n20 ~ 120\n30 ~ 180\n40 ~ 240\n50 ~ 300\n60 ~ 360\n70 ~ 420\n80 ~ 480\n90 ~ 540\n100 ~ 600" output="progress: %p%% " separator="" param="-q %q" step="1" profiles="" enabled="true" />
+ <bitrate>
+ <cbr output="progress: %p%% " param="-b %b" enabled="true" />
+ </bitrate>
+ <channels stereo_param="-m 0" joint_stereo_enabled="true" stereo_enabled="true" joint_stereo_param="-m 1" />
+ </lossy>
+ </enc>
+</soundkonverter>
diff --git a/src/plugins/plugins/135.flac.soundkonverter.xml b/src/plugins/plugins/135.flac.soundkonverter.xml
new file mode 100755
index 0000000..8d5c0c7
--- /dev/null
+++ b/src/plugins/plugins/135.flac.soundkonverter.xml
@@ -0,0 +1,11 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="FLAC, Free Lossless Audio Codec should be shipped with your distribution." author="Daniel Faust" version="301" name="FLAC (flac)" />
+ <enc bin="flac" param="" overwrite="--force" mime_types="audio/x-flac" rank="100" silent_param="--silent" in_out_files="%p -o %o %i" enabled="true" >
+ <strength range_min="0" range_max="8" separator="" param="-%c" step="1" profiles="" default_value="5" enabled="true" />
+ <lossless output="%*s %p%% complete, %*s" param="" enabled="true" />
+ <replay_gain avoid="" use="--replay-gain" enabled="true" />
+ <tag title="-T title=%tt" comment="-T comment=%tc" param="" track="-T tracknum=%tn" artist="-T artist=%ta" album="-T album=%tb" genre="-T genre=%tg" year="-T date=%ty" enabled="true" />
+ </enc>
+ <dec output="%*s %p%% complete" bin="flac" param="--decode" overwrite="--force" max_version="" mime_types="audio/x-flac" rank="100" silent_param="--silent" in_out_files="%p -o %o %i" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/136.flake.soundkonverter.xml b/src/plugins/plugins/136.flake.soundkonverter.xml
new file mode 100755
index 0000000..1ec9b0d
--- /dev/null
+++ b/src/plugins/plugins/136.flake.soundkonverter.xml
@@ -0,0 +1,8 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Flake is a fast flac encoder. Get it at http://flake-enc.sourceforge.net" author="Daniel Faust" version="302" name="Flake (flake)" />
+ <enc bin="flake" mime_types="audio/x-flac" rank="60" in_out_files="%p %i -o %o" enabled="true" >
+ <strength range_min="0" range_max="12" separator="" param="-%c" step="1" profiles="" default_value="5" enabled="true" />
+ <lossless output="\rprogress: %p%% " param="" enabled="true" />
+ </enc>
+</soundkonverter>
diff --git a/src/plugins/plugins/137.mac.soundkonverter.xml b/src/plugins/plugins/137.mac.soundkonverter.xml
new file mode 100755
index 0000000..f0421c7
--- /dev/null
+++ b/src/plugins/plugins/137.mac.soundkonverter.xml
@@ -0,0 +1,10 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Encode and decode ape files with mac. Get mac at http://www.monkeysaudio.com ." author="Daniel Faust" version="301" name="Ape (mac)" />
+ <enc bin="mac" param="" overwrite="" mime_types="audio/x-ape" rank="100" silent_param="" in_out_files="%i %o %p" enabled="true" >
+ <lossless output="Progress: %p.%*s%%" param="" enabled="true" />
+ <replay_gain avoid="" use="" enabled="" />
+ <strength range_min="1000" range_max="5000" separator="" param="-c%c" step="1000" profiles="" default_value="2000" enabled="true" />
+ </enc>
+ <dec output="Progress: %p.%*s%%" bin="mac" param="-d" overwrite="" mime_types="audio/x-ape" rank="100" silent_param="" in_out_files="%i %o %p" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/140.mplayer.soundkonverter.xml b/src/plugins/plugins/140.mplayer.soundkonverter.xml
new file mode 100755
index 0000000..d2bdc2d
--- /dev/null
+++ b/src/plugins/plugins/140.mplayer.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Decodes mp3, mp2, ogg, flac, m4a, wma and ra files with MPlayer. It supports some video decoding too. mplayer is required." author="Daniel Faust" version="300" name="MPlayer (mplayer)" />
+ <dec output="A: %t.%*s" bin="mplayer" param="-vo null" overwrite="" mime_types="audio/x-mp3,audio/mpeg,audio/x-mp2,audio/vorbis,application/ogg,audio/x-flac,audio/mp4,audio/aac,audio/x-ms-wma,video/x-ms-asf,audio/vnd.rn-realaudio,video/vnd.rn-realvideo,application/vnd.rn-realmedia,video/x-ogm,video/x-theora,video/x-msvideo,video/mpeg,video/x-ms-wmv,video/quicktime,video/mp4,video/x-flv,audio/x-m4a" rank="50" silent_param="" in_out_files="%p %i -ao pcm:file=%o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/145.ffmpeg.soundkonverter.xml b/src/plugins/plugins/145.ffmpeg.soundkonverter.xml
new file mode 100755
index 0000000..e2613c4
--- /dev/null
+++ b/src/plugins/plugins/145.ffmpeg.soundkonverter.xml
@@ -0,0 +1,16 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Decodes: ac3, mp2, mp3, ogg, m4a, ra and wma files and encodes: ac3, mp2, mp3, ogg, m4a and ra files. Video decoding is also supported. ffmpeg is required." version="303" author="Daniel Faust" name="FFMPEG (ffmpeg)" />
+ <enc bin="ffmpeg" param="" overwrite="-y" mime_types="audio/ac3,audio/x-mp3,audio/mpeg,audio/x-mp2,audio/vorbis,application/ogg,audio/mp4,audio/vnd.rn-realaudio,audio/x-m4a,audio/x-ms-wma" silent_param="" rank="60" in_out_files="%p -i %i %o" enabled="true" >
+ <strength range_min="0" range_max="1" separator="" param="%c" step="1" profiles=" ,-hq" default_value="0" enabled="false" />
+ <samplingrate unit="Hz" param="-ar %s" enabled="true" />
+ <channels stereo_param="-ac 2" stereo_enabled="true" mono_param="-ac 1" mono_enabled="true" />
+ <lossy enabled="true" >
+ <bitrate>
+ <cbr output="size= %*s time=%t.%*s" param="-ab %b" enabled="true" />
+ </bitrate>
+ </lossy>
+ <tag comment="-comment %tc" title="-title %tt" track="-track %tn" param="" artist="-author %ta" album="" year="-year %ty" genre="" enabled="true" />
+ </enc>
+ <dec bin="ffmpeg" output="size= %*s time=%t.%*s" param="" overwrite="-y" mime_types="audio/ac3,audio/mpeg,audio/x-mp3,audio/x-mp2,audio/vnd.rn-realaudio,video/vnd.rn-realvideo,application/vnd.rn-realmedia,audio/vorbis,application/ogg,audio/x-ms-wma,video/x-ms-asf,audio/mp4,video/mp4,audio/aac,video/x-msvideo,video/mpeg,video/x-ms-wmv,video/x-flv,audio/x-m4a,audio/amr" silent_param="" rank="60" in_out_files="%p -i %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/146.ffmpeg-lossless.soundkonverter.xml b/src/plugins/plugins/146.ffmpeg-lossless.soundkonverter.xml
new file mode 100755
index 0000000..d33ae6a
--- /dev/null
+++ b/src/plugins/plugins/146.ffmpeg-lossless.soundkonverter.xml
@@ -0,0 +1,10 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Decodes flac, shn and au files and encodes au files. ffmpeg is required." version="302" author="Daniel Faust" name="FFMPEG Lossless (ffmpeg)" />
+ <enc bin="ffmpeg" param="" overwrite="-y" mime_types="audio/basic" silent_param="" rank="60" in_out_files="%p -i %i %o" enabled="true" >
+ <strength range_min="0" range_max="1" separator="" param="%c" step="1" profiles=" ,-hq" default_value="0" enabled="false" />
+ <lossless output="size= %*s time=%t.%*s" param="" enabled="true" />
+ <tag comment="-comment %tc" title="-title %tt" track="-track %tn" param="" artist="-author %ta" album="" year="-year %ty" genre="" enabled="true" />
+ </enc>
+ <dec bin="ffmpeg" output="size= %*s time=%t.%*s" param="" overwrite="-y" mime_types="audio/x-flac,application/x-shorten,audio/basic,audio/x-tta,audio/x-wavpack" silent_param="" rank="60" in_out_files="%p -i %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/150.shorten.soundkonverter.xml b/src/plugins/plugins/150.shorten.soundkonverter.xml
new file mode 100755
index 0000000..8e64a13
--- /dev/null
+++ b/src/plugins/plugins/150.shorten.soundkonverter.xml
@@ -0,0 +1,9 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for shorten. Get shorten at http://www.hornig.net/shorten.html" version="300" author="Daniel Faust" name="Shorten (shorten)" />
+ <enc bin="shorten" param="" overwrite="" mime_types="application/x-shorten" silent_param="" rank="100" in_out_files="%p %i %o" enabled="true" >
+ <channels mono_param="-c 1" mono_enabled="true" />
+ <lossless output="" param="" enabled="true" />
+ </enc>
+ <dec bin="shorten" output="" param="-x" overwrite="" mime_types="application/x-shorten" silent_param="" rank="100" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/151.tta.soundkonverter.xml b/src/plugins/plugins/151.tta.soundkonverter.xml
new file mode 100755
index 0000000..27a3f97
--- /dev/null
+++ b/src/plugins/plugins/151.tta.soundkonverter.xml
@@ -0,0 +1,8 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for tta. Get ttaenc at http://tta.sourceforge.net" version="300" author="Daniel Faust" name="TTA (ttaenc)" />
+ <enc bin="ttaenc" param="-e" overwrite="" mime_types="audio/x-tta" silent_param="-s" rank="100" in_out_files="%p -o %o %i" enabled="true" >
+ <lossless output="Encode: wrote %*s bytes, %p%% complete, ratio: %*s, time: %*s" param="" enabled="true" />
+ </enc>
+ <dec bin="ttaenc" output="Decode: wrote %*s bytes, %p%% complete, ratio: %*s, time: %*s" param="-d" overwrite="" mime_types="audio/x-tta" silent_param="-s" rank="100" in_out_files="%p -o %o %i" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/153.bonk.soundkonverter.xml b/src/plugins/plugins/153.bonk.soundkonverter.xml
new file mode 100755
index 0000000..ca3d8c2
--- /dev/null
+++ b/src/plugins/plugins/153.bonk.soundkonverter.xml
@@ -0,0 +1,12 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for Bonk. Get bonk at http://www.logarithmic.net/pfh/bonk" author="Daniel Faust" version="300" name="Bonk (bonk)" />
+ <enc bin="bonk" param="encode" overwrite="" mime_types="audio/x-bonk" rank="100" silent_param="-s" in_out_files="%p -o %o %i" enabled="true" >
+ <lossless output=" %p.%*s%% complete, " param="-l" enabled="true" />
+ <lossy enabled="true" >
+ <quality range_min="10" range_max="300" help="0 ~ 9\n10 ~ 8\n20 ~ 7\n30 ~ 6\n40 ~ 5\n50 ~ 5\n60 ~ 4\n70 ~ 3\n80 ~ 2\n90 ~ 1\n100 ~ 0" output=" %p.%*s%% complete, " separator="" param="-s %q" step="10" profiles="" enabled="true" />
+ <channels stereo_param="-m off" joint_stereo_enabled="true" stereo_enabled="true" joint_stereo_param="-m on" />
+ </lossy>
+ </enc>
+ <dec output=" %p.%*s%% complete, " bin="bonk" param="decode" overwrite="" mime_types="audio/x-bonk" rank="100" silent_param="" in_out_files="%p -o %o %i" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/155.optimfrog.soundkonverter.xml b/src/plugins/plugins/155.optimfrog.soundkonverter.xml
new file mode 100755
index 0000000..800c881
--- /dev/null
+++ b/src/plugins/plugins/155.optimfrog.soundkonverter.xml
@@ -0,0 +1,11 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Encode and decode OptimFROG files with ofr. Get ofr at http://www.losslessaudio.org ." version="301" author="Daniel Faust" name="OptimFROG (ofr)" />
+ <enc bin="ofr" param="--encode" overwrite="--overwrite" mime_types="application/x-ofr" rank="100" silent_param="--silent" in_out_files="%p %i --output %o" enabled="true" >
+ <strength range_min="0" range_max="7" separator="" param="--mode %c" step="1" profiles="fast,normal,high,extra,best,highnew,extranew,bestnew" default_value="1" enabled="true" />
+ <samplingrate unit="Hz" param="--rate %s" enabled="true" />
+ <channels stereo_param="--channelconfig STEREO_LR" stereo_enabled="true" mono_param="--channelconfig MONO" mono_enabled="true" />
+ <lossless output=" Compressing %p.%*s%%" param="" enabled="true" />
+ </enc>
+ <dec bin="ofr" output=" Decompressing %p.%*s%%" param="--decode" overwrite="--overwrite" mime_types="application/x-ofr" rank="100" silent_param="--silent" in_out_files="%p %i --output %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/156.optimfrog-dualstream.soundkonverter.xml b/src/plugins/plugins/156.optimfrog-dualstream.soundkonverter.xml
new file mode 100755
index 0000000..80ba27a
--- /dev/null
+++ b/src/plugins/plugins/156.optimfrog-dualstream.soundkonverter.xml
@@ -0,0 +1,15 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for ofs. You can get it at http://www.losslessaudio.org" version="301" author="Daniel Faust" name="OptimFROG DualStream (ofs)" />
+ <enc bin="ofs" param="--encode" overwrite="--overwrite" mime_types="application/x-ofs" rank="100" silent_param="--silent" in_out_files="%p %i --output %o" enabled="true" >
+ <lossy enabled="true" >
+ <quality range_min="0" range_max="6" help="" output=" Compressing %p.%*s%%," separator="." param="--quality %q" step="0.01" profiles="" enabled="true" />
+ <bitrate>
+ <abr output=" Compressing %p.%*s%%," param="--bitrate %b" enabled="true" />
+ </bitrate>
+ </lossy>
+ <hybrid output=" Compressing %p.%*s%%," correction_file_mime_type="application/x-ofc" param="--bitrate %b --correction" enabled="true" />
+ <strength range_min="0" range_max="7" separator="" param="--mode %c" step="1" profiles="fast,normal,high,extra,best,highnew,extranew,bestnew" default_value="1" enabled="true" />
+ </enc>
+ <dec bin="ofs" output=" Decompressing %p.%*s%%" param="--decode --correction" overwrite="--overwrite" mime_types="application/x-ofs" rank="100" silent_param="--silent" in_out_files="%p %i --output %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/158.wavpack.soundkonverter.xml b/src/plugins/plugins/158.wavpack.soundkonverter.xml
new file mode 100755
index 0000000..96a5842
--- /dev/null
+++ b/src/plugins/plugins/158.wavpack.soundkonverter.xml
@@ -0,0 +1,11 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for WavPack, a lossless and lossy audio compressor. Get wavpack at http://www.wavpack.com/downloads.html ." version="302" author="Daniel Faust" name="WavPack (wavpack)" />
+ <enc bin="wavpack" param="" overwrite="-y" mime_types="audio/x-wavpack" rank="100" silent_param="-q" in_out_files="%p %i -o %o" enabled="true" >
+ <hybrid output=" %p%% done..." correction_file_mime_type="audio/x-wavpack-correction" param="-b%b -c" enabled="true" />
+ <lossless output=" %p%% done..." param="" enabled="true" />
+ <tag title="-w Title=%tt" comment="-w Comment=%tc" param="" track="-w Track=%tn" disc="" artist="-w Artist=%ta" album="-w Album=%tb" genre="-w Genre=%tg" year="-w Year=%ty" composer="-w Composer=%tp" enabled="true" />
+ <strength range_min="0" range_max="3" separator="" param="%c" step="1" profiles="-f, ,-h,-hh" default_value="1" enabled="true" />
+ </enc>
+ <dec bin="wvunpack" output=" %p%% done..." param="" overwrite="-y" mime_types="audio/x-wavpack" formats="wv" rank="100" silent_param="-q" in_out_files="%p %i -o %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/160.lac.soundkonverter.xml b/src/plugins/plugins/160.lac.soundkonverter.xml
new file mode 100755
index 0000000..65f0a16
--- /dev/null
+++ b/src/plugins/plugins/160.lac.soundkonverter.xml
@@ -0,0 +1,9 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Lossless Audio plugin. Get lac at http://www.lossless-audio.com" version="301" author="Daniel Faust" name="Lossless Audio Compresser (lac)" />
+ <enc bin="lac" param="" overwrite="-overwrite" mime_types="application/x-la" rank="100" silent_param="" in_out_files="%p %i %o" enabled="true" >
+ <strength range_min="0" range_max="1" separator="" param="%c" step="1" profiles=" ,-high" default_value="0" enabled="true" />
+ <lossless output=" (%p.%*s%%)" param="" enabled="true" />
+ </enc>
+ <dec bin="lac" output="" param="" overwrite="-overwrite" mime_types="application/x-la" rank="100" silent_param="" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/165.lpac.soundkonverter.xml b/src/plugins/plugins/165.lpac.soundkonverter.xml
new file mode 100755
index 0000000..2a368b9
--- /dev/null
+++ b/src/plugins/plugins/165.lpac.soundkonverter.xml
@@ -0,0 +1,9 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Lossless Predictive Audio Compression plugin. Get lpac at http://www.nue.tu-berlin.de/wer/liebchen/lpac.html" author="Daniel Faust" version="301" name="LPAC (lpac)" />
+ <enc bin="lpac" param="-v" overwrite="" mime_types="audio/x-pac" rank="100" silent_param="" in_out_files="%p %i %o" enabled="true" >
+ <strength range_min="1" range_max="5" separator="" param="-%c" step="1" profiles="" default_value="3" enabled="true" />
+ <lossless output=" %p%%" param="" enabled="true" />
+ </enc>
+ <dec output="" bin="lpac" param="-x" overwrite="" mime_types="audio/x-pac" rank="100" silent_param="" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/170.speex.soundkonverter.xml b/src/plugins/plugins/170.speex.soundkonverter.xml
new file mode 100755
index 0000000..24b1f5d
--- /dev/null
+++ b/src/plugins/plugins/170.speex.soundkonverter.xml
@@ -0,0 +1,16 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Plugin for speexenc and speexdec. Speex is used to encode speech files. speex should be shipped with your distribution." author="Daniel Faust" version="301" name="Speex (speexenc &amp; speexdec)" />
+ <enc bin="speexenc" param="" overwrite="" mime_types="audio/x-speex" silent_param="" rank="100" in_out_files="%p %i %o" enabled="true" >
+ <strength range_min="0" range_max="10" separator="" param="--comp %c" step="1" profiles="" default_value="3" enabled="true" />
+ <lossy enabled="true" >
+ <quality range_min="0" output="" help="" range_max="10" separator="" param="--quality %q" step="1" profiles="" enabled="true" />
+ <bitrate>
+ <abr output="" param="--abr %b" enabled="true" />
+ <cbr output="" param="--bitrate %b" enabled="true" />
+ </bitrate>
+ </lossy>
+ <tag title="--title %tt" comment="" param="" track="" artist="--author %ta" album="" genre="" year="" enabled="true" />
+ </enc>
+ <dec output="" bin="speexdec" param="" overwrite="" mime_types="audio/x-speex" silent_param="" rank="100" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/180.timidity.soundkonverter.xml b/src/plugins/plugins/180.timidity.soundkonverter.xml
new file mode 100755
index 0000000..8f8c918
--- /dev/null
+++ b/src/plugins/plugins/180.timidity.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Decodes midi files with TiMidity." version="300" author="Daniel Faust" name="TiMidity++ (timidity)" />
+ <dec bin="timidity" output="" param="-Ow" overwrite="" mime_types="audio/x-midi,audio/x-mod" silent_param="" rank="50" in_out_files="%p -o %o %i" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/190.sox.soundkonverter.xml b/src/plugins/plugins/190.sox.soundkonverter.xml
new file mode 100644
index 0000000..4de2401
--- /dev/null
+++ b/src/plugins/plugins/190.sox.soundkonverter.xml
@@ -0,0 +1,8 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="converter" >
+ <info about="Encodes and decodes aiff with sox" author="Ron Fischler" version="302" name="Sox (sox)" />
+ <enc bin="sox" param="" overwrite="" mime_types="audio/x-aiff" silent_param="" rank="90" in_out_files="%p %i %o" enabled="true" >
+ <lossless output="%*s %p%% complete, %*s" param="" enabled="true" />
+ </enc>
+ <dec bin="sox" output="" param="" overwrite="" mime_types="audio/x-aiff" silent_param="" rank="90" in_out_files="%p %i %o" enabled="true" />
+</soundkonverter>
diff --git a/src/plugins/plugins/210.vorbisgain.soundkonverter.xml b/src/plugins/plugins/210.vorbisgain.soundkonverter.xml
new file mode 100755
index 0000000..7848503
--- /dev/null
+++ b/src/plugins/plugins/210.vorbisgain.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="replaygain" >
+ <info about="Plugin for vorbisgain. vorbisgain is used to calculate the replaygain value of ogg vorbis files." version="300" author="Daniel Faust" name="Ogg Vorbis Replay Gain (vorbisgain)" />
+ <replaygain bin="vorbisgain" output_multiple="%p%%" param="" track="" force="" remove="--clean" mime_types="application/ogg,audio/vorbis" in_files="%p %i" output_single="%p%%" album="--album" silent_param="--quiet" rank="100" skip="--fast" />
+</soundkonverter>
diff --git a/src/plugins/plugins/220.mp3gain.soundkonverter.xml b/src/plugins/plugins/220.mp3gain.soundkonverter.xml
new file mode 100755
index 0000000..7058439
--- /dev/null
+++ b/src/plugins/plugins/220.mp3gain.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="replaygain" >
+ <info about="Add replaygain to Mp3 files. Get mp3gain at http://mp3gain.sourceforge.net ." author="Daniel Faust" version="300" name="Mp3 ReplayGain (mp3gain)" />
+ <replaygain bin="mp3gain" output_multiple="%*s %p%%" force="-s r" track="-r" param="-c" remove="-s d" output_single=" %p%%" mime_types="audio/x-mp3,audio/mpeg" in_files="%p %i" album="-a" rank="100" silent_param="-q" skip="" />
+</soundkonverter>
diff --git a/src/plugins/plugins/225.aacgain.soundkonverter.xml b/src/plugins/plugins/225.aacgain.soundkonverter.xml
new file mode 100755
index 0000000..84f1774
--- /dev/null
+++ b/src/plugins/plugins/225.aacgain.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="replaygain" >
+ <info about="Add replaygain to AAC files. Get aacgain at http://altosdesign.com/aacgain/ ." author="Daniel Faust" version="300" name="AAC Replay Gain (aacgain)" />
+ <replaygain bin="aacgain" output_multiple="%*s %p%%" force="-s r" track="-r" param="-c" remove="-s d" output_single=" %p%%" mime_types="audio/x-mp3,audio/mpeg,audio/mp4,audio/x-m4a" in_files="%p %i" album="-a" rank="95" silent_param="-q" skip="" />
+</soundkonverter>
diff --git a/src/plugins/plugins/230.replaygain.soundkonverter.xml b/src/plugins/plugins/230.replaygain.soundkonverter.xml
new file mode 100755
index 0000000..1a0a0f6
--- /dev/null
+++ b/src/plugins/plugins/230.replaygain.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="replaygain" >
+ <info about="Add Replay Gain tags to Musepack files. You can get replaygain at http://www.musepack.net ." author="Daniel Faust" version="300" name="Musepack ReplayGain (replaygain)" />
+ <replaygain bin="replaygain" track="--auto" param="" force="" remove="" in_files="%p %i" mime_types="audio/x-musepack" album="--auto" silent_param="" rank="100" skip="" />
+</soundkonverter>
diff --git a/src/plugins/plugins/240.metaflac.soundkonverter.xml b/src/plugins/plugins/240.metaflac.soundkonverter.xml
new file mode 100755
index 0000000..25d5c7d
--- /dev/null
+++ b/src/plugins/plugins/240.metaflac.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="replaygain" >
+ <info about="Add Replay Gain tags to flac files. metaflac is required." version="300" author="Daniel Faust" name="Flac ReplayGain (metaflac)" />
+ <replaygain bin="metaflac" force="" param="--no-filename" track="--add-replay-gain" remove="--remove-tag=REPLAYGAIN_TRACK_PEAK --remove-tag=REPLAYGAIN_TRACK_GAIN --remove-tag=REPLAYGAIN_ALBUM_PEAK --remove-tag=REPLAYGAIN_ALBUM_GAIN" in_files="%p %i" mime_types="audio/x-flac" album="--add-replay-gain" silent_param="" rank="100" skip="" />
+</soundkonverter>
diff --git a/src/plugins/plugins/250.wvgain.soundkonverter.xml b/src/plugins/plugins/250.wvgain.soundkonverter.xml
new file mode 100755
index 0000000..fa502c5
--- /dev/null
+++ b/src/plugins/plugins/250.wvgain.soundkonverter.xml
@@ -0,0 +1,5 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="replaygain" >
+ <info about="Add Replay Gain tags to WavPack files. wvgain is required." author="Daniel Faust" version="300" name="WavPack ReplayGain (wvgain)" />
+ <replaygain bin="wvgain" track="" param="" force="" remove="-c" mime_types="audio/x-wavpack" in_files="%p %i" album="-a" rank="100" silent_param="-q" skip="" />
+</soundkonverter>
diff --git a/src/plugins/plugins/310.cdda2wav.soundkonverter.xml b/src/plugins/plugins/310.cdda2wav.soundkonverter.xml
new file mode 100755
index 0000000..1df26a8
--- /dev/null
+++ b/src/plugins/plugins/310.cdda2wav.soundkonverter.xml
@@ -0,0 +1,7 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="ripper" >
+ <info about="Plugin for ripping audio CDs with cdda2wav. You can get cdda2wav at http://www.cdda2wav.de but it should be shipped with your distribution." author="Daniel Faust" version="300" name="CD ripper (cdda2wav)" />
+ <rip output="%p%%" bin="cdda2wav" param="-gui -no-infofile" track="-t %t" out_file="%p %o" overwrite="" device="-D %d" silent_param="-quiet" rank="90" >
+ <full_disc output="%a%%" param="-t 1+%n" enabled="true" />
+ </rip>
+</soundkonverter>
diff --git a/src/plugins/plugins/320.cdparanoia.soundkonverter.xml b/src/plugins/plugins/320.cdparanoia.soundkonverter.xml
new file mode 100755
index 0000000..177f9c4
--- /dev/null
+++ b/src/plugins/plugins/320.cdparanoia.soundkonverter.xml
@@ -0,0 +1,7 @@
+<?xml version = '1.0' encoding = 'UTF-8'?>
+<soundkonverter type="ripper" >
+ <info about="Plugin for ripping audio CDs with cdparanoia. cdparanoia may be shipped with your distribution." author="Daniel Faust" version="301" name="CD ripper (cdparanoia)" />
+ <rip output="" bin="cdparanoia" param="" track="%t" out_file="%p %o" overwrite="" device="-d %d" silent_param="-quiet" rank="80" >
+ <full_disc output="" param="1-%n" enabled="true" />
+ </rip>
+</soundkonverter>
diff --git a/src/plugins/plugins/Makefile.am b/src/plugins/plugins/Makefile.am
new file mode 100644
index 0000000..8471ac6
--- /dev/null
+++ b/src/plugins/plugins/Makefile.am
@@ -0,0 +1,14 @@
+pluginsdir = $(kde_datadir)/soundkonverter/plugins
+
+plugins_DATA = 110.oggvorbis.soundkonverter.xml 120.lame.soundkonverter.xml \
+ 122.twolame.soundkonverter.xml 123.toolame.soundkonverter.xml 125.gogo.soundkonverter.xml \
+ 127.faac.soundkonverter.xml 130.musepack.soundkonverter.xml 132.aften.soundkonverter.xml \
+ 135.flac.soundkonverter.xml 136.flake.soundkonverter.xml 137.mac.soundkonverter.xml \
+ 140.mplayer.soundkonverter.xml 145.ffmpeg.soundkonverter.xml 146.ffmpeg-lossless.soundkonverter.xml \
+ 150.shorten.soundkonverter.xml 151.tta.soundkonverter.xml 153.bonk.soundkonverter.xml \
+ 155.optimfrog.soundkonverter.xml 156.optimfrog-dualstream.soundkonverter.xml \
+ 158.wavpack.soundkonverter.xml 160.lac.soundkonverter.xml 165.lpac.soundkonverter.xml \
+ 170.speex.soundkonverter.xml 180.timidity.soundkonverter.xml 190.sox.soundkonverter.xml \
+ 210.vorbisgain.soundkonverter.xml 220.mp3gain.soundkonverter.xml 225.aacgain.soundkonverter.xml \
+ 230.replaygain.soundkonverter.xml 240.metaflac.soundkonverter.xml 250.wvgain.soundkonverter.xml \
+ 310.cdda2wav.soundkonverter.xml 320.cdparanoia.soundkonverter.xml
diff --git a/src/plugins/rohling/Makefile.am b/src/plugins/rohling/Makefile.am
new file mode 100755
index 0000000..a346d10
--- /dev/null
+++ b/src/plugins/rohling/Makefile.am
@@ -0,0 +1,6 @@
+pluginsdir = $(kde_datadir)/soundkonverter/plugins
+format_infos_endir = $(kde_datadir)/soundkonverter/format_infos/en
+plugins_DATA = 127.faac-280.soundkonverter.xml
+format_infos_en_DATA = faac_3gp.280.xml \
+ faac_aac.280.xml \
+ faac_m4a.280.xml
diff --git a/src/progressindicator.cpp b/src/progressindicator.cpp
new file mode 100755
index 0000000..91a40a2
--- /dev/null
+++ b/src/progressindicator.cpp
@@ -0,0 +1,144 @@
+
+#include "progressindicator.h"
+
+#include <qlayout.h>
+#include <qlabel.h>
+#include <qprogressbar.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <ksystemtray.h>
+//#include <kiconloader.h>
+//#include <kmessagebox.h>
+// #include <kdebug.h>
+
+
+ProgressIndicator::ProgressIndicator( KSystemTray* _systemTray, QWidget* parent, const char* name )
+ : QWidget( parent, name )
+{
+ systemTray = _systemTray;
+
+ time = processedTime = 0;
+
+ QGridLayout *grid = new QGridLayout( this, 1, 1, 0, 6, "grid" );
+
+ QHBoxLayout *box = new QHBoxLayout( );
+ grid->addLayout( box, 0, 0 );
+
+ pBar = new QProgressBar( this, "pBar" );
+ box->addWidget( pBar );
+
+ QGridLayout *statusChildGrid = new QGridLayout( box, 2, 2, 6, "statusChildGrid" );
+
+ QLabel *lSpeedText = new QLabel( i18n("Speed")+":", this, "lSpeedText" );
+ statusChildGrid->addWidget( lSpeedText, 0, 0, Qt::AlignVCenter );
+
+ lSpeed = new QLabel( "0.0x", this, "lSpeed" );
+ lSpeed->setFont( QFont( "Courier" ) );
+ statusChildGrid->addWidget( lSpeed, 0, 1, Qt::AlignVCenter | Qt::AlignRight );
+ speedTime.setHMS( 24, 0, 0 );
+
+ QLabel *lTimeText = new QLabel( i18n("Time remaining")+":", this, "lTimeText" );
+ statusChildGrid->addWidget( lTimeText, 1, 0, Qt::AlignVCenter );
+
+ lTime = new QLabel( "00:00", this, "lTime" );
+ lTime->setFont( QFont( "Courier" ) );
+ statusChildGrid->addWidget( lTime, 1, 1, Qt::AlignVCenter | Qt::AlignRight );
+ elapsedTime.setHMS( 24, 0, 0 );
+
+ QToolTip::add( systemTray, i18n("Waiting") );
+}
+
+ProgressIndicator::~ProgressIndicator()
+{}
+
+void ProgressIndicator::increaseTime( float t )
+{
+ time += t;
+// kdDebug() << "increaseTime: " << time << " (" << t << ")" << endl;
+}
+
+void ProgressIndicator::decreaseTime( float t )
+{
+ time -= t;
+// kdDebug() << "decreaseTime: " << time << " (" << t << ")" << endl;
+}
+
+void ProgressIndicator::countTime( float t )
+{
+ processedTime += t;
+// kdDebug() << "countTime: " << processedTime << " (" << t << ")" << endl;
+}
+
+void ProgressIndicator::uncountTime( float t )
+{
+ processedTime -= t;
+// kdDebug() << "uncountTime: " << processedTime << " (" << t << ")" << endl;
+}
+
+void ProgressIndicator::setTime( float t )
+{
+ time = t;
+// kdDebug() << "setTime: " << time << " (" << t << ")" << endl;
+}
+
+void ProgressIndicator::finished( float t )
+{
+ time = t;
+// kdDebug() << "finished: " << time << " (" << t << ")" << endl;
+ processedTime = 0;
+ pBar->setProgress( 100, 100 );
+ elapsedTime.setHMS( 24, 0, 0 );
+ lTime->setText( "00:00" );
+ speedTime.setHMS( 24, 0, 0 );
+ lSpeed->setText( "0.0x" );
+ QToolTip::add( systemTray, i18n("Finished") );
+ emit setTitle( i18n("Finished") );
+}
+
+void ProgressIndicator::update( float t )
+{
+// kdDebug() << "update: " << (processedTime + t) << " / " << time << endl;
+ pBar->setProgress( int(processedTime + t), int(time) );
+ float fPercent;
+// if( pBar->totalSteps() > 0 ) fPercent = pBar->progress() * 100 / pBar->totalSteps();
+ if( pBar->totalSteps() > 0 ) fPercent = (processedTime+t)*100.0f / time;
+ else fPercent = 0.1f;
+
+ if( !elapsedTime.isValid() ) elapsedTime.start();
+ if( !speedTime.isValid() ) speedTime.start();
+ if( speedTime.elapsed() > 1000 ) {
+ if( elapsedTime.elapsed() > 10000 ) {
+// int tim = (int)( 1 + ((100/fPercent)-1) * (elapsedTime.elapsed()/1000) );
+ int tim = (int)(( 1.0f + ((100.0f/fPercent)-1.0f) * (elapsedTime.elapsed()/1000.0f) ));
+ //float fPercent = pBar->progress() / pBar->totalSteps();
+ //int tim = (int)( 1 + (elapsedTime.elapsed()/fPercent - elapsedTime.elapsed()) / 1000 );
+ if( tim >= 0 && tim < 1000000 ) {
+ int sec = tim % 60;
+ int min = ( tim / 60 ) % 60;
+ int hou = tim / 3600;
+ QString timeLeft;
+ if( hou > 0 )
+ timeLeft.sprintf( "%d:%02d:%02d", hou, min, sec );
+ else
+ timeLeft.sprintf( "%02d:%02d", min, sec );
+ lTime->setText( timeLeft );
+ }
+ }
+
+ int tim = speedTime.restart() / 1000;
+ float speed = ( processedTime + t - speedProcessedTime ) / tim;
+ speedProcessedTime = processedTime + t;
+ if( speed >= 0.0f && speed < 100000.0f ) {
+ QString actSpeed;
+ actSpeed.sprintf( "%.1fx", speed );
+ lSpeed->setText( actSpeed );
+ }
+ }
+
+ QString percent;
+ percent.sprintf( "%i%%", (int)fPercent );
+ emit setTitle( percent );
+ QToolTip::add( systemTray, percent );
+}
+
diff --git a/src/progressindicator.h b/src/progressindicator.h
new file mode 100755
index 0000000..873a4f9
--- /dev/null
+++ b/src/progressindicator.h
@@ -0,0 +1,61 @@
+
+
+#ifndef PROGRESSINDICATOR_H
+#define PROGRESSINDICATOR_H
+
+#include <qwidget.h>
+#include <qdatetime.h>
+
+class QProgressBar;
+class QLabel;
+class KSystemTray;
+
+/**
+ * @short Displays the current progress
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ProgressIndicator : public QWidget
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ ProgressIndicator( KSystemTray* _systemTray, QWidget* parent = 0, const char* name = 0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~ProgressIndicator();
+
+public slots:
+ void increaseTime( float );
+ void decreaseTime( float );
+ void countTime( float );
+ void uncountTime( float );
+ void setTime( float );
+ void finished( float );
+
+ void update( float );
+
+//private slots:
+
+private:
+ QProgressBar* pBar;
+ QLabel* lSpeed;
+ QLabel* lTime;
+ KSystemTray* systemTray;
+
+ QTime elapsedTime;
+ QTime speedTime;
+ float speedProcessedTime;
+
+ float time;
+ float processedTime;
+
+signals:
+ void setTitle( const QString& );
+};
+
+#endif // PROGRESSINDICATOR_H
diff --git a/src/replaygain.cpp b/src/replaygain.cpp
new file mode 100755
index 0000000..f815a21
--- /dev/null
+++ b/src/replaygain.cpp
@@ -0,0 +1,96 @@
+
+#include "replaygain.h"
+#include "config.h"
+#include "logger.h"
+#include "replaygainpluginloader.h"
+
+#include <qfile.h>
+
+#include <kprocess.h>
+#include <klocale.h>
+#include <kurl.h>
+
+ReplayGain::ReplayGain( Config* _config, Logger* _logger )
+{
+ config = _config;
+ logger = _logger;
+}
+
+ReplayGain::~ReplayGain()
+{}
+
+bool ReplayGain::apply( QStringList files, const QString& format, KProcess* proc, int logID, Mode mode )
+{
+ QStringList params;
+ QString param, paramSplinter;
+
+ proc->clearArguments();
+
+ ReplayGainPlugin* plugin = config->replaygainForFormat( format );
+ if( plugin == 0 ) { // shouldn't happen
+ logger->log( logID, " NULL POINTER: ReplayGain::replaygain( ... ) / plugin" );
+ return false;
+ }
+
+ param = QString::null;
+ if( plugin->replaygain.param ) param.append( " " + plugin->replaygain.param );
+ if( mode & remove ) {
+ if( plugin->replaygain.remove ) param.append( " " + plugin->replaygain.remove );
+ }
+ else {
+ if( (mode & calc_track) && plugin->replaygain.track ) param.append( " " + plugin->replaygain.track );
+ if( (mode & calc_album) && plugin->replaygain.album ) param.append( " " + plugin->replaygain.album );
+ if( mode & force ) {
+ if( plugin->replaygain.force ) param.append( " " + plugin->replaygain.force );
+ }
+ else {
+ if( plugin->replaygain.skip ) param.append( " " + plugin->replaygain.skip );
+ }
+ }
+
+// if( plugin->replaygain.in_files.find("%p") != -1 ) {
+// QString t_str = plugin->replaygain.in_files;
+// t_str.replace( "%p", param );
+// param = plugin->replaygain.bin + " " + t_str;
+// }
+// else {
+// param = plugin->replaygain.bin + param + " " + plugin->replaygain.in_files;
+// }
+
+ QString t_str = plugin->replaygain.in_files;
+ t_str.replace( "%p", param );
+ param = config->binaries[plugin->replaygain.bin] + " " + t_str;
+
+ // cosmetic surgery
+ param.simplifyWhiteSpace();
+
+ params = QStringList::split( ' ', param );
+
+ for( QStringList::Iterator it = params.begin(); it != params.end(); ++it )
+ {
+ paramSplinter = *it;
+ if( paramSplinter == "%i" ) {
+ for( QStringList::Iterator b = files.begin(); b != files.end(); ++b ) {
+ *(proc) << KURL::decode_string( *b );
+ }
+ }
+ else {
+ *(proc) << paramSplinter;
+ }
+ }
+
+ for( QStringList::Iterator it = files.begin(); it != files.end(); ++it )
+ {
+ *it = KURL::decode_string( *it );
+ }
+
+ param.replace( "%i", "\""+files.join("\" \"")+"\"" );
+ logger->log( logID, " " + i18n("Executing") + ": `" + param + "'" );
+
+ proc->setPriority( config->data.general.priority );
+ proc->start( KProcess::NotifyOnExit, KProcess::AllOutput );
+
+ return true;
+}
+
+//QValueList<float> ReplayGain::getReplayGain( QString file ) {} // obsolete
diff --git a/src/replaygain.h b/src/replaygain.h
new file mode 100755
index 0000000..ff9e38a
--- /dev/null
+++ b/src/replaygain.h
@@ -0,0 +1,58 @@
+
+
+#ifndef REPLAYGAIN_H
+#define REPLAYGAIN_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+class Config;
+class Logger;
+class KProcess;
+
+/**
+ * @short Starts a process to add/remove Replay Gain tags to/from a given file
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ReplayGain : public QObject
+{
+ Q_OBJECT
+public:
+ enum Mode {
+ calc_track = 0x0001,
+ calc_album = 0x0002,
+ remove = 0x0004,
+ force = 0x0008
+ };
+
+ /**
+ * Constructor
+ */
+ ReplayGain( Config*, Logger* );
+
+ /**
+ * Destructor
+ */
+ virtual ~ReplayGain();
+
+ /**
+ * Add/remove Replay Gain
+ * @param files a list of files (of the same format)
+ * @param format the format of the file/files (extension or mime type)
+ * @param prc a pointer to a KProcess
+ * @param remove if true the Replay Gain tags are being removed, if false (default) the tags are calculated and added
+ */
+ bool apply( QStringList files, const QString& format, KProcess* proc, int logID, Mode mode = Mode(calc_track|calc_album) ); // NOTE const QStringList& ?
+
+ /*
+ * Returns the track and the album gain (in this order) of the @p file
+ */
+ //static QValueList<float> getReplayGain( QString file ); // obsolete
+
+private:
+ Config* config;
+ Logger* logger;
+};
+
+#endif // REPLAYGAIN_H
diff --git a/src/replaygainfilelist.cpp b/src/replaygainfilelist.cpp
new file mode 100755
index 0000000..2d71806
--- /dev/null
+++ b/src/replaygainfilelist.cpp
@@ -0,0 +1,1262 @@
+
+#include "replaygainfilelist.h"
+#include "tagengine.h"
+#include "logger.h"
+#include "config.h"
+#include "replaygain.h"
+#include "replaygainpluginloader.h"
+
+#include <qdir.h>
+#include <qpainter.h>
+#include <qsimplerichtext.h>
+#include <qapplication.h>
+#include <qheader.h>
+#include <qlayout.h>
+#include <qtimer.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kurldrag.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+#include <kactioncollection.h>
+#include <kmessagebox.h>
+#include <kmountpoint.h>
+#include <kprogress.h>
+#include <kprocess.h>
+#include <kmimetype.h>
+#include <kapplication.h>
+#include <kuser.h>
+
+// TODO move listDir, addFiles, addDir, etc -- done?
+
+// ### soundkonverter 0.4: give the 'track' and 'album' column a minimum width and fill the rest of the space with the 'file' column - make some margins, too
+
+
+ReplayGainFileListItem::ReplayGainFileListItem( QListView* parent )
+ : KListViewItem( parent )
+{
+ m_type = File;
+ mimeType = "application/octet-stream";
+ addingReplayGain = false;
+ queued = false;
+}
+
+// ReplayGainFileListItem::ReplayGainFileListItem( QListView* parent, QListViewItem* after )
+// : KListViewItem( parent, after )
+// {
+// m_type = File;
+// mimeType = "application/octet-stream";
+// addingReplayGain = false;
+// queued = false;
+// }
+
+ReplayGainFileListItem::ReplayGainFileListItem( ReplayGainFileListItem* parent )
+ : KListViewItem( parent )
+{
+ m_type = File;
+ mimeType = "application/octet-stream";
+ addingReplayGain = false;
+ queued = false;
+}
+
+ReplayGainFileListItem::~ReplayGainFileListItem()
+{}
+
+void ReplayGainFileListItem::paintCell( QPainter *p, const QColorGroup &cg, int column, int width, int alignment )
+{
+ // NOTE speed up this function
+ // NOTE calculate the red color
+
+ QColorGroup _cg( cg );
+ QColor c;
+
+ if( column == ((ReplayGainFileList*)listView())->columnByName(i18n("File")) )
+ {
+ int margin = listView()->itemMargin();
+ int w = width - 2*margin;
+ int h = height();
+ QRect textRect = p->boundingRect( margin, 0, w, h, alignment, text(column) );
+
+ if( textRect.width() > w ) {
+ alignment = Qt::AlignRight | Qt::SingleLine;
+ }
+ }
+
+ if( isSelected() && addingReplayGain ) {
+ _cg.setColor( QColorGroup::Highlight, QColor( 215, 62, 62 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( addingReplayGain && column != listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 255, 234, 234 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( addingReplayGain && column == listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 247, 227, 227 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+
+ if( isSelected() && queued ) {
+ _cg.setColor( QColorGroup::Highlight, QColor( 230, 232, 100 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( queued && column != listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 255, 255, 190 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+ else if( queued && column == listView()->sortColumn() ) {
+ _cg.setColor( QColorGroup::Base, QColor( 255, 243, 168 ) );
+ QListViewItem::paintCell( p, _cg, column, width, alignment );
+ return;
+ }
+
+ KListViewItem::paintCell( p, _cg, column, width, alignment );
+}
+
+void ReplayGainFileListItem::setType( Type type )
+{
+ if( type == m_type ) return;
+
+ m_type = type;
+
+ if( type == Album ) {
+ setOpen( true );
+ setPixmap( 0, KGlobal::iconLoader()->loadIcon("cdrom_unmount",KIcon::Small) );
+ }
+}
+
+void ReplayGainFileListItem::updateReplayGainCells( TagData* tags )
+{
+ if( !tags ) {
+ setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Track")), i18n("Unknown") );
+ setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), i18n("Unknown") );
+ }
+ else {
+ if( tags->track_gain != 210588 ) {
+ setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) );
+ }
+ else {
+ setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Track")), i18n("Unknown") );
+ }
+ if( tags->album_gain != 210588 ) {
+ setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) );
+ }
+ else {
+ setText( ((ReplayGainFileList*)listView())->columnByName(i18n("Album")), i18n("Unknown") );
+ }
+ }
+}
+
+int ReplayGainFileListItem::compare( QListViewItem* item, int column, bool ascending ) const
+{
+ // NOTE looking at the types, not the strings would be better
+ if( text(1) == "" && item->text(1) != "" ) return -1;
+ else if( text(1) != "" && item->text(1) == "" ) return 1;
+ else return KListViewItem::compare( item, column, ascending );
+}
+
+ReplayGainFileList::ReplayGainFileList( TagEngine* _tagEngine, Config* _config, Logger* _logger, QWidget *parent, const char *name )
+ : KListView( parent, name )
+{
+ tagEngine = _tagEngine;
+ config = _config;
+ logger = _logger;
+
+ processing = false;
+ queue = false;
+
+ time = 0;
+ processedTime = 0;
+
+ addColumn( i18n("File"), 390 );
+ setColumnWidthMode( 0, QListView::Manual );
+ addColumn( i18n("Track"), 90 );
+ setColumnAlignment( 1, Qt::AlignRight );
+ addColumn( i18n("Album"), 90 );
+ setColumnAlignment( 2, Qt::AlignRight );
+
+ setSelectionMode( QListView::Extended );
+ setAllColumnsShowFocus( true );
+ setResizeMode( QListView::LastColumn );
+ setShowSortIndicator( true );
+ setSorting( 0 );
+ setRootIsDecorated( true );
+
+ setDragEnabled( true );
+ setAcceptDrops( true );
+
+ QGridLayout* grid = new QGridLayout( this, 2, 1, 11, 6 );
+ grid->setRowStretch( 0, 1 );
+ grid->setRowStretch( 2, 1 );
+ grid->setColStretch( 0, 1 );
+ grid->setColStretch( 2, 1 );
+ pScanStatus = new KProgress( this, "pScanStatus" );
+ pScanStatus->setMinimumHeight( pScanStatus->height() );
+ pScanStatus->setFormat( "%v / %m" );
+ pScanStatus->hide();
+ grid->addWidget( pScanStatus, 1, 1 );
+ grid->setColStretch( 1, 2 );
+
+ contextMenu = new KPopupMenu( this );
+ connect( this, SIGNAL(contextMenuRequested(QListViewItem*,const QPoint&,int)),
+ this, SLOT(showContextMenu(QListViewItem*,const QPoint&,int))
+ );
+
+ // we haven't got access to the action collection of soundKonverter, so let's create a new one
+ actionCollection = new KActionCollection( this );
+
+ calc_gain = new KAction( i18n("Calculate Replay Gain tags"), "apply", 0, this, SLOT(calcSelectedItemsGain()), actionCollection, "calc_album" );
+ remove_gain = new KAction( i18n("Remove Replay Gain tags"), "cancel", 0, this, SLOT(removeSelectedItemsGain()), actionCollection, "remove_gain" );
+ remove = new KAction( i18n("Remove"), "edittrash", Key_Delete, this, SLOT(removeSelectedItems()), actionCollection, "remove" );
+ paste = new KAction( i18n("Paste"), "editpaste", 0, this, 0, actionCollection, "paste" );
+ newalbum = new KAction( i18n("New album"), "filenew", 0, this, SLOT(createNewAlbum()), actionCollection, "newalbum" );
+ open_albums = new KAction( i18n("Open all albums"), "view_tree", 0, this, SLOT(openAlbums()), actionCollection, "open_albums" );
+ close_albums = new KAction( i18n("Cloase all albums"), "view_text", 0, this, SLOT(closeAlbums()), actionCollection, "close_albums" );
+
+ replayGain = new ReplayGain( config, logger );
+
+ bubble = new QSimpleRichText( i18n( "<div align=center>"
+ "<h3>Replay Gain Tool</h3>"
+ "With this tool you can add Replay Gain tags to your audio files and remove them."
+ //"<br>Replay Gain adds a volume correction information to the files for playing them at the same volume."
+ //"Replay Gain allows you to play all audio files at the same volume level without modifying the audio data."
+ "<br>Replay Gain adds a volume correction information to the files so that they can be played at an equal volume level."
+// "<br><a href=\"documenation:replaygaintool\">Learn more about Replay Gain ...</a><br/>"
+ "</div>" ), QApplication::font() );
+
+ connect( header(), SIGNAL(sizeChange( int, int, int )),
+ SLOT(columnResizeEvent( int, int, int ))
+ );
+ connect( this, SIGNAL( dropped(QDropEvent*, QListViewItem*, QListViewItem*) ),
+ SLOT( slotDropped(QDropEvent*, QListViewItem*, QListViewItem*) )
+ );
+
+ process = new KProcess();
+ connect( process, SIGNAL(receivedStdout(KProcess*,char*,int)),
+ this, SLOT(processOutput(KProcess*,char*,int))
+ );
+ connect( process, SIGNAL(receivedStderr(KProcess*,char*,int)),
+ this, SLOT(processOutput(KProcess*,char*,int))
+ );
+ connect( process, SIGNAL(processExited(KProcess*)),
+ this, SLOT(processExit(KProcess*))
+ );
+
+ tUpdateProgress = new QTimer( this, "tUpdateProgress" );
+ connect( tUpdateProgress, SIGNAL(timeout()),
+ this, SLOT(update())
+ );
+}
+
+ReplayGainFileList::~ReplayGainFileList()
+{
+ delete replayGain;
+}
+
+int ReplayGainFileList::columnByName( const QString& name )
+{
+ for( int i = 0; i < columns(); ++i ) {
+ if( columnText( i ) == name ) return i;
+ }
+ return -1;
+}
+
+void ReplayGainFileList::viewportPaintEvent( QPaintEvent* e )
+{
+ KListView::viewportPaintEvent( e );
+
+ // the bubble help
+ if( childCount() == 0 ) {
+ QPainter p( viewport() );
+
+ bubble->setWidth( width() - 50 );
+
+ const uint w = bubble->width() + 20;
+ const uint h = bubble->height() + 20;
+
+ p.setBrush( colorGroup().background() );
+ p.drawRoundRect( 15, 15, w, h, (8*200)/w, (8*200)/h );
+ bubble->draw( &p, 20, 20, QRect(), colorGroup() );
+ }
+}
+
+void ReplayGainFileList::viewportResizeEvent( QResizeEvent* )
+{
+ // needed for correct redraw of bubble help
+ triggerUpdate();
+}
+
+void ReplayGainFileList::columnResizeEvent( int, int, int )
+{
+ // needed for correct redraw of bubble help
+ triggerUpdate();
+}
+
+bool ReplayGainFileList::acceptDrag( QDropEvent* e ) const
+{
+ return ( e->source() == viewport() || KURLDrag::canDecode(e) ); // TODO verify the files
+}
+
+void ReplayGainFileList::slotDropped( QDropEvent* e, QListViewItem*, QListViewItem* )
+{
+ QString file;
+ KURL::List list;
+ if( KURLDrag::decode( e, list ) )
+ {
+ for( KURL::List::Iterator it = list.begin(); it != list.end(); ++it )
+ {
+ // TODO verify the files (necessary when multiple files are being dropped)
+ file = QDir::convertSeparators( (*it).pathOrURL() );
+ QFileInfo fileInfo( file );
+ if( fileInfo.isDir() )
+ {
+ addDir( file );
+ }
+ else
+ {
+ addFile( (*it).url() );
+ }
+ }
+ }
+}
+
+void ReplayGainFileList::contentsDragEnterEvent( QDragEnterEvent *e )
+{
+ e->accept( e->source() == viewport() || KURLDrag::canDecode(e) );
+}
+
+void ReplayGainFileList::contentsDragMoveEvent( QDragMoveEvent *e )
+{ // the mouse has moved while dragging some stuff
+
+ // if the file is added from an external app, don't let the user select the position to drop
+ if( e->source() != viewport() ) {
+ setDropHighlighter( false );
+ setDropVisualizer( false );
+ cleanItemHighlighter();
+ cleanDropVisualizer();
+
+ // the rest is handled by KListView
+ KListView::contentsDragMoveEvent( e );
+
+ return;
+ }
+
+ // translate the coordinates of the mouse pointer
+ QPoint vp = contentsToViewport( e->pos() );
+ // and get the list view item below the pointer
+ ReplayGainFileListItem* item = itemAt( vp );
+
+ if( item && item->type() == ReplayGainFileListItem::Album ) { // the pointer is above an 'album' element
+ // draw a nice rect around the item
+ setDropHighlighter( true );
+ setDropVisualizer( false );
+ cleanDropVisualizer();
+ }
+ else if( item ) { // the pointer is above an 'file' element
+ // draw a line above or below the item
+ setDropVisualizer( true );
+ setDropHighlighter( false );
+ cleanItemHighlighter();
+ }
+
+ // the rest is handled by KListView
+ KListView::contentsDragMoveEvent( e );
+}
+
+void ReplayGainFileList::contentsDropEvent( QDropEvent *e )
+{ // the stuff has been dropped
+
+ emit dropped( e, 0, 0 ); // NOTE it works this way
+
+ bool formatError = false;
+
+ cleanDropVisualizer();
+ cleanItemHighlighter();
+
+ // get the item below the mouse pointer, where the stuff has been dropped
+ QPoint vp = contentsToViewport( e->pos() );
+ ReplayGainFileListItem* newParent = itemAt( vp );
+
+ // if the item is a 'file', use the parent item
+ if( newParent && newParent->type() != ReplayGainFileListItem::Album ) {
+ newParent = newParent->parent();
+ }
+
+ // TODO if the mouse is on the left side under the parent but not under the sibling item, use the parent
+/* if( newParent == 0 && vp.x() >= 2*treeStepSize() ) {
+ QPoint p = vp;
+ int height = 0;
+ if( firstChild() ) {
+ height = firstChild()->height();
+ }
+ p.setY( p.y() - height );
+ ReplayGainFileListItem* it = itemAt( p );
+ if( it && it->type() != ReplayGainFileListItem::Album ) {
+ newParent = it->parent();
+ }
+ else if( it ) {
+ newParent = it;
+ }
+ }*/
+
+ ReplayGainFileListItem* i;
+
+ // iterate through all items and move all selected ones
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( newParent == 0 ) {
+ item = item->nextSibling();
+ continue;
+ }
+ if( item->isSelected() ) {
+ if( newParent == 0 || newParent->mimeType == item->mimeType || newParent->mimeType == "application/octet-stream" ) {
+ if( newParent->mimeType == "application/octet-stream" ) newParent->mimeType = item->mimeType;
+ i = item;
+ item = item->nextSibling();
+ takeItem( i );
+ if( newParent ) newParent->insertItem( i );
+ else insertItem( i );
+ }
+ else {
+ item = item->nextSibling();
+ formatError = true;
+ }
+ }
+ else {
+ item = item->nextSibling();
+ }
+ }
+ else {
+ if( item == newParent ) {
+ item = item->nextSibling();
+ continue;
+ }
+ if( item->isSelected() ) {
+ if( newParent == 0 || newParent->mimeType == item->mimeType || newParent->mimeType == "application/octet-stream" ) {
+ if( newParent->mimeType == "application/octet-stream" ) newParent->mimeType = item->mimeType;
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; ) {
+ i = sub_item;
+ sub_item = sub_item->nextSibling();
+ item->takeItem( i );
+ if( newParent ) newParent->insertItem( i );
+ else insertItem( i );
+ }
+ i = item;
+ item = item->nextSibling();
+ delete i;
+ }
+ else {
+ item = item->nextSibling();
+ formatError = true;
+ }
+ }
+ else {
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; ) {
+ if( sub_item->isSelected() ) {
+ if( newParent == 0 || newParent->mimeType == item->mimeType || newParent->mimeType == "application/octet-stream" ) {
+ if( newParent && newParent->mimeType == "application/octet-stream" ) newParent->mimeType = item->mimeType;
+ i = sub_item;
+ sub_item = sub_item->nextSibling();
+ item->takeItem( i );
+ if( newParent ) newParent->insertItem( i );
+ else insertItem( i );
+ }
+ else {
+ sub_item = sub_item->nextSibling();
+ formatError = true;
+ }
+ }
+ else {
+ sub_item = sub_item->nextSibling();
+ }
+ }
+ if( item->childCount() == 0 ) {
+ i = item;
+ item = item->nextSibling();
+ delete i;
+ }
+ else {
+ item = item->nextSibling();
+ }
+ }
+ }
+ }
+
+ // FIXME make the mouse pointer look normal
+
+ if( formatError ) {
+ KMessageBox::information( this,
+ i18n("You can't place files of different formats in the same \'album\'."), i18n("Different file formats") );
+ }
+}
+
+int ReplayGainFileList::listDir( const QString& directory, QStringList filter, bool recursive, bool fast, int count )
+{ // NOTE speed up?
+ QDir dir( directory );
+ dir.setFilter( QDir::Files | QDir::Dirs | QDir::NoSymLinks | QDir::Readable );
+
+ QStringList list = dir.entryList();
+
+ for( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) {
+ if( *it == "." || *it == ".." ) continue;
+ QFileInfo fileInfo( directory + "/" + *it );
+ if( fast ) {
+ if( fileInfo.isDir() && recursive ) {
+ count = listDir( directory + "/" + *it, filter, recursive, fast, count );
+ }
+ else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names
+ // NOTE filter feature
+ for( QStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) {
+ if( (*it).endsWith("."+(*jt),false) ) {
+ count++;
+ pScanStatus->setTotalSteps( count );
+ break;
+ }
+ }
+ if( filter.first() == "" ) {
+ count++;
+ pScanStatus->setTotalSteps( count );
+ }
+ }
+ }
+ else {
+ if( fileInfo.isDir() && recursive ) {
+ count = listDir( directory + "/" + *it, filter, recursive, fast, count );
+ }
+ else if( !fileInfo.isDir() || !recursive ) { // NOTE checking for isFile may not work with all file names
+ // NOTE filter feature
+ for( QStringList::Iterator jt = filter.begin(); jt != filter.end(); ++jt ) {
+ if( (*it).endsWith("."+(*jt),false) ) {
+ addFile( KURL::encode_string(directory + "/" + *it) );
+ count++;
+ pScanStatus->setProgress( count );
+ break;
+ }
+ }
+ if( filter.first() == "" ) {
+ addFile( KURL::encode_string(directory + "/" + *it) );
+ count++;
+ pScanStatus->setProgress( count );
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+void ReplayGainFileList::showContextMenu( QListViewItem* item, const QPoint& point, int )
+{
+ // remove all items from the context menu
+ contextMenu->clear();
+
+ // add a tilte to our context manu
+ //contextMenu->insertTitle( static_cast<FileListItem*>(item)->fileName );
+
+ // if item is null, we can abort here
+ if( item ) {
+ if( !processing ) {
+ calc_gain->plug( contextMenu );
+ if( item->text(columnByName(i18n("Track"))) != i18n("Unknown") || item->text(columnByName(i18n("Album"))) != i18n("Unknown") ) {
+ remove_gain->plug( contextMenu );
+ }
+ }
+ newalbum->plug( contextMenu );
+ remove->plug( contextMenu );
+ }
+ else {
+ newalbum->plug( contextMenu );
+ }
+ paste->plug( contextMenu );
+ contextMenu->insertSeparator();
+ open_albums->plug( contextMenu );
+ close_albums->plug( contextMenu );
+
+ // show the popup menu
+ contextMenu->popup( point );
+}
+
+void ReplayGainFileList::addFile( const QString& file )
+{
+ QString filename = file;
+ QString filePathName;
+ QString device;
+
+ if( filename.left( 1 ) == "/" ) {
+ filePathName = filename;
+ }
+ else if( filename.left( 7 ) == "file://" ) {
+ filePathName = filename;
+ filePathName.remove( 0, 7 );
+ }
+ else if( filename.left( 13 ) == "system:/home/" ) {
+ filePathName = filename;
+ filePathName.remove( 0, 13 );
+ filePathName = QDir::homeDirPath() + "/" + filePathName;
+ }
+ else if( filename.left( 14 ) == "system:/users/" || filename.left( 6 ) == "home:/" ) {
+ int length = ( filename.left(6) == "home:/" ) ? 6 : 14;
+ QString username = filename;
+ username.remove( 0, length );
+ username = username.left( username.find("/") );
+ filePathName = filename;
+ filePathName.remove( 0, length + username.length() );
+ KUser user( username );
+ filePathName = user.homeDir() + filePathName;
+ }
+ else if( filename.left( 14 ) == "system:/media/" || filename.left( 7 ) == "media:/" ) {
+ int length = ( filename.left(7) == "media:/" ) ? 7 : 14;
+ device = filename;
+ device.remove( 0, length );
+ device = "/dev/" + device.left( device.find( "/" ) );
+
+ KMountPoint::List mountPoints = KMountPoint::possibleMountPoints();
+
+ for( KMountPoint::List::ConstIterator jt = mountPoints.begin(); jt != mountPoints.end(); ++jt )
+ {
+ const KSharedPtr<KMountPoint> mp = *jt;
+ if( mp->mountedFrom() == device )
+ {
+ filePathName = ( mp->mountPoint() == "/" ) ? mp->mountPoint() : mp->mountPoint() + "/";
+ filePathName += filename.right( filename.length() - device.length() - length + 4 );
+ }
+ }
+ }
+ else {
+ return;
+ }
+
+ TagData* tags = tagEngine->readTags( KURL::decode_string(filePathName) );
+ if( !tags || tags->album.isEmpty() ) {
+ ReplayGainFileListItem* item = new ReplayGainFileListItem( this );
+ item->originalFileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 );
+ item->mimeType = KMimeType::findByFileContent( filePathName )->name();
+ item->fileFormat = KMimeType::findByFileContent( filePathName )->patterns().first();
+ if( item->mimeType.isEmpty() || item->mimeType == "application/octet-stream" || item->mimeType == "text/plain" ) {
+ item->mimeType = KMimeType::findByURL( filePathName.lower() )->name();
+ item->fileFormat = KMimeType::findByURL( filePathName.lower() )->patterns().first();
+ }
+ // check whether the mime type has a decoder registered
+ if( !config->acceptReplayGainFile( item->mimeType ) ) {
+ delete item;
+ return;
+ }
+ item->filePathName = filePathName;
+ if( tags ) item->time = tags->length;
+ else {
+ FormatItem* formatItem = config->getFormatItem( item->mimeType );
+ if( formatItem && formatItem->size > 0 ) {
+ QFileInfo fileInfo( filePathName );
+ item->time = fileInfo.size() / formatItem->size;
+ }
+ else {
+ item->time = 210;
+ }
+ }
+ item->setText( columnByName(i18n("File")), KURL::decode_string(filePathName).replace("%2f","/").replace("%%","%") );
+ if( tags && tags->track_gain != 210588 ) {
+ item->setText( columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) );
+ }
+ else {
+ item->setText( columnByName(i18n("Track")), i18n("Unknown") );
+ }
+ if( tags && tags->album_gain != 210588 ) {
+ item->setText( columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) );
+ }
+ else {
+ item->setText( columnByName(i18n("Album")), i18n("Unknown") );
+ }
+ }
+ else {
+ QString mimeType = KMimeType::findByFileContent( filePathName )->name();
+ QString fileFormat = KMimeType::findByFileContent( filePathName )->patterns().first();
+ if( mimeType.isEmpty() || mimeType == "application/octet-stream" || mimeType == "text/plain" ) {
+ mimeType = KMimeType::findByURL( filePathName.lower() )->name();
+ fileFormat = KMimeType::findByURL( filePathName.lower() )->patterns().first();
+ }
+ // check whether the mime type has a decoder registered
+ if( !config->acceptReplayGainFile( mimeType ) ) {
+ return;
+ }
+
+ for( ReplayGainFileListItem* it = firstChild(); it != 0; it = it->nextSibling() ) {
+ //if( it->text(0) == QString(tags->artist+" - "+tags->album) ) {
+ if( it->text(columnByName(i18n("File"))) == tags->album && it->type() == ReplayGainFileListItem::Album && it->mimeType == mimeType ) {
+ ReplayGainFileListItem* item = new ReplayGainFileListItem( it );
+ item->originalFileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 );
+ item->filePathName = filePathName;
+ item->mimeType = mimeType;
+ item->fileFormat = fileFormat;
+ item->time = tags->length;
+ item->setText( columnByName(i18n("File")), KURL::decode_string(filePathName).replace("%2f","/").replace("%%","%") );
+ if( tags->track_gain != 210588 ) {
+ item->setText( columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) );
+ }
+ else {
+ item->setText( columnByName(i18n("Track")), i18n("Unknown") );
+ }
+ if( tags->album_gain != 210588 ) {
+ item->setText( columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) );
+ }
+ else {
+ item->setText( columnByName(i18n("Album")), i18n("Unknown") );
+ }
+ tags = 0; // <--,
+ break; // |
+ } // |
+ } // |
+ if( tags ) { // <--'
+ ReplayGainFileListItem* parent = new ReplayGainFileListItem( this );
+ //parent->setText( 0, tags->artist+" - "+tags->album );
+ parent->setText( columnByName(i18n("File")), tags->album );
+ parent->setType( ReplayGainFileListItem::Album );
+ parent->mimeType = mimeType;
+ parent->fileFormat = fileFormat;
+ ReplayGainFileListItem* item = new ReplayGainFileListItem( parent );
+ item->originalFileFormat = filePathName.right( filePathName.length() - filePathName.findRev(".") - 1 );
+ item->filePathName = filePathName;
+ item->mimeType = mimeType;
+ item->fileFormat = fileFormat;
+ item->time = tags->length;
+ item->setText( columnByName(i18n("File")), KURL::decode_string(filePathName).replace("%2f","/").replace("%%","%") );
+ if( tags->track_gain != 210588 ) {
+ item->setText( columnByName(i18n("Track")), QString().sprintf("%+.2f dB",tags->track_gain) );
+ }
+ else {
+ item->setText( columnByName(i18n("Track")), i18n("Unknown") );
+ }
+ if( tags->album_gain != 210588 ) {
+ item->setText( columnByName(i18n("Album")), QString().sprintf("%+.2f dB",tags->album_gain) );
+ }
+ else {
+ item->setText( columnByName(i18n("Album")), i18n("Unknown") );
+ }
+ }
+ }
+}
+
+void ReplayGainFileList::addDir( const QString& directory, const QStringList& filter, bool recursive )
+{
+ pScanStatus->setProgress( 0 );
+ pScanStatus->setTotalSteps( 0 );
+ pScanStatus->show(); // show the status while scanning the directories
+ kapp->processEvents();
+
+ int count = listDir( directory, filter, recursive, true );
+ listDir( directory, filter, recursive );
+
+ pScanStatus->hide(); // hide the status bar, when the scan is done
+}
+
+void ReplayGainFileList::openAlbums()
+{
+ // iterate through all items and open all albums
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::Album ) {
+ item->setOpen( true );
+ }
+ }
+}
+
+void ReplayGainFileList::closeAlbums()
+{
+ // iterate through all items and close all albums
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::Album ) {
+ item->setOpen( false );
+ }
+ }
+}
+
+void ReplayGainFileList::removeSelectedItems()
+{
+ ReplayGainFileListItem* i;
+
+ // iterate through all items and remove all selected ones
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( item->isSelected() ) {
+ i = item;
+ item = item->nextSibling();
+ delete i;
+ }
+ else {
+ item = item->nextSibling();
+ }
+ }
+ else {
+ if( item->isSelected() ) {
+ i = item;
+ item = item->nextSibling();
+ delete i;
+ }
+ else {
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; ) {
+ if( sub_item->isSelected() ) {
+ i = sub_item;
+ sub_item = sub_item->nextSibling();
+ delete i;
+ }
+ else {
+ sub_item = sub_item->nextSibling();
+ }
+ }
+ if( item->childCount() == 0 ) {
+ i = item;
+ item = item->nextSibling();
+ delete i;
+ }
+ else {
+ item = item->nextSibling();
+ }
+ }
+ }
+ }
+}
+
+void ReplayGainFileList::createNewAlbum()
+{
+ ReplayGainFileListItem* item = new ReplayGainFileListItem( this );
+ item->setText( columnByName(i18n("File")), i18n("New album") );
+ item->setType( ReplayGainFileListItem::Album );
+ item->mimeType = "application/octet-stream";
+}
+
+void ReplayGainFileList::calcSelectedItemsGain()
+{
+ if( processing ) return;
+
+ // iterate through all items and remove the replay gain from all selected ones
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( item->isSelected() ) {
+ item->queued = true;
+ item->repaint();
+ item->mode = ReplayGainFileListItem::force;
+ }
+ }
+ else {
+ if( item->isSelected() ) {
+ item->queued = true;
+ item->repaint();
+ item->mode = ReplayGainFileListItem::force;
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ sub_item->queued = true;
+ sub_item->repaint();
+ sub_item->mode = ReplayGainFileListItem::force;
+ }
+ }
+ else {
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ if( sub_item->isSelected() ) {
+ item->queued = true;
+ item->repaint();
+ item->mode = ReplayGainFileListItem::force;
+ for( ReplayGainFileListItem* sub_item2 = item->firstChild(); sub_item2 != 0; sub_item2 = sub_item2->nextSibling() ) {
+ sub_item2->queued = true;
+ sub_item2->repaint();
+ sub_item2->mode = ReplayGainFileListItem::force;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ startProcess();
+}
+
+void ReplayGainFileList::removeSelectedItemsGain()
+{
+ // iterate through all items and remove the replay gain from all selected ones
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( item->isSelected() && !item->addingReplayGain ) {
+ item->queued = true;
+ item->repaint();
+ item->mode = ReplayGainFileListItem::remove;
+ }
+ }
+ else {
+ if( item->isSelected() && !item->addingReplayGain ) {
+ item->queued = true;
+ item->repaint();
+ item->mode = ReplayGainFileListItem::remove;
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ sub_item->queued = true;
+ sub_item->repaint();
+ sub_item->mode = ReplayGainFileListItem::remove;
+ }
+ }
+ else {
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ if( sub_item->isSelected() && !sub_item->addingReplayGain ) {
+ sub_item->queued = true;
+ sub_item->repaint();
+ sub_item->mode = ReplayGainFileListItem::remove;
+ }
+ }
+ }
+ }
+ }
+ startProcess();
+}
+
+void ReplayGainFileList::calcReplayGain( ReplayGainFileListItem* item )
+{
+ logID = logger->registerProcess( KURL::encode_string(item->text(columnByName(i18n("File")))) );
+ logger->log( logID, "Mime Type: " + item->mimeType );
+ logger->log( logID, i18n("Applying Replay Gain") );
+
+ QStringList fileList;
+ bool force = false;
+ if( mode & ReplayGainFileListItem::force ) force = true;
+ QString album_gain = "";
+ bool skip = true;
+
+ timeCount = 0;
+ file = 0;
+ files = 0;
+
+ if( item->type() == ReplayGainFileListItem::Album ) {
+ item->queued = false;
+ item->addingReplayGain = true;
+ item->repaint();
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ if( sub_item->queued && sub_item->mode & ReplayGainFileListItem::force ) force = true; // NOTE can this be replaced by checking item?
+ sub_item->queued = false;
+ sub_item->addingReplayGain = true;
+ sub_item->repaint();
+
+ fileList += sub_item->filePathName;
+ files++;
+ timeCount += sub_item->time;
+
+ QString current_gain = sub_item->text( columnByName(i18n("Album")) );
+ if( album_gain == "" ) album_gain = current_gain;
+ else if( album_gain != current_gain ) skip = false;
+ }
+
+ if( !skip || album_gain == i18n("Unknown") || force ) {
+ if( force ) {
+ replayGain->apply( fileList, item->mimeType, process, logID, ReplayGain::Mode(ReplayGain::calc_album|ReplayGain::force) );
+ }
+ else {
+ replayGain->apply( fileList, item->mimeType, process, logID );
+ }
+ }
+ else {
+ logger->processCompleted( logID, 0 );
+ item->addingReplayGain = false;
+ item->repaint();
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ sub_item->addingReplayGain = false;
+ sub_item->repaint();
+ }
+ processNextFile();
+ }
+ }
+ else {
+ if( item->queued && item->mode & ReplayGainFileListItem::force ) force = true;
+
+ item->queued = false;
+ item->addingReplayGain = true;
+ item->repaint();
+
+ files = 1;
+ timeCount = item->time;
+
+ if( force ) {
+ replayGain->apply( item->filePathName, item->mimeType, process, logID, ReplayGain::Mode(ReplayGain::calc_album|ReplayGain::force) );
+ }
+ else {
+ replayGain->apply( item->filePathName, item->mimeType, process, logID );
+ }
+ }
+}
+
+void ReplayGainFileList::removeReplayGain( ReplayGainFileListItem* item )
+{
+ logID = logger->registerProcess( KURL::encode_string(item->text(columnByName(i18n("File")))) );
+ logger->log( logID, "Mime Type: " + item->mimeType );
+ logger->log( logID, i18n("Removing Replay Gain") );
+
+ if( item->type() == ReplayGainFileListItem::File ) {
+ item->queued = false;
+ item->addingReplayGain = true;
+ item->repaint();
+ timeCount = item->time;
+ replayGain->apply( item->filePathName, item->mimeType, process, logID, ReplayGain::remove );
+ }
+ else {
+ item->queued = false;
+ item->repaint();
+ processNextFile();
+ }
+}
+
+void ReplayGainFileList::calcAllReplayGain( bool force )
+{
+ queue = true;
+ if( force ) mode = ReplayGainFileListItem::force;
+ else mode = ReplayGainFileListItem::Mode(0x0000);
+ startProcess();
+}
+
+void ReplayGainFileList::removeAllReplayGain()
+{
+ queue = true;
+ mode = ReplayGainFileListItem::remove;
+ startProcess();
+}
+
+void ReplayGainFileList::cancelProcess()
+{
+ queue = false;
+ if( process->isRunning() )
+ {
+ bool ret = process->kill( SIGKILL );
+ if( ret ) {
+ logger->log( logID, i18n("Killing process ...") );
+ }
+ else {
+ logger->log( logID, i18n("Killing process failed. Stopping after files are completed ...") );
+ }
+ }
+}
+
+void ReplayGainFileList::startProcess()
+{
+ emit processStarted();
+ processing = true;
+ time = 0;
+
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( queue ) {
+ time += item->time;
+ }
+ else if( item->queued ) {
+ time += item->time;
+ }
+ }
+ else {
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ if( queue ) {
+ time += sub_item->time;
+ }
+ else if( sub_item->queued ) {
+ time += sub_item->time;
+ }
+ }
+ }
+ }
+
+ emit updateProgress( 0, 100 );
+ if( !tUpdateProgress->isActive() ) {
+ tUpdateProgress->start( 200 ); // TODO use config value
+ }
+
+ currentItem = 0;
+ processNextFile();
+}
+
+void ReplayGainFileList::processNextFile()
+{
+ percent = 0;
+ lastPercent = 0;
+
+ ReplayGainFileListItem* currentSubItem = 0;
+
+ if( !currentItem ) { currentItem = firstChild(); }
+ else if( currentItem->type() == ReplayGainFileListItem::File && currentItem->parent() == 0 ) { currentItem = currentItem->nextSibling(); }
+ else if( currentItem->type() == ReplayGainFileListItem::Album ) { currentItem = currentItem->nextSibling(); }
+ else { currentSubItem = currentItem->nextSibling(); currentItem = currentItem->parent(); if( !currentSubItem ) { currentItem = currentItem->nextSibling(); } }
+
+ for( ReplayGainFileListItem* item = currentItem; item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( queue ) {
+ currentItem = item;
+ if( mode & ReplayGainFileListItem::remove ) removeReplayGain( item );
+ else calcReplayGain( item );
+ return;
+ }
+ else if( item->queued ) {
+ currentItem = item;
+ if( item->mode & ReplayGainFileListItem::remove ) removeReplayGain( item );
+ else calcReplayGain( item );
+ return;
+ }
+ }
+ else {
+ if( queue ) {
+ currentItem = item;
+ if( mode & ReplayGainFileListItem::remove ) {}
+ else { calcReplayGain( item ); return; }
+ }
+ else if( item->queued ) {
+ currentItem = item;
+ if( item->mode & ReplayGainFileListItem::remove ) { item->queued = false; }
+ else { calcReplayGain( item ); return; }
+ }
+
+ if( !currentSubItem ) currentSubItem = item->firstChild();
+ for( ReplayGainFileListItem* sub_item = currentSubItem; sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ if( queue ) {
+ currentItem = sub_item;
+ if( mode & ReplayGainFileListItem::remove ) removeReplayGain( sub_item );
+ return;
+ }
+ else if( sub_item->queued ) {
+ currentItem = sub_item;
+ if( sub_item->mode & ReplayGainFileListItem::remove ) removeReplayGain( sub_item );
+ return;
+ }
+ }
+
+ currentSubItem = 0;
+ }
+ }
+ queue = false;
+ tUpdateProgress->stop();
+ processedTime = 0;
+ processing = false;
+ emit processStopped();
+}
+
+void ReplayGainFileList::processOutput( KProcess* proc, char* data, int )
+{
+ int iPercent = 0, iTime = 0, iPos = 0, iNum = 0;
+
+ QString log_data = data;
+ log_data.replace("\n","\\n");
+ log_data.replace("\t","\\t");
+ log_data.replace("\r","\\r");
+ log_data.replace("\b","\\b");
+ logger->log( logID, " " + i18n("Output") + ": " + log_data );
+
+ ReplayGainPlugin* plugin = config->replaygainForFormat( currentItem->mimeType );
+ if( plugin == 0 ) { // shouldn't happen
+ logger->log( logID, " NULL POINTER: ReplayGainScanner::processOutput( ... ) / plugin" );
+ return;
+ }
+ if( plugin->info.name == i18n("built-in") ) { // shouldn't happen // TODO implement a check for this
+ logger->log( logID, " Backend is an encoder" );
+ return;
+ }
+
+ QString outputPattern = ( files > 1 ) ? plugin->replaygain.output_multiple : plugin->replaygain.output_single;
+ //outputPattern.replace( "%i", "%p" ); // for compatibility with old plugins
+
+ if( outputPattern.find("%p") != -1 || outputPattern.find("%a") != -1 ) {
+ outputPattern.replace( "%p", "%i" );
+ //outputPattern.replace( "%a", "%i" ); // for compatibility with old plugins
+ sscanf( data, outputPattern, &iPercent );
+ }
+ /*else if( outputPattern.find("%t") != -1 ) { // NOTE a little bit complicated and not necessary
+ outputPattern.replace( "%t", "%i" );
+ sscanf( data, outputPattern, &iTime );
+ iPercent = iTime * 100 / currentItem->time;
+ }*/
+ else if( outputPattern.find("%0") != -1 && outputPattern.find("%1") != -1 ) {
+ if( outputPattern.find("%0") < outputPattern.find("%1") ) {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iPos, &iNum );
+ }
+ else {
+ outputPattern.replace( "%0", "%i" );
+ outputPattern.replace( "%1", "%i" );
+ sscanf( data, outputPattern, &iNum, &iPos );
+ }
+ if( iPos != 0 && iNum != 0 ) iPercent = iPos * 100 / iNum;
+ }
+
+ if( iPercent > 0 && iPercent <= 100 )
+ {
+ // TODO guess progress, when no signal is received
+ //lastOutputTimer.start();
+ if( files > 1 ) {
+ if( iPercent < lastPercent ) file++;
+ lastPercent = iPercent;
+ percent = file * 100 / files + iPercent / files;
+ }
+ else {
+ percent = iPercent;
+ }
+ }
+}
+
+void ReplayGainFileList::processExit( KProcess* proc )
+{
+ logger->processCompleted( logID, ( proc->signalled() ) ? -1 : 0 );
+
+ for( ReplayGainFileListItem* item = firstChild(); item != 0; item = item->nextSibling() ) {
+ if( item->type() == ReplayGainFileListItem::File ) {
+ if( item->addingReplayGain ) {
+ processedTime += item->time;
+ item->addingReplayGain = false;
+ item->repaint();
+ item->updateReplayGainCells( tagEngine->readTags(KURL::decode_string(item->filePathName)) );
+ }
+ if( item->queued && proc->signalled() ) {
+ item->queued = false;
+ item->repaint();
+ }
+ }
+ else {
+ if( item->addingReplayGain ) {
+ item->addingReplayGain = false;
+ item->repaint();
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ processedTime += sub_item->time;
+ sub_item->addingReplayGain = false;
+ sub_item->repaint();
+ sub_item->updateReplayGainCells( tagEngine->readTags(KURL::decode_string(sub_item->filePathName)) );
+ }
+ }
+ if( item->queued && proc->signalled() ) {
+ item->queued = false;
+ item->repaint();
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ sub_item->queued = false;
+ sub_item->repaint();
+ }
+ }
+ for( ReplayGainFileListItem* sub_item = item->firstChild(); sub_item != 0; sub_item = sub_item->nextSibling() ) {
+ if( sub_item->addingReplayGain ) {
+ processedTime += sub_item->time;
+ sub_item->addingReplayGain = false;
+ sub_item->repaint();
+ sub_item->updateReplayGainCells( tagEngine->readTags(KURL::decode_string(sub_item->filePathName)) );
+ }
+ if( sub_item->queued && proc->signalled() ) {
+ sub_item->queued = false;
+ sub_item->repaint();
+ }
+ }
+ }
+ }
+ if( proc->signalled() ) {
+ queue = false;
+ tUpdateProgress->stop();
+ processedTime = 0;
+ processing = false;
+ emit processStopped();
+ return;
+ }
+ else {
+ processNextFile();
+ }
+}
+
+void ReplayGainFileList::update()
+{
+ emit updateProgress( int(processedTime) + percent * int(timeCount) / 100, int(time) );
+}
+
diff --git a/src/replaygainfilelist.h b/src/replaygainfilelist.h
new file mode 100755
index 0000000..a147c44
--- /dev/null
+++ b/src/replaygainfilelist.h
@@ -0,0 +1,242 @@
+
+
+#ifndef REPLAYGAINFILELIST_H
+#define REPLAYGAINFILELIST_H
+
+#include <klistview.h>
+
+#include <qdatetime.h>
+
+
+class TagEngine;
+class TagData;
+class ReplayGain;
+class Config;
+class Logger;
+
+class QSimpleRichText;
+class KProgress;
+
+class KPopupMenu;
+class KAction;
+class KActionCollection;
+class KProcess;
+
+// FIXME differ diffrent sampling rates, too!
+
+/**
+ * @short The items for the file list of the Replay Gain scanner
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ReplayGainFileListItem : public KListViewItem
+{
+public:
+ enum Type {
+ File,
+ Album
+ };
+
+ enum Mode {
+ remove = 0x0001,
+ force = 0x0002
+ };
+
+ /**
+ * Constructor
+ * @param parent The parent list view
+ */
+ ReplayGainFileListItem( QListView* parent );
+
+ /*
+ * Constructor
+ * @param parent The parent list view
+ * @param after The item, the new item should be placed after
+ */
+ //ReplayGainFileListItem( QListView* parent, QListViewItem* after );
+
+ /**
+ * Constructor
+ * @param parent The parent list view item
+ */
+ ReplayGainFileListItem( ReplayGainFileListItem* parent );
+
+ /**
+ * Destructor
+ */
+ virtual ~ReplayGainFileListItem();
+
+ virtual void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int alignment );
+
+ int compare( QListViewItem* item, int column, bool ascending ) const;
+
+ void updateReplayGainCells( TagData* );
+
+ ReplayGainFileListItem* firstChild() const { return static_cast<ReplayGainFileListItem*>( KListViewItem::firstChild() ); }
+ ReplayGainFileListItem* nextSibling() const { return static_cast<ReplayGainFileListItem*>( KListViewItem::nextSibling() ); }
+ //ReplayGainFileListItem* itemBelow() { return static_cast<ReplayGainFileListItem*>( KListViewItem::itemBelow() ); }
+ ReplayGainFileListItem* parent() const { return static_cast<ReplayGainFileListItem*>( KListViewItem::parent() ); }
+
+ Type type() { return m_type; }
+ void setType( Type );
+
+ // FIXME file list
+
+ /* TODO check sampling rate, too
+ * metaflac: 8, 11.025, 12, 16, 22.05, 24, 32, 44.1, or 48 kHz.
+ */
+
+ QString filePathName; // the path and name of the file
+ //QString fileName; // just the _name_ of the file
+ QString mimeType; // the mime type of the file / the mime type of all files in this album
+ QString fileFormat; // just the _format_ of the file / the format of all files in this album (for easier use)
+ QString originalFileFormat; // after renaming we need to re-rename the file
+ bool addingReplayGain; // are we adding replay gain tags at the moment?
+ bool queued; // is this item queued for adding/removing replay gain?
+ Mode mode;
+ float time; // the duration of the track, used for the calculation of the progress bar
+
+private:
+ Type m_type;
+};
+
+
+/**
+ * @short The file list of the Replay Gain scanner
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ReplayGainFileList : public KListView
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ * @param parent The parent widget
+ * @param name The name of the file list
+ */
+ ReplayGainFileList( TagEngine*, Config*, Logger*, QWidget *parent=0, const char *name=0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~ReplayGainFileList();
+
+ ReplayGainFileListItem* firstChild() const { return static_cast<ReplayGainFileListItem*>( KListView::firstChild() ); }
+ ReplayGainFileListItem* itemAt( const QPoint& point ) const { return static_cast<ReplayGainFileListItem*>( KListView::itemAt(point) ); }
+
+ int columnByName( const QString& name );
+
+protected:
+ virtual bool acceptDrag( QDropEvent *e ) const;
+
+private slots:
+ void columnResizeEvent( int, int, int );
+ void openAlbums();
+ void closeAlbums();
+ void update();
+
+public slots:
+ void addFile( const QString& );
+ void addDir( const QString&, const QStringList& filter = "", bool recursive = true );
+ void calcAllReplayGain( bool force );
+ void removeAllReplayGain();
+ void cancelProcess();
+
+private:
+ /** Lists all file in a directory and adds them to the file list, if @p fast is false. The number of listed files is returned */
+ int listDir( const QString& directory, QStringList filter, bool recursive = true, bool fast = false, int count = 0 );
+
+ /** A progressbar, that is shown, when a directory is added recursive */
+ KProgress* pScanStatus;
+
+ TagEngine* tagEngine;
+ Config* config;
+ Logger* logger;
+
+ KProcess* process;
+ ReplayGain* replayGain;
+ int logID;
+
+ void contentsDragEnterEvent( QDragEnterEvent *e );
+ void contentsDragMoveEvent( QDragMoveEvent *e );
+ void contentsDropEvent( QDropEvent *e );
+
+ void viewportPaintEvent( QPaintEvent* );
+ void viewportResizeEvent( QResizeEvent* );
+
+ QSimpleRichText* bubble;
+
+ void startProcess();
+
+ void processNextFile();
+
+ void calcReplayGain( ReplayGainFileListItem* );
+ void removeReplayGain( ReplayGainFileListItem* );
+
+ bool queue;
+ ReplayGainFileListItem::Mode mode;
+ ReplayGainFileListItem* currentItem;
+
+ QTimer* tUpdateProgress;
+ bool processing; // true, if the progress is active (hide some options in the context menu)
+ int percent; // the progress of the current file / album
+ int lastPercent; // cache the last percent in order to record a 'track change'
+ float time; // track length of all files
+ float processedTime; // the sum of all track lengths of the processed files
+ int files; // the number of files in the current album
+ int file; // the file that is being 'replay gained'
+ float timeCount; // the sum of all track lengths in the current album / the track length of the current file
+
+ /** The context menu for editing or starting the files */
+ KPopupMenu* contextMenu;
+
+ KActionCollection* actionCollection;
+ KAction* calc_gain;
+ KAction* remove_gain;
+ KAction* newalbum;
+ KAction* remove;
+ KAction* paste;
+ KAction* open_albums;
+ KAction* close_albums;
+
+private slots:
+ void showContextMenu( QListViewItem*, const QPoint&, int );
+
+ /**
+ * Remove selected items from the file list
+ */
+ void removeSelectedItems();
+
+ /**
+ * Creates a new 'album' item in the list view
+ */
+ void createNewAlbum();
+
+ /**
+ * Calculates the replay gain tags of the selected items
+ */
+ void calcSelectedItemsGain();
+
+ /**
+ * Remove the replay gain tags of the selected items
+ */
+ void removeSelectedItemsGain();
+
+ void slotDropped( QDropEvent*, QListViewItem*, QListViewItem* ); // NOTE rename?
+
+ void processOutput( KProcess*, char*, int );
+ void processExit( KProcess* );
+
+signals:
+ //void calcGain();
+ //void removeGain();
+// void addFile( const QString& filename );
+
+ void processStarted();
+ void processStopped();
+ void updateProgress( int, int );
+
+};
+
+#endif // REPLAYGAINFILELIST_H
diff --git a/src/replaygainscanner.cpp b/src/replaygainscanner.cpp
new file mode 100755
index 0000000..9eb0bcd
--- /dev/null
+++ b/src/replaygainscanner.cpp
@@ -0,0 +1,233 @@
+
+#include "replaygainscanner.h"
+#include "logger.h"
+#include "combobutton.h"
+#include "config.h"
+#include "dirdialog.h"
+
+#include <qlayout.h>
+#include <qstringlist.h>
+#include <qlabel.h>
+#include <qcheckbox.h>
+#include <qtooltip.h>
+
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kpushbutton.h>
+#include <kprogress.h>
+
+// FIXME file name encoding !!!
+
+
+ReplayGainScanner::ReplayGainScanner( TagEngine* _tagEngine, Config* _config, Logger* _logger, QWidget *parent, const char *name, bool modal, WFlags f )
+ : KDialog( parent, name, modal, f )
+{
+ tagEngine = _tagEngine;
+ config = _config;
+ logger = _logger;
+
+ // create an icon loader object for loading icons
+ KIconLoader* iconLoader = new KIconLoader();
+
+ setCaption( i18n("Replay Gain Tool") );
+ resize( 600, 400 );
+ setIcon( iconLoader->loadIcon("soundkonverter_replaygain",KIcon::Small) );
+
+ QGridLayout* grid = new QGridLayout( this, 4, 1, 11, 6 );
+
+ QHBoxLayout* filterBox = new QHBoxLayout();
+ grid->addLayout( filterBox, 0, 0 );
+
+ cAdd = new ComboButton( this, "cAdd" );
+ cAdd->insertItem( iconLoader->loadIcon("folder",KIcon::Small),i18n("Add Folder ...") );
+ cAdd->insertItem( iconLoader->loadIcon("sound",KIcon::Small),i18n("Add Files ...") );
+ filterBox->addWidget( cAdd );
+ connect( cAdd, SIGNAL(clicked(int)),
+ this, SLOT(addClicked(int))
+ );
+
+ filterBox->addStretch();
+
+ cForce = new QCheckBox( i18n("Force recalculation"), this, "cForce" );
+ QToolTip::add( cForce, i18n("Recalculate Replay Gain tag for files that already have a Replay Gain tag set.") );
+ filterBox->addWidget( cForce );
+
+ /*QLabel *lFilter=new QLabel(i18n("Show:"),this,"lFilter");
+ filterBox->addWidget(lFilter);
+
+ cFilter=new QComboBox(this,"cFilter");
+ cFilter->insertItem(i18n("All"));
+ cFilter->insertItem(i18n("With Replay Gain"));
+ cFilter->insertItem(i18n("Without Replay Gain"));
+ cFilter->insertItem(i18n("Unknown"));
+ filterBox->addWidget(cFilter);*/
+
+ lList = new ReplayGainFileList( tagEngine, config, logger, this, "lList" );
+ grid->addWidget( lList, 1, 0 );
+ connect( this, SIGNAL(addFile(const QString&)),
+ lList, SLOT(addFile(const QString&))
+ );
+ connect( this, SIGNAL(addDir(const QString&,const QStringList&,bool)),
+ lList, SLOT(addDir(const QString&,const QStringList&,bool))
+ );
+ connect( this, SIGNAL(calcAllReplayGain(bool)),
+ lList, SLOT(calcAllReplayGain(bool))
+ );
+ connect( this, SIGNAL(removeAllReplayGain()),
+ lList, SLOT(removeAllReplayGain())
+ );
+ connect( this, SIGNAL(cancelProcess()),
+ lList, SLOT(cancelProcess())
+ );
+ connect( lList, SIGNAL(processStarted()),
+ this, SLOT(processStarted())
+ );
+ connect( lList, SIGNAL(processStopped()),
+ this, SLOT(processStopped())
+ );
+ connect( lList, SIGNAL(updateProgress(int,int)),
+ this, SLOT(updateProgress(int,int))
+ );
+
+ QHBoxLayout* progressBox = new QHBoxLayout();
+ grid->addLayout( progressBox, 2, 0 );
+
+ pProgressBar = new KProgress( this, "pProgressBar" );
+ progressBox->addWidget( pProgressBar );
+
+ QHBoxLayout* buttonBox = new QHBoxLayout();
+ grid->addLayout( buttonBox, 3, 0 );
+
+ pTagVisible = new KPushButton( iconLoader->loadIcon("apply",KIcon::Small), i18n("Tag untagged"), this, "pTagVisible" );
+ QToolTip::add( pTagVisible, i18n("Calculate Replay Gain tag for all files in the file list without Replay Gain tag.") );
+ buttonBox->addWidget( pTagVisible );
+ connect( pTagVisible, SIGNAL(clicked()),
+ this, SLOT(calcReplayGainClicked())
+ );
+
+ pRemoveTag = new KPushButton( iconLoader->loadIcon("cancel",KIcon::Small), i18n("Untag tagged"), this, "pRemoveTag" );
+ QToolTip::add( pRemoveTag, i18n("Remove the Replay Gain tag from all files in the file list.") );
+ buttonBox->addWidget( pRemoveTag );
+ connect( pRemoveTag, SIGNAL(clicked()),
+ this, SLOT(removeReplayGainClicked())
+ );
+
+ pCancel = new KPushButton( iconLoader->loadIcon("cancel",KIcon::Small),i18n("Cancel"), this, "pCancel" );
+ pCancel->hide();
+ buttonBox->addWidget( pCancel );
+ connect( pCancel, SIGNAL(clicked()),
+ this, SLOT(cancelClicked())
+ );
+
+ buttonBox->addStretch();
+
+ pOk = new KPushButton( iconLoader->loadIcon("exit",KIcon::Small), i18n("Close"), this, "pOk" );
+ pOk->setFocus();
+ buttonBox->addWidget( pOk );
+ connect( pOk, SIGNAL(clicked()),
+ this, SLOT(accept())
+ );
+
+ // delete the icon loader object
+ delete iconLoader;
+}
+
+ReplayGainScanner::~ReplayGainScanner()
+{}
+
+void ReplayGainScanner::addClicked( int index )
+{
+ if( index == 1 ) {
+ showFileDialog();
+ }
+ else {
+ showDirDialog();
+ }
+}
+
+void ReplayGainScanner::showFileDialog()
+{
+ KFileDialog* dialog = new KFileDialog( ":file_open", config->replayGainFilter(), this, i18n("Choose files!"), true );
+ dialog->setMode ( KFile::Files | KFile::ExistingOnly );
+ if( dialog->exec() == KDialog::Accepted ) {
+ QStringList urls = dialog->selectedURLs().toStringList();
+ for( QStringList::Iterator it = urls.begin(); it != urls.end(); ++it ) {
+ emit addFile( *it );
+ }
+ }
+}
+
+void ReplayGainScanner::showDirDialog()
+{
+// QString directory = KFileDialog::getExistingDirectory( ":file_open", this, i18n("Choose a directory!") );
+// if( directory != NULL )
+// {
+// emit addDir( directory );
+// }
+
+ DirDialog *dialog = new DirDialog( config, DirDialog::ReplayGain, this, "DirDialog" );
+
+ Q_CHECK_PTR( dialog );
+
+ if( dialog->exec() ) {
+ emit addDir( dialog->directory, dialog->selectedFileTypes, dialog->recursive );
+ }
+
+ delete dialog;
+}
+
+void ReplayGainScanner::addFiles( QStringList files )
+{
+ for( QStringList::Iterator it = files.begin(); it != files.end(); ++it ) {
+ emit addFile( *it );
+ }
+}
+
+void ReplayGainScanner::calcReplayGainClicked()
+{
+ emit calcAllReplayGain( cForce->isChecked() );
+}
+
+void ReplayGainScanner::removeReplayGainClicked()
+{
+ emit removeAllReplayGain();
+}
+
+void ReplayGainScanner::cancelClicked()
+{
+ emit cancelProcess();
+}
+
+void ReplayGainScanner::processStarted()
+{
+ pTagVisible->hide();
+ pRemoveTag->hide();
+ pCancel->show();
+}
+
+void ReplayGainScanner::processStopped()
+{
+ pTagVisible->show();
+ pRemoveTag->show();
+ pCancel->hide();
+ pProgressBar->setProgress( 100 );
+ pProgressBar->setTotalSteps( 100 );
+ setCaption( i18n("Finished") + " - " + i18n("Replay Gain Tool") );
+}
+
+void ReplayGainScanner::updateProgress( int progress, int totalSteps )
+{
+/* pProgressBar->setProgress( int(processedTime) + percent * int(timeCount) / 100 );
+ pProgressBar->setTotalSteps( int(time) );*/
+ pProgressBar->setProgress( progress );
+ pProgressBar->setTotalSteps( totalSteps );
+ float fPercent;
+ if( pProgressBar->totalSteps() > 0 ) fPercent = pProgressBar->progress() * 100 / pProgressBar->totalSteps();
+ else fPercent = 0;
+
+ QString percent;
+ percent.sprintf( "%i%%", (int)fPercent );
+ setCaption( percent + " - " + i18n("Replay Gain Tool") );
+}
+
diff --git a/src/replaygainscanner.h b/src/replaygainscanner.h
new file mode 100755
index 0000000..6b2bfdc
--- /dev/null
+++ b/src/replaygainscanner.h
@@ -0,0 +1,77 @@
+
+
+#ifndef REPLAYGAINSCANNER_H
+#define REPLAYGAINSCANNER_H
+
+#include <kdialog.h>
+
+#include "replaygainfilelist.h"
+
+class Config;
+class Logger;
+class ComboButton;
+
+class QCheckBox;
+
+class KPushButton;
+class KProgress;
+
+
+/**
+ * @short The Replay Gain Tool
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class ReplayGainScanner : public KDialog
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ ReplayGainScanner( TagEngine*, Config*, Logger*, QWidget* parent=0, const char* name=0, bool modal=true, WFlags f=0 );
+
+ /**
+ * Destructor
+ */
+ virtual ~ReplayGainScanner();
+
+ void addFiles( QStringList );
+
+private slots:
+ void addClicked( int );
+ void showDirDialog();
+ void showFileDialog();
+ void calcReplayGainClicked();
+ void removeReplayGainClicked();
+ void cancelClicked();
+ void processStarted();
+ void processStopped();
+ void updateProgress( int, int );
+
+private:
+ ComboButton* cAdd;
+ QCheckBox* cForce;
+ //QComboBox* cFilter;
+ ReplayGainFileList* lList;
+ KProgress* pProgressBar;
+ KPushButton* pTagVisible;
+ KPushButton* pRemoveTag;
+ KPushButton* pCancel;
+ KPushButton* pOk;
+
+ TagEngine* tagEngine;
+ Config* config;
+ Logger* logger;
+
+ QTime elapsedTime;
+
+signals:
+ void addFile( const QString& );
+ void addDir( const QString&, const QStringList& filter = "", bool recursive = true );
+ void calcAllReplayGain( bool force );
+ void removeAllReplayGain();
+ void cancelProcess();
+};
+
+#endif // REPLAYGAINSCANNER_H
diff --git a/src/soundkonverter.cpp b/src/soundkonverter.cpp
new file mode 100755
index 0000000..692f898
--- /dev/null
+++ b/src/soundkonverter.cpp
@@ -0,0 +1,780 @@
+
+#include "soundkonverter.h"
+#include "config.h"
+#include "configdialog.h"
+#include "logger.h"
+#include "logviewer.h"
+#include "options.h"
+#include "filelist.h"
+#include "progressindicator.h"
+#include "cdmanager.h"
+#include "cdopener.h"
+#include "tagengine.h"
+#include "combobutton.h"
+#include "convert.h"
+#include "progressindicator.h"
+#include "replaygainscanner.h"
+#include "cuesheeteditor.h"
+// ### #include "aboutplugins.h" delayed to a future soundkonverter version
+#include "optionsrequester.h"
+#include "dirdialog.h"
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kfiledialog.h>
+#include <kinputdialog.h>
+#include <kpushbutton.h>
+#include <kpopupmenu.h>
+//#include <kmenubar.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kedittoolbar.h>
+#include <kmessagebox.h>
+#include <kstddirs.h>
+#include <kio/job.h>
+#include <kprocess.h>
+#include <ksystemtray.h>
+
+#include <qlayout.h>
+#include <qprogressbar.h>
+#include <qfont.h>
+#include <qhbox.h>
+#include <qeventloop.h>
+
+// TODO stop all processes on exit / clean tmp up
+
+// ### soundkonverter 0.4: save file list when kde session gets terminated
+
+soundKonverter::soundKonverter()
+ : KMainWindow( 0, "soundkonverter-mainwindow", WType_TopLevel | WDestructiveClose | WStyle_ContextHelp ),
+ DCOPObject( "soundkonverter-app" )
+{
+ // this is our first startup, set instances to zero
+ instances = 0;
+
+ replayGainScanner = 0;
+ cuesheetEditor = 0;
+ logViewer = 0;
+
+ device = "";
+ visible = true;
+ autoclose = false;
+ configStartPage = ConfigDialog::GeneralPage;
+
+ logger = new Logger();
+ logger->log( 1000, "This is soundKonverter 0.3.7" );
+ #ifdef HAVE_MP4V2
+ logger->log( 1000, "MP4v2 is enabled" );
+ #else
+ logger->log( 1000, "MP4v2 is disabled" );
+ #endif
+
+ config = new Config( logger );
+ config->read();
+
+ // (re)move the 0.2 config file
+ if( config->data.app.configVersion < 280 ) {
+ KIO::rename( locateLocal("config","soundkonverterrc"), locateLocal("config","soundkonverterrc.back"), true );
+// KMessageBox::information( this, "You are using a pre-release of soundKonverter. This version is designed for testing only.\nThere are a few things not implemented at the moment but it should run without crashes and big problems. So feel free to report such an error if you find one.\nPlease have a look at the README.\n\nYour old configuration file has been moved to soundkonverterrc.back." );
+ }
+// if( config->data.app.configVersion < 290 ) {
+// QFile plugin_dir( locateLocal("data","soundkonverter/plugins") );
+// if( plugin_dir.exists() ) {
+// KIO::rename( locateLocal("data","soundkonverter/plugins"), locateLocal("data","soundkonverter/plugins.back"), true );
+// int ret = KMessageBox::questionYesNo( this, i18n("soundKonverter has cleaned up it's old installation. It seems to be necessary to restart soundKonverter!\n\nDo you want to restart soundKonverter now?") );
+// if( ret == KMessageBox::Yes ) {
+// config->write();
+// // TODO parameters are lost
+// KProcess restart;
+// restart << "soundkonverter";
+// restart.start( KProcess::DontCare );
+// kapp->quit();
+// return;
+// }
+// }
+// }
+
+ cdManager = new CDManager();
+ tagEngine = new TagEngine();
+
+ resize( 640, 470 );
+
+ systemTray = new KSystemTray( this, "systemTray" );
+ systemTray->hide();
+
+ // create an icon loader object for loading icons
+ KIconLoader *iconLoader = new KIconLoader();
+
+ QWidget* widget = new QWidget( this, "widget" );
+ setCentralWidget( widget );
+
+ // NOTE created here because of some dependences
+ options = new Options( config, i18n("Choose your prefered output options and click on \"Add files ...\"!"), widget, "options" );
+ fileList = new FileList( cdManager, tagEngine, config, options, logger, widget, "fileList" );
+
+ startAction = new KAction( i18n("&Start conversion"), "run", 0, fileList, SLOT(startConversion()), actionCollection(), "start" );
+ startAction->setEnabled( false );
+ new KAction( i18n("&Replay Gain Tool ..."), "soundkonverter_replaygain", CTRL+Key_R, this, SLOT(showReplayGainScanner()), actionCollection(), "replaygainscanner" );
+ //new KAction( i18n("R&epair Tool ..."), "soundkonverter_repair", CTRL+Key_E, this, SLOT(showRepairTool()), actionCollection(), "repairtool" );
+ new KAction( i18n("C&uesheet Editor ..."), "kwrite", CTRL+Key_U, this, SLOT(showCuesheetEditor()), actionCollection(), "cuesheeteditor" );
+ new KAction( i18n("Show &Log ..."), "view_text", CTRL+Key_L, this, SLOT(showLogViewer()), actionCollection(), "log" );
+// new KAction( i18n("About &Plugins ..."), "connect_creating", CTRL+Key_P, this, SLOT(showAboutPlugins()), actionCollection(), "about_plugins" );
+
+ stopAction = new KAction( i18n("S&top after current file is complete"), "stop", CTRL+Key_O, fileList, SLOT(stopConversion()), actionCollection(), "stop" );
+ continueAction = new KAction( i18n("&Continue after current file is complete"), "run", CTRL+Key_T, fileList, SLOT(continueConversion()), actionCollection(), "continue" );
+ killAction = new KAction( i18n("Stop &immediately"), "exit", CTRL+Key_K, fileList, SLOT(killConversion()), actionCollection(), "kill" );
+ stopActionMenu = new KActionMenu( i18n("Stop"), "stop", actionCollection(), "stopMenu" );
+ stopActionMenu->setDelayed( false );
+ stopActionMenu->setEnabled( false );
+// stopActionMenu->insert( stopAction );
+// stopActionMenu->insert( continueAction );
+// stopActionMenu->insert( killAction );
+
+/* veryHighPriorityAction = new KToggleAction( i18n("Very hi&gh"), CTRL+Key_1, this, SLOT(priorityChanged()), actionCollection(), "veryhigh" );
+ veryHighPriorityAction->setExclusiveGroup("priorityActionMenu");
+ highPriorityAction = new KToggleAction( i18n("&High"), CTRL+Key_2, this, SLOT(priorityChanged()), actionCollection(), "high" );
+ highPriorityAction->setExclusiveGroup("priorityActionMenu");
+ normalPriorityAction = new KToggleAction( i18n("&Normal"), CTRL+Key_3, this, SLOT(priorityChanged()), actionCollection(), "nomal" );
+ normalPriorityAction->setExclusiveGroup("priorityActionMenu");
+ normalPriorityAction->setChecked(true);
+ lowPriorityAction = new KToggleAction( i18n("&Low"), CTRL+Key_4, this, SLOT(priorityChanged()), actionCollection(), "low" );
+ lowPriorityAction->setExclusiveGroup("priorityActionMenu");
+ veryLowPriorityAction = new KToggleAction( i18n("Very lo&w"), CTRL+Key_5, this, SLOT(priorityChanged()), actionCollection(), "verylow" );
+ veryLowPriorityAction->setExclusiveGroup("priorityActionMenu");
+ priorityActionMenu = new KActionMenu( i18n("En-/Decoder priority"), "ksysguard", actionCollection(), "priorityMenu" );
+ priorityActionMenu->setDelayed( false );
+ priorityActionMenu->insert(veryHighPriorityAction);
+ priorityActionMenu->insert(highPriorityAction);
+ priorityActionMenu->insert(normalPriorityAction);
+ priorityActionMenu->insert(lowPriorityAction);
+ priorityActionMenu->insert(veryLowPriorityAction);*/
+ //priorityChanged();
+
+ new KAction( i18n("A&dd Files ..."), "sound", CTRL+Key_D, this, SLOT(showFileDialog()), actionCollection(), "add_files" );
+ new KAction( i18n("Add &Folder ..."), "folder", CTRL+Key_F, this, SLOT(showDirDialog()), actionCollection(), "add_folder" );
+ new KAction( i18n("Add CD &tracks ..."), "cdaudio_unmount", CTRL+Key_T, this, SLOT(showCdDialog()), actionCollection(), "add_audiocd" );
+ new KAction( i18n("Add &URL ..."), "browser", CTRL+Key_U, this, SLOT(showUrlDialog()), actionCollection(), "add_url" );
+ KStdAction::quit(this, SLOT(close()), actionCollection());
+
+ new KAction( i18n("L&oad file list"), "fileopen", 0, fileList, SLOT(load()), actionCollection(), "load" );
+ new KAction( i18n("Sa&ve file list"), "filesave", 0, fileList, SLOT(save()), actionCollection(), "save" );
+
+ // TODO //KStdAction::paste( this, SLOT(pasteFiles()), actionCollection() );
+
+ KStdAction::preferences( this, SLOT(showConfigDialog()), actionCollection() );
+
+ showToolBarAction = KStdAction::showToolbar( this, SLOT(showToolbar()), actionCollection() );
+ //KStdAction::showStatusbar( 0, 0, actionCollection() );
+ KStdAction::configureToolbars( this, SLOT(editToolbar()), actionCollection() );
+
+ createGUI();
+
+ if( config->data.general.showToolBar ) {
+ toolBar()->show();
+ showToolBarAction->setChecked( true );
+ }
+ else {
+ toolBar()->hide();
+ showToolBarAction->setChecked( false );
+ }
+
+ // the grid for all widgets in the main window
+ QGridLayout* gridLayout = new QGridLayout( widget, 1, 1, 6, 6, "gridLayout" );
+
+ // generate the options input area
+ // NOTE created above because of some dependences
+ //options = new Options( config, i18n("Choose your prefered output options and click on \"Add files ...\"!"), this, "options" );
+ connect( options, SIGNAL(showConfigPluginsPage()),
+ this, SLOT(showConfigPluginsPage())
+ );
+ connect( options, SIGNAL(showConfigEnvironmentPage()),
+ this, SLOT(showConfigEnvironmentPage())
+ );
+ gridLayout->addWidget( options, 0, 0 );
+
+ // generate the list view for the file list
+ // NOTE created above because of some dependences
+ //fileList = new FileList( cdManager, tagEngine, config, options, this, "fileList" );
+ gridLayout->addWidget( fileList, 1, 0 );
+ gridLayout->setRowStretch( 1, 1 );
+ /*connect( this, SIGNAL(windowMoved(int,int)),
+ fileList, SLOT(moveOptionsEditor(int,int))
+ );*/
+ connect( fileList, SIGNAL(fileCountChanged(int)),
+ this, SLOT(fileCountChanged(int))
+ );
+ connect( fileList, SIGNAL(startedConversion()),
+ this, SLOT(startedConversion())
+ );
+ connect( fileList, SIGNAL(stopClicked()),
+ this, SLOT(stopClicked())
+ );
+ connect( fileList, SIGNAL(continueClicked()),
+ this, SLOT(continueClicked())
+ );
+ connect( fileList, SIGNAL(stoppedConversion()),
+ this, SLOT(stoppedConversion())
+ );
+
+ convert = new Convert( config, tagEngine, cdManager, fileList, logger );
+// connect( this, SIGNAL(setPriority(int)),
+// convert, SLOT(priorityChanged(int))
+// );
+
+ // add a horizontal box layout for the add combobutton to the grid
+ QHBoxLayout *addBox = new QHBoxLayout( -1, "addBox" );
+ gridLayout->addLayout( addBox, 2, 0 );
+
+ // create the combobutton for adding files to the file list
+ cAdd = new ComboButton( widget, "cAdd" );
+ QFont font = cAdd->font();
+ //font.setWeight( QFont::DemiBold );
+ font.setPointSize( font.pointSize() + 1 );
+ cAdd->setFont( font );
+ cAdd->insertItem( iconLoader->loadIcon("sound",KIcon::Toolbar), i18n("Add files ...") );
+ cAdd->insertItem( iconLoader->loadIcon("folder",KIcon::Toolbar), i18n("Add folder ...") );
+ cAdd->insertItem( iconLoader->loadIcon("cdaudio_unmount",KIcon::Toolbar), i18n("Add CD tracks ...") );
+ cAdd->insertItem( iconLoader->loadIcon("browser",KIcon::Toolbar), i18n("Add URL ...") );
+ addBox->addWidget( cAdd );
+ connect( cAdd, SIGNAL(clicked(int)),
+ this, SLOT(addClicked(int))
+ );
+
+ addBox->addSpacing( 18 );
+
+ pStart = new KPushButton( iconLoader->loadIcon("run",KIcon::Small), i18n("Start"), widget, "pStart" );
+ pStart->setFixedHeight( pStart->size().height() );
+ pStart->setEnabled( false );
+ addBox->addWidget( pStart );
+ connect( pStart, SIGNAL(clicked()),
+ fileList, SLOT(startConversion())
+ );
+
+ pStop = new KPushButton( iconLoader->loadIcon("stop",KIcon::Small), i18n("Stop"), widget, "pStop" );
+ pStop->setFixedHeight( pStop->size().height() );
+ pStop->hide();
+ pStop->setPopup( stopActionMenu->popupMenu() );
+ addBox->addWidget( pStop );
+
+ addBox->addSpacing( 8 );
+
+ progressIndicator = new ProgressIndicator( systemTray, widget, "progressIndicator" );
+ addBox->addWidget( progressIndicator );
+ connect( fileList, SIGNAL(increaseTime(float)),
+ progressIndicator, SLOT(increaseTime(float))
+ );
+ connect( fileList, SIGNAL(decreaseTime(float)),
+ progressIndicator, SLOT(decreaseTime(float))
+ );
+ /*connect( fileList, SIGNAL(setTime(float)),
+ progressIndicator, SLOT(setTime(float))
+ );*/
+ connect( fileList, SIGNAL(finished(float)),
+ progressIndicator, SLOT(finished(float))
+ );
+ connect( convert, SIGNAL(countTime(float)),
+ progressIndicator, SLOT(countTime(float))
+ );
+ connect( convert, SIGNAL(uncountTime(float)),
+ progressIndicator, SLOT(uncountTime(float))
+ );
+ connect( convert, SIGNAL(update(float)),
+ progressIndicator, SLOT(update(float))
+ );
+ connect( progressIndicator, SIGNAL(setTitle(const QString&)),
+ this, SLOT(setTitle(const QString&))
+ );
+
+ cAdd->increaseHeight( /*progressIndicator->height() - cAdd->height()*/ 10 ); // FIXME detect the height automaticly
+ cAdd->adjustSize();
+
+ // delete the icon loader object
+ delete iconLoader;
+
+ fileList->load( true ); // restore file list after a crash
+}
+
+soundKonverter::~soundKonverter()
+{
+ // TODO clean tmp dir (stop all running processes)
+ delete replayGainScanner;
+ delete cuesheetEditor;
+ delete logViewer;
+}
+
+void soundKonverter::increaseInstances()
+{
+ // a new instance should be created, so this isn't the initial startup no longer
+ instances++;
+}
+
+bool soundKonverter::queryClose()
+{
+ config->removeProfile( i18n("Last used") );
+ config->addProfile( i18n("Last used"), options->getCurrentOptions() );
+
+ config->write( false );
+
+ convert->cleanUp();
+ logger->cleanUp();
+
+ QFile::remove( locateLocal("data","soundkonverter/filelist.autosave.xml") );
+
+ return true;
+}
+
+void soundKonverter::setNotify( const QString& cmd )
+{
+ fileList->setNotify( cmd );
+}
+
+/*void soundKonverter::moveEvent( QMoveEvent* e )
+{
+// emit windowMoved( pos().x(), pos().y() + height() );
+}*/
+
+void soundKonverter::openArgFiles( const QStringList &files )
+{
+ if( ( instances <= 1 || config->data.general.askForNewOptions ) && ( profile == "" || format == "" || directory == "" ) )
+ {
+ OptionsRequester* dialog = new OptionsRequester( config, files, this );
+
+ connect( dialog, SIGNAL(setCurrentOptions(const ConversionOptions&)),
+ options, SLOT(setCurrentOptions(const ConversionOptions&))
+ );
+ connect( dialog, SIGNAL(addFiles(QStringList)),
+ fileList, SLOT(addFiles(QStringList))
+ );
+
+ Q_CHECK_PTR( dialog );
+
+ if( profile != "" ) {
+ dialog->setProfile( profile );
+ profile = "";
+ }
+ if( format != "" ) {
+ dialog->setFormat( format );
+ format = "";
+ }
+ if( directory != "" ) {
+ dialog->setOutputDirectory( directory );
+ directory = "";
+ }
+
+ dialog->exec();
+
+ disconnect( dialog, SIGNAL(setCurrentOptions(const ConversionOptions&)), 0, 0 );
+ disconnect( dialog, SIGNAL(addFiles(QStringList)), 0, 0 );
+
+ delete dialog;
+ }
+ else
+ {
+ ConversionOptions conversionOptions = options->getCurrentOptions();
+
+ if( profile != "" ) {
+ options->setProfile( profile );
+ profile = "";
+ }
+ if( format != "" ) {
+ options->setFormat( format );
+ format = "";
+ }
+ if( directory != "" ) {
+ options->setOutputDirectory( directory );
+ directory = "";
+ }
+ fileList->addFiles( files );
+
+ options->setCurrentOptions( conversionOptions );
+ }
+
+ if( !visible ) fileList->startConversion(); // NOTE should be save!
+}
+
+void soundKonverter::openArgReplayGainFiles( const QStringList &files )
+{
+ showReplayGainScanner();
+ if( replayGainScanner ) replayGainScanner->addFiles( files );
+}
+
+// void soundKonverter::openArgRepairFiles( const QStringList &files )
+// {}
+
+void soundKonverter::startedConversion()
+{
+ pStart->hide();
+ if( fileList->queueEnabled() )
+ {
+ startAction->setEnabled( false );
+ startAction->setText( i18n("&Start conversion") );
+ stopActionMenu->remove( startAction );
+ stopActionMenu->remove( stopAction );
+ stopActionMenu->remove( continueAction );
+ stopActionMenu->remove( killAction );
+ stopActionMenu->insert( stopAction );
+ stopActionMenu->insert( continueAction );
+ stopActionMenu->insert( killAction );
+ }
+ else
+ {
+ startAction->setEnabled( true );
+ startAction->setText( i18n("&Start queue") );
+ stopActionMenu->remove( startAction );
+ stopActionMenu->remove( stopAction );
+ stopActionMenu->remove( continueAction );
+ stopActionMenu->remove( killAction );
+ stopActionMenu->insert( killAction );
+ stopActionMenu->insert( startAction );
+ }
+ pStop->show();
+ stopActionMenu->setEnabled( true );
+ stopAction->setEnabled( true );
+ continueAction->setEnabled( false );
+ killAction->setEnabled( true );
+}
+
+void soundKonverter::stopClicked()
+{
+ stopAction->setEnabled( false );
+ continueAction->setEnabled( true );
+}
+
+void soundKonverter::continueClicked()
+{
+ stopAction->setEnabled( true );
+ continueAction->setEnabled( false );
+}
+
+void soundKonverter::stoppedConversion()
+{
+ pStart->show();
+ startAction->setEnabled( true );
+ startAction->setText( i18n("&Start conversion") );
+ pStop->hide();
+ stopActionMenu->setEnabled( false );
+
+ if( autoclose ) kapp->quit(); // NOTE close app on conversion stop (may irritate the user when stopping the conversion)
+}
+
+// void soundKonverter::priorityChanged()
+// {
+// int priority = 0;
+//
+// //if( veryHighPriorityAction->isChecked() ) priority=-19;
+// //else if( highPriorityAction->isChecked() ) priority=-10;
+// if( lowPriorityAction->isChecked() ) priority = 10;
+// else if( veryLowPriorityAction->isChecked() ) priority = 20;
+//
+// emit setPriority( priority );
+// }
+
+void soundKonverter::addClicked( int index )
+{
+ fileList->save( true );
+
+ if( index == 0 ) {
+ showFileDialog();
+ }
+ else if( index == 1 ) {
+ showDirDialog();
+ }
+ else if( index == 2 ) {
+ showCdDialog( true );
+ }
+ else {
+ showUrlDialog();
+ }
+
+ fileList->save( true );
+}
+
+void soundKonverter::showFileDialog()
+{
+ QStringList urls = KFileDialog::getOpenURLs( ":file_open", config->fileFilter(), this, i18n("Choose files to convert") ).toStringList();
+ if( !urls.empty() ) {
+/* for( QStringList::Iterator it = urls.begin(); it != urls.end(); ++it ) {
+ *it = KURL::decode_string( *it );
+ }*/
+ fileList->addFiles( urls );
+ }
+}
+
+void soundKonverter::showDirDialog()
+{
+// QString directory = KFileDialog::getExistingDirectory( ":file_open", this, i18n("Choose a directory") );
+// if( !directory.isEmpty() )
+// {
+// fileList->addDir( directory );
+// }
+
+ DirDialog *dialog = new DirDialog( config, DirDialog::Convert, this, "DirDialog" );
+
+ Q_CHECK_PTR( dialog );
+
+ if( dialog->exec() ) {
+ fileList->addDir( dialog->directory, dialog->selectedFileTypes, dialog->recursive );
+ }
+
+ delete dialog;
+}
+
+void soundKonverter::showCdDialog( bool intern )
+{
+ ConversionOptions conversionOptions = options->getCurrentOptions();
+
+ if( ( instances <= 1 || config->data.general.askForNewOptions ) && ( profile == "" || format == "" || directory == "" ) && !intern )
+ {
+ OptionsRequester* dialog = new OptionsRequester( config, "", this );
+
+ connect( dialog, SIGNAL(setCurrentOptions(const ConversionOptions&)),
+ options, SLOT(setCurrentOptions(const ConversionOptions&))
+ );
+// connect( dialog, SIGNAL(addFiles(QStringList)),
+// fileList, SLOT(addFiles(QStringList))
+// );
+
+ Q_CHECK_PTR( dialog );
+
+ if( profile != "" ) {
+ dialog->setProfile( profile );
+ profile = "";
+ }
+ if( format != "" ) {
+ dialog->setFormat( format );
+ format = "";
+ }
+ if( directory != "" ) {
+ dialog->setOutputDirectory( directory );
+ directory = "";
+ }
+
+ dialog->exec();
+
+ disconnect( dialog, SIGNAL(setCurrentOptions(const ConversionOptions&)), 0, 0 );
+// disconnect( dialog, SIGNAL(addFiles(QStringList)), 0, 0 );
+
+ delete dialog;
+ }
+ else
+ {
+ if( profile != "" ) {
+ options->setProfile( profile );
+ profile = "";
+ }
+ if( format != "" ) {
+ options->setFormat( format );
+ format = "";
+ }
+ if( directory != "" ) {
+ options->setOutputDirectory( directory );
+ directory = "";
+ }
+ }
+
+ // support for media:/ and system:/ urls
+ if( !device.isEmpty() ) {
+ device.replace( "system:/media", "/dev" );
+ device.replace( "media:", "/dev" );
+ device.replace( "devices:", "/dev" ); // NOTE obsolete, since soundkonverter 0.3 won't run on KDE < 3.5
+ }
+ if( device.left(5) != "/dev/" || device == "auto" ) {
+ device = "";
+ }
+
+ kapp->eventLoop()->exitLoop();
+
+ // create a new CDOpener object for letting the user add some tracks from a CD
+ CDOpener *dialog = new CDOpener( config, cdManager, tagEngine, device, this, "CDOpener" );
+
+ device = "";
+
+ Q_CHECK_PTR( dialog );
+
+ if( !dialog->noCD )
+ {
+ connect( dialog, SIGNAL(addTracks(const QString&,QValueList<int>)),
+ fileList, SLOT(addTracks(const QString&,QValueList<int>))
+ );
+ connect( dialog, SIGNAL(addDisc(const QString&)),
+ fileList, SLOT(addDisc(const QString&))
+ );
+ /*connect( dialog, SIGNAL(openCuesheetEditor(const QString&)),
+ this, SLOT(openCuesheetEditor(const QString&))
+ );*/
+
+ dialog->exec();
+
+ disconnect( dialog, SIGNAL(addTracks(const QString&,QValueList<int>)), 0, 0 );
+ disconnect( dialog, SIGNAL(addDisc(const QString&)), 0, 0 );
+ //disconnect( dialog, SIGNAL(openCuesheetEditor(const QString&)), 0, 0 );
+ }
+ delete dialog;
+
+ kapp->eventLoop()->enterLoop();
+
+ options->setCurrentOptions( conversionOptions );
+}
+
+void soundKonverter::showUrlDialog()
+{
+ bool ok;
+ QString url = KInputDialog::getText( i18n("Open URL"), i18n("Enter a URL:"), "", &ok );
+ if( ok )
+ {
+ // if it isn't a local file and no protocol is given, we assume, it's http
+ if( url.left(1) != "/" && !url.contains(":/") )
+ url.prepend( "http://" );
+
+ fileList->addFiles( url );
+ }
+}
+
+void soundKonverter::showReplayGainScanner()
+{
+ if( replayGainScanner == 0 ) {
+ replayGainScanner = new ReplayGainScanner( tagEngine, config, logger, 0, "replayGainScanner", false );
+ if( replayGainScanner == 0 ) {
+ // TODO error message
+ return;
+ }
+// connect( this, SIGNAL(addFilesToReplayGainScanner(QStringList)),
+// replayGainScanner, SLOT(addFiles(QStringList))
+// );
+ }
+ replayGainScanner->show();
+ replayGainScanner->raise();
+}
+
+void soundKonverter::showRepairTool()
+{}
+
+void soundKonverter::showCuesheetEditor()
+{
+ if( cuesheetEditor == 0 ) {
+ cuesheetEditor = new CuesheetEditor( 0, "cuesheetEditor", false );
+ if( cuesheetEditor == 0 ) {
+ // TODO error message
+ return;
+ }
+ }
+ cuesheetEditor->show();
+ cuesheetEditor->raise();
+}
+
+/*void soundKonverter::openCuesheetEditor( const QString& content )
+{
+ if( cuesheetEditor == 0 ) {
+ cuesheetEditor = new CuesheetEditor( 0, "cuesheetEditor", false );
+ if( cuesheetEditor == 0 ) {
+ // TODO error message
+ return;
+ }
+ }
+ cuesheetEditor->setContent( content );
+ cuesheetEditor->show();
+}*/
+
+void soundKonverter::showLogViewer()
+{
+ if( logViewer == 0 ) {
+ logViewer = new LogViewer( logger, 0, "logViewer", false );
+ if( logViewer == 0 ) {
+ // TODO error message
+ return;
+ }
+// connect( convert, SIGNAL(finishedProcess(int,int)),
+// logViewer, SLOT(processCompleted(int,int))
+// );
+ }
+ logViewer->show();
+ logViewer->raise();
+}
+
+void soundKonverter::showConfigDialog()
+{
+ ConfigDialog *dialog = new ConfigDialog( config, this, "ConfigDialog", ConfigDialog::Page(configStartPage) );
+
+ Q_CHECK_PTR( dialog );
+
+ //dialog->setGeometry( frameGeometry() );
+ dialog->resize( size() );
+
+ dialog->exec();
+
+ delete dialog;
+
+ configStartPage = ConfigDialog::GeneralPage;
+}
+
+void soundKonverter::showConfigPluginsPage()
+{
+ configStartPage = ConfigDialog::PluginsPage;
+ showConfigDialog();
+}
+
+void soundKonverter::showConfigEnvironmentPage()
+{
+ configStartPage = ConfigDialog::EnvironmentPage;
+ showConfigDialog();
+}
+
+// void soundKonverter::showAboutPlugins()
+// {
+// AboutPlugins *dialog = new AboutPlugins( config, this, "AboutPlugins" );
+//
+// Q_CHECK_PTR( dialog );
+//
+// dialog->exec();
+//
+// delete dialog;
+// }
+
+void soundKonverter::showToolbar()
+{
+ if( showToolBarAction->isChecked() ) {
+ toolBar()->show();
+ config->data.general.showToolBar = true;
+ }
+ else {
+ toolBar()->hide();
+ config->data.general.showToolBar = false;
+ }
+}
+
+void soundKonverter::editToolbar()
+{
+ saveMainWindowSettings( kapp->config(), "MainWindow" );
+ KEditToolbar dlg( actionCollection() );
+ connect( &dlg, SIGNAL(newToolbarConfig()),
+ this, SLOT(newToolbarConfig())
+ );
+ dlg.exec();
+}
+
+void soundKonverter::newToolbarConfig()
+{
+ createGUI();
+ applyMainWindowSettings( kapp->config(), "MainWindow" );
+}
+
+void soundKonverter::fileCountChanged( int count )
+{
+ if( count > 0 ) {
+ startAction->setEnabled( true );
+ pStart->setEnabled( true );
+ }
+ else {
+ startAction->setEnabled( false );
+ pStart->setEnabled( false );
+ }
+}
+
+void soundKonverter::setTitle( const QString& title )
+{
+ setCaption( title );
+}
+
+
+#include "soundkonverter.moc"
diff --git a/src/soundkonverter.desktop b/src/soundkonverter.desktop
new file mode 100755
index 0000000..967b83a
--- /dev/null
+++ b/src/soundkonverter.desktop
@@ -0,0 +1,15 @@
+[Desktop Entry]
+Categories=Qt;KDE;AudioVideo;AudioVideoEditing
+Encoding=UTF-8
+Name=soundKonverter
+Exec=soundkonverter
+Icon=soundkonverter
+Type=Application
+X-DCOP-ServiceType=Unique
+Comment=An audio converter and CD ripper
+Comment[de]=Ein Audiokonverter und CD Ripper
+Comment[fr]=Convertisseur de fichiers audio
+GenericName=Audio files converter and CD ripper
+GenericName[de]=Audiodateien Konvertierer und CD Ripper
+GenericName[fr]=Convertisseur Audio
+Terminal=false
diff --git a/src/soundkonverter.h b/src/soundkonverter.h
new file mode 100755
index 0000000..ce441cf
--- /dev/null
+++ b/src/soundkonverter.h
@@ -0,0 +1,206 @@
+
+
+#ifndef SOUNDKONVERTER_H
+#define SOUNDKONVERTER_H
+
+#include "dcopinterface.h"
+
+#include <kmainwindow.h>
+
+class Config;
+class Options;
+class FileList;
+class ProgressIndicator;
+class ComboButton;
+class CDManager;
+class TagEngine;
+class Convert;
+class ReplayGainScanner;
+class CuesheetEditor;
+class Logger;
+class LogViewer;
+
+class QProgressBar;
+class KPushButton;
+class KAction;
+class KToggleAction;
+class KActionMenu;
+class KSystemTray;
+
+/**
+ * @short The main window and connections to all other classes
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class soundKonverter : public KMainWindow, virtual public DCOPInterface
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor
+ */
+ soundKonverter();
+
+ /**
+ * Destructor
+ */
+ virtual ~soundKonverter();
+
+ /**
+ * When a new instance of soundKonverter should be created,
+ * this function is called and all @p files are passed, that should be opened (for conversion).
+ */
+ void openArgFiles( const QStringList &files );
+
+ /**
+ * When a new instance of soundKonverter should be created,
+ * this function is called and all @p files are passed, that should be opened for editing the replaygain tag.
+ */
+ void openArgReplayGainFiles( const QStringList &files );
+
+ /*
+ * When a new instance of soundKonverter should be created,
+ * this function is called and all @p files are passed, that should be opened for repair.
+ */
+// void openArgRepairFiles( const QStringList &files );
+
+ /**
+ * When a new instance of soundKonverter should be created,
+ * this function is called and instances is increased.
+ */
+ void increaseInstances();
+
+ /**
+ * Returns the number of 'started' instances
+ */
+ int getInstances() { return instances; }
+
+ QString device;
+ QString format;
+ QString profile;
+ QString directory;
+ bool visible;
+ bool autoclose;
+ void setNotify( const QString& cmd );
+
+ /** A system tray icon that is shown, when the main window is hidden */
+ KSystemTray* systemTray;
+
+ //virtual void moveEvent( QMoveEvent* );
+
+protected:
+ virtual bool queryClose();
+
+private:
+ /** Saves the amount of calls to create a new instance (only one instance is running at the same time) */
+ int instances;
+
+ /** All configuration information */
+ Config* config;
+
+ /** Log everything */
+ Logger* logger;
+
+ /** Show the logs */
+ LogViewer* logViewer;
+
+ /** The widget, where we can set our output options */
+ Options* options;
+
+ /** The list view with all files (for conversion) */
+ FileList* fileList;
+
+ /** Displays the current progress */
+ ProgressIndicator* progressIndicator;
+
+ /** The CD manager */
+ CDManager* cdManager;
+
+ /** The tag engine */
+ TagEngine* tagEngine;
+
+ /** The conversion engine */
+ Convert* convert;
+
+ /** The Replay Gain tool */
+ ReplayGainScanner* replayGainScanner;
+
+ /** The cuesheet editor */
+ CuesheetEditor* cuesheetEditor;
+
+ /** The combobutton for adding files */
+ ComboButton* cAdd;
+
+ /** The button to start the conversion */
+ KPushButton* pStart;
+
+ /** The button to stop the conversion */
+ KPushButton* pStop;
+
+ KAction* startAction;
+ KAction* stopAction;
+ KAction* continueAction;
+ KAction* killAction;
+ KActionMenu* stopActionMenu;
+ KToggleAction* showToolBarAction;
+// KToggleAction* veryHighPriorityAction;
+// KToggleAction* highPriorityAction;
+// KToggleAction* normalPriorityAction;
+// KToggleAction* lowPriorityAction;
+// KToggleAction* veryLowPriorityAction;
+// KActionMenu* priorityActionMenu;
+
+ int configStartPage;
+
+private slots:
+ /**
+ * The conversion has started
+ */
+ void startedConversion();
+
+ /**
+ * The user clicked on stop/continue conversion
+ */
+ void stopClicked();
+ void continueClicked();
+
+ /**
+ * The conversion has stopped
+ */
+ void stoppedConversion();
+// void priorityChanged();
+
+ /**
+ * The combobutton for adding files was clicked and the item @p index was selected
+ */
+ void addClicked( int index );
+
+ void showReplayGainScanner();
+ void showRepairTool();
+ void showCuesheetEditor();
+ void showLogViewer();
+ void showConfigDialog();
+ void showConfigPluginsPage();
+ void showConfigEnvironmentPage();
+// void showAboutPlugins();
+ void showToolbar();
+ void editToolbar();
+ void newToolbarConfig();
+ void fileCountChanged( int );
+ void setTitle( const QString& );
+
+ //void openCuesheetEditor( const QString& content );
+
+public slots:
+ void showFileDialog();
+ void showDirDialog();
+ void showCdDialog( bool intern = true );
+ void showUrlDialog();
+
+signals:
+ //void windowMoved( int x, int y );
+// void setPriority( int );
+// void addFilesToReplayGainScanner( QStringList );
+};
+
+#endif // SOUNDKONVERTER_H
diff --git a/src/soundkonverterapp.cpp b/src/soundkonverterapp.cpp
new file mode 100755
index 0000000..91ea877
--- /dev/null
+++ b/src/soundkonverterapp.cpp
@@ -0,0 +1,133 @@
+
+#include "soundkonverterapp.h"
+#include "soundkonverter.h"
+
+#include <qstringlist.h>
+#include <qfile.h>
+#include <qmovie.h>
+
+#include <kglobal.h>
+#include <kstartupinfo.h>
+#include <kcmdlineargs.h>
+#include <dcopclient.h>
+#include <ksystemtray.h>
+#include <kstandarddirs.h>
+
+soundKonverterApp::soundKonverterApp()
+ : KUniqueApplication()
+{}
+
+soundKonverterApp::~soundKonverterApp()
+{}
+
+int soundKonverterApp::newInstance()
+{
+ // register ourselves as a dcop client
+ if( !dcopClient()->isRegistered() )
+ dcopClient()->registerAs( name(), false );
+
+ // see if we are starting with session management
+ if( restoringSession() )
+ {
+ RESTORE( soundKonverter );
+ }
+ else
+ {
+ // no session.. just start up normally
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ if( !mainWidget() )
+ {
+ soundKonverter *widget = new soundKonverter();
+ setMainWidget(widget);
+ //widget->show();
+ }
+ else
+ KStartupInfo::setNewStartupId( mainWidget(), kapp->startupId());
+
+ soundKonverter *widget = ::qt_cast<soundKonverter*>( mainWidget() );
+
+ widget->increaseInstances();
+
+ QCString notify = args->getOption( "command" );
+ if( notify ) {
+ widget->setNotify( notify );
+ }
+
+ QCString profile = args->getOption( "profile" );
+ if( profile ) {
+ widget->profile = profile;
+ }
+
+ QCString format = args->getOption( "format" );
+ if( format ) {
+ widget->format = format;
+ }
+
+ QCString directory = args->getOption( "output" );
+ if( directory ) {
+ widget->directory = directory;
+ }
+
+ QCString device = args->getOption( "rip" );
+ if( device ) {
+ if( !args->isSet( "invisible" ) ) {
+ widget->visible = true;
+ widget->show();
+ widget->systemTray->hide();
+ widget->systemTray->setPixmap( 0 );
+ }
+ widget->device = device;
+ widget->showCdDialog( false );
+ }
+
+ widget->autoclose = args->isSet( "autoclose" );
+
+ if( args->isSet( "invisible" ) ) {
+ widget->visible = false;
+ widget->autoclose = true;
+ widget->hide();
+ widget->systemTray->show();
+ KStandardDirs* stdDirs = new KStandardDirs();
+ widget->systemTray->setMovie( QMovie(stdDirs->findResource("data","soundkonverter/pics/systray.mng")) );
+ delete stdDirs;
+ }
+ else {
+ widget->visible = true;
+ widget->show();
+ widget->systemTray->hide();
+ widget->systemTray->setPixmap( 0 );
+ }
+
+ // add the files to the file lists depending on the used switch
+ if( args->isSet( "replaygain" ) ) {
+ QStringList replayGainFiles;
+ for( int i = 0; i < args->count(); i++ ) {
+ replayGainFiles.append(KURL::encode_string(args->arg(i)));
+ }
+ if(!replayGainFiles.isEmpty())
+ widget->openArgReplayGainFiles(replayGainFiles);
+ }
+// else if( args->isSet( "repair" ) ) {
+// QStringList repairFiles;
+// for( int i = 0; i < args->count(); i++ ) {
+// repairFiles.append(QFile::decodeName(args->arg(i)));
+// }
+// if(!repairFiles.isEmpty())
+// widget->openArgRepairFiles(repairFiles);
+// }
+ else {
+ QStringList files;
+ for( int i = 0; i < args->count(); i++ )
+ {
+ files.append(KURL::encode_string(args->arg(i)));
+ }
+ if(!files.isEmpty())
+ widget->openArgFiles(files);
+ }
+
+ args->clear();
+ }
+ return 0;
+}
+
diff --git a/src/soundkonverterapp.h b/src/soundkonverterapp.h
new file mode 100755
index 0000000..77e5b9f
--- /dev/null
+++ b/src/soundkonverterapp.h
@@ -0,0 +1,37 @@
+
+
+#ifndef SOUNDKONVERTERAPP_H
+#define SOUNDKONVERTERAPP_H
+
+#include "dcopinterface.h"
+
+#include <kuniqueapplication.h>
+
+class soundKonverter;
+
+/**
+ * @short The soundKonverter application. It controlles ensures that there can only run one instance of soundKonverter.
+ * @author Daniel Faust <[email protected]>
+ * @version 0.3
+ */
+class soundKonverterApp : public KUniqueApplication
+{
+ Q_OBJECT
+public:
+ /**
+ * Constructor.
+ */
+ soundKonverterApp();
+
+ /**
+ * Destructor
+ */
+ virtual ~soundKonverterApp();
+
+ /**
+ * This function is called, when a new instance of soundKonverter should be created.
+ */
+ virtual int newInstance();
+};
+
+#endif // SOUNDKONVERTERAPP_H
diff --git a/src/soundkonverterui.rc b/src/soundkonverterui.rc
new file mode 100755
index 0000000..cfda25d
--- /dev/null
+++ b/src/soundkonverterui.rc
@@ -0,0 +1,40 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="soundKonverter" version="2">
+ <MenuBar>
+ <Menu name="file">
+ <Action name="add_files" />
+ <Action name="add_folder" />
+ <Action name="add_audiocd" />
+ <Action name="add_url" />
+ <Separator />
+ <Action name="load" />
+ <Action name="save" />
+ </Menu>
+ <Menu name="conversion"><Text>&amp;Conversion</Text>
+ <Action name="start" />
+ <Action name="stopMenu" />
+<!-- <Action name="priorityMenu" /> -->
+ <Action name="log" />
+ </Menu>
+ <Menu name="tools">
+ <Action name="replaygainscanner" />
+ <Action name="cuesheeteditor" />
+ </Menu>
+ <!--<Menu name="help">
+ <Action name="about_plugins" />
+ </Menu>-->
+ </MenuBar>
+ <ToolBar name="mainToolBar" hidden="true"><Text>Main Toolbar</Text>
+ <Action name="add_files" />
+ <Action name="add_folder" />
+ <Action name="add_audiocd" />
+ <Action name="add_url" />
+ <Separator lineSeparator="true" />
+ <Action name="start" />
+ <Action name="stopMenu" />
+<!-- <Action name="priorityMenu" /> -->
+ <Separator />
+ <Action name="replaygainscanner" />
+ <Action name="cuesheeteditor" />
+ </ToolBar>
+</kpartgui>
diff --git a/src/userscript.sh b/src/userscript.sh
new file mode 100755
index 0000000..3f7714b
--- /dev/null
+++ b/src/userscript.sh
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+COVERNAMES="cover.jpg cover.png front.jpg front.png"
+
+for COVERNAME in $COVERNAMES; do
+
+ S_FILENAME=`echo "$1" | sed "s/^.*\///"`
+ S_DIRECTORY=`echo "$1" | sed "s/$S_FILENAME//"`
+ SOURCE=`ls "$S_DIRECTORY" | grep -F $COVERNAME`
+
+ D_FILENAME=`echo "$2" | sed "s/^.*\///"`
+ D_DIRECTORY=`echo "$2" | sed "s/$D_FILENAME//"`
+ DESTINATION="$COVERNAME"
+
+ if [ $SOURCE ]; then
+# echo "cp $S_DIRECTORY$SOURCE $D_DIRECTORY$DESTINATION"
+ cp --update "$S_DIRECTORY$SOURCE" "$D_DIRECTORY$DESTINATION"
+ fi
+
+done
+
+exit 0